Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2018-08-31 19:05:22 -04:00
commit 672b4affe1
53 changed files with 1750 additions and 1515 deletions

View File

@ -50,4 +50,4 @@ ZM_PATH_SWAP=@ZM_TMPDIR@
# Full path to optional arp binary
# ZoneMinder will find the arp binary automatically on most systems
ZM_PATH_ARP=@ZM_PATH_ARP@
ZM_PATH_ARP="@ZM_PATH_ARP@"

View File

@ -582,7 +582,7 @@ DROP TABLE IF EXISTS `Stats`;
CREATE TABLE `Stats` (
`MonitorId` int(10) unsigned NOT NULL default '0',
`ZoneId` int(10) unsigned NOT NULL default '0',
`EventId` int(10) unsigned NOT NULL default '0',
`EventId` BIGINT UNSIGNED NOT NULL,
`FrameId` int(10) unsigned NOT NULL default '0',
`PixelDiff` tinyint(3) unsigned NOT NULL default '0',
`AlarmPixels` int(10) unsigned NOT NULL default '0',
@ -781,6 +781,7 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
--
-- Add some monitor preset values
--
@ -822,6 +823,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);

View File

@ -5,7 +5,7 @@
-- Add Refresh column to Monitors table
--
ALTER TABLE `zm`.`Monitors`
ALTER TABLE `Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(

2
db/zm_update-1.31.46.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE Stats MODIFY COLUMN EventId bigint unsigned NOT NULL;

2
db/zm_update-1.31.47.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL;

View File

@ -37,7 +37,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl
, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl
, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libjson-maybexs-perl
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl

View File

@ -26,7 +26,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.31.45
Version: 1.31.47
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons

View File

@ -50,6 +50,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdevice-serialport-perl
,libimage-info-perl
,libjson-any-perl
,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl

View File

@ -32,7 +32,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl,
libsys-mmap-perl, liburi-encode-perl, libwww-perl
Description: Perl libraries for ZoneMinder
ZoneMinder is a video camera security and surveillance solution.

View File

@ -45,7 +45,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl,
libsys-mmap-perl, liburi-encode-perl, libwww-perl
Description: Perl libraries for ZoneMinder
ZoneMinder is a video camera security and surveillance solution.

View File

@ -53,6 +53,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdevice-serialport-perl
,libimage-info-perl
,libjson-any-perl
,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl

View File

@ -149,7 +149,7 @@ BEGIN {
foreach my $str ( <$CONFIG> ) {
next if ( $str =~ /^\s*$/ );
next if ( $str =~ /^\s*#/ );
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/;
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/;
if ( ! $name ) {
print( STDERR "Warning, bad line in $config_file: $str\n" );
next;

View File

@ -3882,6 +3882,15 @@ our @options = (
readonly => 1,
category => 'dynamic',
},
{
name => 'ZM_SHOW_PRIVACY',
default => 'yes',
description => 'Present the privacy statment',
help => '',
type => $types{boolean},
readonly => 1,
category => 'dynamic',
},
{
name => 'ZM_SSMTP_MAIL',
default => 'no',

View File

@ -74,39 +74,36 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub open
{
sub open {
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'open';
}
sub printMsg
{
sub printMsg {
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
Debug($msg.'['.$msg_len.']');
}
sub sendCmd
{
sub sendCmd {
my $self = shift;
my $cmd = shift;
my $msg = shift;
my $content_type = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
printMsg($cmd, 'Tx');
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/$cmd";
my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd;
my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => $content_type);
$req->header('Host' => $self->{Monitor}->{ControlAddress});
@ -122,14 +119,13 @@ sub sendCmd
} else {
Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'");
}
return( $result );
return $result;
}
sub getCamParams
{
sub getCamParams {
my $self = shift;
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging';
my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $self->{Monitor}->{ControlAddress});
@ -150,22 +146,19 @@ sub getCamParams
if ( $content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/ ) {
$CamParams{$1} = $2;
}
}
else
{
} else {
Error("Unable to retrieve camera image settings:'".$res->status_line()."'");
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
sub autoStop {
my $self = shift;
my $autostop = shift;
if ( $autostop ) {
Debug( "Auto Stop" );
Debug('Auto Stop');
my $cmd = 'onvif/PTZ';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
@ -175,20 +168,18 @@ sub autoStop
}
# Reset the Camera
sub reset
{
Debug( "Camera Reset" );
sub reset {
Debug('Camera Reset');
my $self = shift;
my $cmd = "";
my $cmd = '';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd($cmd, $msg, $content_type);
}
#Up Arrow
sub moveConUp
{
Debug( "Move Up" );
sub moveConUp {
Debug('Move Up');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -198,9 +189,8 @@ sub moveConUp
}
#Down Arrow
sub moveConDown
{
Debug( "Move Down" );
sub moveConDown {
Debug('Move Down');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -210,9 +200,8 @@ sub moveConDown
}
#Left Arrow
sub moveConLeft
{
Debug( "Move Left" );
sub moveConLeft {
Debug('Move Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -222,9 +211,8 @@ sub moveConLeft
}
#Right Arrow
sub moveConRight
{
Debug( "Move Right" );
sub moveConRight {
Debug('Move Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -234,9 +222,8 @@ sub moveConRight
}
#Zoom In
sub zoomConTele
{
Debug( "Zoom Tele" );
sub zoomConTele {
Debug('Zoom Tele');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -246,9 +233,8 @@ sub zoomConTele
}
#Zoom Out
sub zoomConWide
{
Debug( "Zoom Wide" );
sub zoomConWide {
Debug('Zoom Wide');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -259,9 +245,8 @@ sub zoomConWide
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
Debug( "Move Diagonally Up Right" );
sub moveConUpRight {
Debug('Move Diagonally Up Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -272,9 +257,8 @@ sub moveConUpRight
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
Debug( "Move Diagonally Down Right" );
sub moveConDownRight {
Debug('Move Diagonally Down Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -285,9 +269,8 @@ sub moveConDownRight
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
Debug( "Move Diagonally Up Left" );
sub moveConUpLeft {
Debug('Move Diagonally Up Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -298,9 +281,8 @@ sub moveConUpLeft
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
Debug( "Move Diagonally Down Left" );
sub moveConDownLeft {
Debug('Move Diagonally Down Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -310,9 +292,8 @@ sub moveConDownLeft
}
#Stop
sub moveStop
{
Debug( "Move Stop" );
sub moveStop {
Debug('Move Stop');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
@ -321,8 +302,7 @@ sub moveStop
}
#Set Camera Preset
sub presetSet
{
sub presetSet {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
@ -334,8 +314,7 @@ sub presetSet
}
#Recall Camera Preset
sub presetGoto
{
sub presetGoto {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
@ -348,45 +327,42 @@ sub presetGoto
#Horizontal Patrol
#To be determined if this camera supports this feature
sub horizontalPatrol
{
Debug( "Horizontal Patrol" );
sub horizontalPatrol {
Debug('Horizontal Patrol');
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
Error('PTZ Command not implemented in control script.');
}
#Horizontal Patrol Stop
#To be determined if this camera supports this feature
sub horizontalPatrolStop
{
Debug( "Horizontal Patrol Stop" );
sub horizontalPatrolStop {
Debug('Horizontal Patrol Stop');
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
Error('PTZ Command not implemented in control script.');
}
# Increase Brightness
sub irisAbsOpen
{
Debug( "Iris $CamParams{'Brightness'}" );
sub irisAbsOpen {
Debug("Iris $CamParams{Brightness}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Brightness'});
$self->getCamParams() unless($CamParams{Brightness});
my $step = $self->getParam($params, 'step');
my $max = 100;
$CamParams{'Brightness'} += $step;
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
$CamParams{Brightness} += $step;
$CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
@ -394,57 +370,55 @@ sub irisAbsOpen
# Decrease Brightness
sub irisAbsClose
{
Debug( "Iris $CamParams{'Brightness'}" );
Debug( "Iris $CamParams{Brightness}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
$self->getCamParams() unless($CamParams{brightness});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Brightness'} -= $step;
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
$CamParams{Brightness} -= $step;
$CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Increase Contrast
sub whiteAbsIn
{
Debug( "Iris $CamParams{'Contrast'}" );
sub whiteAbsIn {
Debug("Iris $CamParams{Contrast}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
$self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Contrast'} += $step;
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
$CamParams{Contrast} += $step;
$CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
# Decrease Contrast
sub whiteAbsOut
{
Debug( "Iris $CamParams{'Contrast'}" );
sub whiteAbsOut {
Debug("Iris $CamParams{Contrast}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
$self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam($params, 'step');
my $min = 0;
$CamParams{'Contrast'} -= $step;
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
$CamParams{Contrast} -= $step;
$CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
1;
__END__

View File

@ -1,504 +0,0 @@
# =========================================================================
#
# ZoneMinder Trendnet TV-IP862IC IP Control Protocol Module, $Date: $, $Revision: $
# Copyright (C) 2014 Vincent Giovannone
#
#
# ==========================================================================
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
#
# This module contains the implementation of the Trendnet TV-IP672PI IP camera control
# protocol. Also works or TV-IP862IC
#
# For Zoneminder 1.26+
#
# Under control capability:
#
# * Main: name it (suggest TVIP672PI), type is FFMPEG (or remote if you're using MJPEG), protocol is TVIP672PI
# * Main (more): Can wake, can sleep, can reset
# * Move: Can move, can move diagonally, can move mapped, can move relative
# * Pan: Can pan
# * Tilt: Can tilt
# * Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.)
#
# Under control tab in the monitor itself:
#
# * Controllable
# * Control type is the name you gave it in control capability above
# * Control device is the password you use to authenticate to the camera (see further below if you need to change the username from "admin")
# * Control address is the camera's ip address AND web port. example: 192.168.1.1:80
#
#
# If using with anything but a TV-IP672PI (ex: TV-IP672WI), YOU MUST MATCH THE REALM TO MATCH YOUR CAMERA FURTHER DOWN!
#
#
# Due to how the TVIP672 represents presets internally, you MUST define the presets in order... i.e. 1,2,3,4... not 1,10,3,4.
# (see much further down for why, if you care...)
#
package ZoneMinder::Control::TVIP862;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
#
# ******** YOU MUST CHANGE THE FOLLOWING LINES TO MATCH YOUR CAMERA! **********
#
# I assume that "TV-IP672WI" would work for the TV-IP672WI, but can't test since I don't own one.
#
# TV-IP672PI works for the PI version, of course.
#
# Finally, the username is the username you'd like to authenticate as.
#
our $REALM = 'TV-IP862IC';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
# ==========================================================================
#
# Trendnet TV-IP672PI Control Protocol
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
sub open
{
my $self = shift;
$self->loadMonitor();
my ( $protocol, $username, $password, $address )
= $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/;
if ( $username ) {
$USERNAME = $username;
$PASSWORD = $password;
$ADDRESS = $address;
} else {
Error( "Failed to parse auth from address");
$ADDRESS = $self->{Monitor}->{ControlAddress};
}
if ( ! $ADDRESS =~ /:/ ) {
Error( "You generally need to also specify the port. I will append :80" );
$ADDRESS .= ':80';
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug ( "sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
# Detect REALM
my $req = HTTP::Request->new( GET=>"http://".$ADDRESS."/cgi/ptdc.cgi" );
my $res = $self->{ua}->request($req);
if ( ! $res->is_success ) {
Debug("Need newer REALM");
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
if ( $$headers{'www-authenticate'} ) {
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ( $tokens =~ /\w+="([^"]+)"/i ) {
$REALM = $1;
Debug( "Changing REALM to $REALM" );
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
} # end if
} else {
Debug("No headers line");
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
} # end if ! $res->is_success
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
# This routine is used for all moving, which are all GET commands...
my $self = shift;
my $cmd = shift;
my $result = undef;
my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd;
my $req = HTTP::Request->new( GET=>$url );
Debug ("sendCmd command: " . $url );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
}
}
return( $result );
}
sub sendCmdPost
{
#
# This routine is used for setting/clearing presets and IR commands, which are POST commands...
#
my $self = shift;
my $url = shift;
my $cmd = shift;
my $result = undef;
if ($url eq undef)
{
Error ("url passed to sendCmdPost is undefined.");
return(-1);
}
Debug ("sendCmdPost url: " . $url . " cmd: " . $cmd);
my $req = HTTP::Request->new(POST => "http://".$ADDRESS.$url);
$req->content_type('application/x-www-form-urlencoded');
$req->content($cmd);
Debug ( "sendCmdPost credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "sendCmdPost Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} else {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} # endif
}
return( $result );
}
sub move
{
my $self = shift;
my $panSteps = shift;
my $tiltSteps = shift;
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
$self->sendCmd( $cmd );
}
sub moveRelUpLeft
{
my $self = shift;
Debug( "Move Up Left" );
$self->move(-3, 3);
}
sub moveRelUp
{
my $self = shift;
Debug( "Move Up" );
$self->move(0, 3);
}
sub moveRelUpRight
{
my $self = shift;
Debug( "Move Up Right" );
$self->move(3, 3);
}
sub moveRelLeft
{
my $self = shift;
Debug( "Move Left" );
$self->move(-3, 0);
}
sub moveRelRight
{
my $self = shift;
Debug( "Move Right" );
$self->move(3, 0);
}
sub moveRelDownLeft
{
my $self = shift;
Debug( "Move Down Left" );
$self->move(-3, -3);
}
sub moveRelDown
{
my $self = shift;
Debug( "Move Down" );
$self->move(0, -3);
}
sub moveRelDownRight
{
my $self = shift;
Debug( "Move Down Right" );
$self->move(3, -3);
}
# moves the camera to center on the point that the user clicked on in the video image.
# This isn't mega accurate but good enough for most purposes
sub moveMap
{
# If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!)
# They scale the movement and are here to compensate for manufacturing variation.
# It's never going to be perfect, so just get somewhere in the ballpark and call it a day.
# (Don't forget to kill the zmcontrol process while tweaking!)
# 1280x800
my $hscale = 31;
my $vscale = 25;
# 1280x800 with fisheye
#my $hscale = 15;
#my $vscale = 15;
# 640x400
#my $hscale = 14;
#my $vscale = 12;
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale;
my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale;
$hor = int($hor);
$ver = -1 * int($ver);
Debug( "Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver" );
$self->move( $hor, $ver );
}
# **** PRESETS ****
#
# OK, presets work a little funky but they DO work, provided you define them in order and don't skip any.
#
# The problem is that when you load the web page for this camera, it gives a list of preset names tied to index numbers.
# So let's say you have four presets... A, B, C, and D, and defined them in that order.
# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell the camera to go to a preset, you actually tell it by number, not by name.
# (So "Go to D" is really "go to index 3".)
#
# Now let's say somebody deletes C via the camera's web GUI. The camera re-numbers the existing presets A=0, B=1, D=2.
# There's really no easy way for ZM to discover this re-numbering, so zoneminder would still send "go to preset 3" thinking
# it's telling the camera to go to point D. In actuality it's telling the camera to go to a preset that no longer exists.
#
# As long as you define your presets in order (i.e. define preset 1, then preset 2, then preset 3, etc.) everything will work just
# fine in ZoneMinder.
#
# (Home preset needs to be set via the camera's web gui, and is unaffected by any of this.)
#
# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only through!) ZM AND DON'T SKIP ANY.
#
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "presetName=$preset&command=del";
my $url = "/eng/admin/cam_control.cgi";
Debug ("presetClear: " . $preset . " cmd: " . $cmd);
$self->sendCmdPost($url,$cmd);
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "presetName=$preset&command=add";
my $url = "/eng/admin/cam_control.cgi";
Debug ("presetSet " . $preset . " cmd: " . $cmd);
$self->sendCmdPost ($url,$cmd);
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
$preset = $preset - 1;
Debug( "Goto Preset $preset" );
my $cmd = "goto_preset_position&index=$preset";
$self->sendCmd( $cmd );
}
sub presetHome
{
my $self = shift;
Debug( "Home Preset" );
my $cmd = "go_home";
$self->sendCmd( $cmd );
}
#
# **** IR CONTROLS ****
#
#
# Wake: Force IR on, always. (always night mode)
#
# Sleep: Force IR off, always. (always day mode)
#
# Reset: Automatic IR mode. (day/night mode determined by camera)
#
sub wake
{
# force IR on ("always night mode")
my $self = shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=3";
Debug("Wake -- IR on");
$self->sendCmdPost ($url,$cmd);
}
sub sleep
{
# force IR off ("always day mode")
my $self=shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=2";
Debug("Sleep -- IR off");
$self->sendCmdPost ($url,$cmd);
}
sub reset
{
# IR auto
my $self=shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=0";
Debug("Reset -- IR auto");
$self->sendCmdPost ($url,$cmd);
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for Trendnet TVIP672
=head1 SYNOPSIS
use ZoneMinder::Database;
stuff this in /usr/share/perl5/ZoneMinder/Control , then eat a sandwich
=head1 DESCRIPTION
Stub documentation for Trendnet TVIP672, created by Vince.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Read the comments at the beginning of this file to see the usage for zoneminder 1.25.0
=head1 AUTHOR
Vincent Giovannone, I'd rather you not email me.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2014 by Vincent Giovannone
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -0,0 +1,431 @@
package ZoneMinder::Control::Trendnet;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
# You do not need to change the REALM, but you can get slightly faster response
# by setting so that the first auth request succeeds.
#
# The username and password should be passed in the ControlAddress field but you
# can set them here if you want.
#
our $REALM = '';
our $PROTOCOL = 'http://';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
sub open {
my $self = shift;
$self->loadMonitor();
if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?<PROTOCOL>https?:\/\/)?(?<USERNAME>[^:@]+)?:?(?<PASSWORD>[^\/@]+)?@?(?<ADDRESS>.*)$/ ) ) {
$PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL};
$USERNAME = $+{USERNAME} if $+{USERNAME};
$PASSWORD = $+{PASSWORD} if $+{PASSWORD};
$ADDRESS = $+{ADDRESS} if $+{ADDRESS};
} else {
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
$ADDRESS = $self->{Monitor}->{ControlAddress};
}
if ( !($ADDRESS =~ /:/) ) {
Error('You generally need to also specify the port. I will append :80');
$ADDRESS .= ':80';
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent('ZoneMinder Control Agent/'.$ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'closed';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug ( "sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
# Detect REALM
my $res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi');
if ( $res->is_success ) {
$self->{state} = 'open';
return;
}
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
}
if ( $$headers{'www-authenticate'} ) {
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ( $tokens =~ /\w+="([^"]+)"/i ) {
if ( $REALM ne $1 ) {
$REALM = $1;
Debug("Changing REALM to $REALM");
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
$res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi');
if ( $res->is_success() ) {
$self->{state} = 'open';
return;
}
Error('Authentication still failed after updating REALM' . $res->status_line);
$headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
} else {
Error('Authentication failed, not a REALM problem');
}
} else {
Error('Failed to match realm in tokens');
} # end if
} else {
Debug('No headers line');
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
} # end sub open
sub printMsg {
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug($msg.'['.$msg_len.']');
}
sub sendCmd {
# This routine is used for all moving, which are all GET commands...
my $self = shift;
my $cmd = shift;
my $url = $PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi?command='.$cmd;
my $res = $self->{ua}->get($url);
Debug('sendCmd command: ' . $url);
if ( $res->is_success ) {
return !undef;
}
Error("Error check failed: '".$res->status_line()."' cmd:'".$cmd."'");
return;
}
sub sendCmdPost {
#
# This routine is used for setting/clearing presets and IR commands, which are POST commands...
#
my $self = shift;
my $url = shift;
my $form = shift;
my $result = undef;
if ( $url eq undef ) {
Error('url passed to sendCmdPost is undefined.');
return -1;
}
#Debug('sendCmdPost url: ' . $url . ' cmd: ' . $cmd);
my $res;
$res = $self->{ua}->post(
$PROTOCOL.$ADDRESS.$url,
Referer=>$PROTOCOL.$ADDRESS.$url,
Content=>$form
);
Debug("sendCmdPost credentials control to: $PROTOCOL$ADDRESS$url realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
if ( $res->is_success ) {
return !undef;
}
Error("sendCmdPost Error check failed: '".$res->status_line()."' cmd:");
return $result;
} # end sub sendCmdPost
sub move {
my $self = shift;
my $panSteps = shift;
my $tiltSteps = shift;
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
$self->sendCmd($cmd);
}
sub moveRelUpLeft {
my $self = shift;
Debug('Move Up Left');
$self->move(-3, 3);
}
sub moveRelUp {
my $self = shift;
Debug('Move Up');
$self->move(0, 3);
}
sub moveRelUpRight {
my $self = shift;
Debug('Move Up Right');
$self->move(3, 3);
}
sub moveRelLeft {
my $self = shift;
Debug('Move Left');
$self->move(-3, 0);
}
sub moveRelRight {
my $self = shift;
Debug('Move Right');
$self->move(3, 0);
}
sub moveRelDownLeft {
my $self = shift;
Debug('Move Down Left');
$self->move(-3, -3);
}
sub moveRelDown {
my $self = shift;
Debug('Move Down');
$self->move(0, -3);
}
sub moveRelDownRight {
my $self = shift;
Debug('Move Down Right');
$self->move(3, -3);
}
# moves the camera to center on the point that the user clicked on in the video image.
# This isn't mega accurate but good enough for most purposes
sub moveMap {
# If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!)
# They scale the movement and are here to compensate for manufacturing variation.
# It's never going to be perfect, so just get somewhere in the ballpark and call it a day.
# (Don't forget to kill the zmcontrol process while tweaking!)
# 1280x800
my $hscale = 31;
my $vscale = 25;
# 1280x800 with fisheye
#my $hscale = 15;
#my $vscale = 15;
# 640x400
#my $hscale = 14;
#my $vscale = 12;
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale;
my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale;
$hor = int($hor);
$ver = -1 * int($ver);
Debug("Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver");
$self->move($hor, $ver);
}
# **** PRESETS ****
#
# OK, presets work a little funky but they DO work, provided you define them
# in order and don't skip any.
#
# The problem is that when you load the web page for this camera, it gives a
# list of preset names tied to index numbers.
# So let's say you have four presets... A, B, C, and D, and defined them in
# that order.
# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell
# the camera to go to a preset, you actually tell it by number, not by name.
# (So "Go to D" is really "go to index 3".)
#
# Now let's say somebody deletes C via the camera's web GUI. The camera
# re-numbers the existing presets A=0, B=1, D=2.
# There's really no easy way for ZM to discover this re-numbering, so
# zoneminder would still send "go to preset 3" thinking
# it's telling the camera to go to point D. In actuality it's telling the
# camera to go to a preset that no longer exists.
#
# As long as you define your presets in order (i.e. define preset 1, then
# preset 2, then preset 3, etc.) everything will work just
# fine in ZoneMinder.
#
# (Home preset needs to be set via the camera's web gui, and is unaffected by
# any of this.)
#
# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only
# through!) ZM AND DON'T SKIP ANY.
#
sub presetClear {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
my $cmd = "presetName=$preset&command=del";
my $url = '/eng/admin/cam_control.cgi';
Debug('presetClear: ' . $preset . ' cmd: ' . $cmd);
$self->sendCmdPost($url,{presetName=>$preset, command=>'del'});
}
sub presetSet {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
my $cmd = "presetName=$preset&command=add";
my $url = '/eng/admin/cam_control.cgi';
Debug('presetSet ' . $preset . ' cmd: ' . $cmd);
$self->sendCmdPost($url,{presetName=>$preset, command=>'add', Submit=>'Add'});
}
sub presetGoto {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
$preset = $preset - 1;
Debug("Goto Preset $preset");
my $cmd = "goto_preset_position&index=$preset";
$self->sendCmd($cmd);
}
sub presetHome {
my $self = shift;
Debug('Home Preset');
my $cmd = 'go_home';
$self->sendCmd($cmd);
}
#
# **** IR CONTROLS ****
#
#
# Wake: Force IR on, always. (always night mode)
#
# Sleep: Force IR off, always. (always day mode)
#
# Reset: Automatic IR mode. (day/night mode determined by camera)
#
sub wake {
# force IR on ("always night mode")
my $self = shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=3';
Debug('Wake -- IR on');
$self->sendCmdPost($url,$cmd);
}
sub sleep {
# force IR off ("always day mode")
my $self = shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=2';
Debug('Sleep -- IR off');
$self->sendCmdPost($url,$cmd);
}
sub reset {
# IR auto
my $self=shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=0';
Debug('Reset -- IR auto');
$self->sendCmdPost($url,$cmd);
}
1;
__END__
=head1 NAME
ZoneMinder::Control::Trendnet - Perl module for Trendnet cameras
=head1 SYNOPSIS
use ZoneMinder::Control::Trendnet;
place this in /usr/share/perl5/ZoneMinder/Control
=head1 DESCRIPTION
This module contains the implementation of the Trendnet # IP camera control
protocol. Has been tested with TV-IP862IC
Under control capability:
* Main: Can wake, can sleep, can reset
* Move: Can move, can move diagonally, can move mapped, can move relative
* Pan: Can pan
* Tilt: Can tilt
* Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.)
Under control tab in the monitor itself:
Controllable
Control type is the name you gave it in control capability above
Control address is the camera's ip address AND web port. example: 192.168.1.1:80
You can also put the authentication information here and change the
protocol to https using something like https://admin:password@192.168.1.1:80
=head2 EXPORT
None by default.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2018 ZoneMinder LLC
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=cut

View File

@ -184,16 +184,19 @@ sub zmDbGetMonitor {
my $id = shift;
return( undef ) if ( !defined($id) );
if ( !defined($id) ) {
croak("Undefined id in zmDbgetMonitor");
return undef ;
}
my $sql = "select * from Monitors where Id = ?";
my $sql = 'SELECT * FROM Monitors WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or croak("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($id)
or croak("Can't execute '$sql': ".$sth->errstr());
my $monitor = $sth->fetchrow_hashref();
return( $monitor );
return $monitor;
}
sub zmDbGetMonitorAndControl {

View File

@ -527,6 +527,7 @@ sub logPrint {
my $this = shift;
my $level = shift;
my $string = shift;
my ($caller, undef, $line) = @_ ? @_ : caller;
if ( $level <= $this->{effectiveLevel} ) {
$string =~ s/[\r\n]+$//g;
@ -537,12 +538,14 @@ sub logPrint {
my ($seconds, $microseconds) = gettimeofday();
if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) {
my $message = sprintf(
'%s.%06d %s[%d].%s [%s]'
'%s.%06d %s[%d].%s [%s:%d] [%s]'
, strftime('%x %H:%M:%S', localtime($seconds))
, $microseconds
, $this->{id}
, $$
, $codes{$level}
, $caller
, $line
, $string
);
if ( $this->{trace} ) {
@ -660,39 +663,39 @@ sub Dump {
sub debug {
my $log = shift;
$log->logPrint(DEBUG, @_);
$log->logPrint(DEBUG, @_, caller);
}
sub Debug( @ ) {
fetch()->logPrint(DEBUG, @_);
fetch()->logPrint(DEBUG, @_, caller);
}
sub Info( @ ) {
fetch()->logPrint(INFO, @_);
fetch()->logPrint(INFO, @_, caller);
}
sub info {
my $log = shift;
$log->logPrint(INFO, @_);
$log->logPrint(INFO, @_, caller);
}
sub Warning( @ ) {
fetch()->logPrint(WARNING, @_);
fetch()->logPrint(WARNING, @_, caller);
}
sub warn {
my $log = shift;
$log->logPrint(WARNING, @_);
$log->logPrint(WARNING, @_, caller);
}
sub Error( @ ) {
fetch()->logPrint(ERROR, @_);
fetch()->logPrint(ERROR, @_, caller);
}
sub error {
my $log = shift;
$log->logPrint(ERROR, @_);
$log->logPrint(ERROR, @_, caller);
}
sub Fatal( @ ) {
fetch()->logPrint(FATAL, @_);
fetch()->logPrint(FATAL, @_, caller);
if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) {
$SIG{TERM}();
}
@ -700,7 +703,7 @@ sub Fatal( @ ) {
}
sub Panic( @ ) {
fetch()->logPrint(PANIC, @_);
fetch()->logPrint(PANIC, @_, caller);
confess($_[0]);
}

View File

@ -21,32 +21,6 @@
#
# ==========================================================================
=head1 NAME
zmcontrol.pl - ZoneMinder control script
=head1 SYNOPSIS
zmcontrol.pl --id {monitor_id} --command={command} [various options]
=head1 DESCRIPTION
FIXME FIXME
=head1 OPTIONS
--autostop -
--xcoord [ arg ] - X-coord
--ycoord [ arg ] - Y-coord
--speed [ arg ] - Speed
--step [ arg ] -
--panspeed [ arg ] -
--panstep [ arg ] -
--tiltspeed [ arg ] -
--tiltstep [ arg ] -
--preset [ arg ] -
=cut
use strict;
@EXTRA_PERL_LIB@
@ -58,7 +32,7 @@ use Socket;
#use Data::Dumper;
use Module::Load::Conditional qw{can_load};;
use constant MAX_CONNECT_DELAY => 10;
use constant MAX_CONNECT_DELAY => 15;
use constant MAX_COMMAND_WAIT => 1800;
$| = 1;
@ -89,15 +63,14 @@ GetOptions(
'autostop' =>\$options{autostop},
) or pod2usage(-exitstatus => -1);
if ( !$id || !$options{command} )
{
if ( !$id || !$options{command} ) {
print( STDERR "Please give a valid monitor id and command\n" );
pod2usage(-exitstatus => -1);
}
( $id ) = $id =~ /^(\w+)$/;
Debug( $arg_string );
Debug("zmcontrol: arg string: $arg_string");
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
@ -106,33 +79,28 @@ socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
my $saddr = sockaddr_un($sock_file);
my $server_up = connect(CLIENT, $saddr);
if ( !$server_up )
{
if ( !$server_up ) {
# The server isn't there
my $monitor = zmDbGetMonitorAndControl($id);
if ( !$monitor )
{
if ( !$monitor ) {
Fatal("Unable to load control data for monitor $id");
}
my $protocol = $monitor->{Protocol};
if ( -x $protocol )
{
if ( -x $protocol ) {
# Protocol is actually a script!
# Holdover from previous versions
my $command .= $protocol.' '.$arg_string;
Debug( $command."\n" );
Debug($command);
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() )
{
if ( $status || logDebugging() ) {
chomp($output);
Debug( "Output: $output\n" );
Debug("Output: $output");
}
if ( $status )
{
Error( "Command '$command' exited with status: $status\n" );
if ( $status ) {
Error("Command '$command' exited with status: $status");
exit($status);
}
exit(0);
@ -142,26 +110,22 @@ if ( !$server_up )
close(CLIENT);
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol");
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
}
if ( my $cpid = fork() )
{
if ( my $cpid = fork() ) {
logReinit();
# Parent process just sleep and fall through
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
or die("Can't open socket: $!");
my $attempts = 0;
while (!connect( CLIENT, $saddr ))
{
while ( !connect(CLIENT, $saddr) ) {
$attempts++;
Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY);
Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY;
sleep(1);
}
}
elsif ( defined($cpid) )
{
} elsif ( defined($cpid) ) {
close(STDOUT);
close(STDERR);
@ -192,17 +156,14 @@ if ( !$server_up )
my $win = $rin;
my $ein = $win;
my $timeout = MAX_COMMAND_WAIT;
while( 1 )
{
while( 1 ) {
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
if ( $nfound > 0 )
{
if ( vec( $rout, fileno(SERVER), 1 ) )
{
if ( $nfound > 0 ) {
if ( vec( $rout, fileno(SERVER), 1 ) ) {
my $paddr = accept(CLIENT, SERVER);
my $message = <CLIENT>;
next if ( !$message );
next if !$message;
my $params = jsonDecode($message);
#Debug( Dumper( $params ) );
@ -213,41 +174,28 @@ if ( !$server_up )
last;
}
$control->$command($params);
} else {
Fatal('Bogus descriptor');
}
else
{
Fatal( "Bogus descriptor" );
}
}
elsif ( $nfound < 0 )
{
if ( $! == EPIPE )
{
} elsif ( $nfound < 0 ) {
if ( $! == EPIPE ) {
Error("Can't select: $!");
}
else
{
} else {
Fatal("Can't select: $!");
}
}
else
{
} else {
#print( "Select timed out\n" );
last;
}
}
Info( "Control server $id/$protocol exiting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
} # end while forever
Info("Control server $id/$protocol exiting");
unlink($sock_file);
$control->close();
exit(0);
}
else
{
} else {
Fatal("Can't fork: $!");
}
}
} # end if !server up
# The server is there, connect to it
#print( "Writing commands\n" );
@ -258,3 +206,33 @@ print( CLIENT $message );
shutdown(CLIENT, 1);
exit(0);
1;
__END__
=head1 NAME
zmcontrol.pl - ZoneMinder control script
=head1 SYNOPSIS
zmcontrol.pl --id {monitor_id} --command={command} [various options]
=head1 DESCRIPTION
FIXME FIXME
=head1 OPTIONS
--autostop -
--xcoord [ arg ] - X-coord
--ycoord [ arg ] - Y-coord
--speed [ arg ] - Speed
--step [ arg ] -
--panspeed [ arg ] -
--panstep [ arg ] -
--tiltspeed [ arg ] -
--tiltstep [ arg ] -
--preset [ arg ] -
=cut

View File

@ -274,6 +274,8 @@ sub run {
."\n"
);
# We don't want to leave killall zombies, so ignore SIGCHLD
$SIG{CHLD} = 'IGNORE';
# Tell any existing processes to die, wait 1 second between TERM and KILL
killAll(1);

View File

@ -922,6 +922,11 @@ if ( $version ) {
zmDbDisconnect();
die( "Can't find upgrade from version '$version'" );
}
# Re-enable the privacy popup after each upgrade
my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
}
zmDbDisconnect();

View File

@ -55,7 +55,6 @@ protected:
bool record_audio;
unsigned int bytes;
int mVideoStreamId;
int mAudioStreamId;
AVCodecContext *mVideoCodecContext;
@ -79,37 +78,37 @@ public:
);
virtual ~Camera();
unsigned int getId() const { return( monitor_id ); }
unsigned int getId() const { return monitor_id; }
Monitor *getMonitor();
void setMonitor( Monitor *p_monitor );
SourceType Type() const { return( type ); }
bool IsLocal() const { return( type == LOCAL_SRC ); }
bool IsRemote() const { return( type == REMOTE_SRC ); }
bool IsFile() const { return( type == FILE_SRC ); }
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
bool IscURL() const { return( type == CURL_SRC ); }
unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); }
SourceType Type() const { return type; }
bool IsLocal() const { return type == LOCAL_SRC; }
bool IsRemote() const { return type == REMOTE_SRC; }
bool IsFile() const { return type == FILE_SRC; }
bool IsFfmpeg() const { return type == FFMPEG_SRC; }
bool IsLibvlc() const { return type == LIBVLC_SRC; }
bool IscURL() const { return type == CURL_SRC; }
unsigned int Width() const { return width; }
unsigned int Height() const { return height; }
unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); }
unsigned int SubpixelOrder() const { return subpixelorder; }
unsigned int Pixels() const { return pixels; }
unsigned int ImageSize() const { return imagesize; }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; }
virtual int Hue( int/*p_hue*/=-1 ) { return -1; }
virtual int Colour( int/*p_colour*/=-1 ) { return -1; }
virtual int Contrast( int/*p_contrast*/=-1 ) { return -1; }
bool CanCapture() const { return( capture ); }
bool CanCapture() const { return capture; }
bool SupportsNativeVideo() const {
return (type == FFMPEG_SRC);
//return (type == FFMPEG_SRC )||(type == REMOTE_SRC);
}
virtual int PrimeCapture() { return( 0 ); }
virtual int PrimeCapture() { return 0; }
virtual int PreCapture() = 0;
virtual int Capture(ZMPacket &p) = 0;
virtual int PostCapture() = 0;

View File

@ -124,9 +124,9 @@ void process_configfile( char* configFile) {
if ( *line_ptr == '\0' || *line_ptr == '#' )
continue;
// Remove trailing white space
// Remove trailing white space and trailing quotes
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) {
while ( *temp_ptr == ' ' || *temp_ptr == '\t' || *temp_ptr == '\'' || *temp_ptr == '\"') {
*temp_ptr-- = '\0';
temp_ptr--;
}
@ -148,8 +148,9 @@ void process_configfile( char* configFile) {
temp_ptr--;
} while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
// Remove leading white space from the value part
// Remove leading white space and leading quotes from the value part
white_len = strspn( val_ptr, " \t" );
white_len += strspn( val_ptr, "\'\"" );
val_ptr += white_len;
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )

View File

@ -146,10 +146,9 @@ Event::Event(
errno = 0;
if ( mkdir(path.c_str(), 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
}
if ( i == 2 )
strncpy(date_path, path.c_str(), sizeof(date_path));
else if ( i >= 3 )

View File

@ -657,10 +657,8 @@ void Image::Assign( const Image &image ) {
(*fptr_imgbufcpy)(buffer, image.buffer, size);
}
Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits )
{
if ( colours != ZM_COLOUR_GRAY8 )
{
Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) {
if ( colours != ZM_COLOUR_GRAY8 ) {
Panic("Attempt to highlight image edges when colours = %d", colours);
}

View File

@ -162,12 +162,12 @@ public:
static void Initialise();
static void Deinitialise();
inline unsigned int Width() const { return( width ); }
inline unsigned int Height() const { return( height ); }
inline unsigned int Pixels() const { return( pixels ); }
inline unsigned int Colours() const { return( colours ); }
inline unsigned int SubpixelOrder() const { return( subpixelorder ); }
inline unsigned int Size() const { return( size ); }
inline unsigned int Width() const { return width; }
inline unsigned int Height() const { return height; }
inline unsigned int Pixels() const { return pixels; }
inline unsigned int Colours() const { return colours; }
inline unsigned int SubpixelOrder() const { return subpixelorder; }
inline unsigned int Size() const { return size; }
inline unsigned int AVPixFormat() {
if ( colours == ZM_COLOUR_RGB32 ) {
@ -187,8 +187,8 @@ public:
}
/* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return( buffer ); }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); }
inline const uint8_t* Buffer() const { return buffer; }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[colours*((y*width)+x)]; }
/* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
// Is only acceptable on a pre-allocated buffer
@ -218,7 +218,7 @@ public:
}
inline Image &operator=( const unsigned char *new_buffer ) {
(*fptr_imgbufcpy)(buffer, new_buffer, size);
return( *this );
return *this;
}
bool ReadRaw( const char *filename );

View File

@ -505,7 +505,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
va_start(argPtr, fstring);
if ( hex ) {
unsigned char *data = va_arg(argPtr, unsigned char *);
int len = va_arg( argPtr, int32_t );
int len = va_arg(argPtr, int);
int i;
logPtr += snprintf(logPtr, sizeof(logString)-(logPtr-logString), "%d:", len);
for ( i = 0; i < len; i++ ) {
@ -538,10 +538,13 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
db_mutex.lock();
char escapedString[(strlen(syslogStart)*2)+1];
mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart));
char sql[ZM_SQL_MED_BUFSIZ];
snprintf(sql, sizeof(sql),
"insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
"INSERT INTO Logs "
"( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line )"
" VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line
);
if ( mysql_query(&dbconn, sql) ) {
Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel(NOLOG);

View File

@ -559,6 +559,13 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
// Should maybe store this for later use
char monitor_dir[PATH_MAX] = "";
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
if ( purpose == CAPTURE ) {
if ( mkdir(monitor_dir, 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
}
}
}
//this0>delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),

View File

@ -484,7 +484,7 @@ void MonitorStream::runStream() {
swap_path = staticConfig.PATH_SWAP;
Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
if ( checkSwapPath(swap_path.c_str(), false) ) {
if ( checkSwapPath(swap_path.c_str(), true) ) {
swap_path += stringtf("/zmswap-m%d", monitor->Id());
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());

View File

@ -267,7 +267,20 @@ bool StreamBase::sendTextFrame( const char *frame_text ) {
void StreamBase::openComms() {
if ( connkey > 0 ) {
unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey);
// Have to mkdir because systemd is now chrooting and the dir may not exist
if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir ZM_PATH_SOCKS %s: %s.", staticConfig.PATH_SOCKS.c_str(), strerror(errno));
}
}
unsigned int length = snprintf(
sock_path_lock,
sizeof(sock_path_lock),
"%s/zms-%06d.lock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated.");
}
@ -292,7 +305,13 @@ void StreamBase::openComms() {
Debug(1, "Have socket %d", sd);
}
length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
length = snprintf(
loc_sock_path,
sizeof(loc_sock_path),
"%s/zms-%06ds.sock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1;

View File

@ -236,7 +236,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
if ( pixel_diff_count && alarm_pixels )
pixel_diff = pixel_diff_count/alarm_pixels;
Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d",
alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
if ( alarm_pixels ) {
if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
@ -252,7 +254,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
if (max_alarm_pixels != 0)
score = (100*alarm_pixels)/max_alarm_pixels;
else
score = (100*alarm_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);
@ -312,7 +318,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
Debug(5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
Debug(5, "Got %d filtered pixels, need %d -> %d",
alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
if ( alarm_filter_pixels ) {
if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
@ -328,7 +335,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
score = (100*alarm_filter_pixels)/(polygon.Area());
if (max_filter_pixels != 0)
score = (100*alarm_filter_pixels)/max_filter_pixels;
else
score = (100*alarm_filter_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);
@ -438,7 +449,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
alarm_blobs--;
Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs);
Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs",
bss->tag, bsm->tag, x, y, alarm_blobs);
// Clear out the old blob
bss->tag = 0;
@ -476,7 +488,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
BlobStats *bs = &blob_stats[i];
// See if we can recycle one first, only if it's at least two rows up
if ( bs->count && bs->hi_y < (int)(y-1) ) {
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) {
if (
(min_blob_pixels && bs->count < min_blob_pixels)
||
(max_blob_pixels && bs->count > max_blob_pixels)
) {
if ( config.create_analysis_images || config.record_diag_images ) {
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
@ -590,7 +606,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
score = (100*alarm_blob_pixels)/(polygon.Area());
if (max_blob_pixels != 0)
score = (100*alarm_blob_pixels)/(max_blob_pixels);
else
score = (100*alarm_blob_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);

View File

@ -1 +1 @@
1.31.45
1.31.47

View File

@ -59,7 +59,7 @@ switch ( $_REQUEST['task'] ) {
$sortField = $_REQUEST['sortField'];
}
}
$sortOrder = (isset($_REQUEST['sortOrder']) and $_REQUEST['sortOrder']) == 'asc' ? 'asc':'desc';
$sortOrder = (isset($_REQUEST['sortOrder']) and ($_REQUEST['sortOrder'] == 'asc')) ? 'asc' : 'desc';
$filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array();
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
@ -93,7 +93,10 @@ switch ( $_REQUEST['task'] ) {
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
$logs = array();
foreach ( dbFetchAll($sql, NULL, $values) as $log ) {
$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
#Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']);
#$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
foreach( $filterFields as $field ) {
@ -347,7 +350,7 @@ switch ( $_REQUEST['task'] ) {
</logexport>' );
break;
}
$exportExt = "xml";
$exportExt = 'xml';
break;
}
fclose( $exportFP );
@ -363,10 +366,10 @@ switch ( $_REQUEST['task'] ) {
ajaxError('Insufficient permissions to download logs');
if ( empty($_REQUEST['key']) )
Fatal( "No log export key given" );
Fatal('No log export key given');
$exportKey = $_REQUEST['key'];
if ( empty($_REQUEST['format']) )
Fatal( "No log export format given" );
Fatal('No log export format given');
$format = $_REQUEST['format'];
switch( $format ) {
@ -389,15 +392,15 @@ switch ( $_REQUEST['task'] ) {
$exportFile = "zm-log.$exportExt";
$exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt";
header( "Pragma: public" );
header( "Expires: 0" );
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
header( "Cache-Control: private", false ); // required by certain browsers
header( "Content-Description: File Transfer" );
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false ); // required by certain browsers
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename="'.$exportFile.'"' );
header( "Content-Transfer-Encoding: binary" );
header( "Content-Type: application/force-download" );
header( "Content-Length: ".filesize($exportPath) );
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($exportPath));
readfile($exportPath);
exit(0);
break;

View File

@ -10,6 +10,9 @@ if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) {
ajaxError("Unexpected received message type '$type'");
}
if (!mkdir(ZM_PATH_SOCKS) ) {
}
# The file that we point ftok to has to exist, and only exist if zms is running, so we are pointing it at the .sock
$key = ftok(ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock', 'Z');
$semaphore = sem_get($key,1);

@ -1 +1 @@
Subproject commit c3976f1478c681b0bbc132ec3a3e82c3984eeed5
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef

View File

@ -210,11 +210,13 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
} else if ( $action == 'eventdetail' ) {
if ( !empty($_REQUEST['eid']) ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid']) );
} else {
$dbConn->beginTransaction();
foreach( getAffectedIds('markEid') as $markEid ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid) );
}
$dbConn->commit();
}
@ -265,7 +267,9 @@ if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
$zmuOutput = exec($zmuCommand);
list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput);
dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid));
dbQuery(
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
array($brightness, $contrast, $hue, $colour, $mid));
}
}
@ -282,8 +286,8 @@ if ( canEdit('Control') ) {
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markCids']) ) {
foreach( $_REQUEST['markCids'] as $markCid ) {
dbQuery( 'delete from Controls where Id = ?', array($markCid) );
dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) );
dbQuery('DELETE FROM Controls WHERE Id = ?', array($markCid));
dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($markCid));
$refreshParent = true;
}
}
@ -310,7 +314,6 @@ if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
$Monitor->zmaControl('start');
}
}
} // end foreach mid
$refreshParent = true;
} // end if action == save
@ -328,11 +331,12 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$oldFunction = $monitor['Function'];
$oldEnabled = $monitor['Enabled'];
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) );
dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?',
array($newFunction, $newEnabled, $mid));
$monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled;
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl($monitor, 'stop');
zmcControl($monitor, $restart?'restart':'');
@ -373,16 +377,16 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} else {
dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid));
}
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl($monitor, 'stop');
zmcControl($monitor, 'restart');
zmaControl($monitor, 'start');
} else {
zmaControl( $mid, 'restart' );
zmaControl($monitor, 'restart');
}
}
if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) {
if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor['Controllable'] ) {
require_once('control_functions.php');
sendControlCommand($mid, 'quit');
}
@ -401,19 +405,19 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
}
}
if ( $changes > 0 ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
zmaControl($mid, 'restart');
}
$refreshParent = true;
}
$view = 'none';
} elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) {
} elseif ( ($action == 'sequence') && isset($_REQUEST['smid']) ) {
$smid = validInt($_REQUEST['smid']);
$monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) );
$smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid));
$smonitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($smid));
dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) );
dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) );
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($smonitor['Sequence'], $monitor['Id']));
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($monitor['Sequence'], $smonitor['Id']));
$refreshParent = true;
fixSequences();
@ -421,8 +425,8 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
if ( isset($_REQUEST['markZids']) ) {
$deletedZid = 0;
foreach ( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) );
dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) );
$zone = dbFetchOne('SELECT * FROM Zones WHERE Id=?', NULL, array($markZid));
dbQuery('DELETE FROM Zones WHERE MonitorId=? AND Id=?', array($mid, $markZid));
$deletedZid = 1;
}
if ( $deletedZid ) {
@ -476,11 +480,12 @@ if ( canEdit( 'Monitors' ) ) {
);
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] );
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne(
'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId'] );
if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
Logger::Debug("Auto selecting server to " . ZM_SERVER_ID);
Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID);
}
}
@ -537,12 +542,14 @@ if ( canEdit( 'Monitors' ) ) {
$changes = getFormChanges($zone, $newZone, $types);
if ( count($changes) ) {
dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) );
}
}
dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
array($mid, $zone['Id']));
}
} // end foreach zone
} // end if width and height
$restart = true;
} else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors
} else if ( ! $user['MonitorIds'] ) {
// Can only create new monitors if we are not restricted to specific monitors
# FIXME This is actually a race condition. Should lock the table.
$maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
$changes[] = 'Sequence = '.($maxSeq+1);
@ -550,7 +557,7 @@ if ( canEdit( 'Monitors' ) ) {
if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) {
$mid = dbInsertId();
$zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height'];
dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
//$view = 'none';
$Storage = new Storage($_REQUEST['newMonitor']['StorageId']);
mkdir($Storage->Path().'/'.$mid, 0755);
@ -558,17 +565,17 @@ if ( canEdit( 'Monitors' ) ) {
symlink($mid, $Storage->Path().'/'.$saferName);
} else {
Error("Error saving new Monitor.");
Error('Error saving new Monitor.');
return;
}
} else {
Error("Users with Monitors restrictions cannot create new monitors.");
Error('Users with Monitors restrictions cannot create new monitors.');
return;
}
$restart = true;
} else {
Logger::Debug("No action due to no changes to Monitor");
Logger::Debug('No action due to no changes to Monitor');
} # end if count(changes)
if (
@ -593,17 +600,17 @@ if ( canEdit( 'Monitors' ) ) {
if ( count($x10Changes) ) {
if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) {
dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) );
dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid));
} elseif ( !$user['MonitorIds'] ) {
if ( !$x10Monitor ) {
dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) );
dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid));
} else {
dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) );
dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid));
}
}
$restart = true;
}
}
} # end if has x10Changes
} # end if ZM_OPT_X10
if ( $restart ) {
@ -647,9 +654,11 @@ if ( canEdit( 'Devices' ) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) ) {
dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
dbQuery('UPDATE Devices SET Name=?, KeyString=? WHERE Id=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
} else {
dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) );
dbQuery('INSERT INTO Devices SET Name=?, KeyString=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString']) );
}
$refreshParent = true;
$view = 'none';
@ -657,7 +666,7 @@ if ( canEdit( 'Devices' ) ) {
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markDids']) ) {
foreach( $_REQUEST['markDids'] as $markDid ) {
dbQuery( 'delete from Devices where Id=?', array($markDid) );
dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid));
$refreshParent = true;
}
}
@ -665,7 +674,7 @@ if ( canEdit( 'Devices' ) ) {
} // end if canedit devices
// Group view actions
if ( canView( 'Groups' ) && $action == 'setgroup' ) {
if ( canView('Groups') && ($action == 'setgroup') ) {
if ( !empty($_REQUEST['gid']) ) {
setcookie('zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10);
} else {
@ -675,19 +684,31 @@ if ( canView( 'Groups' ) && $action == 'setgroup' ) {
}
// Group edit actions
# Should probably verify that each monitor id is a valid monitor, that we have access to. However at the moment, you have to have System permissions to do this
# Should probably verify that each monitor id is a valid monitor, that we have access to.
# However at the moment, you have to have System permissions to do this
if ( canEdit('Groups') ) {
if ( $action == 'group' ) {
$monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$group_id = null;
if ( !empty($_POST['gid']) ) {
$group_id = $_POST['gid'];
dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) );
dbQuery(
'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
$group_id,
)
);
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id));
} else {
dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) );
dbQuery(
'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
)
);
$group_id = dbInsertId();
}
if ( $group_id ) {
@ -738,19 +759,24 @@ if ( canEdit( 'System' ) ) {
} else if ( $_REQUEST['object'] == 'server' ) {
if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) )
$dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) );
else
if ( !empty($_REQUEST['id']) ) {
$dbServer = dbFetchOne(
'SELECT * FROM Servers WHERE Id=?',
NULL,
array($_REQUEST['id']) );
} else {
$dbServer = array();
}
$types = array();
$changes = getFormChanges($dbServer, $_REQUEST['newServer'], $types);
if ( count($changes) ) {
if ( !empty($_REQUEST['id']) ) {
dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
dbQuery('UPDATE Servers SET '.implode(', ', $changes).' WHERE Id = ?',
array($_REQUEST['id']) );
} else {
dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Servers SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -804,7 +830,7 @@ if ( canEdit( 'System' ) ) {
}
case 'ignore' :
{
dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" );
dbQuery("UPDATE Config SET Value = '".ZM_DYN_LAST_VERSION."' WHERE Name = 'ZM_DYN_CURR_VERSION'");
break;
}
case 'hour' :
@ -819,12 +845,12 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
break;
}
case 'never' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_CHECK_FOR_UPDATES'");
break;
}
}
@ -852,17 +878,38 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
break;
}
case 'never' :
case 'already' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
break;
}
} // end switch option
}
if ( ($action == 'privacy') && isset($_REQUEST['option']) ) {
switch( $_REQUEST['option'] ) {
case 'decline' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
case 'accept' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
default: # Enable the privacy statement if we somehow submit something other than accept or decline
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_SHOW_PRIVACY'");
} // end switch option
return;
}
if ( $action == 'options' && isset($_REQUEST['tab']) ) {
$configCat = $configCats[$_REQUEST['tab']];
$changed = false;
@ -902,9 +949,10 @@ if ( canEdit( 'System' ) ) {
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab'];
}
loadConfig(false);
return;
} elseif ( $action == 'user' ) {
if ( !empty($_REQUEST['uid']) )
$dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) );
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
else
$dbUser = array();
@ -918,12 +966,12 @@ if ( canEdit( 'System' ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['uid']) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) );
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid']));
# If we are updating the logged in user, then update our session user data.
if ( $user and ( $dbUser['Username'] == $user['Username'] ) )
userLogin($dbUser['Username'], $dbUser['Password']);
} else {
dbQuery( 'insert into Users set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -938,22 +986,21 @@ if ( canEdit( 'System' ) ) {
if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) {
$sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id';
$definitions = array();
foreach( dbFetchAll( $sql ) as $monitor )
{
foreach( dbFetchAll($sql) as $monitor ) {
$definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled'];
}
$definition = join(',', $definitions);
if ( $_REQUEST['newState'] )
$_REQUEST['runState'] = $_REQUEST['newState'];
dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) );
dbQuery('REPLACE INTO States SET Name=?, Definition=?', array($_REQUEST['runState'],$definition));
}
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['runState']) )
dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) );
dbQuery('DELETE FROM States WHERE Name=?', array($_REQUEST['runState']));
if ( isset($_REQUEST['markUids']) ) {
foreach( $_REQUEST['markUids'] as $markUid )
dbQuery( 'delete from Users where Id = ?', array($markUid) );
dbQuery('DELETE FROM Users WHERE Id = ?', array($markUid));
if ( $markUid == $user['Id'] )
userLogout();
}
@ -972,7 +1019,7 @@ if ( canEdit( 'System' ) ) {
else
unset($changes['Password']);
if ( count($changes) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) );
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
$refreshParent = true;
}
$view = 'none';

View File

@ -212,7 +212,7 @@ function process_configfile($configFile) {
continue;
elseif ( preg_match( '/^\s*#/', $str ))
continue;
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches ))
$configvals[$matches[1]] = $matches[2];
}
fclose( $cfg );

View File

@ -139,9 +139,9 @@ function dbQuery( $sql, $params=NULL ) {
}
if ( defined('ZM_DB_DEBUG') ) {
if ( $params )
Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() );
Logger::Debug("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() );
else
Warning("SQL: $sql: rows:" . $result->rowCount() );
Logger::Debug("SQL: $sql: rows:" . $result->rowCount() );
}
} catch(PDOException $e) {
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') );

View File

@ -49,8 +49,14 @@ function loadLanguage( $prefix="" )
return( false );
}
if ( $langFile = loadLanguage() )
if ( $langFile = loadLanguage() ) {
require_once( $langFile );
require_once( 'lang/default.php' );
foreach ($DLANG as $key => $value) {
if ( ! array_key_exists( $key, $SLANG ) )
$SLANG[$key] = $DLANG[$key];
}
}
//

View File

@ -406,7 +406,7 @@ class Logger {
$result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) );
} catch(PDOException $ex) {
$this->databaseLevel = self::NOLOG;
Fatal( "Can't write log entry '$sql': ". $ex->getMessage() );
Error("Can't write log entry '$sql': ". $ex->getMessage());
}
}
// This has to be last as trigger_error can be fatal

View File

@ -205,7 +205,16 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL;
isset($action) || $action = NULL;
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) {
Logger::Debug("View: $view Request: $request Action: $action");
if (
ZM_ENABLE_CSRF_MAGIC &&
( $action != 'login' ) &&
( $view != 'view_video' ) &&
( $view != 'image' ) &&
( $request != 'control' ) &&
( $view != 'frames' ) &&
( $view != 'archive' )
) {
require_once( 'includes/csrf/csrf-magic.php' );
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();
@ -216,12 +225,15 @@ require_once( 'includes/actions.php' );
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
if ( ZM_OPT_USE_AUTH and !isset($user) ) {
Logger::Debug("Redirecting to login" );
Logger::Debug('Redirecting to login');
$view = 'login';
$request = null;
} else if ( ZM_SHOW_PRIVACY && ($action != 'privacy') && ($view !='options') && (!$request) && canEdit('System') ) {
Logger::Debug('Redirecting to privacy');
$view = 'privacy';
$request = null;
}
if ( $redirect ) {
header('Location: '.$redirect);
return;

44
web/lang/default.php Normal file
View File

@ -0,0 +1,44 @@
<?php
//
// ZoneMinder DEFAULT English language file, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// The translate function will return the literal name of the key if the key
// name does not exist in the language file.
// This file provides a means to return a result, other than the key name, if
// it does not exist in the language file.
// Simple String Replacements
$DLANG = array(
'Privacy' => 'Privacy',
'PrivacyAbout' => 'About',
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
'PrivacyContact' => 'Contact',
'PrivacyContactText' => 'Please contact us <a href="https://zoneminder.com/contact/">here</a> for any questions regarding our privacy policy or to have your information removed.<br><br>For support, there are three primary ways to engage with the community:<ul><li>The ZoneMinder <a href="https://forums.zoneminder.com/">user forum</a></li><li>The ZoneMinder <a href="https://zoneminder-chat.herokuapp.com/">Slack channel</a></li><li>The ZoneMinder <a href="https://github.com/ZoneMinder/zoneminder/issues">Github forum</a></li></ul><p>Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.</p>',
'PrivacyCookies' => 'Cookies',
'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.',
'PrivacyTelemetry' => 'Telemetry',
'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.',
'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:<ul><li>A unique identifier (UUID) <li>City based location is gathered by querying <a href="https://ipinfo.io/geo">ipinfo.io</a>. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!<li>Current time<li>Total number of monitors<li>Total number of events<li>System architecture<li>Operating system kernel, distro, and distro version<li>Version of ZoneMinder<li>Total amount of memory<li>Number of cpu cores</ul>',
'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:<ul><li>Id<li>Name<li>Type<li>Function<li>Width<li>Height<li>Colours<li>MaxFPS<li>AlarmMaxFPS</ul>',
'PrivacyConclusionText' => 'We are <u>NOT</u> collecting any image specific data from your cameras. We dont know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.',
);
?>

View File

@ -601,6 +601,18 @@ $SLANG = array(
'Preset' => 'Preset',
'Presets' => 'Presets',
'Prev' => 'Prev',
'Privacy' => 'Privacy',
'PrivacyAbout' => 'About',
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
'PrivacyContact' => 'Contact',
'PrivacyContactText' => 'Please contact us <a href="https://zoneminder.com/contact/">here</a> for any questions regarding our privacy policy or to have your information removed.<br><br>For support, there are three primary ways to engage with the community:<ul><li>The ZoneMinder <a href="https://forums.zoneminder.com/">user forum</a></li><li>The ZoneMinder <a href="https://zoneminder-chat.herokuapp.com/">Slack channel</a></li><li>The ZoneMinder <a href="https://github.com/ZoneMinder/zoneminder/issues">Github forum</a></li></ul><p>Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.</p>',
'PrivacyCookies' => 'Cookies',
'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.',
'PrivacyTelemetry' => 'Telemetry',
'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.',
'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:<ul><li>A unique identifier (UUID) <li>City based location is gathered by querying <a href="https://ipinfo.io/geo">ipinfo.io</a>. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!<li>Current time<li>Total number of monitors<li>Total number of events<li>System architecture<li>Operating system kernel, distro, and distro version<li>Version of ZoneMinder<li>Total amount of memory<li>Number of cpu cores</ul>',
'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:<ul><li>Id<li>Name<li>Type<li>Function<li>Width<li>Height<li>Colours<li>MaxFPS<li>AlarmMaxFPS</ul>',
'PrivacyConclusionText' => 'We are <u>NOT</u> collecting any image specific data from your cameras. We dont know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.',
'Probe' => 'Probe',
'ProfileProbe' => 'Stream Probe',
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>',

View File

@ -0,0 +1,14 @@
h6 {
text-align: left;
font-weight: bold;
text-decoration: underline;
}
p {
text-align: left;
}
ul {
text-align: left;
list-style-type: disc;
}

View File

@ -199,7 +199,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
console.log( "Request Failed: " + err );
// The idea is that this should only fail due to auth, so reload the page
// which should go to login if it can't stay logged in.
window.location.href = thisUrl;
window.location.href = thisUrl+'?view='+currentView;
});
}

View File

@ -195,6 +195,8 @@ var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
console.log('Got an error from getCmdResponse');
console.log(respObj);
console.log(respText);
zmsBroke = true;
return;
}
@ -205,15 +207,21 @@ function getCmdResponse( respObj, respText ) {
streamCmdTimer = clearTimeout(streamCmdTimer);
streamStatus = respObj.status;
if (streamStatus.progress >= Math.round(parseFloat(eventData.Length))) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
if ( streamStatus.progress >= Math.round(parseFloat(eventData.Length)) )
streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
var eventId = streamStatus.event;
if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax
if ( lastEventId ) {
if ( eventId != lastEventId ) {
//Doesn't run on first load, prevents a double hit on event and nearEvents ajax
eventQuery(eventId);
initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render
lastEventId = eventId;
}
if (lastEventId == 0) lastEventId = eventId; //Only fires on first load.
} else {
lastEventId = eventId; //Only fires on first load.
}
if ( streamStatus.paused == true ) {
streamPause( );
} else {
@ -239,7 +247,13 @@ function getCmdResponse( respObj, respText ) {
streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display
}
var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } );
var streamReq = new Request.JSON( {
url: thisUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onSuccess: getCmdResponse
} );
function pauseClicked() {
if ( vid ) {
@ -305,7 +319,8 @@ function streamFastFwd( action ) {
if ( vid ) {
if ( revSpeed != .5 ) stopFastRev();
vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100);
if (rates.indexOf(vid.playbackRate()*100)-1 == -1) setButtonState($('fastFwdBtn'), 'unavail');
if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 )
setButtonState($('fastFwdBtn'), 'unavail');
$j('#rateValue').html(vid.playbackRate());
} else {
streamReq.send(streamParms+"&command="+CMD_FASTFWD);
@ -369,6 +384,9 @@ function streamFastRev( action ) {
function streamPrev(action) {
if ( action ) {
$j(".vjsMessage").remove();
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
return;
if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) {
CurEventDefVideoPath = PrevEventDefVideoPath;
eventQuery(prevEventId);
@ -392,7 +410,12 @@ function streamNext(action) {
if ( vid == null ) zmsBroke = true;
return;
}
if (vid && NextEventDefVideoPath.indexOf("view_video") > 0) { //on and staying with videojs
// We used to try to dynamically update all the bits in the page, which is really complex
// How about we just reload the page?
//
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
return;
if ( vid && ( NextEventDefVideoPath.indexOf("view_video") > 0 ) ) { //on and staying with videojs
CurEventDefVideoPath = NextEventDefVideoPath;
eventQuery(nextEventId);
} else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs

View File

@ -0,0 +1,9 @@
function submitForm( element ) {
var form = element.form;
if ( form.option.selectedIndex == 0 )
form.view.value = currentView;
else
form.view.value = 'none';
form.submit();
}

View File

@ -114,6 +114,7 @@ if ( monitorType != 'WebSite' ) {
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onError: getStreamCmdError,
onSuccess: getStreamCmdResponse,
onFailure: getStreamCmdFailure
} );
@ -122,11 +123,17 @@ if ( monitorType != 'WebSite' ) {
var streamStatus;
function getStreamCmdError(text,error) {
console.log(error);
// Error are normally due to failed auth. reload the page.
window.location.reload();
}
function getStreamCmdFailure(xhr) {
console.log(xhr);
}
function getStreamCmdResponse(respObj, respText) {
watchdogOk("stream");
console.log('stream');
if ( streamCmdTimer )
streamCmdTimer = clearTimeout(streamCmdTimer);
if ( respObj.result == 'Ok' ) {
@ -148,14 +155,14 @@ function getStreamCmdResponse( respObj, respText ) {
var delayString = secsToTime(streamStatus.delay);
if ( streamStatus.paused == true ) {
$('modeValue').set( 'text', "Paused" );
$('modeValue').set('text', 'Paused');
$('rate').addClass('hidden');
$('delayValue').set('text', delayString);
$('delay').removeClass('hidden');
$('level').removeClass('hidden');
streamCmdPause(false);
} else if ( streamStatus.delayed == true ) {
$('modeValue').set( 'text', "Replay" );
$('modeValue').set('text', 'Replay');
$('rateValue').set('text', streamStatus.rate);
$('rate').removeClass('hidden');
$('delayValue').set('text', delayString);
@ -209,16 +216,19 @@ function getStreamCmdResponse( respObj, respText ) {
} // end if canEditMonitors
if ( streamStatus.auth ) {
console.log("Haev a new auth hash" + streamStatus.auth);
console.log("Have a new auth hash" + streamStatus.auth);
// Try to reload the image stream.
var streamImg = $('liveStream');
if ( streamImg )
streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
} // end if haev a new auth hash
} // end if have a new auth hash
} // end if respObj.status
} else {
checkStreamForErrors("getStreamCmdResponse", respObj);//log them
// Try to reload the image stream.
// If it's an auth error, we should reload the whole page.
window.location.reload();
if ( 0 ) {
var streamImg = $('liveStream'+monitorId);
if ( streamImg ) {
streamImg.src = streamImg.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
@ -227,6 +237,7 @@ function getStreamCmdResponse( respObj, respText ) {
console.log("Unable to find streamImg liveStream");
}
}
}
var streamCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT )
@ -429,7 +440,13 @@ function getActResponse( respObj, respText ) {
function deleteEvent( event, eventId ) {
var actParms = "view=request&request=event&action=delete&id="+eventId;
var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: 3000, data: actParms, onSuccess: getActResponse } );
var actReq = new Request.JSON( {
url: thisUrl,
method: 'post',
timeout: 3000,
data: actParms,
onSuccess: getActResponse
} );
actReq.send();
event.stop();
}

View File

@ -0,0 +1,74 @@
<?php
//
// ZoneMinder privacy view file, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canEdit( 'System' ) )
{
$view = "error";
return;
}
$options = array(
"accept" => translate('Accept'),
"decline" => translate('Decline'),
);
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('Privacy') );
?>
<body>
<div id="page">
<div id="header">
<h2><?php echo translate('Privacy') ?></h2>
<h1>ZoneMinder - <?php echo translate('Privacy') ?></h1>
</div>
<div id="content">
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="none"/>
<input type="hidden" name="action" value="privacy"/>
<h6><?php echo translate('PrivacyAbout') ?></h6>
<p><?php echo translate('PrivacyAboutText') ?></p>
<br>
<h6><?php echo translate('PrivacyContact') ?></h6>
<p><?php echo translate('PrivacyContactText') ?></p>
<br>
<h6><?php echo translate('PrivacyCookies') ?></h6>
<p><?php echo translate('PrivacyCookiesText') ?></p>
<br>
<h6><?php echo translate('PrivacyTelemetry') ?></h6>
<p><?php echo translate('PrivacyTelemetryText') ?></p>
<br>
<p><?php echo translate('PrivacyTelemetryList') ?></p>
<p><?php echo translate('PrivacyMonitorList') ?></p>
<p><?php echo translate('PrivacyConclusionText') ?></p>
<p><?php echo buildSelect( "option", $options ); ?></p>
<div id="contentButtons">
<input type="submit" value="<?php echo translate('Apply') ?>" onclick="submitForm( this )">
</div>
</form>
</div>
</div>
</body>
</html>

View File

@ -81,6 +81,7 @@ if ( empty($_REQUEST['path']) ) {
if ( !$Frame )
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid']));
if ( !$Frame ) {
Warning("No frame found for event " + $_REQUEST['eid']);
$Frame = new Frame();
$Frame->Delta(1);
$Frame->FrameId('snapshot');
@ -141,7 +142,7 @@ Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousB
if ( !file_exists($path) ) {
Logger::Debug("$path does not exist");
# Generate the frame JPG
if ( $show == 'capture' and $Event->DefaultVideo() ) {
if ( ($show == 'capture') and $Event->DefaultVideo() ) {
if ( !file_exists($Event->Path().'/'.$Event->DefaultVideo()) ) {
header('HTTP/1.0 404 Not Found');
Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() );
@ -222,8 +223,13 @@ if ( !empty($_REQUEST['height']) ) {
}
}
if ( $errorText ) {
Error($errorText);
} else {
# Clears the output buffer. Not sure what is there, but have had troubles.
ob_end_clean();
header('Content-type: image/jpeg');
if ( ( $scale==0 || $scale==100 ) && $width==0 && $height==0 ) {
# This is so that Save Image As give a useful filename
if ( $Event ) {
$filename = $Event->MonitorId().'_'.$Event->Id().'_'.$Frame->FrameId().'.jpg';
@ -231,11 +237,6 @@ if ( $Event ) {
}
ob_clean();
flush();
if ( $errorText ) {
Error($errorText);
} else {
if ( ( $scale==0 || $scale==100 ) && $width==0 && $height==0 ) {
if ( !readfile($path) ) {
Error('No bytes read from '. $path);
}
@ -261,7 +262,13 @@ if ( $errorText ) {
# Slight optimisation, thumbnails always specify width and height, so we can cache them.
$scaled_path = preg_replace('/\.jpg$/', "-${width}x${height}.jpg", $path);
if ( ! file_exists($scaled_path) or ! readfile($scaled_path) ) {
if ( $Event ) {
$filename = $Event->MonitorId().'_'.$Event->Id().'_'.$Frame->FrameId()."-${width}x${height}.jpg";
header('Content-Disposition: inline; filename="' . $filename . '"');
}
//ob_clean();
//flush();
if ( !( file_exists($scaled_path) and readfile($scaled_path) ) ) {
Logger::Debug("Cached scaled image does not exist at $scaled_path or is no good.. Creating it");
ob_start();
if ( !$i )
@ -274,6 +281,15 @@ if ( $errorText ) {
file_put_contents($scaled_path, $scaled_jpeg_data);
ob_end_clean();
echo $scaled_jpeg_data;
} else {
Logger::Debug("Sending $scaled_path");
$bytes = readfile($scaled_path);
if ( !$bytes ) {
Error('No bytes read from '. $scaled_path);
} else {
Logger::Debug("$bytes sent");
}
}
}
}
exit();

View File

@ -68,7 +68,7 @@ $end = $size-1;
$length = $size;
if ( isset($_SERVER['HTTP_RANGE']) ) {
Logger::Debug("Using Range " . $_SERVER['HTTP_RANGE'] );
Logger::Debug('Using Range ' . $_SERVER['HTTP_RANGE']);
if ( preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches) ) {
$begin = intval($matches[1]);
if ( ! empty($matches[2]) ) {