diff --git a/.travis.yml b/.travis.yml index 8ab86bf7d..7cfe96854 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,24 +33,23 @@ install: env: - SMPFLAGS=-j4 OS=el DIST=7 - - SMPFLAGS=-j4 OS=el DIST=8 - - SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=30 - - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack USE_SFTP=yes + - SMPFLAGS=-j4 OS=fedora DIST=31 + - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386 - SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386 - - SMPFLAGS=-j4 OS=ubuntu DIST=bionic ARCH=i386 - SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386 - SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386 - SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386 - - SMPFLAGS=-j4 OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=eslint DIST=eslint compiler: - gcc @@ -58,12 +57,6 @@ services: - mysql - docker -jobs: - include: - - name: eslint - install: npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5 - script: eslint --ext .php,.js . - script: - utils/packpack/startpackpack.sh diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 44eb1a048..c9b2b6ea1 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -434,6 +434,7 @@ DROP TABLE IF EXISTS `Monitors`; CREATE TABLE `Monitors` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', + `Notes` TEXT, `ServerId` int(10) unsigned, `StorageId` smallint(5) unsigned default 0, `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', diff --git a/db/zm_update-1.33.16.sql b/db/zm_update-1.33.16.sql new file mode 100644 index 000000000..87058e3f8 --- /dev/null +++ b/db/zm_update-1.33.16.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Notes' + ) > 0, + "SELECT 'Column Notes already exists in Monitors'", + "ALTER TABLE `Monitors` ADD `Notes` TEXT AFTER `Name`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.34.0.sql b/db/zm_update-1.34.0.sql new file mode 100644 index 000000000..b8bd3e4ed --- /dev/null +++ b/db/zm_update-1.34.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.33.16 database to 1.34.0 +-- +-- No changes required +-- diff --git a/distros/redhat/readme/README b/distros/redhat/readme/README index 0b3e4bb9c..f0fff828f 100644 --- a/distros/redhat/readme/README +++ b/distros/redhat/readme/README @@ -22,14 +22,10 @@ What's New switching between httpd <-> nginx requires manaully changing ownership of all event folders and the php session folder after the change. -4. If you have installed ZoneMinder from the FedBerry repositories, this build - of ZoneMinder has support for Raspberry Pi hardware acceleration when using - ffmpeg. Unforunately, there is a problem with the same hardware acceleration - when using libvlc. Consequently, libvlc support in this build of ZoneMinder - has been disabled until the problem is resolved. See the following bug - report for details: https://trac.videolan.org/vlc/ticket/18594 +4. The timezone must now be set from the ZoneMinder web console. See the + appropriate README, mentioned in the next step, for details. -5. Continue on to the next README that corresponds to the chosen webserver: +6. Continue on to the next README that corresponds to your chosen webserver: README.httpd - Follow these steps when using Apache README.nginx - Follow these steps when using Nginx diff --git a/distros/redhat/readme/README.httpd b/distros/redhat/readme/README.httpd index 5301850df..1ba0ee688 100644 --- a/distros/redhat/readme/README.httpd +++ b/distros/redhat/readme/README.httpd @@ -36,20 +36,17 @@ NOTE: EL7 users should replace "dnf" with "yum" in the instructions below. sudo chown root:apache *.conf sudo chmod 640 *.conf -4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local - timezone. PHP will complain loudly if this is not set, or if it is set - incorrectly, and these complaints will show up in the zoneminder logging - system as errors. +4. Manually setting the timezone in /etc/php.ini is deprecated. - If you are not sure of the proper timezone specification to use, look at - http://php.net/date.timezone + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this after completing step 10, below. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. 5. Disable SELinux - We currently do not have the resources to create and maintain an accurate - SELinux policy for ZoneMinder on Fedora. We will gladly accept pull - reqeusts from anyone who wishes to do the work. In the meantime, SELinux - will need to be disabled or put into permissive mode. + SELinux must be disabled or put into permissive mode. This is not optional! To immediately disbale SELinux for the current seesion, issue the following from the command line: @@ -166,3 +163,11 @@ Upgrades sudo systemctl restart httpd sudo systemctl start zoneminder +6. Manually setting the timezone in /etc/php.ini is deprecated. + + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this now. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. + diff --git a/distros/redhat/readme/README.nginx b/distros/redhat/readme/README.nginx index cca4e72c2..8bc3bbdc1 100644 --- a/distros/redhat/readme/README.nginx +++ b/distros/redhat/readme/README.nginx @@ -34,13 +34,13 @@ New installs sudo chown root:nginx *.conf sudo chmod 640 *.conf -4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local - timezone. PHP will complain loudly if this is not set, or if it is set - incorrectly, and these complaints will show up in the zoneminder logging - system as errors. +4. Manually setting the timezone in /etc/php.ini is deprecated. - If you are not sure of the proper timezone specification to use, look at - http://php.net/date.timezone + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this after completing step 10, below. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. 5. Disable SELinux @@ -169,3 +169,11 @@ Upgrades sudo systemctl restart php-fpm sudo systemctl start zoneminder +6. Manually setting the timezone in /etc/php.ini is deprecated. + + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this now. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. + diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 2773a4ad0..345139284 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -14,16 +14,21 @@ # This will tell zoneminder's cmake process we are building against a known distro %global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}} -# Fedora >= 25 needs apcu backwards compatibility module -%if 0%{?fedora} >= 25 +# Fedora needs apcu backwards compatibility module +%if 0%{?fedora} %global with_apcu_bc 1 %endif +# Newer php's keep json functions in a subpackage +%if 0%{?fedora} || 0%{?rhel} >= 8 +%global with_php_json 1 +%endif + # The default for everything but el7 these days %global _hardened_build 1 Name: zoneminder -Version: 1.33.15 +Version: 1.34.0 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -105,7 +110,7 @@ Summary: Common files for ZoneMinder, not tied to a specific web server Requires: php-mysqli Requires: php-common Requires: php-gd -%{?fedora:Requires: php-json} +%{?with_php_json:Requires: php-json} Requires: php-pecl-apcu %{?with_apcu_bc:Requires: php-pecl-apcu-bc} Requires: cambozola @@ -411,29 +416,20 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog -* Sun Dec 08 2019 Isaac Connor - 1.33.15-1 -- Bump to 1.33.15 Development +* Sat Jan 18 2020 Andrew Bauer - 1.34.0-1 +- 1.34.0 Release -* Sun Aug 11 2019 Andrew Bauer - 1.33.14-1 -- Bump to 1.33.13 Development +* Tue Dec 17 2019 Leigh Scott - 1.32.3-5 +- Mass rebuild for x264 -* Sun Jul 07 2019 Andrew Bauer - 1.33.12-1 -- Bump to 1.33.12 Development +* Wed Aug 07 2019 Leigh Scott - 1.32.3-4 +- Rebuild for new ffmpeg version -* Sun Jun 23 2019 Andrew Bauer - 1.33.9-1 -- Bump to 1.33.9 Development +* Tue Mar 12 2019 Sérgio Basto - 1.32.3-3 +- Mass rebuild for x264 -* Tue Apr 30 2019 Andrew Bauer - 1.33.8-1 -- Bump to 1.33.8 Development - -* Sun Apr 07 2019 Andrew Bauer - 1.33.6-1 -- Bump to 1.33.6 Development - -* Sat Mar 30 2019 Andrew Bauer - 1.33.4-1 -- Bump to 1.33.4 Development - -* Tue Dec 11 2018 Andrew Bauer - 1.33.0-1 -- Bump to 1.33.0 Development +* Tue Mar 05 2019 RPM Fusion Release Engineering - 1.32.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild * Sat Dec 08 2018 Andrew Bauer - 1.32.3-1 - 1.32.3 Release diff --git a/distros/ubuntu1604/conf/apache2/zoneminder.conf b/distros/ubuntu1604/conf/apache2/zoneminder.conf index 598996bc0..e3164d36c 100644 --- a/distros/ubuntu1604/conf/apache2/zoneminder.conf +++ b/distros/ubuntu1604/conf/apache2/zoneminder.conf @@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" Require all granted + # Order matters. This alias must come first. Alias /zm/cache /var/cache/zoneminder/cache diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 7e57c98a8..617b3e852 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -61,7 +61,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl - ,libwww-perl + ,libwww-perl, liburi-perl ,libdata-dump-perl ,libdatetime-perl ,libclass-std-fast-perl @@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libfile-slurp-perl ,mysql-client | mariadb-client | virtual-mysql-client ,perl-modules - ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc + ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc, php-json | php5-json ,policykit-1 ,rsyslog | system-log-daemon ,zip diff --git a/misc/logcheck b/misc/logcheck index e59948e86..820108a66 100644 --- a/misc/logcheck +++ b/misc/logcheck @@ -28,3 +28,4 @@ zmtelemetry\[[[:digit:]]+\]: INF \[Telemetry data uploaded successfully.\]$ zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$ zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$ web_php\[[[:digit:]]+\]: INF \[Login successful for user "[[:alnum:]]+"\]$ +zmeventnotification\[[[:digit:]]+\]: INF diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 31fafc1bc..cd93dfe15 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -1127,7 +1127,7 @@ our @options = ( }, { name => 'ZM_LOG_LEVEL_FILE', - default => '-5', + default => '1', description => 'Save logging output to component files', help => q` ZoneMinder logging is now more integrated between @@ -1312,7 +1312,7 @@ our @options = ( }, { name => 'ZM_LOG_DEBUG_FILE', - default => '@ZM_LOGDIR@/zm_debug.log+', + default => '', description => 'Where extra debug is output to', help => q` This option allows you to specify a different target for debug @@ -2468,7 +2468,7 @@ our @options = ( }, { name => 'ZM_WATCH_MAX_DELAY', - default => '5', + default => '45', description => 'The maximum delay allowed since the last captured image', help => q` The zmwatch daemon checks the image capture performance of the diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm index e95f86cba..3fd36a0c4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Acrest HTTP API Control Protocol Module, 20180214, Rev 3.0 +# ZoneMinder Amcrest HTTP API Control Protocol Module, 20180214, Rev 3.0 # # Change Log # @@ -8,7 +8,7 @@ # - Fixes incorrect method names # - Updates control sequences to Amcrest HTTP Protocol API v 2.12 # - Extends control features -# +# # Rev 2.0: # - Fixed installation instructions text, no changes to functionality. # @@ -38,6 +38,7 @@ use Time::HiRes qw( usleep ); require ZoneMinder::Base; require ZoneMinder::Control; +require LWP::UserAgent; our @ISA = qw(ZoneMinder::Control); @@ -50,130 +51,109 @@ our @ISA = qw(ZoneMinder::Control); use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +sub new { + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new($id); + bless($self, $class); + return $self; } -our $AUTOLOAD; +sub open { + my $self = shift; -sub AUTOLOAD -{ - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - Debug( "Received command: $name" ); - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); -} + $self->loadMonitor(); + my $username; + my $password; + my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice}; -sub open -{ - my $self = shift; + if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) { + $username = $1; + $password = $2; + $$self{address} = $3; + } - $self->loadMonitor(); + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->credentials($$self{address}, $realm, $username, $password); + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); + + # Detect REALM + my $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); + + if ( $res->is_success ) { $self->{state} = 'open'; -} + return; + } -sub initUA -{ - my $self = shift; - my $user = undef; - my $password = undef; - my $address = undef; + if ( $res->status_line() eq '401 Unauthorized' ) { - if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) - { - $user = $1; - $password = $2; - $address = $3; + my $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); } - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->credentials("$address", "Login to " . $self->{Monitor}->{ControlDevice}, "$user", "$password"); - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + 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($$self{address}, $realm, $username, $password); + $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.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' + + $self->{state} = 'open'; } -sub destroyUA -{ - my $self = shift; - - $self->{ua} = undef; +sub close { + my $self = shift; + $self->{state} = 'closed'; } -sub close -{ - my $self = shift; - $self->{state} = 'closed'; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $result = undef; + + $self->printMsg($cmd, 'Tx'); + + my $req = HTTP::Request->new( GET=>"http://$$self{address}/$cmd" ); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + # Command to camera appears successful, write Info item to log + Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. + } else { + Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + } + + return $result; } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); -} - -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - my $result = undef; - - destroyUA($self); - initUA($self); - - my $user = undef; - my $password = undef; - my $address = undef; - - if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) - { - $user = $1; - $password = $2; - $address = $3; - } - - printMsg( $cmd, "Tx" ); - - my $req = HTTP::Request->new( GET=>"http://$address/$cmd" ); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - # Command to camera appears successful, write Info item to log - Info( "Camera control: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" ); - # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. - } - else - { - Error( "Camera control command FAILED: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" ); - } - - return( $result ); -} - -sub reset -{ - my $self = shift; - # This reboots the camera effectively resetting it - Debug( "Camera Reset" ); - $self->sendCmd( 'cgi-bin/magicBox.cgi?action=reboot' ); - ##FIXME: Exit is a bad idea as it appears to cause zmc to run away. - #Exit (0); +sub reset { + my $self = shift; + # This reboots the camera effectively resetting it + $self->sendCmd('cgi-bin/magicBox.cgi?action=reboot'); } # NOTE: I'm putting this in, but absolute camera movement does not seem to be well supported in the classic skin ATM. @@ -184,163 +164,148 @@ sub reset sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here... { - my $self = shift; - my $pan_degrees = shift || 0; - my $tilt_degrees = shift || 0; - my $speed = shift || 1; - Debug( "Move ABS" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed ); + my $self = shift; + my $pan_degrees = shift || 0; + my $tilt_degrees = shift || 0; + my $speed = shift || 1; + Debug('Move ABS'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed); } -sub moveConUp -{ - my $self = shift; - Debug( "Move Up" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); ##XXX Should this be passed in as a "speed" parameter? - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUp { + my $self = shift; + Debug('Move Up'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); ##XXX Should this be passed in as a "speed" parameter? + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDown -{ - my $self = shift; - Debug( "Move Down" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDown { + my $self = shift; + Debug('Move Down'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConLeft -{ - my $self = shift; - Debug( "Move Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConLeft { + my $self = shift; + Debug('Move Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConRight -{ - my $self = shift; - Debug( "Move Right" ); -# $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - Debug( "Move Right Stop" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConRight { + my $self = shift; + Debug('Move Right'); + # $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' ); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + Debug('Move Right Stop'); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConUpRight -{ - my $self = shift; - Debug( "Move Diagonally Up Right" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUpRight { + my $self = shift; + Debug('Move Diagonally Up Right'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDownRight -{ - my $self = shift; - Debug( "Move Diagonally Down Right" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDownRight { + my $self = shift; + Debug('Move Diagonally Down Right'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConUpLeft -{ - my $self = shift; - Debug( "Move Diagonally Up Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUpLeft { + my $self = shift; + Debug('Move Diagonally Up Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDownLeft -{ - my $self = shift; - Debug( "Move Diagonally Down Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDownLeft { + my $self = shift; + Debug('Move Diagonally Down Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0'); + usleep (500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0'); } # Stop is not "correctly" implemented as control_functions.php translates this to "Center" # So we'll just send the camera to 0* Horz, 0* Vert, zoom out; Also, Amcrest does not seem to # support a generic stop-all-current-action command. -sub moveStop -{ - my $self = shift; - Debug( "Move Stop/Center" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1' ); +sub moveStop { + my $self = shift; + Debug('Move Stop/Center'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1'); } # Move Camera to Home Position # The current API does not support a Home per se, so we'll just send the camera to preset #1 # NOTE: It goes without saying that the user must have set up preset #1 for this to work. -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0' ); +sub presetHome { + my $self = shift; + Debug('Home Preset'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0'); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Go To Preset $preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Go To Preset $preset"); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0'); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug('Set Preset'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0'); } # NOTE: This does not appear to be implemented in the classic skin. But we'll leave it here for later. -sub moveMap -{ - my $self = shift; - my $params = shift; +sub moveMap { + my $self = shift; + my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 ); - my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 ); - # if the camera is mounted upside down, you may have to inverse these coordinates - # just use 360 minus pan instead of pan, 90 minus tilt instead of tilt - # Convert xcoord into pan position 0 to 359 - my $pan = int(360 * $xcoord / $self->{Monitor}{Width}); - # Convert ycoord into tilt position 0 to 89 - my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height}); - # Now get the following url: - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1'); + my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 ); + my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 ); + # if the camera is mounted upside down, you may have to inverse these coordinates + # just use 360 minus pan instead of pan, 90 minus tilt instead of tilt + # Convert xcoord into pan position 0 to 359 + my $pan = int(360 * $xcoord / $self->{Monitor}{Width}); + # Convert ycoord into tilt position 0 to 89 + my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height}); + # Now get the following url: + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1'); } -sub zoomConTele -{ - my $self = shift; - Debug( "Zoom continuous tele" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' ); - usleep (100000); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' ); +sub zoomConTele { + my $self = shift; + Debug('Zoom continuous tele'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0'); + usleep(100000); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0'); } -sub zoomConWide -{ - my $self = shift; - Debug( "Zoom continuous wide" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' ); - usleep (100000); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' ); +sub zoomConWide { + my $self = shift; + Debug('Zoom continuous wide'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0'); + usleep (100000); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0'); } 1; @@ -355,7 +320,7 @@ ZoneMinder::Control::Amcrest_HTTP - Amcrest camera control =head1 DESCRIPTION -This module contains the implementation of the Amcrest Camera +This module contains the implementation of the Amcrest Camera controllable SDK API. NOTE: This module implements interaction with the camera in clear text. diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm index d63236c86..ddb1dbe54 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Axis version 2 API Control Protocol Module, $Date$, $Revision$ +# ZoneMinder Axis version 2 API Control Protocol Module # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or @@ -43,349 +43,359 @@ use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); +use URI; -sub open -{ - my $self = shift; +our $ADDRESS; - $self->loadMonitor(); +sub open { + my $self = shift; - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + $self->loadMonitor(); + if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) { + # Has no scheme at the beginning, so won't parse as a URI + $self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress}; + } + my $uri = URI->new($self->{Monitor}->{ControlAddress}); + $ADDRESS = $uri->scheme.'://'.$uri->authority().$uri->path().($uri->port()?':'.$uri->port():''); + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->cookie_jar( {} ); + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); + $self->{state} = 'closed'; + + my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ ); + my $realm = $self->{Monitor}->{ControlDevice}; + + $self->{ua}->credentials($ADDRESS, $realm, $username, $password); + + # test auth + my $res = $self->{ua}->get($ADDRESS.'/cgi/ptdc.cgi'); + + if ( $res->is_success ) { $self->{state} = 'open'; -} + return; + } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); + if ( $res->status_line() eq '401 Unauthorized' ) { - Debug( $msg."[".$msg_len."]" ); -} - -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - - my $result = undef; - - printMsg( $cmd, "Tx" ); - - #print( "http://$address/$cmd\n" ); - my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" ); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "Error check failed: '".$res->status_line()."'" ); + my $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); } - return( $result ); + if ( $$headers{'www-authenticate'} ) { + Debug('Authenticating'); + 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($host, $realm, $username, $password); + $res = $self->{ua}->get($ADDRESS); + 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 sendCmd { + my $self = shift; + my $cmd = shift; + + $self->printMsg($cmd, 'Tx'); + + my $url = $ADDRESS.$cmd; + my $res = $self->{ua}->get($url); + + if ( $res->is_success ) { + Debug('sndCmd command: ' . $url . ' content: '.$res->content); + return !undef; + } + + Error("Error cmd $url failed: '".$res->status_line()."'"); + + return undef; } -sub cameraReset -{ - my $self = shift; - Debug( "Camera Reset" ); - my $cmd = "/axis-cgi/admin/restart.cgi"; - $self->sendCmd( $cmd ); +sub cameraReset { + my $self = shift; + Debug('Camera Reset'); + my $cmd = '/axis-cgi/admin/restart.cgi'; + $self->sendCmd($cmd); } -sub moveConUp -{ - my $self = shift; - Debug( "Move Up" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=up"; - $self->sendCmd( $cmd ); +sub moveConUp { + my $self = shift; + Debug('Move Up'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=up'; + $self->sendCmd($cmd); } -sub moveConDown -{ - my $self = shift; - Debug( "Move Down" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=down"; - $self->sendCmd( $cmd ); +sub moveConDown { + my $self = shift; + Debug('Move Down'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=down'; + $self->sendCmd($cmd); } -sub moveConLeft -{ - my $self = shift; - Debug( "Move Left" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=left"; - $self->sendCmd( $cmd ); +sub moveConLeft { + my $self = shift; + Debug('Move Left'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=left'; + $self->sendCmd($cmd); } -sub moveConRight -{ - my $self = shift; - Debug( "Move Right" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=right"; - $self->sendCmd( $cmd ); +sub moveConRight { + my $self = shift; + Debug('Move Right'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=right'; + $self->sendCmd($cmd); } -sub moveConUpRight -{ - my $self = shift; - Debug( "Move Up/Right" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=upright"; - $self->sendCmd( $cmd ); +sub moveConUpRight { + my $self = shift; + Debug('Move Up/Right'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=upright'; + $self->sendCmd($cmd); } -sub moveConUpLeft -{ - my $self = shift; - Debug( "Move Up/Left" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=upleft"; - $self->sendCmd( $cmd ); +sub moveConUpLeft { + my $self = shift; + Debug('Move Up/Left'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=upleft'; + $self->sendCmd($cmd); } -sub moveConDownRight -{ - my $self = shift; - Debug( "Move Down/Right" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=downright"; - $self->sendCmd( $cmd ); +sub moveConDownRight { + my $self = shift; + Debug('Move Down/Right'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=downright'; + $self->sendCmd( $cmd ); } -sub moveConDownLeft -{ - my $self = shift; - Debug( "Move Down/Left" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=downleft"; - $self->sendCmd( $cmd ); +sub moveConDownLeft { + my $self = shift; + Debug('Move Down/Left'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=downleft'; + $self->sendCmd($cmd); } -sub moveMap -{ - my $self = shift; - my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); - Debug( "Move Map to $xcoord,$ycoord" ); - my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}."&imageheight=".$self->{Monitor}->{Height}; - $self->sendCmd( $cmd ); +sub moveMap { + my $self = shift; + my $params = shift; + my $xcoord = $self->getParam($params, 'xcoord'); + my $ycoord = $self->getParam($params, 'ycoord'); + Debug("Move Map to $xcoord,$ycoord"); + my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}.'&imageheight='.$self->{Monitor}->{Height}; + $self->sendCmd($cmd); } -sub moveRelUp -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Up $step" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=$step"; - $self->sendCmd( $cmd ); +sub moveRelUp { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Up $step"); + my $cmd = '/axis-cgi/com/ptz.cgi?rtilt='.$step; + $self->sendCmd($cmd); } -sub moveRelDown -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Down $step" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=-$step"; - $self->sendCmd( $cmd ); +sub moveRelDown { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Down $step"); + my $cmd = '/axis-cgi/com/ptz.cgi?rtilt=-'.$step; + $self->sendCmd($cmd); } -sub moveRelLeft -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); - Debug( "Step Left $step" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$step"; - $self->sendCmd( $cmd ); +sub moveRelLeft { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'panstep'); + Debug("Step Left $step"); + my $cmd = '/axis-cgi/com/ptz.cgi?rpan=-'.$step; + $self->sendCmd($cmd); } -sub moveRelRight -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); - Debug( "Step Right $step" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$step"; - $self->sendCmd( $cmd ); +sub moveRelRight { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'panstep'); + Debug("Step Right $step"); + my $cmd = '/axis-cgi/com/ptz.cgi?rpan='.$step; + $self->sendCmd($cmd); } -sub moveRelUpRight -{ - my $self = shift; - my $params = shift; - my $panstep = $self->getParam( $params, 'panstep' ); - my $tiltstep = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Up/Right $tiltstep/$panstep" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=$tiltstep"; - $self->sendCmd( $cmd ); +sub moveRelUpRight { + my $self = shift; + my $params = shift; + my $panstep = $self->getParam($params, 'panstep'); + my $tiltstep = $self->getParam($params, 'tiltstep'); + Debug("Step Up/Right $tiltstep/$panstep"); + my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=$tiltstep"; + $self->sendCmd($cmd); } -sub moveRelUpLeft -{ - my $self = shift; - my $params = shift; - my $panstep = $self->getParam( $params, 'panstep' ); - my $tiltstep = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Up/Left $tiltstep/$panstep" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=$tiltstep"; - $self->sendCmd( $cmd ); +sub moveRelUpLeft { + my $self = shift; + my $params = shift; + my $panstep = $self->getParam($params, 'panstep'); + my $tiltstep = $self->getParam($params, 'tiltstep'); + Debug("Step Up/Left $tiltstep/$panstep"); + my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=$tiltstep"; + $self->sendCmd($cmd); } -sub moveRelDownRight -{ - my $self = shift; - my $params = shift; - my $panstep = $self->getParam( $params, 'panstep' ); - my $tiltstep = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Down/Right $tiltstep/$panstep" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=-$tiltstep"; - $self->sendCmd( $cmd ); +sub moveRelDownRight { + my $self = shift; + my $params = shift; + my $panstep = $self->getParam($params, 'panstep'); + my $tiltstep = $self->getParam($params, 'tiltstep'); + Debug("Step Down/Right $tiltstep/$panstep"); + my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=-$tiltstep"; + $self->sendCmd($cmd); } -sub moveRelDownLeft -{ - my $self = shift; - my $params = shift; - my $panstep = $self->getParam( $params, 'panstep' ); - my $tiltstep = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Down/Left $tiltstep/$panstep" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=-$tiltstep"; - $self->sendCmd( $cmd ); +sub moveRelDownLeft { + my $self = shift; + my $params = shift; + my $panstep = $self->getParam($params, 'panstep'); + my $tiltstep = $self->getParam($params, 'tiltstep'); + Debug("Step Down/Left $tiltstep/$panstep"); + my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=-$tiltstep"; + $self->sendCmd($cmd); } -sub zoomRelTele -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Zoom Tele" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step"; - $self->sendCmd( $cmd ); +sub zoomRelTele { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Zoom Tele'); + my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step"; + $self->sendCmd($cmd); } -sub zoomRelWide -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Zoom Wide" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step"; - $self->sendCmd( $cmd ); +sub zoomRelWide { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Zoom Wide'); + my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step"; + $self->sendCmd($cmd); } -sub focusRelNear -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Focus Near" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step"; - $self->sendCmd( $cmd ); +sub focusRelNear { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Focus Near'); + my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step"; + $self->sendCmd($cmd); } -sub focusRelFar -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Focus Far" ); - my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step"; - $self->sendCmd( $cmd ); +sub focusRelFar { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Focus Far'); + my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step"; + $self->sendCmd($cmd); } -sub focusAuto -{ - my $self = shift; - Debug( "Focus Auto" ); - my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=on"; - $self->sendCmd( $cmd ); +sub focusAuto { + my $self = shift; + Debug('Focus Auto'); + my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=on'; + $self->sendCmd($cmd); } -sub focusMan -{ - my $self = shift; - Debug( "Focus Manual" ); - my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off"; - $self->sendCmd( $cmd ); +sub focusMan { + my $self = shift; + Debug('Focus Manual'); + my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=off'; + $self->sendCmd($cmd); } -sub irisRelOpen -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Iris Open" ); - my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step"; - $self->sendCmd( $cmd ); +sub irisRelOpen { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Iris Open'); + my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step"; + $self->sendCmd($cmd); } -sub irisRelClose -{ - my $self = shift; - my $params = shift; - my $step = $self->getParam( $params, 'step' ); - Debug( "Iris Close" ); - my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step"; - $self->sendCmd( $cmd ); +sub irisRelClose { + my $self = shift; + my $params = shift; + my $step = $self->getParam($params, 'step'); + Debug('Iris Close'); + my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step"; + $self->sendCmd($cmd); } -sub irisAuto -{ - my $self = shift; - Debug( "Iris Auto" ); - my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=on"; - $self->sendCmd( $cmd ); +sub irisAuto { + my $self = shift; + Debug('Iris Auto'); + my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=on'; + $self->sendCmd($cmd); } -sub irisMan -{ - my $self = shift; - Debug( "Iris Manual" ); - my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=off"; - $self->sendCmd( $cmd ); +sub irisMan { + my $self = shift; + Debug('Iris Manual'); + my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=off'; + $self->sendCmd($cmd); } -sub presetClear -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); - my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; - $self->sendCmd( $cmd ); +sub presetClear { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Clear Preset $preset"); + my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; + $self->sendCmd($cmd); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "/axis-cgi/com/ptz.cgi?setserverpresetno=$preset"; - $self->sendCmd( $cmd ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Set Preset $preset"); + my $cmd = "/axis-cgi/com/ptz.cgi?setserverpresetno=$preset"; + $self->sendCmd($cmd); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "/axis-cgi/com/ptz.cgi?gotoserverpresetno=$preset"; - $self->sendCmd( $cmd ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Goto Preset $preset"); + my $cmd = "/axis-cgi/com/ptz.cgi?gotoserverpresetno=$preset"; + $self->sendCmd($cmd); } -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - my $cmd = "/axis-cgi/com/ptz.cgi?move=home"; - $self->sendCmd( $cmd ); +sub presetHome { + my $self = shift; + Debug('Home Preset'); + my $cmd = '/axis-cgi/com/ptz.cgi?move=home'; + $self->sendCmd($cmd); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm index 7175d1e84..ebbaf3a8a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module, $Date: 2008-09-13 17:30:29 +0000 (Sat, 13 Sept 2008) $, $Revision: 2229 $ +# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module # Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com) # # This program is free software; you can redistribute it and/or @@ -43,8 +43,6 @@ our @ISA = qw(ZoneMinder::Control); use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -use Time::HiRes qw( usleep ); - sub open { my $self = shift; @@ -52,58 +50,50 @@ sub open { 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 { - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); -} - sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; - printMsg( $cmd, "Tx" ); + $self->printMsg($cmd, 'Tx'); my $url; - if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { + if ( $self->{Monitor}->{ControlAddress} =~ /^http/i ) { $url = $self->{Monitor}->{ControlAddress}.$cmd; } else { $url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd; - } # en dif - my $req = HTTP::Request->new( GET=>$url ); + } # end if + my $req = HTTP::Request->new(GET=>$url); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { - Error( "Error check failed: '".$res->status_line()."'" ); + Error('Error check failed: \''.$res->status_line().'\''); } - return( $result ); + return $result; } sub reset { my $self = shift; - Debug( "Camera Reset" ); - my $cmd = "/admin/ptctl.cgi?move=reset"; - $self->sendCmd( $cmd ); + Debug('Camera Reset'); + my $cmd = '/admin/ptctl.cgi?move=reset'; + $self->sendCmd($cmd); } sub moveMap { my $self = shift; my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); + my $xcoord = $self->getParam($params, 'xcoord'); + my $ycoord = $self->getParam($params, 'ycoord'); my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; @@ -125,81 +115,81 @@ sub moveMap { elsif ( $hor > 50 ) { # right $horSteps = (($hor - 50) / 50) * $maxhor; - $horDir = "right"; + $horDir = 'right'; } # Vertical movement if ( $ver < 50 ) { # up $verSteps = ((50 - $ver) / 50) * $maxver; - $verDir = "up"; + $verDir = 'up'; } elsif ( $ver > 50 ) { # down $verSteps = (($ver - 50) / 50) * $maxver; - $verDir = "down"; + $verDir = 'down'; } my $v = int($verSteps); my $h = int($horSteps); - Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir"); + Debug("Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir"); my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h"; - $self->sendCmd( $cmd ); + $self->sendCmd($cmd); $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v"; - $self->sendCmd( $cmd ); + $self->sendCmd($cmd); } sub moveRelUp { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Up $step" ); - my $cmd = "/admin/ptctl.cgi?move=up"; - $self->sendCmd( $cmd ); + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Up $step"); + my $cmd = '/admin/ptctl.cgi?move=up'; + $self->sendCmd($cmd); } sub moveRelDown { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Down $step" ); - my $cmd = "/admin/ptctl.cgi?move=down"; - $self->sendCmd( $cmd ); + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Down $step"); + my $cmd = '/admin/ptctl.cgi?move=down'; + $self->sendCmd($cmd); } sub moveRelLeft { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); + my $step = $self->getParam($params, 'panstep'); - if ( $self->{Monitor}->{Orientation} eq "hori" ) { - Debug( "Stepping Right because flipped horizontally " ); - $self->sendCmd( "/admin/ptctl.cgi?move=right" ); + if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) { + Debug('Stepping Right because flipped horizontally'); + $self->sendCmd('/admin/ptctl.cgi?move=right'); } else { - Debug( "Step Left" ); - $self->sendCmd( "/admin/ptctl.cgi?move=left" ); + Debug('Step Left'); + $self->sendCmd('/admin/ptctl.cgi?move=left'); } } sub moveRelRight { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); - if ( $self->{Monitor}->{Orientation} eq "hori" ) { - Debug( "Stepping Left because flipped horizontally " ); - $self->sendCmd( "/admin/ptctl.cgi?move=left" ); + my $step = $self->getParam($params, 'panstep'); + if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) { + Debug('Stepping Left because flipped horizontally'); + $self->sendCmd('/admin/ptctl.cgi?move=left'); } else { - Debug( "Step Right" ); - $self->sendCmd( "/admin/ptctl.cgi?move=right" ); + Debug('Step Right'); + $self->sendCmd('/admin/ptctl.cgi?move=right'); } } sub presetClear { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); + my $preset = $self->getParam($params, 'preset'); + Debug("Clear Preset $preset"); #my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; #$self->sendCmd( $cmd ); } @@ -207,26 +197,26 @@ sub presetClear { sub presetSet { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset"; + my $preset = $self->getParam($params, 'preset'); + Debug("Set Preset $preset"); + my $cmd = '/admin/ptctl.cgi?position=' . ($preset - 1) . "&positionname=zm$preset"; $self->sendCmd( $cmd ); } sub presetGoto { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1); - $self->sendCmd( $cmd ); + my $preset = $self->getParam($params, 'preset'); + Debug("Goto Preset $preset"); + my $cmd = '/admin/ptctl.cgi?move=p'.($preset - 1); + $self->sendCmd($cmd); } sub presetHome { my $self = shift; - Debug( "Home Preset" ); - my $cmd = "/admin/ptctl.cgi?move=h"; - $self->sendCmd( $cmd ); + Debug('Home Preset'); + my $cmd = '/admin/ptctl.cgi?move=h'; + $self->sendCmd($cmd); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm index 47ec7c557..963f75638 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm @@ -36,9 +36,172 @@ require ZoneMinder::Server; #our @ISA = qw(Exporter ZoneMinder::Base); use parent qw(ZoneMinder::Object); -use vars qw/ $table $primary_key /; +use vars qw/ $table $primary_key %fields $serial %defaults $debug/; $table = 'Monitors'; -$primary_key = 'Id'; +$serial = $primary_key = 'Id'; +%fields = map { $_ => $_ } qw( + Id + Name + Notes + ServerId + StorageId + Type + Function + Enabled + LinkedMonitors + Triggers + Device + Channel + Format + V4LMultiBuffer + V4LCapturesPerFrame + Protocol + Method + Host + Port + SubPath + Path + Options + User + Pass + Width + Height + Colours + Palette + Orientation + Deinterlacing + DecoderHWAccelName + DecoderHWAccelDevice + SaveJPEGs + VideoWriter + OutputCodec + OutputContainer + EncoderParameters + RecordAudio + RTSPDescribe + Brightness + Contrast + Hue + Colour + EventPrefix + LabelFormat + LabelX + LabelY + LabelSize + ImageBufferCount + WarmupCount + PreEventCount + PostEventCount + StreamReplayBuffer + AlarmFrameCount + SectionLength + MinSectionLength + FrameSkip + MotionFrameSkip + AnalysisFPSLimit + AnalysisUpdateDelay + MaxFPS + AlarmMaxFPS + FPSReportInterval + RefBlendPerc + AlarmRefBlendPerc + Controllable + ControlId + ControlDevice + ControlAddress + AutoStopTimeout + TrackMotion + TrackDelay + ReturnLocation + ReturnDelay + DefaultRate + DefaultScale + SignalCheckPoints + SignalCheckColour + WebColour + Exif + Sequence + ); + +%defaults = ( + ServerId => 0, + StorageId => 0, + Type => 'Ffmpeg', + Function => 'Mocord', + Enabled => 1, + LinkedMonitors => undef, + Device => '', + Channel => 0, + Format => 0, + V4LMultiBuffer => undef, + V4LCapturesPerFrame => 1, + Protocol => undef, + Method => '', + Host => undef, + Port => '', + SubPath => '', + Path => undef, + Options => undef, + User => undef, + Pass => undef, + Width => undef, + Height => undef, + Colours => 4, + Palette => 0, + Orientation => undef, + Deinterlacing => 0, + DecoderHWAccelName => undef, + DecoderHWAccelDevice => undef, + SaveJPEGs => 3, + VideoWriter => 0, + OutputCodec => undef, + OutputContainer => undef, + EncoderParameters => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", + RecordAudio=>0, + RTSPDescribe=>0, + Brightness => -1, + Contrast => -1, + Hue => -1, + Colour => -1, + EventPrefix => 'Event-', + LabelFormat => '%N - %d/%m/%y %H:%M:%S', + LabelX => 0, + LabelY => 0, + LabelSize => 1, + ImageBufferCount => 20, + WarmupCount => 0, + PreEventCount => 5, + PostEventCount => 5, + StreamReplayBuffer => 0, + AlarmFrameCount => 1, + SectionLength => 600, + MinSectionLength => 10, + FrameSkip => 0, + MotionFrameSkip => 0, + AnalysisFPSLimit => undef, + AnalysisUpdateDelay => 0, + MaxFPS => undef, + AlarmMaxFPS => undef, + FPSReportInterval => 100, + RefBlendPerc => 6, + AlarmRefBlendPerc => 6, + Controllable => 0, + ControlId => undef, + ControlDevice => undef, + ControlAddress => undef, + AutoStopTimeout => undef, + TrackMotion => 0, + TrackDelay => undef, + ReturnLocation => -1, + ReturnDelay => undef, + DefaultRate => 100, + DefaultScale => 100, + SignalCheckPoints => 0, + SignalCheckColour => '#0000BE', + WebColour => '#ff0000', + Exif => 0, + Sequence => undef, + ); sub Server { return new ZoneMinder::Server( $_[0]{ServerId} ); diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index b68697ebc..eb1718655 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -108,6 +108,9 @@ if ( $options{command} ) { Fatal("Unable to load control data for monitor $id"); } my $protocol = $monitor->{Protocol}; + if ( !$protocol ) { + Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field'); + } if ( -x $protocol ) { # Protocol is actually a script! diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index efd5e735e..9b5e7c89d 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -23,6 +23,7 @@ use strict; use bytes; +use utf8; @EXTRA_PERL_LIB@ use ZoneMinder; @@ -34,6 +35,7 @@ use Sys::MemInfo qw(totalmem); use Sys::CPU qw(cpu_count); use POSIX qw(strftime uname); use JSON::MaybeXS; +use Encode; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; @@ -166,7 +168,7 @@ sub sendData { $req->header('content-length' => length($msg)); $req->header('connection' => 'Close'); - $req->content($msg); + $req->content(encode('UTF-8',$msg)); my $resp = $ua->request($req); my $resp_msg = $resp->decoded_content; diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index b1743aeae..c56107276 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -329,14 +329,13 @@ sub loadMonitor { } # end sub loadMonitor sub loadMonitors { - Debug('Loading monitors'); $monitor_reload_time = time(); my %new_monitors = (); - my $sql = q`SELECT * FROM Monitors - WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )`. - ( $Config{ZM_SERVER_ID} ? ' AND ServerId=?' : '' ) + my $sql = 'SELECT * FROM `Monitors` + WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect\' )'. + ( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' ) ; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 6b6036db5..f0b3dc76e 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -81,7 +81,7 @@ void FFMPEGInit() { av_log_set_callback(log_libav_callback); Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options"); } else { - Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor not part of your debug targets"); + Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets"); av_log_set_level(AV_LOG_QUIET); } #if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0) @@ -291,17 +291,18 @@ static void zm_log_fps(double d, const char *postfix) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar ( const AVCodecParameters *par ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d %s) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)", - par->codec_type, - par->codec_id, - avcodec_get_name(par->codec_id), - par->codec_tag, - par->width, - par->height, - par->bit_rate, - par->format, - ((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) -); + Debug(1, "Dumping codecpar codec_type(%d %s) codec_id(%d %s) codec_tag(%" PRIu32 ") width(%d) height(%d) bit_rate(%" PRIu64 ") format(%d %s)", + par->codec_type, + av_get_media_type_string(par->codec_type), + par->codec_id, + avcodec_get_name(par->codec_id), + par->codec_tag, + par->width, + par->height, + par->bit_rate, + par->format, + (((AVPixelFormat)par->format == AV_PIX_FMT_NONE) ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) + ); } #endif diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index ef4dfc057..3cc634e7d 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -517,19 +517,19 @@ int FfmpegCamera::OpenFfmpeg() { Debug(1, "Selected hw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt)); - mVideoCodecContext->get_format = get_hw_format; - ret = av_hwdevice_ctx_create(&hw_device_ctx, type, (hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0); if ( ret < 0 ) { - Error("Failed to create hwaccel device."); - return -1; + Error("Failed to create hwaccel device. %s",av_make_error_string(ret).c_str()); + hw_pix_fmt = AV_PIX_FMT_NONE; + } else { + Debug(1, "Created hwdevice for %s", hwaccel_device.c_str()); + mVideoCodecContext->get_format = get_hw_format; + mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx); + hwFrame = zm_av_frame_alloc(); } - Debug(1, "Created hwdevice for %s", hwaccel_device.c_str()); - mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx); - hwFrame = zm_av_frame_alloc(); } else { - Debug(1, "Failed to setup hwaccel."); + Debug(1, "Failed to find suitable hw_pix_fmt."); } #else Debug(1, "AVCodec not new enough for hwaccel"); @@ -537,7 +537,7 @@ int FfmpegCamera::OpenFfmpeg() { #else Warning("HWAccel support not compiled in."); #endif - } // end if hwacel_name + } // end if hwaccel_name // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) @@ -1087,6 +1087,7 @@ int FfmpegCamera::transfer_to_image( return -1; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + // From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too. int size = av_image_fill_arrays( output_frame->data, output_frame->linesize, directbuffer, imagePixFormat, width, height, 32); @@ -1128,8 +1129,8 @@ int FfmpegCamera::transfer_to_image( mConvertContext, input_frame->data, input_frame->linesize, 0, mVideoCodecContext->height, output_frame->data, output_frame->linesize); - if ( ret <= 0 ) { - Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s : code: %d", + if ( ret < 0 ) { + Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s lines %d: code: %d", input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format), input_frame->linesize, mVideoCodecContext->height, imagePixFormat, @@ -1137,6 +1138,7 @@ int FfmpegCamera::transfer_to_image( output_frame->linesize, frameCount, mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt), + mVideoCodecContext->height, ret ); return -1; diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 1b8afc517..1f52c86dc 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -156,6 +156,7 @@ void Logger::initialise(const std::string &id, const Options &options) { if ( options.mTerminalLevel != NOOPT ) tempTerminalLevel = options.mTerminalLevel; + // DEBUG1 == 1. So >= DEBUG1, we set to DEBUG9?! Why? if ( options.mDatabaseLevel != NOOPT ) tempDatabaseLevel = options.mDatabaseLevel; else @@ -359,7 +360,7 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) { if ( databaseLevel > NOOPT ) { databaseLevel = limit(databaseLevel); if ( mDatabaseLevel != databaseLevel ) { - if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) { + if ( (databaseLevel > NOLOG) && (mDatabaseLevel <= NOLOG) ) { // <= NOLOG would be NOOPT if ( !zmDbConnect() ) { databaseLevel = NOLOG; } diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 0c71b5c61..49d617f7d 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -1,21 +1,21 @@ // // ZoneMinder Monitor Class Implementation, $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. -// +// #include "zm.h" #include "zm_db.h" @@ -73,7 +73,7 @@ bool MonitorStream::checkSwapPath(const char *path, bool create_path) { return false; } return true; -} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path ) +} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path ) void MonitorStream::processCommand(const CmdMsg *msg) { Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); @@ -265,7 +265,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) { //status_data.enabled = monitor->shared_data->active; status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; - Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", + Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", status_data.buffer_level, status_data.delayed, status_data.paused, @@ -327,7 +327,7 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - + fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout); fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { @@ -383,7 +383,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - + fputs("--ZoneMinderFrame\r\n", stdout); switch( type ) { case STREAM_JPEG : @@ -412,7 +412,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { } fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { - if ( !zm_terminate ) { + if ( !zm_terminate ) { // If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate Warning("Unable to send stream frame: %s", strerror(errno)); } @@ -430,7 +430,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { Warning("Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps); } - } + } // Not mpeg last_frame_sent = TV_2_FLOAT(now); return true; } // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) @@ -455,7 +455,7 @@ void MonitorStream::runStream() { fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout); // point to end which is theoretically not a valid value because all indexes are % image_buffer_count - unsigned int last_read_index = monitor->image_buffer_count; + unsigned int last_read_index = monitor->image_buffer_count; time_t stream_start_time; time(&stream_start_time); @@ -474,22 +474,21 @@ void MonitorStream::runStream() { Image *paused_image = NULL; struct timeval paused_timestamp; - // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id - const int max_swap_len_suffix = 15; - - int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator - int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1; - int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1; - int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; - if ( connkey && ( playback_buffer > 0 ) ) { + // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id + const int max_swap_len_suffix = 15; + + int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator + int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1; + int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1; + int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) { Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX); } else { swap_path = staticConfig.PATH_SWAP; - Debug( 3, "Checking swap path folder: %s", swap_path.c_str() ); + Debug(3, "Checking swap path folder: %s", swap_path.c_str()); if ( checkSwapPath(swap_path.c_str(), true) ) { swap_path += stringtf("/zmswap-m%d", monitor->Id()); @@ -509,8 +508,8 @@ void MonitorStream::runStream() { } else { Debug(2, "Assigning temporary buffer"); temp_image_buffer = new SwapImage[temp_image_buffer_count]; - memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); - Debug( 2, "Assigned temporary buffer" ); + memset(temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count); + Debug(2, "Assigned temporary buffer"); } } } else { @@ -525,7 +524,7 @@ void MonitorStream::runStream() { Debug(1, "Using %.3f for fps instead of current fps %.3f", capture_max_fps, capture_fps); capture_fps = capture_max_fps; } - + if ( capture_fps < 1 ) { max_secs_since_last_sent_frame = 10/capture_fps; Debug(1, "Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f", @@ -562,7 +561,7 @@ void MonitorStream::runStream() { touch(sock_path_lock); last_comm_update = now; } - } // end if connkey + } // end if connkey if ( paused ) { if ( !was_paused ) { @@ -589,7 +588,7 @@ void MonitorStream::runStream() { } else { if ( !paused ) { int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count); - //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); + // Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); SwapImage *swap_image = &temp_image_buffer[temp_index]; if ( !swap_image->valid ) { @@ -597,51 +596,61 @@ void MonitorStream::runStream() { delayed = true; temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count); } else { - //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); - double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + // Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); + double expected_delta_time = ((TV_2_FLOAT(swap_image->timestamp) - TV_2_FLOAT(last_frame_timestamp)) * ZM_RATE_BASE)/replay_rate; + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; - //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); + // Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); // If the next frame is due if ( actual_delta_time > expected_delta_time ) { - //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); + // Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); if ( temp_index%frame_mod == 0 ) { - Debug( 2, "Sending delayed frame %d", temp_index ); + Debug(2, "Sending delayed frame %d", temp_index); // Send the next frame - if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) + if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { zm_terminate = true; + } memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp)); - //frame_sent = true; + // frame_sent = true; } temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count); } } } else if ( step != 0 ) { - temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); + temp_read_index = MOD_ADD(temp_read_index, (step>0?1:-1), temp_image_buffer_count); SwapImage *swap_image = &temp_image_buffer[temp_read_index]; // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) + if ( !sendFrame( + temp_image_buffer[temp_read_index].file_name, + &temp_image_buffer[temp_read_index].timestamp + ) ) { zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; + } + memcpy( + &last_frame_timestamp, + &(swap_image->timestamp), + sizeof(last_frame_timestamp) + ); + // frame_sent = true; step = 0; } else { //paused? int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count); - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( got_command || actual_delta_time > 5 ) { + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; + if ( got_command || (actual_delta_time > 5) ) { // Send keepalive Debug(2, "Sending keepalive frame %d", temp_index); // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + if ( !sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { zm_terminate = true; - //frame_sent = true; + } + // frame_sent = true; } - } // end if (!paused) or step or paused - } // end if have exceeded buffer or not + } // end if (!paused) or step or paused + } // end if have exceeded buffer or not if ( temp_read_index == temp_write_index ) { // Go back to live viewing @@ -652,16 +661,16 @@ void MonitorStream::runStream() { delayed = false; replay_rate = ZM_RATE_BASE; } - } // end if ( buffered_playback && delayed ) + } // end if ( buffered_playback && delayed ) if ( last_read_index != monitor->shared_data->last_write_index ) { // have a new image to send - int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary + int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( !paused && !delayed ) { last_read_index = monitor->shared_data->last_write_index; Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", - index, frame_mod, frame_count, paused, delayed ); + index, frame_mod, frame_count, paused, delayed); // Send the next frame Monitor::Snapshot *snap = &monitor->image_buffer[index]; @@ -670,9 +679,13 @@ void MonitorStream::runStream() { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; } - // Perhaps we should use NOW instead. - memcpy(&last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp)); - //frame_sent = true; + // Perhaps we should use NOW instead. + memcpy( + &last_frame_timestamp, + snap->timestamp, + sizeof(last_frame_timestamp) + ); + // frame_sent = true; temp_read_index = temp_write_index; } else { @@ -697,7 +710,7 @@ void MonitorStream::runStream() { if ( !sendFrame(paused_image, &paused_timestamp) ) zm_terminate = true; } else { - Debug(2, "Would have sent keepalive frame, but had no paused_image "); + Debug(2, "Would have sent keepalive frame, but had no paused_image"); } } // end if actual_delta_time > 5 } // end if change in zoom @@ -718,27 +731,27 @@ void MonitorStream::runStream() { temp_index); temp_image_buffer[temp_index].valid = true; } - memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); - monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); - temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); + memcpy(&(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp)); + monitor->image_buffer[index].image->WriteJpeg(temp_image_buffer[temp_index].file_name, config.jpeg_file_quality); + temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count); if ( temp_write_index == temp_read_index ) { // Go back to live viewing - Warning( "Exceeded temporary buffer, resuming live play" ); + Warning("Exceeded temporary buffer, resuming live play"); paused = false; delayed = false; replay_rate = ZM_RATE_BASE; } } else { - Warning( "Unable to store frame as timestamp invalid" ); + Warning("Unable to store frame as timestamp invalid"); } } else { - Warning( "Unable to store frame as shared memory invalid" ); + Warning("Unable to store frame as shared memory invalid"); } } // end if buffered playback frame_count++; } else { Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index); - } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) + } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); Debug(3, "Sleeping for (%d)", sleep_time); @@ -755,10 +768,10 @@ void MonitorStream::runStream() { break; } } - if ( ! last_frame_sent ) { + if ( !last_frame_sent ) { // If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value. last_frame_sent = now.tv_sec; - Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d) ", + Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)", frame_mod, frame_count); } else if ( (!paused) @@ -799,9 +812,9 @@ void MonitorStream::runStream() { } } } - globfree( &pglob ); + globfree(&pglob); if ( rmdir(swap_path.c_str()) < 0 ) { - Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) ); + Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno)); } } // end if checking for swap_path } // end if buffered_playback @@ -809,7 +822,7 @@ void MonitorStream::runStream() { closeComms(); } // end MonitorStream::runStream -void MonitorStream::SingleImage( int scale ) { +void MonitorStream::SingleImage(int scale) { int img_buffer_size = 0; static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; @@ -817,42 +830,45 @@ void MonitorStream::SingleImage( int scale ) { Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + snap_image->EncodeJpeg(img_buffer, &img_buffer_size); + + fprintf(stdout, + "Content-Length: %d\r\n" + "Content-Type: image/jpeg\r\n\r\n", + img_buffer_size); + fwrite(img_buffer, img_buffer_size, 1, stdout); } -void MonitorStream::SingleImageRaw( int scale ) { +void MonitorStream::SingleImageRaw(int scale) { Image scaled_image; Monitor::Snapshot *snap = monitor->getSnapshot(); Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - - fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); - fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); - fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); + + fprintf(stdout, + "Content-Length: %d\r\n" + "Content-Type: image/x-rgb\r\n\r\n", + snap_image->Size()); + fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout); } - #ifdef HAVE_ZLIB_H -void MonitorStream::SingleImageZip( int scale ) { +void MonitorStream::SingleImageZip(int scale) { unsigned long img_buffer_size = 0; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; @@ -861,17 +877,19 @@ void MonitorStream::SingleImageZip( int scale ) { Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - snap_image->Zip( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + snap_image->Zip(img_buffer, &img_buffer_size); + + fprintf(stdout, + "Content-Length: %ld\r\n" + "Content-Type: image/x-rgbz\r\n\r\n", + img_buffer_size); + fwrite(img_buffer, img_buffer_size, 1, stdout); } #endif // HAVE_ZLIB_H diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 96c3707ff..f725d17b9 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -1060,7 +1060,6 @@ int RemoteCameraHttp::PreCapture() { if ( sd < 0 ) { Connect(); if ( sd < 0 ) { - Error("Unable to connect to camera"); return -1; } mode = SINGLE_IMAGE; diff --git a/src/zmc.cpp b/src/zmc.cpp index ad9dea406..511e019ab 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -350,7 +350,7 @@ int main(int argc, char *argv[]) { } if ( result < 0 ) { // Failure, try reconnecting - sleep(1); + sleep(5); break; } } // end while ! zm_terminate diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 3e20b303a..b60996a78 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -175,7 +175,7 @@ cd ../ VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version` -if [ $VERSION == "" ]; then +if [ -z "$VERSION" ]; then exit 1; fi; if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then diff --git a/utils/packpack/rsync_xfer.sh b/utils/packpack/rsync_xfer.sh index 40f5235be..f6ce377e7 100755 --- a/utils/packpack/rsync_xfer.sh +++ b/utils/packpack/rsync_xfer.sh @@ -1,5 +1,10 @@ #!/bin/bash +# We don't deploy during eslint checks, so exit immediately +if [ "${DIST}" == "eslint" ]; then + exit 0 +fi + # Check to see if this script has access to all the commands it needs for CMD in sshfs rsync find fusermount mkdir; do type $CMD 2>&1 > /dev/null @@ -12,53 +17,26 @@ for CMD in sshfs rsync find fusermount mkdir; do fi done -# We only want to deploy packages during cron events -# See https://docs.travis-ci.com/user/cron-jobs/ -if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then - - if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then - targetfolder="debian/master/mini-dinstall/incoming" - else - targetfolder="travis" - fi - - echo - echo "Target subfolder set to $targetfolder" - echo - if [ "${USE_SFTP}" == "yes" ]; then - echo "Running \$(rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)" - rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 - if [ $? -eq 0 ]; then - echo - echo "Files copied successfully." - echo - else - echo - echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!" - echo - exit 99 - fi - else - mkdir -p ./zmrepo - ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect 2>&1)" - - if [ -z "$ssh_mntchk" ]; then - echo - echo "Remote filesystem mounted successfully." - echo "Begin transfering files..." - echo - - # Don't keep packages older than 5 days - find ./zmrepo/$targetfolder/ -maxdepth 1 -type f,l -mtime +5 -delete - rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/ - fusermount -zu zmrepo - else - echo - echo "ERROR: Attempt to mount zmrepo.zoneminder.com failed!" - echo "sshfs gave the following error message:" - echo \"$ssh_mntchk\" - echo - exit 99 - fi - fi +if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then + targetfolder="debian/master/mini-dinstall/incoming" +else + targetfolder="travis" +fi + +echo +echo "Target subfolder set to $targetfolder" +echo + +echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)" +rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 +if [ "$?" -eq 0 ]; then + echo + echo "Files copied successfully." + echo +else + echo + echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!" + echo "See log output for details." + echo + exit 99 fi diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 35a6aeab4..6a51e2995 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -9,7 +9,7 @@ # General sanity checks checksanity () { # Check to see if this script has access to all the commands it needs - for CMD in set echo curl git ln mkdir rmdir cat patch; do + for CMD in set echo curl git ln mkdir rmdir cat patch sed; do type $CMD 2>&1 > /dev/null if [ $? -ne 0 ]; then @@ -30,7 +30,7 @@ checksanity () { ARCH="x86_64" fi - if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" ]]; then + if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" && "${ARCH}" != "aarch64" ]]; then echo echo "ERROR: Unsupported architecture specified \"${ARCH}\"." echo @@ -150,7 +150,7 @@ install_deb () { exit 1 fi - # Install and test the zoneminder package (only) for Ubuntu Trusty + # Install and test the zoneminder package (only) for Ubuntu Xenial pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb" if [ -e $pkgname ]; then @@ -275,6 +275,8 @@ checkdeploytarget () { echo "*** TRACEROUTE ***" echo traceroute -w 2 -m 15 ${DEPLOYTARGET} + + exit 97 fi } @@ -291,43 +293,43 @@ if [ "${TRAVIS}" == "true" ]; then fi checksanity -# We don't want to build packages for all supported distros after every commit -# Only build all packages when executed via cron -# See https://docs.travis-ci.com/user/cron-jobs/ - # Steps common to Redhat distros if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then - commonprep - echo "Begin Redhat build..." + commonprep + echo "Begin Redhat build..." - setrpmpkgname + # Newer Redhat distros use dnf package manager rather than yum + if [ "${DIST}" -gt "7" ]; then + sed -i 's\yum\dnf\' utils/packpack/redhat_package.mk + fi - ln -sfT distros/redhat rpm + setrpmpkgname - # The rpm specfile requires the Crud submodule folder to be empty - rm -rf web/api/app/Plugin/Crud - mkdir web/api/app/Plugin/Crud + ln -sfT distros/redhat rpm - reporpm="rpmfusion-free-release" - dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm" + # The rpm specfile requires the Crud submodule folder to be empty + rm -rf web/api/app/Plugin/Crud + mkdir web/api/app/Plugin/Crud - # Give our downloaded repo rpm a common name so redhat_package.mk can find it - if [ -n "$dlurl" ] && [ $? -eq 0 ]; then - echo "Retrieving ${reporpm} repo rpm..." - curl $dlurl > build/external-repo.noarch.rpm - else - echo "ERROR: Failed to retrieve ${reporpm} repo rpm..." - echo "Download url was: $dlurl" - exit 1 - fi + reporpm="rpmfusion-free-release" + dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm" - setrpmchangelog + # Give our downloaded repo rpm a common name so redhat_package.mk can find it + if [ -n "$dlurl" ] && [ $? -eq 0 ]; then + echo "Retrieving ${reporpm} repo rpm..." + curl $dlurl > build/external-repo.noarch.rpm + else + echo "ERROR: Failed to retrieve ${reporpm} repo rpm..." + echo "Download url was: $dlurl" + exit 1 + fi - echo "Starting packpack..." - execpackpack - fi; - # Steps common to Debian based distros + setrpmchangelog + + echo "Starting packpack..." + execpackpack + +# Steps common to Debian based distros elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then commonprep echo "Begin ${OS} ${DIST} build..." @@ -348,14 +350,27 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia echo "Starting packpack..." execpackpack - # We were not triggered via cron so just build and test trusty - if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ]; then - # If we are running inside Travis then attempt to install the deb we just built - if [ "${TRAVIS}" == "true" ]; then + # Try to install and run the newly built zoneminder package + if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then + echo "Begin Deb package installation..." install_deb - fi fi + +# Steps common to eslint checks +elif [ "${OS}" == "eslint" ] || [ "${DIST}" == "eslint" ]; then + + # Check we've got npm installed + type npm 2>&1 > /dev/null + + if [ $? -ne 0 ]; then + echo + echo "ERROR: The script cannot find the required command \"npm\"." + echo + exit 1 + fi + + npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5 + echo "Begin eslint checks..." + eslint --ext .php,.js . fi -exit 0 - diff --git a/version b/version index 63984dc0b..2b17ffd50 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.33.15 +1.34.0 diff --git a/web/ajax/log.php b/web/ajax/log.php index ef2e450f2..36a29adc3 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -41,7 +41,7 @@ function buildLogQuery($action) { } foreach ( $filter as $field=>$value ) { - if ( ! in_array($field, $filterFields) ) { + if ( !in_array($field, $filterFields) ) { ZM\Error("'$field' is not in valid filter fields " . print_r($filterField,true)); continue; } @@ -58,7 +58,7 @@ function buildLogQuery($action) { $sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit; return array('sql'=>$sql, 'values'=>$values); -} +} # function buildLogQuery($action) switch ( $_REQUEST['task'] ) { case 'create' : @@ -70,14 +70,16 @@ switch ( $_REQUEST['task'] ) { $string = $_POST['message']; $file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : ''; - if ( !empty( $_POST['line'] ) ) + if ( !empty( $_POST['line'] ) ) { $line = validInt($_POST['line']); - else + } else { $line = NULL; + } $levels = array_flip(ZM\Logger::$codes); - if ( !isset($levels[$_POST['level']]) ) + if ( !isset($levels[$_POST['level']]) ) { ZM\Panic('Unexpected logger level '.$_POST['level']); + } $level = $levels[$_POST['level']]; ZM\Logger::fetch()->logPrint($level, $string, $file, $line); } @@ -141,6 +143,10 @@ switch ( $_REQUEST['task'] ) { $logs[] = $log; } + foreach ( $options as $field => $values ) { + asort($options[$field]); + } + $available = count($logs); ajaxResponse( array( 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG), diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 910b18cb5..575eaef39 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -119,13 +119,11 @@ if ( sem_acquire($semaphore,1) !== false ) { switch ( $data['type'] ) { case MSG_DATA_WATCH : $data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg); - ZM\Logger::Debug('FPS: ' . $data['fps']); $data['fps'] = round( $data['fps'], 2 ); - ZM\Logger::Debug('FPS: ' . $data['fps'] ); $data['rate'] /= RATE_BASE; $data['delay'] = round( $data['delay'], 2 ); $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 ); - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) { + if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) { $time = time(); // Regenerate auth hash after half the lifetime of the hash if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) { @@ -144,7 +142,7 @@ if ( sem_acquire($semaphore,1) !== false ) { } $data['rate'] /= RATE_BASE; $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 ); - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) { + if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) { $time = time(); // Regenerate auth hash after half the lifetime of the hash if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) { diff --git a/web/api/app/Controller/GroupsController.php b/web/api/app/Controller/GroupsController.php index 6f0a88300..5d19b5d98 100644 --- a/web/api/app/Controller/GroupsController.php +++ b/web/api/app/Controller/GroupsController.php @@ -77,16 +77,24 @@ class GroupsController extends AppController { } $this->Group->create(); - if ( $this->Group->save($this->request->data) ) { + + if ( $this->request->data['Group']['MonitorIds'] and ! isset($this->request->data['Monitor']) ) { + $this->request->data['Monitor'] = explode(',', $this->request->data['Group']['MonitorIds']); + unset($this->request->data['Group']['MonitorIds']); + } + if ( $this->Group->saveAssociated($this->request->data, array('atomic'=>true)) ) { return $this->flash( __('The group has been saved.'), array('action' => 'index') ); - } - } - $monitors = $this->Group->Monitor->find('list'); + } else { + ZM\Error("Failed to save Group"); + debug($this->Group->invalidFields()); + } + } # end if post + $monitors = $this->Group->Monitor->find('list'); $this->set(compact('monitors')); - } + } # end add /** * edit method diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index 300352949..7ddba57df 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -136,7 +136,7 @@ class HostController extends AppController { }*/ $access_issued_at = time(); - $access_ttl = (ZM_AUTH_HASH_TTL || 2) * 3600; + $access_ttl = max(ZM_AUTH_HASH_TTL,1) * 3600; // by default access token will expire in 2 hrs // you can change it by changing the value of ZM_AUTH_HASH_TLL diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3be9166b8..92114a558 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -44,33 +44,26 @@ class MonitorsController extends AppController { } else { $conditions = array(); } - global $user; $allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null; if ( $allowedMonitors ) { $conditions['Monitor.Id' ] = $allowedMonitors; } - $find_array = array('conditions'=>$conditions,'contain'=>array('Group')); - if ( isset($conditions['GroupId']) ) { - $find_array['joins'] = array( + $find_array = array( + 'conditions' => &$conditions, + 'contain' => array('Group'), + 'joins' => array( array( 'table' => 'Groups_Monitors', - 'type' => 'inner', + 'type' => 'left', 'conditions' => array( - 'Groups_Monitors.MonitorId = Monitor.Id' + 'Groups_Monitors.MonitorId = Monitor.Id', ), ), - //array( - //'table' => 'Groups', - //'type' => 'inner', - //'conditions' => array( - //'Groups.Id = Groups_Monitors.GroupId', - //'Groups.Id' => $this->request->params['GroupId'], - //), - //) - ); - } + ), + 'group' => '`Monitor`.`Id`', + ); $monitors = $this->Monitor->find('all',$find_array); $this->set(array( 'monitors' => $monitors, diff --git a/web/api/app/Model/Group.php b/web/api/app/Model/Group.php index 108f9b9c7..04d783b14 100644 --- a/web/api/app/Model/Group.php +++ b/web/api/app/Model/Group.php @@ -59,7 +59,7 @@ class Group extends AppModel { * * @var array */ - public $hasMany = array( + public $hasAndBelongsToMany = array( 'Monitor' => array( 'className' => 'Monitor', 'joinTable' => 'Groups_Monitors', @@ -77,4 +77,5 @@ class Group extends AppModel { 'counterQuery' => '' ), ); + var $actsAs = array( 'Containable' ); } diff --git a/web/includes/Control.php b/web/includes/Control.php index 39c259092..2121061b1 100644 --- a/web/includes/Control.php +++ b/web/includes/Control.php @@ -128,12 +128,16 @@ class Control extends ZM_Object { $cmds['PresetHome'] = 'presetHome'; if ( $this->CanZoom() ) { - if ( $this->CanZoomCon() ) + if ( $this->CanZoomCon() ) { $cmds['ZoomRoot'] = 'zoomCon'; - elseif ( $this->CanZoomRel() ) + } else if ( $this->CanZoomRel() ) { $cmds['ZoomRoot'] = 'zoomRel'; - elseif ( $this->CanZoomAbs() ) + } else if ( $this->CanZoomAbs() ) { $cmds['ZoomRoot'] = 'zoomAbs'; + } else { + $cmds['ZoomRoot'] = ''; + Error('No zoom type selected. Please select Continuous, Relative, Absolute'); + } $cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele'; $cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide'; $cmds['ZoomStop'] = 'zoomStop'; @@ -142,12 +146,16 @@ class Control extends ZM_Object { } if ( $this->CanFocus() ) { - if ( $this->CanFocusCon() ) + if ( $this->CanFocusCon() ) { $cmds['FocusRoot'] = 'focusCon'; - elseif ( $this->CanFocusRel() ) + } else if ( $this->CanFocusRel() ) { $cmds['FocusRoot'] = 'focusRel'; - elseif ( $this->CanFocusAbs() ) + } else if ( $this->CanFocusAbs() ) { $cmds['FocusRoot'] = 'focusAbs'; + } else { + $cmds['FocusRoot'] = ''; + Error('No focus type selected. Please select Continuous, Relative, Absolute'); + } $cmds['FocusFar'] = $cmds['FocusRoot'].'Far'; $cmds['FocusNear'] = $cmds['FocusRoot'].'Near'; $cmds['FocusStop'] = 'focusStop'; @@ -156,12 +164,16 @@ class Control extends ZM_Object { } if ( $this->CanIris() ) { - if ( $this->CanIrisCon() ) + if ( $this->CanIrisCon() ) { $cmds['IrisRoot'] = 'irisCon'; - elseif ( $this->CanIrisRel() ) + } else if ( $this->CanIrisRel() ) { $cmds['IrisRoot'] = 'irisRel'; - elseif ( $this->CanIrisAbs() ) + } else if ( $this->CanIrisAbs() ) { $cmds['IrisRoot'] = 'irisAbs'; + } else { + $cmds['IrisRoot'] = ''; + Error('No iris type selected. Please select Continuous, Relative, Absolute'); + } $cmds['IrisOpen'] = $cmds['IrisRoot'].'Open'; $cmds['IrisClose'] = $cmds['IrisRoot'].'Close'; $cmds['IrisStop'] = 'irisStop'; @@ -170,12 +182,16 @@ class Control extends ZM_Object { } if ( $this->CanWhite() ) { - if ( $this->CanWhiteCon() ) + if ( $this->CanWhiteCon() ) { $cmds['WhiteRoot'] = 'whiteCon'; - elseif ( $this->CanWhiteRel() ) + } else if ( $this->CanWhiteRel() ) { $cmds['WhiteRoot'] = 'whiteRel'; - elseif ( $this->CanWhiteAbs() ) + } else if ( $this->CanWhiteAbs() ) { $cmds['WhiteRoot'] = 'whiteAbs'; + } else { + Error('No White type selected. Please select Continuous, Relative, Absolute'); + $cmds['WhiteRoot'] = ''; + } $cmds['WhiteIn'] = $cmds['WhiteRoot'].'In'; $cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out'; $cmds['WhiteAuto'] = 'whiteAuto'; @@ -183,12 +199,16 @@ class Control extends ZM_Object { } if ( $this->CanGain() ) { - if ( $this->CanGainCon() ) + if ( $this->CanGainCon() ) { $cmds['GainRoot'] = 'gainCon'; - elseif ( $this->CanGainRel() ) + } else if ( $this->CanGainRel() ) { $cmds['GainRoot'] = 'gainRel'; - elseif ( $this->CanGainAbs() ) + } else if ( $this->CanGainAbs() ) { $cmds['GainRoot'] = 'gainAbs'; + } else { + Error('No Gain type selected'); + $cmds['GainRoot'] = ''; + } $cmds['GainUp'] = $cmds['GainRoot'].'Up'; $cmds['GainDown'] = $cmds['GainRoot'].'Down'; $cmds['GainAuto'] = 'gainAuto'; @@ -207,6 +227,7 @@ class Control extends ZM_Object { $cmds['Center'] = $cmds['PresetHome']; } else { $cmds['MoveRoot'] = ''; + Error('No move type selected. Please select Continuous, Relative, Absolute'); } $cmds['MoveUp'] = $cmds['MoveRoot'].'Up'; diff --git a/web/includes/Event.php b/web/includes/Event.php index da633f439..7e038397a 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -47,6 +47,10 @@ class Event extends ZM_Object { return ZM_Object::_find_one(get_class(), $parameters, $options); } + public static function clear_cache() { + return ZM_Object::_clear_cache(get_class()); + } + public function Storage( $new = null ) { if ( $new ) { $this->{'Storage'} = $new; diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 2ecf7b61e..8bccf3860 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -115,13 +115,17 @@ class Filter extends ZM_Object { public function control($command, $server_id=null) { $Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running')); - if ( !count($Servers) and !$server_id ) { - # This will be the non-multi-server case - $Servers = array(new Server()); + if ( !count($Servers) ) { + if ( !$server_id ) { + # This will be the non-multi-server case + $Servers = array(new Server()); + } else { + Warning("Server not found for id $server_id"); + } } foreach ( $Servers as $Server ) { - if ( !defined('ZM_SERVER_ID') or !$Server->Id() or ZM_SERVER_ID==$Server->Id() ) { + if ( (!defined('ZM_SERVER_ID')) or (!$Server->Id()) or (ZM_SERVER_ID==$Server->Id()) ) { # Local Logger::Debug("Controlling filter locally $command for server ".$Server->Id()); daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon'); @@ -139,7 +143,7 @@ class Filter extends ZM_Object { $url = '?user='.$_SESSION['username']; } } - $url .= '&view=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id(); + $url .= '&view=filter&object=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id(); Logger::Debug("sending command to $url"); $data = array(); if ( defined('ZM_ENABLE_CSRF_MAGIC') ) { diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 1353f375b..3505e83db 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -9,111 +9,112 @@ require_once('Storage.php'); class Monitor extends ZM_Object { protected static $table = 'Monitors'; -protected $defaults = array( - 'Id' => null, - 'Name' => '', - 'ServerId' => 0, - 'StorageId' => 0, - 'Type' => 'Ffmpeg', - 'Function' => 'Mocord', - 'Enabled' => array('type'=>'boolean','default'=>1), - 'LinkedMonitors' => array('type'=>'set', 'default'=>null), - 'Triggers' => array('type'=>'set','default'=>''), - 'Device' => '', - 'Channel' => 0, - 'Format' => '0', - 'V4LMultiBuffer' => null, - 'V4LCapturesPerFrame' => 1, - 'Protocol' => null, - 'Method' => '', - 'Host' => null, - 'Port' => '', - 'SubPath' => '', - 'Path' => null, - 'Options' => null, - 'User' => null, - 'Pass' => null, - // These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME - 'Width' => null, - 'Height' => null, - 'Colours' => 4, - 'Palette' => '0', - 'Orientation' => null, - 'Deinterlacing' => 0, - 'DecoderHWAccelName' => null, - 'DecoderHWAccelDevice' => null, - 'SaveJPEGs' => 3, - 'VideoWriter' => '0', - 'OutputCodec' => null, - 'OutputContainer' => null, - 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", - 'RecordAudio' => array('type'=>'boolean', 'default'=>0), - 'RTSPDescribe' => array('type'=>'boolean','default'=>0), - 'Brightness' => -1, - 'Contrast' => -1, - 'Hue' => -1, - 'Colour' => -1, - 'EventPrefix' => 'Event-', - 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', - 'LabelX' => 0, - 'LabelY' => 0, - 'LabelSize' => 1, - 'ImageBufferCount' => 100, - 'WarmupCount' => 0, - 'PreEventCount' => 0, - 'PostEventCount' => 0, - 'StreamReplayBuffer' => 0, - 'AlarmFrameCount' => 1, - 'SectionLength' => 600, - 'MinSectionLength' => 10, - 'FrameSkip' => 0, - 'MotionFrameSkip' => 0, - 'AnalysisFPSLimit' => null, - 'AnalysisUpdateDelay' => 0, - 'MaxFPS' => null, - 'AlarmMaxFPS' => null, - 'FPSReportInterval' => 100, - 'RefBlendPerc' => 6, - 'AlarmRefBlendPerc' => 6, - 'Controllable' => array('type'=>'boolean','default'=>0), - 'ControlId' => null, - 'ControlDevice' => null, - 'ControlAddress' => null, - 'AutoStopTimeout' => null, - 'TrackMotion' => array('type'=>'boolean','default'=>0), - 'TrackDelay' => null, - 'ReturnLocation' => -1, - 'ReturnDelay' => null, - 'DefaultRate' => 100, - 'DefaultScale' => 100, - 'SignalCheckPoints' => 0, - 'SignalCheckColour' => '#0000BE', - 'WebColour' => 'red', - 'Exif' => array('type'=>'boolean','default'=>0), - 'Sequence' => null, - 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ZoneCount' => 0, - 'Refresh' => null, - 'DefaultCodec' => 'auto', - 'GroupIds' => array('default'=>array(), 'do_not_update'=>1), -); -private $status_fields = array( - 'Status' => null, - 'AnalysisFPS' => null, - 'CaptureFPS' => null, - 'CaptureBandwidth' => null, -); + protected $defaults = array( + 'Id' => null, + 'Name' => '', + 'Notes' => '', + 'ServerId' => 0, + 'StorageId' => 0, + 'Type' => 'Ffmpeg', + 'Function' => 'Mocord', + 'Enabled' => array('type'=>'boolean','default'=>1), + 'LinkedMonitors' => array('type'=>'set', 'default'=>null), + 'Triggers' => array('type'=>'set','default'=>''), + 'Device' => '', + 'Channel' => 0, + 'Format' => '0', + 'V4LMultiBuffer' => null, + 'V4LCapturesPerFrame' => 1, + 'Protocol' => null, + 'Method' => '', + 'Host' => null, + 'Port' => '', + 'SubPath' => '', + 'Path' => null, + 'Options' => null, + 'User' => null, + 'Pass' => null, + // These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME + 'Width' => null, + 'Height' => null, + 'Colours' => 4, + 'Palette' => '0', + 'Orientation' => null, + 'Deinterlacing' => 0, + 'DecoderHWAccelName' => null, + 'DecoderHWAccelDevice' => null, + 'SaveJPEGs' => 3, + 'VideoWriter' => '0', + 'OutputCodec' => null, + 'OutputContainer' => null, + 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", + 'RecordAudio' => array('type'=>'boolean', 'default'=>0), + 'RTSPDescribe' => array('type'=>'boolean','default'=>0), + 'Brightness' => -1, + 'Contrast' => -1, + 'Hue' => -1, + 'Colour' => -1, + 'EventPrefix' => 'Event-', + 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', + 'LabelX' => 0, + 'LabelY' => 0, + 'LabelSize' => 1, + 'ImageBufferCount' => 20, + 'WarmupCount' => 0, + 'PreEventCount' => 5, + 'PostEventCount' => 5, + 'StreamReplayBuffer' => 0, + 'AlarmFrameCount' => 1, + 'SectionLength' => 600, + 'MinSectionLength' => 10, + 'FrameSkip' => 0, + 'MotionFrameSkip' => 0, + 'AnalysisFPSLimit' => null, + 'AnalysisUpdateDelay' => 0, + 'MaxFPS' => null, + 'AlarmMaxFPS' => null, + 'FPSReportInterval' => 100, + 'RefBlendPerc' => 6, + 'AlarmRefBlendPerc' => 6, + 'Controllable' => array('type'=>'boolean','default'=>0), + 'ControlId' => null, + 'ControlDevice' => null, + 'ControlAddress' => null, + 'AutoStopTimeout' => null, + 'TrackMotion' => array('type'=>'boolean','default'=>0), + 'TrackDelay' => null, + 'ReturnLocation' => -1, + 'ReturnDelay' => null, + 'DefaultRate' => 100, + 'DefaultScale' => 100, + 'SignalCheckPoints' => 0, + 'SignalCheckColour' => '#0000BE', + 'WebColour' => '#ff0000', + 'Exif' => array('type'=>'boolean','default'=>0), + 'Sequence' => null, + 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ZoneCount' => 0, + 'Refresh' => null, + 'DefaultCodec' => 'auto', + 'GroupIds' => array('default'=>array(), 'do_not_update'=>1), + ); + private $status_fields = array( + 'Status' => null, + 'AnalysisFPS' => null, + 'CaptureFPS' => null, + 'CaptureBandwidth' => null, + ); public function Control() { if ( !property_exists($this, 'Control') ) { @@ -127,7 +128,14 @@ private $status_fields = array( } public function Server() { - return new Server($this->{'ServerId'}); + if ( !property_exists($this, 'Server') ) { + if ( $this->ServerId() ) + $this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'})); + if ( !property_exists($this, 'Server') ) { + $this->{'Server'} = new Server(); + } + } + return $this->{'Server'}; } public function __call($fn, array $args){ @@ -472,53 +480,53 @@ private $status_fields = array( //ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null); } -public function sendControlCommand($command) { - // command is generally a command option list like --command=blah but might be just the word quit + public function sendControlCommand($command) { + // command is generally a command option list like --command=blah but might be just the word quit - $options = array(); - # Convert from a command line params to an option array - foreach ( explode(' ', $command) as $option ) { - if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) { - $options[$matches[1]] = $matches[2]?$matches[2]:1; - } else if ( $option != '' and $option != 'quit' ) { - Warning("Ignored command for zmcontrol $option in $command"); - } - } - if ( !count($options) ) { - if ( $command == 'quit' ) { - $options['command'] = 'quit'; - } else { - Warning("No commands to send to zmcontrol from $command"); - return false; - } - } - - if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { - # Local - Logger::Debug('Trying to send options ' . print_r($options, true)); - - $optionString = jsonEncode($options); - Logger::Debug("Trying to send options $optionString"); - // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. - $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); - if ( $socket < 0 ) { - Error('socket_create() failed: '.socket_strerror($socket)); - return false; - } - $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock'; - if ( @socket_connect($socket, $sockFile) ) { - if ( !socket_write($socket, $optionString) ) { - Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket))); - return false; + $options = array(); + # Convert from a command line params to an option array + foreach ( explode(' ', $command) as $option ) { + if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) { + $options[$matches[1]] = $matches[2]?$matches[2]:1; + } else if ( $option != '' and $option != 'quit' ) { + Warning("Ignored command for zmcontrol $option in $command"); } - } else if ( $command != 'quit' ) { - $command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'}; - - // Can't connect so use script - $ctrlOutput = exec(escapeshellcmd($command)); } - socket_close($socket); - } else if ( $this->ServerId() ) { + if ( !count($options) ) { + if ( $command == 'quit' ) { + $options['command'] = 'quit'; + } else { + Warning("No commands to send to zmcontrol from $command"); + return false; + } + } + + if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { + # Local + Logger::Debug('Trying to send options ' . print_r($options, true)); + + $optionString = jsonEncode($options); + Logger::Debug("Trying to send options $optionString"); + // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. + $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); + if ( $socket < 0 ) { + Error('socket_create() failed: '.socket_strerror($socket)); + return false; + } + $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock'; + if ( @socket_connect($socket, $sockFile) ) { + if ( !socket_write($socket, $optionString) ) { + Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket))); + return false; + } + } else if ( $command != 'quit' ) { + $command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'}; + + // Can't connect so use script + $ctrlOutput = exec(escapeshellcmd($command)); + } + socket_close($socket); + } else if ( $this->ServerId() ) { $Server = $this->Server(); $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmcontrol.json'; @@ -543,7 +551,7 @@ public function sendControlCommand($command) { } } catch ( Exception $e ) { Error("Except $e thrown trying to restart zma"); - return false; + return false; } } else { Error('Server not assigned to Monitor in a multi-server setup. Please assign a server to the Monitor.'); diff --git a/web/includes/Object.php b/web/includes/Object.php index 1c17ce312..cc595cd74 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -110,8 +110,9 @@ class ZM_Object { public static function _find_one($class, $parameters = array(), $options = array() ) { global $object_cache; - if ( ! isset($object_cache[$class]) ) + if ( ! isset($object_cache[$class]) ) { $object_cache[$class] = array(); + } $cache = &$object_cache[$class]; if ( ( count($parameters) == 1 ) and @@ -127,6 +128,11 @@ class ZM_Object { return $results[0]; } + public static function _clear_cache($class) { + global $object_cache; + $object_cache[$class] = array(); + } + public static function Objects_Indexed_By_Id($class) { $results = array(); foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) { @@ -290,6 +296,18 @@ Logger::Debug("$k => Have default for $v: "); $this->set($new_values); } + # Set defaults. Note that we only replace "" with null, not other values + # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value + foreach ( $this->defaults as $field => $default ) { + if ( (!array_key_exists($field, $this)) or ($this->{$field} == '') ) { + if ( is_array($default) ) { + $this->{$field} = $default['default']; + } else if ( $default == null ) { + $this->{$field} = $default; + } + } + } + $fields = array_filter( $this->defaults, function($v) { diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 286cec8da..695da80c7 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -58,6 +58,13 @@ class Storage extends ZM_Object { return $this->{'Events'}; } + public function EventCount() { + if ( (! property_exists($this, 'EventCount')) or (!$this->{'EventCount'}) ) { + $this->{'EventCount'} = dbFetchOne('SELECT COUNT(*) AS EventCount FROM Events WHERE StorageId=?', 'EventCount', array($this->Id())); + } + return $this->{'EventCount'}; + } + public function disk_usage_percent() { $path = $this->Path(); if ( ! $path ) { @@ -116,13 +123,14 @@ class Storage extends ZM_Object { $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id())); do { - # Do in batches of 1000 so as to not useup all ram + # Do in batches of 1000 so as to not useup all ram, Event will do caching though... $events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000)); foreach ( $events as $Event ) { $Event->Storage($this); // Prevent further db hit # DiskSpace will update the event $used += $Event->DiskSpace(); } #end foreach + Event::clear_cache(); } while ( count($events) == 1000 ); $this->{'DiskSpace'} = $used; } diff --git a/web/includes/actions/controlcap.php b/web/includes/actions/controlcap.php index eec3ffd8b..ad4985f11 100644 --- a/web/includes/actions/controlcap.php +++ b/web/includes/actions/controlcap.php @@ -28,6 +28,65 @@ if ( $action == 'controlcap' ) { require_once('includes/Control.php'); $Control = new ZM\Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null ); + $field_defaults = array( + 'CanWake' => 0, + 'CanSleep' => 0, + 'CanReset' => 0, + 'CanReboot' => 0, + 'CanMove' => 0, + 'CanMoveDiag' => 0, + 'CanMoveMap' => 0, + 'CanMoveRel' => 0, + 'CanMoveAbs' => 0, + 'CanMoveCon' => 0, + 'CanPan' => 0, + 'HasPanSpeed' => 0, + 'HasTurboPan' => 0, + 'CanTilt' => 0, + 'HasTiltSpeed' => 0, + 'HasTurboTilt' => 0, + 'CanZoom' => 0, + 'CanZoomRel' => 0, + 'CanZoomAbs' => 0, + 'CanZoomCon' => 0, + 'HasZoomSpeed' => 0, + 'CanFocus' => 0, + 'CanAutoFocus' => 0, + 'CanFocusRel' => 0, + 'CanFocusAbs' => 0, + 'CanFocusCon' => 0, + 'HasFocusSpeed' => 0, + 'CanGain' => 0, + 'CanAutoGain' => 0, + 'CanGainRel' => 0, + 'CanGainAbs' => 0, + 'CanGainCon' => 0, + 'HasGainSpeed' => 0, + 'CanWhite' => 0, + 'CanAutoWhite' => 0, + 'CanWhiteRel' => 0, + 'CanWhiteAbs' => 0, + 'CanWhiteCon' => 0, + 'HasWhiteSpeed' => 0, + 'CanIris' => 0, + 'CanAutoIris' => 0, + 'CanIrisRel' => 0, + 'CanIrisAbs' => 0, + 'CanIrisCon' => 0, + 'HasIrisSpeed' => 0, + 'HasPresets' => 0, + 'HasHomePreset' => 0, + 'CanSetPresets' => 0, + ); + + # Checkboxes don't return an element in the POST data, so won't be present in newControl. + # So force a value for these fields + foreach ( $field_defaults as $field => $value ) { + if ( ! (isset($_REQUEST['newControl'][$field]) and $_REQUEST['newControl'][$field]) ) { + $_REQUEST['newControl'][$field] = $value; + } + } # end foreach type + //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); $Control->save($_REQUEST['newControl']); $refreshParent = true; diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php index ae6bf3ddc..ac2435302 100644 --- a/web/includes/actions/filter.php +++ b/web/includes/actions/filter.php @@ -64,35 +64,12 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { $_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1; $_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1; $changes = $filter->changes($_REQUEST['filter']); - ZM\Logger::Debug("Changes: " . print_r($changes,true)); - - if ( 0 ) { - $sql .= ', Query = '.dbEscape(jsonEncode($_REQUEST['filter']['Query'])); - $sql .= ', AutoArchive = '.(!empty($_REQUEST['filter']['AutoArchive']) ? 1 : 0); - $sql .= ', AutoVideo = '. ( !empty($_REQUEST['filter']['AutoVideo']) ? 1 : 0); - $sql .= ', AutoUpload = '. ( !empty($_REQUEST['filter']['AutoUpload']) ? 1 : 0); - $sql .= ', AutoEmail = '. ( !empty($_REQUEST['filter']['AutoEmail']) ? 1 : 0); - $sql .= ', AutoMessage = '. ( !empty($_REQUEST['filter']['AutoMessage']) ? 1 : 0); - $sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0); - $sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']); - $sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0); - if ( !empty($_REQUEST['filter']['AutoMove']) ? 1 : 0) { - $sql .= ', AutoMove = 1, AutoMoveTo='. validInt($_REQUEST['filter']['AutoMoveTo']); - } else { - $sql .= ', AutoMove = 0'; - } - $sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0); - $sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0); - $sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0); - } + ZM\Logger::Debug('Changes: ' . print_r($changes,true)); if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) { - if ( 0 ) { - dbQuery('UPDATE Filters SET '.$sql.' WHERE Id=?', array($_REQUEST['Id'])); - } - $filter->save($changes); if ( $filter->Background() ) $filter->control('stop'); + $filter->save($changes); } else { if ( $action == 'execute' ) { diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index 823a06ff2..f1223a6f9 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -219,7 +219,6 @@ if ( $action == 'monitor' ) { } else { ZM\Error('Error saving new Monitor.'); - $error_message = dbError($sql); return; } } diff --git a/web/includes/config.php.in b/web/includes/config.php.in index ef1bf4a16..909a10e15 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -179,14 +179,14 @@ if ( ! defined('ZM_SERVER_ID') ) { # Use Server lookup so that it caches $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_NAME)); if ( !$Server ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { define('ZM_SERVER_ID', $Server->Id()); } } else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) { $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_HOST)); if ( ! $Server ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { define('ZM_SERVER_ID', $Server->Id()); } @@ -199,7 +199,7 @@ function process_configfile($configFile) { if ( is_readable( $configFile ) ) { $configvals = array(); - $cfg = fopen($configFile, 'r') or Error("Could not open config file: $configFile."); + $cfg = fopen($configFile, 'r') or ZM\Error("Could not open config file: $configFile."); while ( !feof($cfg) ) { $str = fgets($cfg, 256); if ( preg_match('/^\s*(#.*)?$/', $str) ) { @@ -207,7 +207,7 @@ function process_configfile($configFile) { } else if ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches )) { $configvals[$matches[1]] = $matches[2]; } else { - Error("Malformed line in config $configFile\n$str"); + ZM\Error("Malformed line in config $configFile\n$str"); } } fclose($cfg); diff --git a/web/includes/functions.php b/web/includes/functions.php index ca2dd6a05..ede3d53a0 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -409,6 +409,11 @@ ZM\Logger::Debug("Event type: " . gettype($event)); global $user; + if ( $event->Archived() ) { + ZM\Info('Cannot delete Archived event.'); + return; + } # end if Archived + if ( $user['Events'] == 'Edit' ) { $event->delete(); } # CAN EDIT @@ -1082,15 +1087,10 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $validQueryConjunctionTypes = getFilterQueryConjunctionTypes(); $StorageArea = NULL; - $terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL; - if ( !isset($terms) ) { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - ZM\Warning("No terms in filter from $file:$line"); - ZM\Warning(print_r($filter, true)); - } - if ( isset($terms) && count($terms) ) { + # It is not possible to pass an empty array in the url, so we have to deal with there not being a terms field. + $terms = (isset($filter['Query']) and isset($filter['Query']['terms']) and is_array($filter['Query']['terms'])) ? $filter['Query']['terms'] : array(); + + if ( count($terms) ) { for ( $i = 0; $i < count($terms); $i++ ) { $term = $terms[$i]; @@ -1267,7 +1267,7 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); case 'StartDateTime': case 'EndDateTime': if ( $value != 'NULL' ) - $value = '\''.strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) ).'\''; + $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; break; case 'Date': case 'StartDate': @@ -1335,10 +1335,10 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($term['val']); $filter['fields'] .= "\n"; } - } // end if ( isset($term['attr']) ) + } // end if isset($term['attr']) if ( isset($term['cbr']) && (string)(int)$term['cbr'] == $term['cbr'] ) { $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($term['cbr']); - $filter['sql'] .= ' '.str_repeat(')', $term['cbr']).' '; + $filter['sql'] .= ' '.str_repeat(')', $term['cbr']); $filter['fields'] .= "\n"; } } // end foreach term @@ -1347,6 +1347,9 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); if ( $saveToSession ) { $_SESSION['filter'] = $filter; } + } else { + $filter['query'] = $querySep; + #.urlencode('filter[Query][terms]=[]'); } // end if terms #if ( 0 ) { @@ -1359,7 +1362,7 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); #$filter['sql'] .= ' LIMIT ' . validInt($filter['Query']['limit']); #} #} -} +} // end function parseFilter(&$filter, $saveToSession=false, $querySep='&') // Please note that the filter is passed in by copy, so you need to use the return value from this function. // @@ -2615,4 +2618,12 @@ function html_radio($name, $values, $selected=null, $options=array(), $attrs=arr return $html; } # end sub html_radio + +function random_colour() { + return '#'. + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT); +} + ?> diff --git a/web/includes/logger.php b/web/includes/logger.php index 3aa8f891b..2b5463dfb 100644 --- a/web/includes/logger.php +++ b/web/includes/logger.php @@ -274,7 +274,7 @@ class Logger { } } } - return( $this->databaseLevel ); + return $this->databaseLevel; } public function fileLevel( $fileLevel ) { @@ -288,7 +288,7 @@ class Logger { $this->openFile(); } } - return( $this->fileLevel ); + return $this->fileLevel; } public function weblogLevel( $weblogLevel ) { @@ -303,7 +303,7 @@ class Logger { $this->weblogLevel = $weblogLevel; } } - return( $this->weblogLevel ); + return $this->weblogLevel; } public function syslogLevel( $syslogLevel ) { @@ -317,30 +317,31 @@ class Logger { $this->openSyslog(); } } - return( $this->syslogLevel ); + return $this->syslogLevel; } private function openSyslog() { - openlog( $this->id, LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); + openlog($this->id, LOG_PID|LOG_NDELAY, LOG_LOCAL1); } private function closeSyslog() { closelog(); } - private function logFile( $logFile ) { - if ( preg_match( '/^(.+)\+$/', $logFile, $matches ) ) + private function logFile($logFile) { + if ( preg_match('/^(.+)\+$/', $logFile, $matches) ) { $this->logFile = $matches[1].'.'.getmypid(); - else + } else { $this->logFile = $logFile; + } } private function openFile() { if ( !$this->useErrorLog ) { - if ( $this->logFd = fopen( $this->logFile, 'a+' ) ) { - if ( strnatcmp( phpversion(), '5.2.0' ) >= 0 ) { + if ( $this->logFd = fopen($this->logFile, 'a+') ) { + if ( strnatcmp(phpversion(), '5.2.0') >= 0 ) { $error = error_get_last(); - trigger_error( "Can't open log file '$logFile': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR ); + trigger_error("Can't open log file '$logFile': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR); } $this->fileLevel = self::NOLOG; } @@ -349,73 +350,83 @@ class Logger { private function closeFile() { if ( $this->logFd ) - fclose( $this->logFd ); + fclose($this->logFd); } public function logPrint( $level, $string, $file=NULL, $line=NULL ) { - if ( $level <= $this->effectiveLevel ) { - $string = preg_replace( '/[\r\n]+$/', '', $string ); - $code = self::$codes[$level]; + if ( $level > $this->effectiveLevel ) { + return; + } - $time = gettimeofday(); - $message = sprintf( '%s.%06d %s[%d].%s [%s]', strftime( '%x %H:%M:%S', $time['sec'] ), $time['usec'], $this->id, getmypid(), $code, $string ); + $string = preg_replace('/[\r\n]+$/', '', $string); + $code = self::$codes[$level]; - if ( is_null($file) ) { - if ( $this->useErrorLog || $this->databaseLevel > self::NOLOG ) { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - if ( $this->hasTerm ) - $rootPath = getcwd(); - else - $rootPath = $_SERVER['DOCUMENT_ROOT']; - $file = preg_replace( '/^'.addcslashes($rootPath,'/').'\/?/', '', $file ); - } - } + $time = gettimeofday(); + $message = sprintf('%s.%06d %s[%d].%s [%s] [%s]', + strftime('%x %H:%M:%S', $time['sec']), $time['usec'], + $this->id, getmypid(), $code, $_SERVER['REMOTE_ADDR'], $string); - if ( $this->useErrorLog ) - $message .= ' at '.$file.' line '.$line; - else - $message = $message; - - if ( $level <= $this->termLevel ) + if ( is_null($file) ) { + if ( $this->useErrorLog || ($this->databaseLevel > self::NOLOG) ) { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; if ( $this->hasTerm ) - print( $message."\n" ); + $rootPath = getcwd(); else - print( preg_replace( "/\n/", '
', htmlspecialchars($message) ).'
' ); - - if ( $level <= $this->fileLevel ) - if ( $this->useErrorLog ) { - if ( !error_log( $message."\n", 3, $this->logFile ) ) { - if ( strnatcmp( phpversion(), '5.2.0' ) >= 0 ) { - $error = error_get_last(); - trigger_error( "Can't write to log file '".$this->logFile."': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR ); - } - } - } elseif ( $this->logFd ) { - fprintf( $this->logFd, $message."\n" ); - } - - $message = $code.' ['.$string.']'; - if ( $level <= $this->syslogLevel ) - syslog( self::$syslogPriorities[$level], $message ); - if ( $level <= $this->databaseLevel ) { - try { - global $dbConn; - $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, ? )'; - $stmt = $dbConn->prepare( $sql ); - $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; - Error("Can't write log entry '$sql': ". $ex->getMessage()); - } + $rootPath = $_SERVER['DOCUMENT_ROOT']; + $file = preg_replace('/^'.addcslashes($rootPath,'/').'\/?/', '', $file); } - // This has to be last as trigger_error can be fatal - if ( $level <= $this->weblogLevel ) { - if ( $this->useErrorLog ) - error_log( $message, 0 ); - else - trigger_error( $message, self::$phpErrorLevels[$level] ); + } + + if ( $this->useErrorLog ) { + $message .= ' at '.$file.' line '.$line; + } else { + $message = $message; + } + + if ( $level <= $this->termLevel ) { + if ( $this->hasTerm ) + print($message."\n"); + else + print(preg_replace("/\n/", '
', htmlspecialchars($message)).'
'); + } + + if ( $level <= $this->fileLevel ) { + if ( $this->useErrorLog ) { + if ( !error_log($message."\n", 3, $this->logFile) ) { + if ( strnatcmp(phpversion(), '5.2.0') >= 0 ) { + $error = error_get_last(); + trigger_error("Can't write to log file '".$this->logFile."': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR); + } + } + } else if ( $this->logFd ) { + fprintf($this->logFd, $message."\n"); + } + } + + $message = $code.' ['.$string.']'; + if ( $level <= $this->syslogLevel ) + syslog( self::$syslogPriorities[$level], $message ); + + if ( $level <= $this->databaseLevel ) { + try { + global $dbConn; + $sql = 'INSERT INTO `Logs` ( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )'; + $stmt = $dbConn->prepare($sql); + $result = $stmt->execute(array(sprintf('%d.%06d', $time['sec'], $time['usec']), $this->id, + (defined('ZM_SERVER_ID') ? ZM_SERVER_ID : null), getmypid(), $level, $code, $string, $file, $line)); + } catch(PDOException $ex) { + $this->databaseLevel = self::NOLOG; + Error("Can't write log entry '$sql': ". $ex->getMessage()); + } + } + // This has to be last as trigger_error can be fatal + if ( $level <= $this->weblogLevel ) { + if ( $this->useErrorLog ) { + error_log($message, 0); + } else { + trigger_error($message, self::$phpErrorLevels[$level]); } } } diff --git a/web/index.php b/web/index.php index 270c23b86..c912075fd 100644 --- a/web/index.php +++ b/web/index.php @@ -152,8 +152,6 @@ if ( setcookie('zmCSS', $css, time()+3600*24*30*12*10); } -# Only one request can open the session file at a time, so let's close the session here to improve concurrency. -# Any file/page that sets session variables must re-open it. require_once('includes/lang.php'); @@ -186,6 +184,8 @@ if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); require_once('includes/auth.php'); +# Only one request can open the session file at a time, so let's close the session here to improve concurrency. +# Any file/page that sets session variables must re-open it. session_write_close(); foreach ( getSkinIncludes('skin.php') as $includeFile ) { @@ -242,6 +242,12 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n ZM\Logger::Debug('Redirecting to login'); $view = 'none'; $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login'; + if ( ! $request ) { + zm_session_start(); + $_SESSION['postLoginQuery'] = $_SERVER['QUERY_STRING']; + ZM\Error("postLoginQuery " . $_SESSION['postLoginQuery']); + session_write_close(); + } $request = null; } else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) { $view = 'none'; diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index fecc57490..85b9bb52e 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -982,6 +982,14 @@ $OLANG = array( "loglevel=debug" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug) ' ), + 'OPTIONS_DECODERHWACCELNAME' => array( + 'Help' => ' + This is equivalent to the ffmpeg -hwaccel command line option. With intel graphics support, use "vaapi". For NVIDIA cuda support use "cuda". To check for support, run ffmpeg -hwaccels on the command line.' + ), + 'OPTIONS_DECODERHWACCELDEVICE' => array( + 'Help' => ' + This is equivalent to the ffmpeg -hwaccel_device command line option. You should only have to specify this if you have multiple GPUs. A typical value for Intel VAAPI would be /dev/dri/renderD128.' + ), 'OPTIONS_RTSPTrans' => array( 'Help' => ' This sets the RTSP Transport Protocol for FFmpeg.~~ diff --git a/web/skins/classic/css/base/views/controlcap.css b/web/skins/classic/css/base/views/controlcap.css new file mode 100644 index 000000000..ed9a664e7 --- /dev/null +++ b/web/skins/classic/css/base/views/controlcap.css @@ -0,0 +1,4 @@ + +input[type="number"] { + width: 70px; +} diff --git a/web/skins/classic/css/base/views/controlcaps.css b/web/skins/classic/css/base/views/controlcaps.css index 1a2783fd7..d860d88ae 100644 --- a/web/skins/classic/css/base/views/controlcaps.css +++ b/web/skins/classic/css/base/views/controlcaps.css @@ -1,3 +1,9 @@ -#content table.major .colCanMove, #content table.major .colCanZoom, #content table.major .colCanFocus, #content table.major .colCanIris, #content table.major .colCanWhiteBal, #content table.major .colHasPresets { +#content table.major .colCanMove, +#content table.major .colCanZoom, +#content table.major .colCanFocus, +#content table.major .colCanIris, +#content table.major .colCanWhiteBal, +#content table.major .colHasPresets { text-align: center; } + diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index 2b41e7d06..217b2f374 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -9,6 +9,10 @@ width: 100%; } +textarea, +input[name="newMonitor[Name]"] { + width: 100%; +} input[name="newMonitor[Width]"], input[name="newMonitor[Height]"] { width: 80px; diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 02d0d86dd..d43c697e3 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -113,17 +113,17 @@ function exportEventDetail($event, $exportFrames, $exportImages) { - + - + - +
Id() ?>
Name()) ?>
MonitorName()) ?> (MonitorId() ?>)
Monitor()->Name()) ?> (MonitorId() ?>)
Cause()) ?>
Notes()) ?>
StartTime()) ) ?>
StartTime())) ?>
Length() ?>
Frames() ?>
AlarmFrames() ?>
TotScore() ?>
AvgScore() ?>
MaxScore() ?>
Archived()?translate('Yes'):translate('No') ?>
Archived()?'Yes':'No') ?>
@@ -226,13 +226,13 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { ?> @@ -247,7 +247,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { if ( $Monitor->VideoWriter() == '2' ) { # Passthrough $Rotation = $event->Orientation(); - if ( in_array($event->Orientation(),array('90','270')) ) + if ( in_array($event->Orientation(),array('ROTATE_90','ROTATE_270')) ) $Zoom = $event->Height()/$event->Width(); } ?> @@ -280,7 +280,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
 
 
+ onmousedown="slide(event,'horizontal', Width()-20)?>, 1, , ,0, 'imageslider_display_id');"> 
@@ -618,10 +618,10 @@ function exportEventImagesMaster($eids) {

Master

$eids)); + + foreach ($events as $event) { //get monitor id and event id - $event = new ZM\Event($eid); $eventMonitorId[$eid] = $event->MonitorId(); $eventPath[$eid] = $event->Relative_Path(); } @@ -653,20 +653,18 @@ function exportEventImagesMaster($eids) {

All

"; echo '

Monitor: ' . $monitorNames[$monitor_id] . '

'; - foreach ( $eids as $eid ) { - $Event = new ZM\Event($eid); - if ( $Event->MonitorId() == $monitor_id ) { - eventlist_html($Event); + foreach ( $events as $event ) { + if ( $event->MonitorId() == $monitor_id ) { + eventlist_html($event); } # end if its the right monitor } # end foreach event echo ''; @@ -780,7 +778,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex } fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } if ( $exportFrames ) { $file = 'zmEventFrames.html'; @@ -789,7 +787,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex } fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } if ( $exportImages ) { @@ -797,7 +795,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex $myfilelist = array(); foreach ( $files as $file ) { if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) { - $myfilelist[$file] = $exportFileList[$file] = $event->Id().'/'.$file; + $myfilelist[$file] = $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -806,12 +804,12 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex // create an image slider if ( !empty($myfilelist) ) { - $file = $event->Id().'/zmEventImages.html'; + $file = 'zmEventImages.html'; if ( !($fp = fopen($file, 'w')) ) ZM\Fatal("Can't open event images export file '$file'"); fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } } # end if exportImages @@ -819,7 +817,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex $filesLeft = array(); foreach ( $files as $file ) { if ( preg_match('/\.(?:mpg|mpeg|mov|swf|mp4|mkv|avi|asf|3gp)$/', $file) ) { - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -829,7 +827,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex if ( $exportMisc ) { foreach ( $files as $file ) { - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } $files = array(); } @@ -850,10 +848,10 @@ function exportEvents( ) { if ( !canView('Events') ) { - ZM\Error("You do not have permission to view events."); + ZM\Error('You do not have permission to view events.'); return false; } else if ( empty($eids) ) { - ZM\Error("Attempt to export an empty list of events."); + ZM\Error('Attempt to export an empty list of events.'); return false; } @@ -863,23 +861,22 @@ function exportEvents( } # Ensure that we are going to be able to do this. - if ( ! file_exists(ZM_DIR_EXPORTS) ) { - if ( ! mkdir(ZM_DIR_EXPORTS) ) { - ZM\Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); - } + if ( ! ( mkdir(ZM_DIR_EXPORTS) or file_exists(ZM_DIR_EXPORTS) ) ) { + ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } + chmod(ZM_DIR_EXPORTS, 0700); $export_dir = ZM_DIR_EXPORTS.'/zmExport_'.$connkey; # Ensure that we are going to be able to do this. - if ( ! file_exists($export_dir) ) { - if ( ! mkdir($export_dir) ) { - ZM\Fatal("Can't create exports dir at '$export_dir'"); - } else { - ZM\Logger::Debug("Successfully created dir '$export_dir'"); - } + if ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) { + ZM\Fatal("Can't create exports dir at '$export_dir'"); + } else { + ZM\Logger::Debug("Successfully created dir '$export_dir'"); } - if ( !chdir($export_dir) ) + chmod($export_dir, 0700); + if ( !chdir($export_dir) ) { ZM\Fatal("Can't chdir to $export_dir"); + } $export_root = 'zmExport'; $export_listFile = 'zmFileList.txt'; @@ -889,27 +886,30 @@ function exportEvents( if ( !is_array($eids) ) { $eids = array($eids); } - ZM\Logger::Debug("Eids: " . print_r($eids,true)); + ZM\Logger::Debug('Eids: ' . print_r($eids,true)); foreach ( $eids as $eid ) { $event = new ZM\Event($eid); $event_dir = $export_dir.'/'.$event->Id(); - if ( !mkdir($event_dir) ) + if ( !(mkdir($event_dir) or file_exists($event_dir) ) ) { ZM\Error("Can't mkdir $event_dir"); + } $event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc); ZM\Logger::Debug("File list for event $eid " . print_r($event_exportFileList, true)); $exportFileList = array_merge($exportFileList,$event_exportFileList); foreach ( $event_exportFileList as $file ) { - if ( preg_match('/\.html$/', $file ) ) + if ( preg_match('/\.html$/', $file) ) continue; - ZM\Logger::Debug('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file); - exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file); + #exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file, $output, $return); + $cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1'; + exec($cmd, $output, $return); + ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true)); } - } + } # end foreach event // create an master image if ( $exportImages ) { if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') ) - ZM\Error("Failed linking jquery.js"); + ZM\Error('Failed linking jquery.js'); //if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') ) //Error("Failed linking video.js"); @@ -940,6 +940,8 @@ function exportEvents( $archive = ''; if ( $exportFormat == 'tar' ) { $archive = ZM_DIR_EXPORTS.'/'.$export_root.($connkey?'_'.$connkey:'').'.tar'; + $version = shell_exec('tar -v'); + $command = 'tar --create --dereference'; if ( $exportCompressed ) { $archive .= '.gz'; @@ -947,8 +949,11 @@ function exportEvents( $exportFormat .= '.gz'; } if ( $exportStructure == 'flat' ) { - //strip file paths if we - $command .= " --xform='s#^.+/##x'"; + if (preg_match("/BSD/i", $version)) { + $command .= " -s '#^.*/##'"; + } else { + $command .= " --xform='s#^.+/##x'"; + } } $command .= ' --file='.escapeshellarg($archive); } elseif ( $exportFormat == 'zip' ) { @@ -960,12 +965,12 @@ function exportEvents( @unlink($archive); $command .= ' zmExport_' . $connkey.'/'; - ZM\Logger::Debug("Command is $command"); exec($command, $output, $status); if ( $status ) { ZM\Error("Command '$command' returned with status $status"); - if ( isset($output[0]) ) - ZM\Error("First line of output is '".$output[0]."'"); + if ( isset($output[0]) ) { + ZM\Error('First line of output is \''.$output[0].'\''); + } return false; } diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 60558f8e8..9f14d93d0 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -30,7 +30,6 @@ function xhtmlHeaders($file, $title) { $skinCssPhpFile = getSkinFile('css/'.$css.'/skin.css.php'); - $skinJsFile = getSkinFile('js/skin.js'); $skinJsPhpFile = getSkinFile('js/skin.js.php'); $cssJsFile = getSkinFile('js/'.$css.'.js'); @@ -84,7 +83,6 @@ echo output_link_if_exists( array( 'css/base/views/'.$basename.'.css', 'js/dateTimePicker/jquery-ui-timepicker-addon.css', 'js/jquery-ui-1.12.1/jquery-ui.structure.min.css', - #'js/jquery-ui-1.12.1/jquery-ui.theme.min.css', ) ); if ( $css != 'base' ) @@ -95,8 +93,9 @@ if ( $css != 'base' ) ) ); ?> + - + - + ZM_LOG_DATABASE_LIMIT ) { - dbQuery('DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?', array($rows - ZM_LOG_DATABASE_LIMIT)); + dbQuery('DELETE low_priority FROM `Logs` ORDER BY `TimeKey` ASC LIMIT ?', array($rows - ZM_LOG_DATABASE_LIMIT)); } } else if ( preg_match('/^\d\s*(hour|minute|day|week|month|year)$/', ZM_LOG_DATABASE_LIMIT, $matches) ) { - dbQuery('DELETE FROM Logs WHERE TimeKey < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.') LIMIT 100'); + dbQuery('DELETE FROM `Logs` WHERE `TimeKey` < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.') LIMIT 100'); } else { ZM\Error('Potentially invalid value for ZM_LOG_DATABASE_LIMIT: ' . ZM_LOG_DATABASE_LIMIT); } diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 4ecc4c234..48747b98d 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -29,7 +29,7 @@ var popupSizes = { 'console': {'width': 750, 'height': 312}, 'control': {'width': 480, 'height': 480}, 'controlcaps': {'width': 780, 'height': 320}, - 'controlcap': {'width': 400, 'height': 400}, + 'controlcap': {'width': 600, 'height': 500}, 'cycle': {'addWidth': 32, 'minWidth': 384, 'addHeight': 62}, 'device': {'width': 260, 'height': 150}, 'devices': {'width': 400, 'height': 240}, diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 5ccddf96b..9e705bbc3 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -175,12 +175,13 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { }); // 'data-on-click' calls the global function in the attribute value with no arguments when a click happens. - document.querySelectorAll("a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { + document.querySelectorAll("i[data-on-click], a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click"); if ( !window[fnName] ) { console.error("Nothing found to bind to " + fnName + " on element " + el.name); return; } + el.onclick = function() { window[fnName](); }; diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 8d8b08ff6..b910b11fa 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -187,7 +187,8 @@ getBodyTopHTML(); translate('New'), 'Type' => "Local", @@ -166,16 +157,12 @@ xhtmlHeaders(__FILE__, translate('ControlCap')." - ".$newControl['Name'] );
    $value ) -{ - if ( $tab == $name ) - { +foreach ( $tabs as $name=>$value ) { + if ( $tab == $name ) { ?>
  • $value ) @@ -201,8 +187,7 @@ if ( $tab != 'main' ) @@ -212,8 +197,7 @@ if ( $tab != 'move' ) @@ -227,8 +211,7 @@ if ( $tab != 'pan' ) @@ -242,8 +225,7 @@ if ( $tab != 'tilt' ) @@ -258,8 +240,7 @@ if ( $tab != 'zoom' ) @@ -275,8 +256,7 @@ if ( $tab != 'focus' ) @@ -292,8 +272,7 @@ if ( $tab != 'iris' ) @@ -309,8 +288,7 @@ if ( $tab != 'gain' ) @@ -326,8 +304,7 @@ if ( $tab != 'white' ) @@ -336,19 +313,17 @@ if ( $tab != 'presets' ) - +
    - -translate('Local'), 'Remote'=>translate('Remote'), 'Ffmpeg'=>translate('Ffmpeg'), 'Libvlc'=>translate('Libvlc'), 'cURL'=>"cURL"); -?> - + + @@ -373,15 +348,15 @@ switch ( $tab ) { ?> - - - - + + + + - - + + - + - - - - + + + + - - + + - + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - +
    translate('Local'), 'Remote'=>translate('Remote'), 'Ffmpeg'=>translate('Ffmpeg'), 'Libvlc'=>translate('Libvlc'), 'cURL'=>'cURL'); + echo buildSelect('newControl[Type]', $types); ?>
    checked="checked"/>
    checked="checked"/>
    checked="checked">
    checked="checked"/>
    checked="checked"/>
    checked="checked">
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    checked="checked"/>
    - disabled="disabled"/> + +
diff --git a/web/skins/classic/views/export.php b/web/skins/classic/views/export.php index b31afa879..c008b0f2b 100644 --- a/web/skins/classic/views/export.php +++ b/web/skins/classic/views/export.php @@ -138,7 +138,7 @@ $disk_space_total = 0; $event_count = 0; while ( $event_row = dbFetchNext($results) ) { $event = new ZM\Event($event_row); - $scale = max(reScale(SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); + $scale = max(reScale(SCALE_BASE, $event->Monitor()->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); ?> Archived() ? ' class="archived"' : '' ?>> diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index be388fe0e..27a434f73 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -229,7 +229,7 @@ for ( $i=0; $i < count($terms); $i++ ) { } ?> 2 ) { echo htmlSelect("filter[Query][terms][$i][obr]", $obracketTypes, $term['obr']); } else { ?>  - + 'checkValue')); ?> 0) { //add and/or to 1+ + if ( rowNum == 0 ) inputTds.eq(0).html(' '); //Remove and from first term + if ( rowNum > 0 ) { //add and/or to 1+ var cnjVal = inputTds.eq(0).children().val(); var conjSelect = $j('').attr('name', queryPrefix + rowNum + '][cnj]').attr('id', queryPrefix + rowNum + '][cnj]'); $j.each(conjTypes, function(i) { @@ -174,14 +174,14 @@ function parseRows(rows) { inputTds.eq(1).html(obrSelect).children().val(obrVal); //Set bracket contents and assign saved value inputTds.eq(5).html(cbrSelect).children().val(cbrVal); } else { - inputTds.eq(1).html(' '); //Blank if there aren't enough terms for brackets + inputTds.eq(1).html(' '); // Blank if there aren't enough terms for brackets inputTds.eq(5).html(' '); } if ( rows.length == 1 ) { - inputTds.eq(6).find(':input[value="-"]').prop('disabled', true); //enable/disable remove row button + inputTds.eq(6).find('button[data-on-click-this="delTerm"]').prop('disabled', true); //enable/disable remove row button } else { - inputTds.eq(6).find(':input[value="-"]').prop('disabled', false); + inputTds.eq(6).find('button[data-on-click-this="delTerm"]').prop('disabled', false); } var attr = inputTds.eq(2).children().val(); @@ -276,7 +276,7 @@ if ( 0 ) { term[2] = rowNum; inputTds.eq(2).children().eq(0).attr('name', 'filter'+stringFilter(term)); inputTds.eq(2).children().eq(0).attr('id', 'filter'+stringFilter(term)); - } // end foreach term/row + } //End for each term/row history.replaceState(null, null, '?view=filter&' + $j('#contentForm').serialize()); } diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index b4b6b696a..811fa5f28 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -172,9 +172,9 @@ function clearLog() { logReq.cancel(); var clearParms = 'view=request&request=log&task=delete'; - var clearReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse} ); - var tbody = $(logTable).getElement( 'tbody' ); - var rows = tbody.getElements( 'tr' ); + var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse}); + var tbody = $(logTable).getElement('tbody'); + var rows = tbody.getElements('tr'); if ( rows ) { var minTime = rows[0].getElement('td').get('text'); clearParms += "&minTime="+encodeURIComponent(minTime); @@ -190,7 +190,7 @@ function filterLog() { filterFields.each( function( field ) { var selector = $('filter['+field+']'); - if ( ! selector ) { + if ( !selector ) { if ( window.console && window.console.log ) { window.console.log('No selector found for ' + field); } @@ -226,9 +226,9 @@ function exportResponse( response ) { function exportFail( request ) { $('exportLog').unspin(); - $('exportErrorText').set('text', request.status+' / '+request.statusText ); + $('exportErrorText').set('text', request.status+' / '+request.statusText); $('exportError').show(); - Error('Export request failed: '+request.status+' / '+request.statusText ); + Error('Export request failed: '+request.status+' / '+request.statusText); } function exportRequest() { @@ -237,7 +237,7 @@ function exportRequest() { $('exportError').hide(); if ( form.validate() ) { var exportParms = "view=request&request=log&task=export"; - var exportReq = new Request.JSON( {url: thisUrl, method: 'post', link: 'cancel', onSuccess: exportResponse, onFailure: exportFail} ); + var exportReq = new Request.JSON({url: thisUrl, method: 'post', link: 'cancel', onSuccess: exportResponse, onFailure: exportFail}); var selection = form.getElement('input[name=selector]:checked').get('value'); if ( selection == 'filter' || selection == 'current' ) { $$('#filters select').each( @@ -256,7 +256,7 @@ function exportRequest() { exportParms += "&maxTime="+encodeURIComponent(maxTime); } } - exportReq.send( exportParms+"&"+form.toQueryString() ); + exportReq.send(exportParms+"&"+form.toQueryString()); $('exportLog').spin(); } } @@ -265,7 +265,7 @@ function updateFilterSelectors() { Object.each(options, function( values, key ) { var selector = $('filter['+key+']'); - if ( ! selector ) { + if ( !selector ) { if ( window.console && window.console.log ) { window.console.log('No selector found for ' + key); } diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index 9d1fc7d7b..3f962ebfb 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -131,9 +131,36 @@ function initPage() { document.querySelectorAll('select[name="newMonitor[ControlId]"]').forEach(function(el) { el.onchange = window['loadLocations'].bind(el, el); }); + document.querySelectorAll('input[name="newMonitor[WebColour]"]').forEach(function(el) { + el.onchange = window['change_WebColour'].bind(el); + }); $j('.chosen').chosen(); } // end function initPage() +function change_WebColour() { + $j('#WebSwatch').css( + 'backgroundColor', + $j('input[name="newMonitor[WebColour]"]').val() + ); +} + +function getRandomColour() { + var letters = '0123456789ABCDEF'; + var colour = '#'; + for (var i = 0; i < 6; i++) { + colour += letters[Math.floor(Math.random() * 16)]; + } + return colour; +} + +function random_WebColour() { + var new_colour = getRandomColour(); + $j('input[name="newMonitor[WebColour]"]').val(new_colour); + $j('#WebSwatch').css( + 'backgroundColor', new_colour + ); +} + window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index c39d7311d..97eab55a2 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -35,7 +35,7 @@ monitorData[monitorData.length] = { 'connKey': connKey() ?>, 'width': ViewWidth() ?>, 'height':ViewHeight() ?>, - 'url': 'UrlToIndex( $monitor->Id() + ZM_MIN_STREAMING_PORT) ?>', + 'url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', ViewWidth(), $monitor->PopupScale() ); ?>, ViewHeight(), $monitor->PopupScale() ); ?> );}, 'type': 'Type() ?>', 'refresh': 'Refresh() ?>' diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index ecea1ba6f..526ce0bb5 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -18,7 +18,7 @@ var speedIndex=; // for history, and fps for live, and dynamically determined (in ms) var currentDisplayInterval=; -var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live) +var playSecsPerInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live) var timerInterval; // milliseconds between interrupts var timerObj; // object to hold timer interval; var freeTimeLastIntervals=[]; // Percentage of current interval used in loading most recent image diff --git a/web/skins/classic/views/js/postlogin.js.php b/web/skins/classic/views/js/postlogin.js.php index 6fa681fad..75c241a91 100644 --- a/web/skins/classic/views/js/postlogin.js.php +++ b/web/skins/classic/views/js/postlogin.js.php @@ -11,12 +11,15 @@ ( function () { // Append '?(GET query)' to URL if the GET query is not empty. - var querySuffix = ""; + ?>'; if ( querySuffix == '?view=login' ) { // If we didn't redirect elsewhere, then don't show login page, go to console diff --git a/web/skins/classic/views/js/state.js b/web/skins/classic/views/js/state.js index 7165c690c..e8b4db36d 100644 --- a/web/skins/classic/views/js/state.js +++ b/web/skins/classic/views/js/state.js @@ -54,7 +54,7 @@ $j(document).ready(function() { url: thisUrl, data: formData, dataType: 'html', - enocde: true + timeout: 0 }).done(function(data) { location.reload(); }); diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 0b4715f75..8d3f01d4c 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -184,7 +184,7 @@ function tlZoomBounds( minTime, maxTime ) { } function tlZoomOut() { - location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+zoom_range); + location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+zoomout_range); } function tlPanLeft() { diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index b6127a68d..242ec80f0 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -44,17 +44,17 @@ function changeScale() { newHeight = monitorHeight * scale / SCALE_BASE; } - Cookie.write( 'zmWatchScale'+monitorId, scale, {duration: 10*365} ); + Cookie.write('zmWatchScale'+monitorId, scale, {duration: 10*365}); /*Stream could be an applet so can't use moo tools*/ var streamImg = $('liveStream'+monitorId); if ( streamImg ) { - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; + streamImg.style.width = newWidth + 'px'; + streamImg.style.height = newHeight + 'px'; streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale)); } else { - console.error("No element found for liveStream."); + console.error('No element found for liveStream.'); } } @@ -64,17 +64,17 @@ var lastAlarmState = STATE_IDLE; function setAlarmState( currentAlarmState ) { alarmState = currentAlarmState; - var stateClass = ""; + var stateClass = ''; if ( alarmState == STATE_ALARM ) { - stateClass = "alarm"; + stateClass = 'alarm'; } else if ( alarmState == STATE_ALERT ) { - stateClass = "alert"; + stateClass = 'alert'; } - $('stateValue').set( 'text', stateStrings[alarmState] ); + $('stateValue').set('text', stateStrings[alarmState]); if ( stateClass ) { - $('stateValue').setProperty( 'class', stateClass ); + $('stateValue').setProperty('class', stateClass); } else { - $('stateValue').removeProperty( 'class' ); + $('stateValue').removeProperty('class'); } var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ); @@ -114,7 +114,7 @@ function setAlarmState( currentAlarmState ) { } if ( monitorType != 'WebSite' ) { - var streamCmdParms = "view=request&request=stream&connkey="+connKey; + var streamCmdParms = 'view=request&request=stream&connkey='+connKey; if ( auth_hash ) { streamCmdParms += '&auth='+auth_hash; } @@ -141,7 +141,7 @@ function getStreamCmdFailure(xhr) { console.log(xhr); } function getStreamCmdResponse(respObj, respText) { - watchdogOk("stream"); + watchdogOk('stream'); if ( streamCmdTimer ) { streamCmdTimer = clearTimeout(streamCmdTimer); } @@ -229,12 +229,18 @@ function getStreamCmdResponse(respObj, respText) { } // end if canEditMonitors if ( streamStatus.auth ) { + 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); } + streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + eventCmdParms = eventCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + actParms = actParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } // end if have a new auth hash } // end if respObj.status } else { @@ -473,6 +479,9 @@ function getActResponse( respObj, respText ) { function deleteEvent( event, eventId ) { var actParms = "view=request&request=event&action=delete&id="+eventId; + if ( auth_hash ) { + actParms += '&auth='+auth_hash; + } var actReq = new Request.JSON( { url: thisUrl, method: 'post', diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index 7dd83ef2a..044a3e788 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -122,9 +122,12 @@ xhtmlHeaders(__FILE__, translate('SystemLog') );
- - - + + + + + +
diff --git a/web/skins/classic/views/login.php b/web/skins/classic/views/login.php index 788f46163..76ebeafd8 100644 --- a/web/skins/classic/views/login.php +++ b/web/skins/classic/views/login.php @@ -7,7 +7,6 @@ xhtmlHeaders(__FILE__, translate('Login')); - - - @@ -278,15 +276,10 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Path()), $canEdit ) ?> Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Type()), $canEdit ) ?> Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Scheme()), $canEdit ) ?> - Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Server()->Name()), $canEdit ) ?> - - disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) -?> - - Events()).' using '.human_filesize($Storage->event_disk_space()) ?> - Events()) or !$canEdit ) { ?> disabled="disabled"Events()) ? ' title="Can\' delete as long as there are events stored here."' : ''?>/> + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Server()->Name()), $canEdit ) ?> + disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> + EventCount().' using '.human_filesize($Storage->event_disk_space()) ?> + EventCount() or !$canEdit ) { ?> disabled="disabled"EventCount() ? ' title="Can\'t delete as long as there are events stored here."' : ''?>/> @@ -319,23 +312,28 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ } function updateSelected() { + # Turn them all off, then selectively turn the checked ones back on dbQuery('UPDATE `Users` SET `APIEnabled`=0'); - foreach ( $_REQUEST["tokenUids"] as $markUid ) { - $minTime = time(); - dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); + + if ( isset($_REQUEST['tokenUids']) ) { + foreach ( $_REQUEST['tokenUids'] as $markUid ) { + $minTime = time(); + dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); + } } - foreach ( $_REQUEST["apiUids"] as $markUid ) { - dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); - + if ( isset($_REQUEST['apiUids']) ) { + foreach ( $_REQUEST['apiUids'] as $markUid ) { + dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); + } } echo ''.translate('Updated').''; } - if ( array_key_exists('revokeAllTokens',$_POST) ) { + if ( array_key_exists('revokeAllTokens', $_POST) ) { revokeAllTokens(); } - if ( array_key_exists('updateSelected',$_POST) ) { + if ( array_key_exists('updateSelected', $_POST) ) { updateSelected(); } ?> diff --git a/web/skins/classic/views/state.php b/web/skins/classic/views/state.php index 12180000d..b5c069600 100644 --- a/web/skins/classic/views/state.php +++ b/web/skins/classic/views/state.php @@ -24,7 +24,11 @@ if ( !canEdit('System') ) { } ?>