Merge branch 'storageareas' of github.com:connortechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2018-08-31 18:43:29 -04:00
commit 14b5fc3660
81 changed files with 3716 additions and 2988 deletions

View File

@ -1,6 +1,6 @@
You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas:
- The ZoneMinder IRC channel - irc.freenode.net #zoneminder
- The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/)
- The [ZoneMinder Forum](https://forums.zoneminder.com/)
**Do not post feature or enhancement requests, general discussions or support questions here.**

View File

@ -3,6 +3,8 @@ ZoneMinder
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://zoneminder-chat.herokuapp.com)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
## Overview

View File

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

View File

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

View File

@ -5,7 +5,7 @@
-- Add Refresh column to Monitors table
--
ALTER TABLE `zm`.`Monitors`
ALTER TABLE `Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(
@ -16,7 +16,7 @@ SET @s = (SELECT IF(
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL"
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
));
PREPARE stmt FROM @s;

23
db/zm_update-1.31.45.sql Normal file
View File

@ -0,0 +1,23 @@
--
-- This updates a 1.31.44 database to 1.31.45
--
-- Add WebSite enum to Monitor.Type
-- Add Refresh column to Monitors table
-- This is the same as the update to 1.31.43, but due to Refresh not being added to zm_create.sql.in we need to have it
-- again in order to fix people who did a fresh install from 1.31.43 or 1.31.44.
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,8 +13,8 @@ The API is built in CakePHP and lives under the ``/api`` directory. It
provides a RESTful service and supports CRUD (create, retrieve, update, delete)
functions for Monitors, Events, Frames, Zones and Config.
Security
^^^^^^^^^
Login, Logout & API Security
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The APIs tie into ZoneMinder's existing security model. This means if you have
OPT_AUTH enabled, you need to log into ZoneMinder using the same browser you plan to
use the APIs from. If you are developing an app that relies on the API, you need
@ -23,11 +23,32 @@ to do a POST login from the app into ZoneMinder before you can access the API.
Then, you need to re-use the authentication information of the login (returned as cookie states)
with subsequent APIs for the authentication information to flow through to the APIs.
This means if you plan to use cuRL to experiment with these APIs, you first need to do
This means if you plan to use cuRL to experiment with these APIs, you first need to login:
**Login process for ZoneMinder v1.32.0 and above**
::
curl -d "username=XXXX&password=YYYY&action=login&view=console" -c cookies.txt http://yourzmip/zm/index.php
curl -XPOST -d "user=XXXX&pass=YYYY" -c cookies.txt http://yourzmip/zm/api/login.json
Staring ZM 1.32.0, you also have a `logout` API that basically clears your session. It looks like this:
::
curl -b cookies.txt http://yourzmip/zm/api/logout.json
**Login process for older versions of ZoneMinder**
::
curl -d "username=XXXX&password=YYYY&action=login&view=console" -c cookies.txt http://yourzmip/zm/index.php
The equivalent logout process for older versions of ZoneMinder is:
::
curl -XPOST -d "username=XXXX&password=YYYY&action=logout&view=console" -b cookies.txt http://yourzmip/zm/index.php
replacing *XXXX* and *YYYY* with your username and password, respectively.
@ -36,25 +57,55 @@ and the command will silently fail.
What the "-c cookies.txt" does is store a cookie state reflecting that you have logged into ZM. You now need
to apply that cookie state to all subsequent APIs. You do that by using a '-b cookies.txt' to subsequent APIs if you are
to apply that cookie state to all subsequent APIs. You do that by using a '-b cookies.txt' to subsequent APIs if you are
using CuRL like so:
::
curl -b cookies.txt http://yourzmip/zm/api/monitors.json
curl -b cookies.txt http://yourzmip/zm/api/monitors.json
This would return a list of monitors and pass on the authentication information to the ZM API layer.
A deeper dive into the login process
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As you might have seen above, there are two ways to login, one that uses the `login.json` API and the other that logs in using the ZM portal. If you are running ZoneMinder 1.32.0 and above, it is *strongly* recommended you use the `login.json` approach. The "old" approach will still work but is not as powerful as the API based login. Here are the reasons why:
* The "old" approach basically uses the same login webpage (`index.php`) that a user would log into when viewing the ZM console. This is not really using an API and more importantly, if you have additional components like reCAPTCHA enabled, this will not work. Using the API approach is much cleaner and will work irrespective of reCAPTCHA
* The new login API returns important information that you can use to stream videos as well, right after login. Consider for example, a typical response to the login API (`/login.json`):
::
{
"credentials": "auth=f5b9cf48693fe8552503c8ABCD5",
"append_password": 0,
"version": "1.31.44",
"apiversion": "1.0"
}
In this example I have `OPT_AUTH` enabled in ZoneMinder and it returns my credential key. You can then use this key to stream images like so:
::
<img src="https://server/zm/cgi-bin/nph-zms?monitor=1&auth=<authval>" />
Where `authval` is the credentials returned to start streaming videos.
The `append_password` field will contain 1 when it is necessary for you to append your ZM password. This is the case when you set `AUTH_RELAY` in ZM options to "plain", for example. In that case, the `credentials` field may contain something like `&user=admin&pass=` and you have to add your password to that string.
.. NOTE:: It is recommended you invoke the `login` API once every 60 minutes to make sure the session stays alive. The same is true if you use the old login method too.
This would return a list of monitors and pass on the authentication information to the ZM API layer.
So remember, if you are using authentication, please add a ``-b cookies.txt`` to each of the commands below if you are using
CuRL. If you are not using CuRL and writing your own app, you need to make sure you pass on cookies to subsequent requests
in your app.
Examples (please read security notice above)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You will see each URL ending in either ``.xml`` or ``.json``. This is the
format of the request, and it determines the format that any data returned to
you will be in. I like json, however you can use xml if you'd like.
Please remember, if you are using authentication, please add a ``-b cookies.txt`` to each of the commands below if you are using
CuRL. If you are not using CuRL and writing your own app, you need to make sure you pass on cookies to subsequent requests
in your app.
(In all examples, replace 'server' with IP or hostname & port where ZoneMinder is running)

View File

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

View File

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

View File

@ -0,0 +1,475 @@
# ==========================================================================
#
# ZoneMinder Dericam P2 Control Protocol Module
# Copyright (C) Roman Dissertori
#
# 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.
#
# ==========================================================================
#
# This module contains the implementation of the Dericam P2 device control protocol
#
package ZoneMinder::Control::DericamP2;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our %CamParams = ();
# ==========================================================================
#
# Dericam P2 Control Protocol
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:@10.1.2.1:80
# zoneminder:zonepass@10.0.100.1:40000
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{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" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
Info( "http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=getimageattr";
my $req = $self->sendCmd( $cmd );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
# Parse results setting values in %FCParams
my $content = $res->decoded_content;
while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $stop_command = shift;
my $autostop = shift;
if( $stop_command && $autostop)
{
Debug( "Auto Stop" );
usleep( $autostop );
my $cmd = "decoder_control.cgi?command=".$stop_command;
$self->sendCmd( $cmd );
}
}
# Reset the Camera
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
# Move to default position
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=home";
$self->sendCmd( $cmd );
# Reset all other values to default
$cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-image_type=1&-default=on";
$self->sendCmd( $cmd );
}
# Reboot Camera (on Sleep button)
sub sleep
{
my $self = shift;
Debug( "Camera Reboot" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=sysreboot";
$self->sendCmd( $cmd );
}
# Stop the Camera
sub moveStop
{
my $self = shift;
Debug( "Camera Stop" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=stop";
$self->sendCmd( $cmd );
}
#Up Arrow
sub moveConUp
{
my $self = shift;
my $stop_command = "1";
Debug( "Move Up" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=up&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Down Arrow
sub moveConDown
{
my $self = shift;
my $stop_command = "3";
Debug( "Move Down" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=down&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Left Arrow
sub moveConLeft
{
my $self = shift;
my $stop_command = "5";
Debug( "Move Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=left&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Right Arrow
sub moveConRight
{
my $self = shift;
my $stop_command = "7";
Debug( "Move Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=right&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom In
sub zoomConTele
{
my $self = shift;
Debug( "Zoom Tele" );
my $cmd = "decoder_control.cgi?command=18";
$self->sendCmd( $cmd );
}
#Zoom Out
sub zoomConWide
{
my $self = shift;
Debug( "Zoom Wide" );
my $cmd = "decoder_control.cgi?command=16";
$self->sendCmd( $cmd );
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=upright&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
my $self = shift;
Debug( "Move Diagonally Down Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=downright&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
my $self = shift;
Debug( "Move Diagonally Up Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=upleft&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
my $self = shift;
Debug( "Move Diagonally Down Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=downnleft&-speed=45";
$self->sendCmd( $cmd );
}
#Set Camera Preset
#Presets must be translated into values internal to the camera
#Those values are: 30,32,34,36,38,40,42,44 for presets 1-8 respectively
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=preset&-act=set&-number=".(($preset*2) + 28);
$self->sendCmd( $cmd );
}
}
#Recall Camera Preset
#Presets must be translated into values internal to the camera
#Those values are: 31,33,35,37,39,41,43,45 for presets 1-8 respectively
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=preset&-act=goto&-number=".(($preset*2) + 29);
$self->sendCmd( $cmd );
}
if ( $preset == 9 ) {
$self->horizontalPatrol();
}
if ( $preset == 10 ) {
$self->verticalPatrol();
}
}
#Horizontal Patrol
sub horizontalPatrol
{
my $self = shift;
Debug( "Horizontal Patrol" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=hscan";
$self->sendCmd( $cmd );
}
#Vertical Patrol
sub verticalPatrol
{
my $self = shift;
Debug( "Vertical Patrol" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=vscan";
$self->sendCmd( $cmd );
}
# Increase Brightness
sub irisAbsOpen
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} += $step;
$CamParams{'brightness'} = 100 if ($CamParams{'brightness'} > 100);
Debug( "Increase Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
# Decrease Brightness
sub irisAbsClose
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} -= $step;
$CamParams{'brightness'} = 0 if ($CamParams{'brightness'} < 0);
Debug( "Decrease Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
# Increase Contrast
sub whiteAbsIn
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} += $step;
$CamParams{'contrast'} = 100 if ($CamParams{'contrast'} > 100);
Debug( "Increase Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
# Decrease Contrast
sub whiteAbsOut
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} -= $step;
$CamParams{'contrast'} = 0 if ($CamParams{'contrast'} < 0);
Debug( "Decrease Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
#TODO Saturation cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=44 [0-255]
sub satIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} += $step;
$CamParams{'saturation'} = 255 if ($CamParams{'saturation'} > 255);
Debug( "Increase Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
sub satDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} -= $step;
$CamParams{'saturation'} = 0 if ($CamParams{'saturation'} < 0);
Debug( "Decrease Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
#TODO Sharpness cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=37 [0-100]
sub sharpIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} += $step;
$CamParams{'sharpness'} = 100 if ($CamParams{'sharpness'} > 100);
Debug( "Increase Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
sub sharpDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} -= $step;
$CamParams{'sharpness'} = 0 if ($CamParams{'sharpness'} < 0);
Debug( "Decrease Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
#TODO Hue cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=37 [0-100]
sub hueIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} += $step;
$CamParams{'hue'} = 100 if ($CamParams{'hue'} > 100);
Debug( "Increase Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
sub hueDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} -= $step;
$CamParams{'hue'} = 0 if ($CamParams{'hue'} < 0);
Debug( "Decrease Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
1;

View File

@ -74,377 +74,351 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub open
{
my $self = shift;
sub open {
my $self = shift;
$self->loadMonitor();
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'open';
$self->{state} = 'open';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
sub printMsg {
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
Debug($msg.'['.$msg_len.']');
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $msg = shift;
my $content_type = shift;
my $result = undef;
sub sendCmd {
my $self = shift;
my $cmd = shift;
my $msg = shift;
my $content_type = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
printMsg($cmd, 'Tx');
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/$cmd";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => $content_type);
$req->header('Host' => $self->{Monitor}->{ControlAddress});
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd;
my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => $content_type);
$req->header('Host' => $self->{Monitor}->{ControlAddress});
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" );
if ( $res->is_success ) {
$result = !undef;
} else {
Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'");
}
return $result;
}
sub getCamParams {
my $self = shift;
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging';
my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $self->{Monitor}->{ControlAddress});
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
# We should really use an xml or soap library to parse the xml tags
my $content = $res->decoded_content;
if ( $content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/ ) {
$CamParams{$1} = $2;
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $self->{Monitor}->{ControlAddress});
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
# We should really use an xml or soap library to parse the xml tags
my $content = $res->decoded_content;
if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
$CamParams{$1} = $2;
}
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
if ( $content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/ ) {
$CamParams{$1} = $2;
}
} else {
Error("Unable to retrieve camera image settings:'".$res->status_line()."'");
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $autostop = shift;
sub autoStop {
my $self = shift;
my $autostop = shift;
if( $autostop ) {
Debug( "Auto Stop" );
my $cmd = 'onvif/PTZ';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep( $autostop );
$self->sendCmd( $cmd, $msg, $content_type );
}
if ( $autostop ) {
Debug('Auto Stop');
my $cmd = 'onvif/PTZ';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep($autostop);
$self->sendCmd($cmd, $msg, $content_type);
}
}
# Reset the Camera
sub reset
{
Debug( "Camera Reset" );
my $self = shift;
my $cmd = "";
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd( $cmd, $msg, $content_type );
sub reset {
Debug('Camera Reset');
my $self = shift;
my $cmd = '';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd($cmd, $msg, $content_type);
}
#Up Arrow
sub moveConUp
{
Debug( "Move Up" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConUp {
Debug('Move Up');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Down Arrow
sub moveConDown
{
Debug( "Move Down" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConDown {
Debug('Move Down');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Left Arrow
sub moveConLeft
{
Debug( "Move Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConLeft {
Debug('Move Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Right Arrow
sub moveConRight
{
Debug( "Move Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConRight {
Debug('Move Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Zoom In
sub zoomConTele
{
Debug( "Zoom Tele" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub zoomConTele {
Debug('Zoom Tele');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Zoom Out
sub zoomConWide
{
Debug( "Zoom Wide" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub zoomConWide {
Debug('Zoom Wide');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
Debug( "Move Diagonally Up Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConUpRight {
Debug('Move Diagonally Up Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
Debug( "Move Diagonally Down Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConDownRight {
Debug('Move Diagonally Down Right');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
Debug( "Move Diagonally Up Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConUpLeft {
Debug('Move Diagonally Up Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
Debug( "Move Diagonally Down Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
sub moveConDownLeft {
Debug('Move Diagonally Down Left');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
}
#Stop
sub moveStop
{
Debug( "Move Stop" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
sub moveStop {
Debug('Move Stop');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
}
#Set Camera Preset
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
sub presetSet {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
Debug("Set Preset $preset");
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
$self->sendCmd($cmd, $msg, $content_type);
}
#Recall Camera Preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
sub presetGoto {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
Debug("Goto Preset $preset");
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Horizontal Patrol
#To be determined if this camera supports this feature
sub horizontalPatrol
{
Debug( "Horizontal Patrol" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
sub horizontalPatrol {
Debug('Horizontal Patrol');
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error('PTZ Command not implemented in control script.');
}
#Horizontal Patrol Stop
#To be determined if this camera supports this feature
sub horizontalPatrolStop
{
Debug( "Horizontal Patrol Stop" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
sub horizontalPatrolStop {
Debug('Horizontal Patrol Stop');
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error('PTZ Command not implemented in control script.');
}
# Increase Brightness
sub irisAbsOpen
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Brightness'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
sub irisAbsOpen {
Debug("Iris $CamParams{Brightness}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{Brightness});
my $step = $self->getParam($params, 'step');
my $max = 100;
$CamParams{'Brightness'} += $step;
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
$CamParams{Brightness} += $step;
$CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Decrease Brightness
sub irisAbsClose
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
Debug( "Iris $CamParams{Brightness}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{brightness});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Brightness'} -= $step;
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
$CamParams{Brightness} -= $step;
$CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Increase Contrast
sub whiteAbsIn
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
sub whiteAbsIn {
Debug("Iris $CamParams{Contrast}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Contrast'} += $step;
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
$CamParams{Contrast} += $step;
$CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
# Decrease Contrast
sub whiteAbsOut
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
sub whiteAbsOut {
Debug("Iris $CamParams{Contrast}");
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam($params, 'step');
my $min = 0;
$CamParams{'Contrast'} -= $step;
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
$CamParams{Contrast} -= $step;
$CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
1;
__END__

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,195 @@
#
# ==========================================================================
use strict;
@EXTRA_PERL_LIB@
use ZoneMinder;
use Getopt::Long;
use autouse 'Pod::Usage'=>qw(pod2usage);
use POSIX qw/strftime EPIPE/;
use Socket;
#use Data::Dumper;
use Module::Load::Conditional qw{can_load};;
use constant MAX_CONNECT_DELAY => 15;
use constant MAX_COMMAND_WAIT => 1800;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
my $arg_string = join( " ", @ARGV );
my $id;
my %options;
GetOptions(
'id=i' =>\$id,
'command=s' =>\$options{command},
'xcoord=i' =>\$options{xcoord},
'ycoord=i' =>\$options{ycoord},
'speed=i' =>\$options{speed},
'step=i' =>\$options{step},
'panspeed=i' =>\$options{panspeed},
'tiltspeed=i' =>\$options{tiltspeed},
'panstep=i' =>\$options{panstep},
'tiltstep=i' =>\$options{tiltstep},
'preset=i' =>\$options{preset},
'autostop' =>\$options{autostop},
) or pod2usage(-exitstatus => -1);
if ( !$id || !$options{command} ) {
print( STDERR "Please give a valid monitor id and command\n" );
pod2usage(-exitstatus => -1);
}
( $id ) = $id =~ /^(\w+)$/;
Debug("zmcontrol: arg string: $arg_string");
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
or Fatal("Can't open socket: $!");
my $saddr = sockaddr_un($sock_file);
my $server_up = connect(CLIENT, $saddr);
if ( !$server_up ) {
# The server isn't there
my $monitor = zmDbGetMonitorAndControl($id);
if ( !$monitor ) {
Fatal("Unable to load control data for monitor $id");
}
my $protocol = $monitor->{Protocol};
if ( -x $protocol ) {
# Protocol is actually a script!
# Holdover from previous versions
my $command .= $protocol.' '.$arg_string;
Debug($command);
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
chomp($output);
Debug("Output: $output");
}
if ( $status ) {
Error("Command '$command' exited with status: $status");
exit($status);
}
exit(0);
}
Info("Starting control server $id/$protocol");
close(CLIENT);
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
}
if ( my $cpid = fork() ) {
logReinit();
# Parent process just sleep and fall through
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
or die("Can't open socket: $!");
my $attempts = 0;
while ( !connect(CLIENT, $saddr) ) {
$attempts++;
Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY;
sleep(1);
}
} elsif ( defined($cpid) ) {
close(STDOUT);
close(STDERR);
setpgrp();
logReinit();
Info("Control server $id/$protocol starting at "
.strftime('%y/%m/%d %H:%M:%S', localtime())
);
$0 = $0." --id $id";
my $control = "ZoneMinder::Control::$protocol"->new($id);
my $control_key = $control->getKey();
$control->loadMonitor();
$control->open();
socket(SERVER, PF_UNIX, SOCK_STREAM, 0)
or Fatal("Can't open socket: $!");
unlink($sock_file);
bind(SERVER, $saddr) or Fatal("Can't bind: $!");
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
my $rin = '';
vec( $rin, fileno(SERVER), 1 ) = 1;
my $win = $rin;
my $ein = $win;
my $timeout = MAX_COMMAND_WAIT;
while( 1 ) {
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
if ( $nfound > 0 ) {
if ( vec( $rout, fileno(SERVER), 1 ) ) {
my $paddr = accept(CLIENT, SERVER);
my $message = <CLIENT>;
next if !$message;
my $params = jsonDecode($message);
#Debug( Dumper( $params ) );
my $command = $params->{command};
close( CLIENT );
if ( $command eq 'quit' ) {
last;
}
$control->$command($params);
} else {
Fatal('Bogus descriptor');
}
} elsif ( $nfound < 0 ) {
if ( $! == EPIPE ) {
Error("Can't select: $!");
} else {
Fatal("Can't select: $!");
}
} else {
#print( "Select timed out\n" );
last;
}
} # end while forever
Info("Control server $id/$protocol exiting");
unlink($sock_file);
$control->close();
exit(0);
} else {
Fatal("Can't fork: $!");
}
} # end if !server up
# The server is there, connect to it
#print( "Writing commands\n" );
CLIENT->autoflush();
my $message = jsonEncode(\%options);
print(CLIENT $message);
shutdown(CLIENT, 1);
exit(0);
1;
__END__
=head1 NAME
zmcontrol.pl - ZoneMinder control script
@ -47,214 +236,3 @@ FIXME FIXME
--preset [ arg ] -
=cut
use strict;
@EXTRA_PERL_LIB@
use ZoneMinder;
use Getopt::Long;
use autouse 'Pod::Usage'=>qw(pod2usage);
use POSIX qw/strftime EPIPE/;
use Socket;
#use Data::Dumper;
use Module::Load::Conditional qw{can_load};;
use constant MAX_CONNECT_DELAY => 10;
use constant MAX_COMMAND_WAIT => 1800;
$| = 1;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
my $arg_string = join( " ", @ARGV );
my $id;
my %options;
GetOptions(
'id=i' =>\$id,
'command=s' =>\$options{command},
'xcoord=i' =>\$options{xcoord},
'ycoord=i' =>\$options{ycoord},
'speed=i' =>\$options{speed},
'step=i' =>\$options{step},
'panspeed=i' =>\$options{panspeed},
'tiltspeed=i' =>\$options{tiltspeed},
'panstep=i' =>\$options{panstep},
'tiltstep=i' =>\$options{tiltstep},
'preset=i' =>\$options{preset},
'autostop' =>\$options{autostop},
) or pod2usage(-exitstatus => -1);
if ( !$id || !$options{command} )
{
print( STDERR "Please give a valid monitor id and command\n" );
pod2usage(-exitstatus => -1);
}
( $id ) = $id =~ /^(\w+)$/;
Debug( $arg_string );
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
or Fatal( "Can't open socket: $!" );
my $saddr = sockaddr_un( $sock_file );
my $server_up = connect( CLIENT, $saddr );
if ( !$server_up )
{
# The server isn't there
my $monitor = zmDbGetMonitorAndControl( $id );
if ( !$monitor )
{
Fatal( "Unable to load control data for monitor $id" );
}
my $protocol = $monitor->{Protocol};
if ( -x $protocol )
{
# Protocol is actually a script!
# Holdover from previous versions
my $command .= $protocol.' '.$arg_string;
Debug( $command."\n" );
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() )
{
chomp( $output );
Debug( "Output: $output\n" );
}
if ( $status )
{
Error( "Command '$command' exited with status: $status\n" );
exit( $status );
}
exit( 0 );
}
Info( "Starting control server $id/$protocol" );
close( CLIENT );
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol");
}
if ( my $cpid = fork() )
{
logReinit();
# Parent process just sleep and fall through
socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
or die( "Can't open socket: $!" );
my $attempts = 0;
while (!connect( CLIENT, $saddr ))
{
$attempts++;
Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY);
sleep(1);
}
}
elsif ( defined($cpid) )
{
close( STDOUT );
close( STDERR );
setpgrp();
logReinit();
Info( "Control server $id/$protocol starting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
$0 = $0." --id $id";
my $control = "ZoneMinder::Control::$protocol"->new( $id );
my $control_key = $control->getKey();
$control->loadMonitor();
$control->open();
socket( SERVER, PF_UNIX, SOCK_STREAM, 0 )
or Fatal( "Can't open socket: $!" );
unlink( $sock_file );
bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" );
listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" );
my $rin = '';
vec( $rin, fileno(SERVER), 1 ) = 1;
my $win = $rin;
my $ein = $win;
my $timeout = MAX_COMMAND_WAIT;
while( 1 )
{
my $nfound = select( my $rout = $rin, undef, undef, $timeout );
if ( $nfound > 0 )
{
if ( vec( $rout, fileno(SERVER), 1 ) )
{
my $paddr = accept( CLIENT, SERVER );
my $message = <CLIENT>;
next if ( !$message );
my $params = jsonDecode( $message );
#Debug( Dumper( $params ) );
my $command = $params->{command};
close( CLIENT );
if ( $command eq 'quit' ) {
last;
}
$control->$command( $params );
}
else
{
Fatal( "Bogus descriptor" );
}
}
elsif ( $nfound < 0 )
{
if ( $! == EPIPE )
{
Error( "Can't select: $!" );
}
else
{
Fatal( "Can't select: $!" );
}
}
else
{
#print( "Select timed out\n" );
last;
}
}
Info( "Control server $id/$protocol exiting at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
unlink( $sock_file );
$control->close();
exit( 0 );
}
else
{
Fatal( "Can't fork: $!" );
}
}
# The server is there, connect to it
#print( "Writing commands\n" );
CLIENT->autoflush();
my $message = jsonEncode( \%options );
print( CLIENT $message );
shutdown( CLIENT, 1 );
exit( 0 );

View File

@ -257,6 +257,8 @@ sub run {
}
my $fd = 0;
# THis also closes dbh and CLIENT and SERVER
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close($fd++);
}
@ -272,6 +274,8 @@ sub run {
."\n"
);
# We don't want to leave killall zombies, so ignore SIGCHLD
$SIG{CHLD} = 'IGNORE';
# Tell any existing processes to die, wait 1 second between TERM and KILL
killAll(1);
@ -298,16 +302,17 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) {
if ( ! ( $secs_count % 60 ) ) {
Debug("Connecting");
while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) {
Warning("Not connected to db ($dbh)".($dbh?' ping('.$dbh->ping().')':''). ($DBI::errstr?" errstr($DBI::errstr)":'').' Reconnecting');
$dbh = zmDbConnect();
}
last if $zm_terminate;
my @cpuload = CpuLoad();
Debug("Updating Server record @cpuload");
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID} :".$dbh->errstr());
}
}
$secs_count += 1;
@ -373,7 +378,7 @@ sub run {
} # end while
dPrint(ZoneMinder::Logger::INFO, 'Server exiting at '
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
.strftime('%y/%m/%d %H:%M:%S', localtime())
."\n"
);
if ( $Config{ZM_SERVER_ID} ) {
@ -707,7 +712,7 @@ sub reaper {
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
}
}
Debug("Delay for $$process{command} is now $$process{delay}");
#Debug("Delay for $$process{command} is now $$process{delay}");
} else {
delete $cmd_hash{$$process{command}};
}

View File

@ -98,6 +98,11 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
logInit($filter_id?(id=>'zmfilter_'.$filter_id):());
sub HupHandler {
# This idea at this time is to just exit, freeing up the memory.
# zmfilter.pl will be respawned by zmdc.
TermHandler();
return;
Info('Received HUP, reloading');
ZoneMinder::Object::init_cache();
&ZoneMinder::Logger::logHupHandler();

View File

@ -33,6 +33,7 @@ use LWP::UserAgent;
use Sys::MemInfo qw(totalmem);
use Sys::CPU qw(cpu_count);
use POSIX qw(strftime uname);
use JSON::MaybeXS;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
@ -87,7 +88,7 @@ while( 1 ) {
# We should keep *BSD systems in mind when calling system commands
my %telemetry;
$telemetry{uuid} = getUUID($dbh);
$telemetry{ip} = getIP();
($telemetry{city}, $telemetry{region}, $telemetry{country}, $telemetry{latitude}, $telemetry{longitude}) = getGeo();
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
$telemetry{monitor_count} = countQuery($dbh,'Monitors');
$telemetry{event_count} = countQuery($dbh,'Events');
@ -203,22 +204,25 @@ sub getUUID {
return $uuid;
}
# Retrieves the local server's external IP address
sub getIP {
my $ipaddr = '0.0.0.0';
# Retrieve this server's general location information from a GeoIP database
sub getGeo {
my $unknown = 'Unknown';
my $endpoint = 'https://ipinfo.io/geo';
my $ua = LWP::UserAgent->new;
my $server_endpoint = 'https://wiki.zoneminder.com/ip.php';
my $req = HTTP::Request->new(GET => $server_endpoint);
my $req = HTTP::Request->new(GET => $endpoint);
my $resp = $ua->request($req);
my $resp_msg = $resp->decoded_content;
my $resp_code = $resp->code;
if ($resp->is_success) {
$ipaddr = $resp->decoded_content;
my $content = decode_json( $resp_msg );
(my $latitude, my $longitude) = split /,/, $content->{loc};
return ($content->{city}, $content->{region}, $content->{country}, $latitude, $longitude);
} else {
Warning("Geoip data retrieval returned HTTP POST error code: $resp_code");
Debug("Geoip data retrieval failure response message: $resp_msg");
return ($unknown, $unknown, $unknown, $unknown);
}
Debug("Found external ip address of: $ipaddr");
return $ipaddr;
}
# As the name implies, just your average mysql count query

View File

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

View File

@ -33,8 +33,7 @@ class Camera;
// Abstract base class for cameras. This is intended just to express
// common attributes
//
class Camera
{
class Camera {
protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
@ -53,49 +52,48 @@ protected:
int contrast;
bool capture;
bool record_audio;
unsigned int bytes;
unsigned int bytes;
public:
Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
virtual ~Camera();
unsigned int getId() const { return( monitor_id ); }
unsigned int getId() const { return monitor_id; }
Monitor *getMonitor();
void setMonitor( Monitor *p_monitor );
SourceType Type() const { return( type ); }
bool IsLocal() const { return( type == LOCAL_SRC ); }
bool IsRemote() const { return( type == REMOTE_SRC ); }
bool IsFile() const { return( type == FILE_SRC ); }
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
bool IscURL() const { return( type == CURL_SRC ); }
unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); }
unsigned int Colours() const { return( colours ); }
unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); }
SourceType Type() const { return type; }
bool IsLocal() const { return type == LOCAL_SRC; }
bool IsRemote() const { return type == REMOTE_SRC; }
bool IsFile() const { return type == FILE_SRC; }
bool IsFfmpeg() const { return type == FFMPEG_SRC; }
bool IsLibvlc() const { return type == LIBVLC_SRC; }
bool IscURL() const { return type == CURL_SRC; }
unsigned int Width() const { return width; }
unsigned int Height() const { return height; }
unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return subpixelorder; }
unsigned int Pixels() const { return pixels; }
unsigned int ImageSize() const { return imagesize; }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; }
virtual int Hue( int/*p_hue*/=-1 ) { return -1; }
virtual int Colour( int/*p_colour*/=-1 ) { return -1; }
virtual int Contrast( int/*p_contrast*/=-1 ) { return -1; }
bool CanCapture() const { return( capture ); }
bool CanCapture() const { return capture; }
bool SupportsNativeVideo() const {
return (type == FFMPEG_SRC);
//return (type == FFMPEG_SRC )||(type == REMOTE_SRC);
}
virtual int PrimeCapture() { return( 0 ); }
virtual int PreCapture()=0;
virtual int Capture( Image &image )=0;
virtual int PostCapture()=0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0;
virtual int Close()=0;
virtual int PrimeCapture() { return 0; }
virtual int PreCapture() = 0;
virtual int Capture(Image &image) = 0;
virtual int PostCapture() = 0;
virtual int CaptureAndRecord(Image &image, timeval recording, char* event_directory) = 0;
virtual int Close() = 0;
};
#endif // ZM_CAMERA_H

View File

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

View File

@ -141,9 +141,8 @@ Event::Event(
errno = 0;
if ( mkdir(path, 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
}
if ( i == 2 )
strncpy(date_path, path, sizeof(date_path));
@ -289,7 +288,7 @@ bool Event::WriteFrameImage(Image *image, struct timeval timestamp, const char *
int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default
bool rc;
Debug(3, "Writing image to %s", event_file );
Debug(3, "Writing image to %s", event_file);
if ( !config.timestamp_on_capture ) {
// stash the image we plan to use in another pointer regardless if timestamped.
@ -594,11 +593,12 @@ Debug(3, "Writing video");
max_score = score;
if ( alarm_image ) {
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
Debug(1, "Writing analysis frame %d", frames);
if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage(alarm_image, timestamp, event_file, true);
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
Debug(1, "Writing analysis frame %d", frames);
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
Error("Failed to write analysis frame image");
}
}
}
}

View File

@ -59,7 +59,7 @@ class FfmpegCamera : public Camera {
bool hwaccel;
#if HAVE_AVUTIL_HWCONTEXT_H
AVFrame *hwFrame;
DecodeContext decode;
DecodeContext decode;
#endif
// Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero.

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
#endif
bool Logger::smInitialised = false;
Logger *Logger::smInstance = 0;
Logger *Logger::smInstance = NULL;
Logger::StringMap Logger::smCodes;
Logger::IntMap Logger::smSyslogPriorities;
@ -58,9 +58,9 @@ static void subtractTime( struct timeval * const tp1, struct timeval * const tp2
void Logger::usrHandler( int sig ) {
Logger *logger = fetch();
if ( sig == SIGUSR1 )
logger->level( logger->level()+1 );
logger->level(logger->level()+1);
else if ( sig == SIGUSR2 )
logger->level( logger->level()-1 );
logger->level(logger->level()-1);
Info("Logger - Level changed to %d", logger->level());
}
@ -79,7 +79,7 @@ Logger::Logger() :
mFlush(false) {
if ( smInstance ) {
Panic( "Attempt to create second instance of Logger class" );
Panic("Attempt to create second instance of Logger class");
}
if ( !smInitialised ) {
@ -133,11 +133,11 @@ void Logger::initialise(const std::string &id, const Options &options) {
std::string tempLogFile;
if ( (envPtr = getTargettedEnv("LOG_FILE")) )
if ( (envPtr = getTargettedEnv("LOG_FILE")) ) {
tempLogFile = envPtr;
else if ( options.mLogFile.size() )
} else if ( options.mLogFile.size() ) {
tempLogFile = options.mLogFile;
else {
} else {
if ( options.mLogPath.size() ) {
mLogPath = options.mLogPath;
}
@ -169,7 +169,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog;
// Legacy
if ( (envPtr = getenv( "LOG_PRINT" )) )
if ( (envPtr = getenv("LOG_PRINT")) )
tempTerminalLevel = atoi(envPtr) ? DEBUG9 : NOLOG;
if ( (envPtr = getTargettedEnv("LOG_LEVEL")) )
@ -218,7 +218,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
mFlush = false;
if ( (envPtr = getenv("LOG_FLUSH")) ) {
mFlush = atoi( envPtr );
mFlush = atoi(envPtr);
} else if ( config.log_debug ) {
mFlush = true;
}
@ -503,17 +503,17 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
);
char *syslogStart = logPtr;
va_start( argPtr, fstring );
va_start(argPtr, fstring);
if ( hex ) {
unsigned char *data = va_arg( argPtr, unsigned char * );
int len = va_arg( argPtr, int );
unsigned char *data = va_arg(argPtr, unsigned char *);
int len = va_arg(argPtr, int);
int i;
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len );
logPtr += snprintf(logPtr, sizeof(logString)-(logPtr-logString), "%d:", len);
for ( i = 0; i < len; i++ ) {
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] );
logPtr += snprintf(logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i]);
}
} else {
logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr );
logPtr += vsnprintf(logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr);
}
va_end(argPtr);
char *syslogEnd = logPtr;
@ -540,7 +540,13 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
if ( ! db_mutex.trylock() ) {
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
snprintf(sql, sizeof(sql),
"INSERT INTO Logs "
"( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line )"
" VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line
);
if ( mysql_query(&dbconn, sql) ) {
Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel(NOLOG);
@ -571,12 +577,12 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
}
}
void logInit( const char *name, const Logger::Options &options ) {
void logInit(const char *name, const Logger::Options &options) {
if ( !Logger::smInstance )
Logger::smInstance = new Logger();
Logger::Options tempOptions = options;
tempOptions.mLogPath = staticConfig.PATH_LOGS;
Logger::smInstance->initialise( name, tempOptions );
Logger::smInstance->initialise(name, tempOptions);
}
void logTerm() {

View File

@ -423,15 +423,9 @@ Monitor::Monitor(
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
if ( purpose == CAPTURE ) {
struct stat statbuf;
if ( stat(monitor_dir, &statbuf) ) {
if ( errno == ENOENT || errno == ENOTDIR ) {
if ( mkdir(monitor_dir, 0755) ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
}
} else {
Warning("Error stat'ing %s, may be fatal. error is %s", monitor_dir, strerror(errno));
if ( mkdir(monitor_dir, 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
}
}
@ -609,17 +603,19 @@ bool Monitor::connect() {
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval;
}
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
*pre_event_buffer[i].timestamp = {0,0};
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
}
if ( purpose == ANALYSIS ) {
if ( analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
*pre_event_buffer[i].timestamp = {0,0};
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
}
}
timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count];
@ -1577,8 +1573,8 @@ Error("Creating new event when one exists");
else
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d) % %d",
pre_index, index, image_buffer_count, pre_event_count, image_buffer_count);
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d)",
pre_index, index, image_buffer_count, pre_event_count);
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
@ -1623,7 +1619,6 @@ Error("Creating new event when one exists");
pre_index = (pre_index + 1)%image_buffer_count;
}
}
event->AddFrames( pre_event_images, images, timestamps );
}
if ( alarm_frame_count ) {
@ -2474,19 +2469,17 @@ int Monitor::Capture() {
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
Info("%s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", name, image_count, new_fps, new_capture_bandwidth);
last_fps_time = now;
if ( new_fps != fps ) {
fps = new_fps;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u",
id, fps, new_capture_bandwidth, fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
} // end if new_fps != fps
fps = new_fps;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u",
id, fps, new_capture_bandwidth, fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
Debug(4,sql);
} // end if time has changed since last update
} // end if it might be time to report the fps
} // end if captureResult

View File

@ -254,39 +254,39 @@ protected:
VideoWriter videowriter;
std::string encoderparams;
std::vector<EncoderParameter_t> encoderparamsvec;
bool record_audio; // Whether to store the audio that we receive
bool record_audio; // Whether to store the audio that we receive
int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera
char event_prefix[64]; // The prefix applied to event names as they are created
char label_format[64]; // The format of the timestamp on the images
Coord label_coord; // The coordinates of the timestamp on the images
int label_size; // Size of the timestamp on the images
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera
char event_prefix[64]; // The prefix applied to event names as they are created
char label_format[64]; // The format of the timestamp on the images
Coord label_coord; // The coordinates of the timestamp on the images
int label_size; // Size of the timestamp on the images
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
// value is pre_event_count + alarm_frame_count - 1
int warmup_count; // How many images to process before looking for events
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
int section_length; // How long events should last in continuous modes
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int frame_skip; // How many frames to skip in continuous modes
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps; // Target framerate for video analysis
int warmup_count; // How many images to process before looking for events
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
int section_length; // How long events should last in continuous modes
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int frame_skip; // How many frames to skip in continuous modes
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps; // Target framerate for video analysis
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
int capture_delay; // How long we wait between capture frames
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not
int capture_delay; // How long we wait between capture frames
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not
bool last_signal;

View File

@ -482,7 +482,7 @@ void MonitorStream::runStream() {
swap_path = staticConfig.PATH_SWAP;
Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
if ( checkSwapPath(swap_path.c_str(), false) ) {
if ( checkSwapPath(swap_path.c_str(), true) ) {
swap_path += stringtf("/zmswap-m%d", monitor->Id());
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
@ -633,7 +633,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
// Send the next frame
Monitor::Snapshot *snap = &monitor->image_buffer[index];
//Debug(2, "sending Frame.");
Debug(2, "sending Frame.");
if ( !sendFrame(snap->image, snap->timestamp) ) {
Debug(2, "sendFrame failed, quiting.");
zm_terminate = true;
@ -687,7 +687,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
} // end if buffered playback
frame_count++;
} else {
Debug(5,"Waiting for capture");
Debug(4,"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 )
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));

View File

@ -267,7 +267,20 @@ bool StreamBase::sendTextFrame( const char *frame_text ) {
void StreamBase::openComms() {
if ( connkey > 0 ) {
unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey);
// Have to mkdir because systemd is now chrooting and the dir may not exist
if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir ZM_PATH_SOCKS %s: %s.", staticConfig.PATH_SOCKS.c_str(), strerror(errno));
}
}
unsigned int length = snprintf(
sock_path_lock,
sizeof(sock_path_lock),
"%s/zms-%06d.lock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated.");
}
@ -275,14 +288,14 @@ void StreamBase::openComms() {
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 ) {
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno));
lock_fd = 0;
} else if ( flock(lock_fd, LOCK_EX) != 0 ) {
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno));
close(lock_fd);
lock_fd = 0;
} else {
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
Debug(1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
}
sd = socket(AF_UNIX, SOCK_DGRAM, 0);
@ -292,7 +305,13 @@ void StreamBase::openComms() {
Debug(1, "Have socket %d", sd);
}
length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
length = snprintf(
loc_sock_path,
sizeof(loc_sock_path),
"%s/zms-%06ds.sock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1;

View File

@ -376,7 +376,10 @@ int X264MP4Writer::x264config() {
x264params.b_annexb = 0;
/* TODO: Setup error handler */
// x264params.i_log_level = X264_LOG_DEBUG;
if ( logDebugging() )
x264params.i_log_level = X264_LOG_DEBUG;
else
x264params.i_log_level = X264_LOG_NONE;
/* Process user parameters (excluding preset, tune and profile) */
for ( unsigned int i = 0; i < user_params.size(); i++ ) {

View File

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

View File

@ -83,7 +83,7 @@ int main( int argc, char *argv[] ) {
while (1) {
int option_index = 0;
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
int c = getopt_long(argc, argv, "m:h:v", long_options, &option_index);
if ( c == -1 ) {
break;
}
@ -144,7 +144,7 @@ int main( int argc, char *argv[] ) {
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
time_t last_analysis_update_time, cur_time;
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time( 0 );
last_analysis_update_time = time(0);
while( (!zm_terminate) && monitor->ShmValid() ) {
// Process the next image
@ -181,5 +181,5 @@ int main( int argc, char *argv[] ) {
Image::Deinitialise();
logTerm();
zmDbClose();
return( 0 );
return 0;
}

View File

@ -1 +1 @@
1.31.44
1.31.47

View File

@ -23,7 +23,7 @@ switch ( $_REQUEST['task'] ) {
if ( !isset($levels[$_POST['level']]) )
Panic("Unexpected logger level '".$_POST['level']."'");
$level = $levels[$_POST['level']];
Logger::fetch()->logPrint( $level, $string, $file, $line );
Logger::fetch()->logPrint($level, $string, $file, $line);
}
ajaxResponse();
break;
@ -45,22 +45,22 @@ switch ( $_REQUEST['task'] ) {
$limit = 100;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_integer( $_REQUEST['limit'] ) and !ctype_digit($_REQUEST['limit']) ) ) {
Error('Invalid value for limit ' . $_REQUEST['limit'] );
if ( ( !is_integer($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
Error('Invalid value for limit ' . $_REQUEST['limit']);
} else {
$limit = $_REQUEST['limit'];
}
}
$sortField = 'TimeKey';
if ( isset($_REQUEST['sortField']) ) {
if ( ! in_array( $_REQUEST['sortField'], $filterFields ) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) {
Error("Invalid sort field " . $_REQUEST['sortField'] );
if ( !in_array($_REQUEST['sortField'], $filterFields) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) {
Error("Invalid sort field " . $_REQUEST['sortField']);
} else {
$sortField = $_REQUEST['sortField'];
}
}
$sortOrder = (isset($_REQUEST['sortOrder']) and $_REQUEST['sortOrder']) == 'asc' ? 'asc':'desc';
$filter = isset($_REQUEST['filter'])?$_REQUEST['filter']:array();
$sortOrder = (isset($_REQUEST['sortOrder']) and ($_REQUEST['sortOrder'] == 'asc')) ? 'asc' : 'desc';
$filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array();
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
$sql = 'SELECT * FROM Logs';
@ -89,15 +89,18 @@ switch ( $_REQUEST['task'] ) {
}
$options = array();
if ( count($where) )
$sql.= ' WHERE '.join( ' AND ', $where );
$sql.= ' WHERE '.join(' AND ', $where);
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
$logs = array();
foreach ( dbFetchAll($sql, NULL, $values) as $log ) {
$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
#Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']);
#$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message'] );
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
foreach( $filterFields as $field ) {
if ( ! isset( $options[$field] ) )
if ( !isset($options[$field]) )
$options[$field] = array();
$value = $log[$field];
@ -119,7 +122,7 @@ switch ( $_REQUEST['task'] ) {
ajaxResponse( array(
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG),
'total' => $total,
'available' => isset($available)?$available:$total,
'available' => isset($available) ? $available : $total,
'logs' => $logs,
'state' => logState(),
'options' => $options
@ -210,8 +213,8 @@ switch ( $_REQUEST['task'] ) {
if ( !($exportFP = fopen( $exportPath, "w" )) )
Fatal("Unable to open log export file $exportPath");
$logs = array();
foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) {
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
foreach ( dbFetchAll($sql, NULL, $values) as $log ) {
$log['DateTime'] = preg_replace('/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey']);
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$logs[] = $log;
}
@ -347,7 +350,7 @@ switch ( $_REQUEST['task'] ) {
</logexport>' );
break;
}
$exportExt = "xml";
$exportExt = 'xml';
break;
}
fclose( $exportFP );
@ -363,10 +366,10 @@ switch ( $_REQUEST['task'] ) {
ajaxError('Insufficient permissions to download logs');
if ( empty($_REQUEST['key']) )
Fatal( "No log export key given" );
Fatal('No log export key given');
$exportKey = $_REQUEST['key'];
if ( empty($_REQUEST['format']) )
Fatal( "No log export format given" );
Fatal('No log export format given');
$format = $_REQUEST['format'];
switch( $format ) {
@ -389,17 +392,17 @@ switch ( $_REQUEST['task'] ) {
$exportFile = "zm-log.$exportExt";
$exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt";
header( "Pragma: public" );
header( "Expires: 0" );
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
header( "Cache-Control: private", false ); // required by certain browsers
header( "Content-Description: File Transfer" );
header( 'Content-Disposition: attachment; filename="'.$exportFile.'"' );
header( "Content-Transfer-Encoding: binary" );
header( "Content-Type: application/force-download" );
header( "Content-Length: ".filesize($exportPath) );
readfile( $exportPath );
exit( 0 );
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false ); // required by certain browsers
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename="'.$exportFile.'"' );
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($exportPath));
readfile($exportPath);
exit(0);
break;
}
}

View File

@ -60,31 +60,47 @@ class AppController extends Controller {
// for role and deny API access in future
// Also checking to do this only if ZM_OPT_USE_AUTH is on
public function beforeFilter() {
$this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_API'));
$config = $this->Config->find('first', $options);
$zmOptApi = $config['Config']['Value'];
if ($zmOptApi !='1') {
if ( ! ZM_OPT_USE_API ) {
throw new UnauthorizedException(__('API Disabled'));
return;
}
// We need to reject methods that are not authenticated
// besides login and logout
if (strcasecmp($this->params->controller, "host") &&
strcasecmp($this->params->action, "login") &&
strcasecmp($this->params->action,"logout")) {
}
if (!$this->Session->read('user.Username')) {
throw new UnauthorizedException(__('Not Authenticated'));
return;
} else if (!$this->Session->read('user.Enabled')) {
throw new UnauthorizedException(__('User is not enabled'));
return;
}
# For use throughout the app. If not logged in, this will be null.
global $user;
$user = $this->Session->read('user');
if ( ZM_OPT_USE_AUTH ) {
require_once '../../../includes/auth.php';
}
$mUser = $this->request->query('user') ? $this->request->query('user') : $this->request->data('user');
$mPassword = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');
$mAuth = $this->request->query('auth') ? $this->request->query('auth') : $this->request->data('auth');
if ( $mUser and $mPassword ) {
$user = userLogin($mUser, $mPassword);
if ( !$user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
} else if ( $mAuth ) {
$user = getAuthUser($mAuth);
if ( !$user ) {
throw new UnauthorizedException(__('Invalid Auth Key'));
return;
}
}
// We need to reject methods that are not authenticated
// besides login and logout
if ( strcasecmp($this->params->action, 'logout') ) {
if ( !( $user and $user['Username'] ) ) {
throw new UnauthorizedException(__('Not Authenticated'));
return;
} else if ( !( $user and $user['Enabled'] ) ) {
throw new UnauthorizedException(__('User is not enabled'));
return;
}
} # end if ! login or logout
} # end if ZM_OPT_AUTH
} # end function beforeFilter()
}

View File

@ -1,5 +1,6 @@
<?php
App::uses('AppController', 'Controller');
/**
* Events Controller
*
@ -7,61 +8,63 @@ App::uses('AppController', 'Controller');
*/
class EventsController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('eventPermission');
if ($canView =='None') {
global $user;
$canView = (!$user) || ($user['Events'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/**
* index method
*
* @return void
* This also creates a thumbnail for each event.
*/
public function index() {
$this->Event->recursive = -1;
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
/**
* index method
*
* @return void
* This also creates a thumbnail for each event.
*/
public function index() {
$this->Event->recursive = -1;
if (!empty($allowedMonitors)) {
global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$mon_options = array('Event.MonitorId' => $allowedMonitors);
} else {
$mon_options='';
$mon_options = '';
}
if ($this->request->params['named']) {
//$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
if ( $this->request->params['named'] ) {
//$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
} else {
$conditions = array();
}
} else {
$conditions = array();
}
$settings = array(
// https://github.com/ZoneMinder/ZoneMinder/issues/995
// 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'],
// 25 events per page which is what the above
// default is, is way too low for an API
// changing this to 100 so we don't kill ZM
// with many event APIs. In future, we can
// make a nice ZM_API_ITEMS_PER_PAGE for all pagination
// API
'limit' => '100',
'order' => array('StartTime'),
'paramType' => 'querystring',
// https://github.com/ZoneMinder/ZoneMinder/issues/995
// 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'],
// 25 events per page which is what the above
// default is, is way too low for an API
// changing this to 100 so we don't kill ZM
// with many event APIs. In future, we can
// make a nice ZM_API_ITEMS_PER_PAGE for all pagination
// API
'limit' => '100',
'order' => array('StartTime'),
'paramType' => 'querystring',
);
if ( isset( $conditions['GroupId'] ) ) {
if ( isset($conditions['GroupId']) ) {
$settings['joins'] = array(
array(
'table' => 'Groups_Monitors',
@ -75,45 +78,46 @@ class EventsController extends AppController {
}
$settings['conditions'] = array($conditions, $mon_options);
// How many events to return
$this->loadModel('Config');
$limit = $this->Config->find('list', array(
'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'),
'fields' => array('Name', 'Value')
));
$this->Paginator->settings = $settings;
$events = $this->Paginator->paginate('Event');
// How many events to return
$this->loadModel('Config');
$limit = $this->Config->find('list', array(
'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'),
'fields' => array('Name', 'Value')
));
$this->Paginator->settings = $settings;
$events = $this->Paginator->paginate('Event');
// For each event, get the frameID which has the largest score
foreach ($events as $key => $value) {
$maxScoreFrameId = $this->getMaxScoreAlarmFrameId($value['Event']['Id']);
$events[$key]['Event']['MaxScoreFrameId'] = $maxScoreFrameId;
}
// For each event, get the frameID which has the largest score
foreach ( $events as $key => $value ) {
$maxScoreFrameId = $this->getMaxScoreAlarmFrameId($value['Event']['Id']);
$events[$key]['Event']['MaxScoreFrameId'] = $maxScoreFrameId;
}
$this->set(compact('events'));
}
$this->set(compact('events'));
} // end public function index()
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
$this->loadModel('Config');
$this->Event->recursive = 1;
if (!$this->Event->exists($id)) {
if ( !$this->Event->exists($id) ) {
throw new NotFoundException(__('Invalid event'));
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if (!empty($allowedMonitors)) {
if ( $allowedMonitors ) {
$mon_options = array('Event.MonitorId' => $allowedMonitors);
} else {
$mon_options='';
$mon_options = '';
}
$options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options));
@ -149,14 +153,16 @@ class EventsController extends AppController {
*/
public function add() {
if ($this->Session->Read('eventPermission') != 'Edit') {
global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->request->is('post')) {
if ( $this->request->is('post') ) {
$this->Event->create();
if ($this->Event->save($this->request->data)) {
if ( $this->Event->save($this->request->data) ) {
return $this->flash(__('The event has been saved.'), array('action' => 'index'));
}
}
@ -173,18 +179,20 @@ class EventsController extends AppController {
*/
public function edit($id = null) {
if ($this->Session->Read('eventPermission') != 'Edit') {
global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Event->id = $id;
if (!$this->Event->exists($id)) {
if ( !$this->Event->exists($id) ) {
throw new NotFoundException(__('Invalid event'));
}
if ($this->Event->save($this->request->data)) {
if ( $this->Event->save($this->request->data) ) {
$message = 'Saved';
} else {
$message = 'Error';
@ -204,16 +212,18 @@ class EventsController extends AppController {
* @return void
*/
public function delete($id = null) {
if ($this->Session->Read('eventPermission') != 'Edit') {
global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Event->id = $id;
if (!$this->Event->exists()) {
if ( !$this->Event->exists() ) {
throw new NotFoundException(__('Invalid event'));
}
$this->request->allowMethod('post', 'delete');
if ($this->Event->delete()) {
if ( $this->Event->delete() ) {
//$this->loadModel('Frame');
//$this->Event->Frame->delete();
return $this->flash(__('The event has been deleted.'), array('action' => 'index'));
@ -228,7 +238,7 @@ class EventsController extends AppController {
foreach ($this->params['named'] as $param_name => $value) {
// Transform params into mysql
if (preg_match("/interval/i", $value, $matches)) {
if ( preg_match('/interval/i', $value, $matches) ) {
$condition = array("$param_name >= (date_sub(now(), $value))");
} else {
$condition = array($param_name => $value);
@ -254,12 +264,12 @@ class EventsController extends AppController {
$this->Event->recursive = -1;
$results = array();
$moreconditions ="";
$moreconditions = '';
foreach ($this->request->params['named'] as $name => $param) {
$moreconditions = $moreconditions . " AND ".$name.$param;
}
$moreconditions = $moreconditions . ' AND '.$name.$param;
}
$query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
$query = $this->Event->query("SELECT MonitorId, COUNT(*) AS Count FROM Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
foreach ($query as $result) {
$results[$result['Events']['MonitorId']] = $result[0]['Count'];
@ -275,7 +285,7 @@ class EventsController extends AppController {
public function createThumbnail($id = null) {
$this->Event->recursive = -1;
if (!$this->Event->exists($id)) {
if ( !$this->Event->exists($id) ) {
throw new NotFoundException(__('Invalid event'));
}
@ -285,13 +295,13 @@ class EventsController extends AppController {
// Find the max Frame for this Event. Error out otherwise.
$this->loadModel('Frame');
if (! $frame = $this->Frame->find('first', array(
if ( !( $frame = $this->Frame->find('first', array(
'conditions' => array(
'EventId' => $event['Event']['Id'],
'Score' => $event['Event']['MaxScore']
)
))) {
throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
))) ) {
throw new NotFoundException(__('Can not find Frame for Event ' . $event['Event']['Id']));
}
$this->loadModel('Config');
@ -304,14 +314,15 @@ class EventsController extends AppController {
$config = $this->Config->find('list', array(
'conditions' => array('OR' => array(
'Name' => array('ZM_WEB_LIST_THUMB_WIDTH',
'ZM_WEB_LIST_THUMB_HEIGHT',
'ZM_EVENT_IMAGE_DIGITS',
'ZM_DIR_IMAGES',
$thumbs,
'ZM_DIR_EVENTS'
)
)),
'Name' => array(
'ZM_WEB_LIST_THUMB_WIDTH',
'ZM_WEB_LIST_THUMB_HEIGHT',
'ZM_EVENT_IMAGE_DIGITS',
'ZM_DIR_IMAGES',
$thumbs,
'ZM_DIR_EVENTS'
)
)),
'fields' => array('Name', 'Value')
));
$config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs];
@ -335,12 +346,12 @@ class EventsController extends AppController {
$thumbData['Width'] = (int)$thumbWidth;
$thumbData['Height'] = (int)$thumbHeight;
return( $thumbData );
return $thumbData;
}
public function archive($id = null) {
$this->Event->recursive = -1;
if (!$this->Event->exists($id)) {
if ( !$this->Event->exists($id) ) {
throw new NotFoundException(__('Invalid event'));
}
@ -365,7 +376,7 @@ class EventsController extends AppController {
public function getMaxScoreAlarmFrameId($id = null) {
$this->Event->recursive = -1;
if (!$this->Event->exists($id)) {
if ( !$this->Event->exists($id) ) {
throw new NotFoundException(__('Invalid event'));
}
@ -382,7 +393,7 @@ class EventsController extends AppController {
'Score' => $event['Event']['MaxScore']
)
))) {
throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
throw new NotFoundException(__('Can not find Frame for Event ' . $event['Event']['Id']));
}
return $frame['Frame']['Id'];
}

View File

@ -8,9 +8,9 @@ class HostController extends AppController {
public function daemonCheck($daemon=false, $args=false) {
$string = Configure::read('ZM_PATH_BIN').'/zmdc.pl check';
if ( $daemon ) {
$string .= " $daemon";
if ( $args )
$string .= " $args";
$string .= " $daemon";
if ( $args )
$string .= " $args";
}
$result = exec($string);
$result = preg_match('/running/', $result);
@ -29,95 +29,9 @@ class HostController extends AppController {
'_serialize' => array('load')
));
}
function login() {
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value'];
if ( $zmOptAuth == '1' ) {
require_once "../../../includes/auth.php";
global $user;
$user = $this->Session->read('user');
$mUser = $this->request->data('user');
$mPassword = $this->request->data('pass');
$mAuth = $this->request->data('auth');
if ( $mUser and $mPassword) {
$user = userLogin($mUser, $mPassword);
if ( !$user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
}
elseif ( $mAuth ) {
$user = getAuthUser($mAuth);
if ( ! $user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
}
else {
throw new UnauthorizedException(__('missing credentials'));
}
if ( 0 and $user ) {
# We have to redo the session variables because cakephp's Session code will overwrite the normal php session
# Actually I'm not sure that is true. Getting indeterminate behaviour
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user', $user) )
$this->log("Error writing session var user");
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user.Username', $user['Username']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('password', $user['Password']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('user.Enabled', $user['Enabled']) )
$this->log("Error writing session var user.Enabled");
if ( ! $this->Session->Write('remoteAddr', $_SERVER['REMOTE_ADDR']) )
$this->log("Error writing session var remoteAddr");
}
// I don't think this is really needed - the Username part
// Enabled check is ok
if ( !$user['Username'] ) {
throw new UnauthorizedException(__('Not Authenticated'));
return;
} else if ( !$user['Enabled'] ) {
throw new UnauthorizedException(__('User is not enabled'));
return;
}
$this->Session->Write('allowedMonitors',$user['MonitorIds']);
$this->Session->Write('streamPermission',$user['Stream']);
$this->Session->Write('eventPermission',$user['Events']);
$this->Session->Write('controlPermission',$user['Control']);
$this->Session->Write('systemPermission',$user['System']);
$this->Session->Write('monitorPermission',$user['Monitors']);
} else {
// if auth is not on, you can do everything
//$userMonitors = $this->User->find('first', $options);
$this->Session->Write('allowedMonitors','');
$this->Session->Write('streamPermission','View');
$this->Session->Write('eventPermission','Edit');
$this->Session->Write('controlPermission','Edit');
$this->Session->Write('systemPermission','Edit');
$this->Session->Write('monitorPermission','Edit');
}
$cred = $this->_getCredentials();
$ver = $this->_getVersion();
$this->set(array(
@ -130,8 +44,7 @@ class HostController extends AppController {
'version',
'apiversion'
)));
}
} // end function login()
// clears out session
function logout() {
@ -143,7 +56,7 @@ class HostController extends AppController {
'_serialize' => array('result')
));
}
} // end function logout()
private function _getCredentials() {
$credentials = '';
@ -152,6 +65,8 @@ class HostController extends AppController {
$isZmAuth = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')))['Config']['Value'];
if ( $isZmAuth ) {
require_once "../../../includes/auth.php"; # in the event we directly call getCredentials.json
$this->Session->read('user'); # this is needed for command line/curl to recognize a session
$zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value'];
if ( $zmAuthRelay == 'hashed' ) {
$zmAuthHashIps= $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value'];
@ -165,8 +80,7 @@ class HostController extends AppController {
}
}
return array($credentials, $appendPassword);
}
} // end function _getCredentials
function getCredentials() {
// ignore debug warnings from other functions
@ -178,8 +92,6 @@ class HostController extends AppController {
'_serialize' => array('credentials', 'append_password')
) );
}
// If $mid is set, only return disk usage for that monitor
// Else, return an array of total disk usage, and per-monitor

View File

@ -18,10 +18,13 @@ class MonitorsController extends AppController {
public function beforeRender() {
$this->set($this->Monitor->enumValues());
}
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission');
if ($canView == 'None') {
global $user;
# We already tested for auth in appController, so we just need to test for specific permission
$canView = (!$user) || ($user['Monitors'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
@ -35,7 +38,7 @@ class MonitorsController extends AppController {
public function index() {
$this->Monitor->recursive = 0;
if ($this->request->params['named']) {
if ( $this->request->params['named'] ) {
$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
@ -43,13 +46,14 @@ class MonitorsController extends AppController {
$conditions = array();
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
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'] ) ) {
if ( isset($conditions['GroupId']) ) {
$find_array['joins'] = array(
array(
'table' => 'Groups_Monitors',
@ -84,11 +88,12 @@ class MonitorsController extends AppController {
*/
public function view($id = null) {
$this->Monitor->recursive = 0;
if (!$this->Monitor->exists($id)) {
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) {
global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors);
} else {
$restricted = '';
@ -114,13 +119,15 @@ class MonitorsController extends AppController {
public function add() {
if ( $this->request->is('post') ) {
if ( $this->Session->Read('systemPermission') != 'Edit' ) {
throw new UnauthorizedException(__('Insufficient privileges'));
global $user;
$canAdd = (!$user) || ($user['System'] == 'Edit' );
if ( !$canAdd ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Monitor->create();
if ($this->Monitor->save($this->request->data)) {
if ( $this->Monitor->save($this->request->data) ) {
$this->daemonControl($this->Monitor->id, 'start');
//return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
$message = 'Saved';
@ -144,10 +151,12 @@ class MonitorsController extends AppController {
public function edit($id = null) {
$this->Monitor->id = $id;
if (!$this->Monitor->exists($id)) {
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('monitorPermission') != 'Edit') {
global $user;
$canEdit = (!$user) || ($user['Monitors'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
@ -163,9 +172,17 @@ class MonitorsController extends AppController {
// - restart or stop this monitor after change
$func = $Monitor['Function'];
// We don't pass the request data as the monitor object because it may be a subset of the full monitor array
$this->daemonControl( $this->Monitor->id, 'stop' );
if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) {
$this->daemonControl( $this->Monitor->id, 'start' );
$this->daemonControl($this->Monitor->id, 'stop');
if (
( $func != 'None' )
and
(
(!defined('ZM_SERVER_ID'))
or
($Monitor['ServerId']==ZM_SERVER_ID)
)
) {
$this->daemonControl($this->Monitor->id, 'start');
}
} else {
$message = 'Error ' . print_r($this->Monitor->invalidFields(), true);
@ -187,10 +204,10 @@ class MonitorsController extends AppController {
*/
public function delete($id = null) {
$this->Monitor->id = $id;
if (!$this->Monitor->exists()) {
if ( !$this->Monitor->exists() ) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('systemPermission') != 'Edit') {
if ( $this->Session->Read('systemPermission') != 'Edit' ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
@ -198,7 +215,7 @@ class MonitorsController extends AppController {
$this->daemonControl($this->Monitor->id, 'stop');
if ($this->Monitor->delete()) {
if ( $this->Monitor->delete() ) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index'));
@ -206,7 +223,7 @@ class MonitorsController extends AppController {
}
public function sourceTypes() {
$sourceTypes = $this->Monitor->query("describe Monitors Type;");
$sourceTypes = $this->Monitor->query('describe Monitors Type;');
preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches);
foreach( explode(',', $matches[1]) as $value ) {
@ -226,7 +243,7 @@ class MonitorsController extends AppController {
public function alarm() {
$id = $this->request->params['named']['id'];
$cmd = strtolower($this->request->params['named']['command']);
if (!$this->Monitor->exists($id)) {
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status' ) {
@ -252,19 +269,18 @@ class MonitorsController extends AppController {
// form auth key based on auth credentials
$this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options);
$config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value'];
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$config = $this->Config->find('first', $options);
$config = $this->Config->find('first', $options);
$zmAuthRelay = $config['Config']['Value'];
$auth='';
$auth = '';
if ( $zmOptAuth ) {
if ( $zmAuthRelay == 'hashed' ) {
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
$config = $this->Config->find('first', $options);
$config = $this->Config->find('first', $options);
$zmAuthHashSecret = $config['Config']['Value'];
$time = localtime();
@ -293,7 +309,7 @@ class MonitorsController extends AppController {
$id = $this->request->params['named']['id'];
$daemon = $this->request->params['named']['daemon'];
if (!$this->Monitor->exists($id)) {
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
@ -306,7 +322,7 @@ class MonitorsController extends AppController {
$monitor = Set::extract('/Monitor/.', $monitor);
// Pass -d for local, otherwise -m
if ($monitor[0]['Type'] == 'Local') {
if ( $monitor[0]['Type'] == 'Local' ) {
$args = '-d '. $monitor[0]['Device'];
} else {
$args = '-m '. $monitor[0]['Id'];
@ -315,7 +331,7 @@ class MonitorsController extends AppController {
// Build the command, and execute it
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
$status = exec( $command );
$status = exec($command);
// If 'not' is present, the daemon is not running, so return false
// https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
@ -351,9 +367,9 @@ class MonitorsController extends AppController {
$zm_path_bin = Configure::read('ZM_PATH_BIN');
foreach ($daemons as $daemon) {
foreach ( $daemons as $daemon ) {
$args = '';
if ( $daemon == 'zmc' and $monitor['Type'] == 'Local') {
if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) {
$args = '-d ' . $monitor['Device'];
} else {
$args = '-m ' . $id;
@ -363,5 +379,4 @@ class MonitorsController extends AppController {
$status = exec( $shellcmd );
}
}
} // end class MonitorsController

View File

@ -8,7 +8,6 @@ App::uses('AppController', 'Controller');
*/
class ServersController extends AppController {
/**
* Components
*
@ -16,18 +15,16 @@ class ServersController extends AppController {
*/
public $components = array('Paginator', 'RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('streamPermission');
if ($canView =='None') {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
public function beforeFilter() {
parent::beforeFilter();
global $user;
$canView = (!$user) || ($user['System'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
}
/**
* index method
*
@ -36,7 +33,7 @@ public function beforeFilter() {
public function index() {
$this->Server->recursive = 0;
$options='';
$options = '';
$servers = $this->Server->find('all',$options);
$this->set(array(
'servers' => $servers,
@ -76,16 +73,17 @@ public function beforeFilter() {
* @return void
*/
public function add() {
if ($this->request->is('post')) {
if ( $this->request->is('post') ) {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Server->create();
if ($this->Server->save($this->request->data)) {
if ( $this->Server->save($this->request->data) ) {
# Might be nice to send it a start request
#$this->daemonControl($this->Server->id, 'start', $this->request->data);
return $this->flash(__('The server has been saved.'), array('action' => 'index'));
@ -103,15 +101,17 @@ public function beforeFilter() {
public function edit($id = null) {
$this->Server->id = $id;
if (!$this->Server->exists($id)) {
throw new NotFoundException(__('Invalid server'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->Server->save($this->request->data)) {
if ( !$this->Server->exists($id) ) {
throw new NotFoundException(__('Invalid server'));
}
if ( $this->Server->save($this->request->data) ) {
$message = 'Saved';
} else {
$message = 'Error';
@ -133,20 +133,22 @@ public function beforeFilter() {
* @return void
*/
public function delete($id = null) {
$this->Server->id = $id;
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Server->id = $id;
if ( !$this->Server->exists() ) {
throw new NotFoundException(__('Invalid server'));
}
$this->request->allowMethod('post', 'delete');
#$this->daemonControl($this->Server->id, 'stop');
if ($this->Server->delete()) {
if ( $this->Server->delete() ) {
return $this->flash(__('The server has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The server could not be deleted. Please, try again.'), array('action' => 'index'));

View File

@ -12,30 +12,28 @@ class StatesController extends AppController {
public $components = array('RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('systemPermission');
if ($canView =='None')
{
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
parent::beforeFilter();
global $user;
$canView = (!$user) || ($user['System'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/**
* index method
*
* @return void
*/
public function index() {
$this->State->recursive = 0;
$states = $this->State->find('all');
$this->set(array(
'states' => $states,
'_serialize' => array('states')
));
}
public function index() {
$this->State->recursive = 0;
$states = $this->State->find('all');
$this->set(array(
'states' => $states,
'_serialize' => array('states')
));
}
/**
* view method
@ -44,35 +42,35 @@ public function beforeFilter() {
* @param string $id
* @return void
*/
public function view($id = null) {
if (!$this->State->exists($id)) {
throw new NotFoundException(__('Invalid state'));
}
$options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
$this->set('state', $this->State->find('first', $options));
}
public function view($id = null) {
if ( !$this->State->exists($id) ) {
throw new NotFoundException(__('Invalid state'));
}
$options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
$this->set('state', $this->State->find('first', $options));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
public function add() {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->request->is('post')) {
$this->State->create();
if ($this->State->save($this->request->data)) {
return $this->flash(__('The state has been saved.'), array('action' => 'index'));
}
}
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->State->create();
if ($this->State->save($this->request->data)) {
return $this->flash(__('The state has been saved.'), array('action' => 'index'));
}
}
}
/**
* edit method
@ -81,26 +79,27 @@ public function beforeFilter() {
* @param string $id
* @return void
*/
public function edit($id = null) {
if (!$this->State->exists($id)) {
throw new NotFoundException(__('Invalid state'));
}
public function edit($id = null) {
if (!$this->State->exists($id)) {
throw new NotFoundException(__('Invalid state'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->request->is(array('post', 'put'))) {
if ($this->State->save($this->request->data)) {
return $this->flash(__('The state has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
$this->request->data = $this->State->find('first', $options);
}
}
if ( $this->request->is(array('post', 'put')) ) {
if ( $this->State->save($this->request->data) ) {
return $this->flash(__('The state has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
$this->request->data = $this->State->find('first', $options);
}
}
/**
* delete method
@ -109,48 +108,50 @@ public function beforeFilter() {
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->State->id = $id;
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
public function delete($id = null) {
$this->State->id = $id;
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if (!$this->State->exists()) {
throw new NotFoundException(__('Invalid state'));
}
$this->request->allowMethod('post', 'delete');
if ($this->State->delete()) {
return $this->flash(__('The state has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The state could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
if (!$this->State->exists()) {
throw new NotFoundException(__('Invalid state'));
}
$this->request->allowMethod('post', 'delete');
if ($this->State->delete()) {
return $this->flash(__('The state has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The state could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
public function change() {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
public function change() {
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$newState = $this->request->params['pass'][0];
$blah = $this->packageControl($newState);
$newState = $this->request->params['pass'][0];
$blah = $this->packageControl($newState);
$this->set(array(
'blah' => $blah,
'_serialize' => array('blah')
));
}
$this->set(array(
'blah' => $blah,
'_serialize' => array('blah')
));
}
public function packageControl( $command ) {
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$string = $zm_path_bin.'/zmpkg.pl '.escapeshellarg( $command );
$status = exec( $string );
public function packageControl( $command ) {
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$string = $zm_path_bin.'/zmpkg.pl '.escapeshellarg( $command );
$status = exec( $string );
return $status;
}
return $status;
}
}

View File

@ -8,92 +8,93 @@ App::uses('AppController', 'Controller');
*/
class ZonePresetsController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler');
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler');
/**
* index method
*
* @return void
*/
public function index() {
$zonePresets = $this->ZonePreset->find('all');
$this->set(array(
'zonePresets' => $zonePresets,
'_serialize' => array('zonePresets')
));
}
/**
* index method
*
* @return void
*/
public function index() {
$zonePresets = $this->ZonePreset->find('all');
$this->set(array(
'zonePresets' => $zonePresets,
'_serialize' => array('zonePresets')
));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
if (!$this->ZonePreset->exists($id)) {
throw new NotFoundException(__('Invalid zone preset'));
}
$options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
$this->set('zonePreset', $this->ZonePreset->find('first', $options));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
if ( !$this->ZonePreset->exists($id) ) {
throw new NotFoundException(__('Invalid zone preset'));
}
$options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
$this->set('zonePreset', $this->ZonePreset->find('first', $options));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
$this->ZonePreset->create();
if ($this->ZonePreset->save($this->request->data)) {
return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
}
}
}
/**
* add method
*
* @return void
*/
public function add() {
if ( $this->request->is('post') ) {
$this->ZonePreset->create();
if ( $this->ZonePreset->save($this->request->data) ) {
return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
}
}
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
if (!$this->ZonePreset->exists($id)) {
throw new NotFoundException(__('Invalid zone preset'));
}
if ($this->request->is(array('post', 'put'))) {
if ($this->ZonePreset->save($this->request->data)) {
return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
$this->request->data = $this->ZonePreset->find('first', $options);
}
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
if ( !$this->ZonePreset->exists($id) ) {
throw new NotFoundException(__('Invalid zone preset'));
}
if ( $this->request->is(array('post', 'put')) ) {
if ( $this->ZonePreset->save($this->request->data) ) {
return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
$this->request->data = $this->ZonePreset->find('first', $options);
}
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->ZonePreset->id = $id;
if (!$this->ZonePreset->exists()) {
throw new NotFoundException(__('Invalid zone preset'));
}
$this->request->allowMethod('post', 'delete');
if ($this->ZonePreset->delete()) {
return $this->flash(__('The zone preset has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The zone preset could not be deleted. Please, try again.'), array('action' => 'index'));
}
}}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->ZonePreset->id = $id;
if ( !$this->ZonePreset->exists() ) {
throw new NotFoundException(__('Invalid zone preset'));
}
$this->request->allowMethod('post', 'delete');
if ( $this->ZonePreset->delete() ) {
return $this->flash(__('The zone preset has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The zone preset could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
} // end class ZonePresetsController

View File

@ -7,148 +7,163 @@ App::uses('AppController', 'Controller');
*/
class ZonesController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler');
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission');
if ($canView =='None')
{
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
public function beforeFilter() {
parent::beforeFilter();
}
global $user;
$canView = (!$user) || $user['Monitors'] != 'None';
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
// Find all zones which belong to a MonitorId
public function forMonitor($id = null) {
// Find all zones which belong to a MonitorId
public function forMonitor($id = null) {
$this->loadModel('Monitor');
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor'));
}
$this->Zone->recursive = -1;
$zones = $this->Zone->find('all', array(
'conditions' => array('MonitorId' => $id)
'conditions' => array('MonitorId' => $id)
));
$this->set(array(
'zones' => $zones,
'_serialize' => array('zones')
'zones' => $zones,
'_serialize' => array('zones')
));
}
public function index() {
}
public function index() {
$this->Zone->recursive = -1;
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors))
{
$mon_options = array('Zones.MonitorId' => $allowedMonitors);
}
else
{
$mon_options='';
global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'],NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$mon_options = array('Zones.MonitorId' => $allowedMonitors);
} else {
$mon_options = '';
}
$zones = $this->Zone->find('all',$mon_options);
$this->set(array(
'zones' => $zones,
'_serialize' => array('zones')
'zones' => $zones,
'_serialize' => array('zones')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
$this->Zone->create();
if ($this->Zone->save($this->request->data)) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
}
}
$monitors = $this->Zone->Monitor->find('list');
$this->set(compact('monitors'));
}
/**
* add method
*
* @return void
*/
public function add() {
if ( $this->request->is('post') ) {
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->Zone->create();
if ( $this->Zone->save($this->request->data) ) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
}
}
$monitors = $this->Zone->Monitor->find('list');
$this->set(compact('monitors'));
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Zone->id = $id;
if ( !$this->Zone->exists($id) ) {
throw new NotFoundException(__('Invalid zone'));
}
if ( $this->request->is(array('post', 'put')) ) {
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->Zone->save($this->request->data) ) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('Zone.' . $this->Zone->primaryKey => $id));
$this->request->data = $this->Zone->find('first', $options);
}
$monitors = $this->Zone->Monitor->find('list');
$this->set(compact('monitors'));
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Zone->id = $id;
if ( !$this->Zone->exists() ) {
throw new NotFoundException(__('Invalid zone'));
}
$this->request->allowMethod('post', 'delete');
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->Zone->delete() ) {
return $this->flash(__('The zone has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
public function createZoneImage($id = null) {
$this->loadModel('Monitor');
$this->Monitor->id = $id;
if ( !$this->Monitor->exists() ) {
throw new NotFoundException(__('Invalid zone'));
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Zone->id = $id;
$this->loadModel('Config');
$zm_dir_images = $this->Config->find('list', array(
'conditions' => array('Name' => 'ZM_DIR_IMAGES'),
'fields' => array('Name', 'Value')
));
if (!$this->Zone->exists($id)) {
throw new NotFoundException(__('Invalid zone'));
}
if ($this->request->is(array('post', 'put'))) {
if ($this->Zone->save($this->request->data)) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
}
} else {
$options = array('conditions' => array('Zone.' . $this->Zone->primaryKey => $id));
$this->request->data = $this->Zone->find('first', $options);
}
$monitors = $this->Zone->Monitor->find('list');
$this->set(compact('monitors'));
}
$zm_dir_images = $zm_dir_images['ZM_DIR_IMAGES'];
$zm_path_web = Configure::read('ZM_PATH_WEB');
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$images_path = "$zm_path_web/$zm_dir_images";
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Zone->id = $id;
if (!$this->Zone->exists()) {
throw new NotFoundException(__('Invalid zone'));
}
$this->request->allowMethod('post', 'delete');
if ($this->Zone->delete()) {
return $this->flash(__('The zone has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
chdir($images_path);
$command = escapeshellcmd("$zm_path_bin/zmu -z -m $id");
system($command, $status);
public function createZoneImage( $id = null ) {
$this->loadModel('Monitor');
$this->Monitor->id = $id;
if (!$this->Monitor->exists()) {
throw new NotFoundException(__('Invalid zone'));
}
$this->loadModel('Config');
$zm_dir_images = $this->Config->find('list', array(
'conditions' => array('Name' => 'ZM_DIR_IMAGES'),
'fields' => array('Name', 'Value')
));
$zm_dir_images = $zm_dir_images['ZM_DIR_IMAGES'];
$zm_path_web = Configure::read('ZM_PATH_WEB');
$zm_path_bin = Configure::read('ZM_PATH_BIN');
$images_path = "$zm_path_web/$zm_dir_images";
chdir($images_path);
$command = escapeshellcmd("$zm_path_bin/zmu -z -m $id");
system( $command, $status );
$this->set(array(
'status' => $status,
'_serialize' => array('status')
));
}
}
$this->set(array(
'status' => $status,
'_serialize' => array('status')
));
}
} // end class

@ -1 +1 @@
Subproject commit ea90c0cd7f6e24333a90885e563b5d30b793db29
Subproject commit ca91b87fda8e006e4fca2ed870f24f9a29c2905d

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

View File

@ -12,6 +12,9 @@ private $defaults = array(
'CanMoveRel' => 0,
'CanMoveCon' => 0,
'CanPan' => 0,
'CanReset' => 0,
'CanSleep' => 0,
'CanWake' => 0,
'MinPanRange' => NULL,
'MaxPanRange' => NULL,
'MinPanStep' => NULL,
@ -103,7 +106,7 @@ private $defaults = array(
if ( $IdOrRow ) {
$row = NULL;
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Control WHERE Id=?', NULL, array( $IdOrRow ) );
$row = dbFetchOne( 'SELECT * FROM Controls WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error("Unable to load Control record for Id=" . $IdOrRow );
}

View File

@ -9,17 +9,19 @@ private $defaults = array(
'Name' => '',
'StorageId' => 0,
'ServerId' => 0,
'Type' => 'Ffmpeg',
'Function' => 'None',
'Enabled' => 1,
'LinkedMonitors' => null,
'Width' => null,
'Height' => null,
'Orientation' => null,
'AnalysisFPSLimit' => null,
'ZoneCount' => 0,
'Triggers' => null,
'Type' => 'Ffmpeg',
'MaxFPS' => null,
'AlarmMaxFPS' => null,
'Refresh' => null,
);
private $status_fields = array(
'AnalysisFPS' => null,

View File

@ -29,16 +29,16 @@ function do_request($method, $url, $data=array(), $optional_headers = null) {
'method' => $method,
'content' => $data
));
if ($optional_headers !== null) {
if ( $optional_headers !== null ) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
if ( !$fp ) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
if ( $response === false ) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
@ -49,16 +49,16 @@ function do_post_request($url, $data, $optional_headers = null) {
'method' => 'POST',
'content' => $data
));
if ($optional_headers !== null) {
if ( $optional_headers !== null ) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
if ( !$fp ) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
if ( $response === false ) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
@ -106,17 +106,17 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're
$responseData = json_decode($res,true);
// PP - credit: https://github.com/google/recaptcha/blob/master/src/ReCaptcha/Response.php
// if recaptcha resulted in error, we might have to deny login
if (isset($responseData['success']) && $responseData['success'] == false) {
if ( isset($responseData['success']) && $responseData['success'] == false ) {
// PP - before we deny auth, let's make sure the error was not 'invalid secret'
// because that means the user did not configure the secret key correctly
// in this case, we prefer to let him login in and display a message to correct
// the key. Unfortunately, there is no way to check for invalid site key in code
// as it produces the same error as when you don't answer a recaptcha
if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) {
if (!in_array('invalid-input-secret',$responseData['error-codes'])) {
if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
if ( !in_array('invalid-input-secret',$responseData['error-codes']) ) {
Error('reCaptcha authentication failed');
userLogout();
$view='login';
$view = 'login';
$refreshParent = true;
return;
} else {
@ -140,19 +140,19 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're
$view = 'none';
} else if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) {
$_COOKIE['zmBandwidth'] = validStr($_REQUEST['newBandwidth']);
setcookie( 'zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10 );
setcookie('zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10);
$refreshParent = true;
}
// Event scope actions, view permissions only required
if ( canView('Events') ) {
if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( $action == 'addterm' ) {
$_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
$_REQUEST['filter'] = addFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
} elseif ( $action == 'delterm' ) {
$_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
} else if ( canEdit( 'Events' ) ) {
$_REQUEST['filter'] = delFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
} else if ( canEdit('Events') ) {
if ( $action == 'delete' ) {
if ( ! empty($_REQUEST['Id']) ) {
dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id']));
@ -210,11 +210,13 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
} else if ( $action == 'eventdetail' ) {
if ( !empty($_REQUEST['eid']) ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid']) );
} else {
$dbConn->beginTransaction();
foreach( getAffectedIds('markEid') as $markEid ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid) );
}
$dbConn->commit();
}
@ -226,7 +228,7 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $_REQUEST['eid']));
} else {
$dbConn->beginTransaction();
foreach( getAffectedIds( 'markEid' ) as $markEid ) {
foreach( getAffectedIds('markEid') as $markEid ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $markEid));
}
$dbConn->commit();
@ -234,8 +236,8 @@ if ( canView('Events') ) {
}
} elseif ( $action == 'delete' ) {
$dbConn->beginTransaction();
foreach( getAffectedIds( 'markEid' ) as $markEid ) {
deleteEvent( $markEid );
foreach( getAffectedIds('markEid') as $markEid ) {
deleteEvent($markEid);
}
$dbConn->commit();
$refreshParent = true;
@ -245,45 +247,47 @@ if ( canView('Events') ) {
} // end canView(Events)
// Monitor control actions, require a monitor id and control view permissions for that monitor
if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
require_once( 'control_functions.php' );
require_once( 'Monitor.php' );
if ( !empty($_REQUEST['mid']) && canView('Control', $_REQUEST['mid']) ) {
require_once('control_functions.php');
require_once('Monitor.php');
$mid = validInt($_REQUEST['mid']);
if ( $action == 'control' ) {
$monitor = new Monitor( $mid );
$monitor = new Monitor($mid);
$ctrlCommand = buildControlCommand( $monitor );
sendControlCommand( $monitor->Id(), $ctrlCommand );
} elseif ( $action == 'settings' ) {
$ctrlCommand = buildControlCommand($monitor);
sendControlCommand($monitor->Id(), $ctrlCommand);
} else if ( $action == 'settings' ) {
$args = ' -m ' . escapeshellarg($mid);
$args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
$args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
$args .= ' -H' . escapeshellarg($_REQUEST['newHue']);
$args .= ' -O' . escapeshellarg($_REQUEST['newColour']);
$zmuCommand = getZmuCommand( $args );
$zmuCommand = getZmuCommand($args);
$zmuOutput = exec( $zmuCommand );
list( $brightness, $contrast, $hue, $colour ) = explode( ' ', $zmuOutput );
dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid));
$zmuOutput = exec($zmuCommand);
list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput);
dbQuery(
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
array($brightness, $contrast, $hue, $colour, $mid));
}
}
// Control capability actions, require control edit permissions
if ( canEdit('Control') ) {
if ( $action == 'controlcap' ) {
require_once( 'Control.php' );
require_once('Control.php');
$Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
$Control->save( $_REQUEST['newControl'] );
$Control->save($_REQUEST['newControl']);
$refreshParent = true;
$view = 'none';
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markCids']) ) {
foreach( $_REQUEST['markCids'] as $markCid ) {
dbQuery( 'delete from Controls where Id = ?', array($markCid) );
dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) );
dbQuery('DELETE FROM Controls WHERE Id = ?', array($markCid));
dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($markCid));
$refreshParent = true;
}
}
@ -293,59 +297,59 @@ if ( canEdit('Control') ) {
if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
if ( $action == 'save' ) {
foreach ( $_REQUEST['mids'] as $mid ) {
$mid = ValidInt( $mid );
if ( ! canEdit('Monitors', $mid ) ) {
$mid = ValidInt($mid);
if ( ! canEdit('Monitors', $mid) ) {
Warning("Cannot edit monitor $mid");
continue;
}
$Monitor = new Monitor( $mid );
$Monitor = new Monitor($mid);
if ( $Monitor->Type() != 'WebSite' ) {
$Monitor->zmaControl('stop');
$Monitor->zmcControl('stop');
}
$Monitor->save( $_REQUEST['newMonitor'] );
if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->save($_REQUEST['newMonitor']);
if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('start');
if ( $Monitor->Enabled() ) {
$Monitor->zmaControl('start');
}
}
} // end foreach mid
$refreshParent = true;
} // end if action == save
} // end if object is Monitor
// Monitor edit actions, require a monitor id and edit permissions for that monitor
if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) {
$mid = validInt($_REQUEST['mid']);
if ( $action == 'function' ) {
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
$newFunction = validStr($_REQUEST['newFunction']);
# Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
$newEnabled = ( !isset( $_REQUEST['newEnabled'] ) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
$newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
$oldFunction = $monitor['Function'];
$oldEnabled = $monitor['Enabled'];
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) );
dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?',
array($newFunction, $newEnabled, $mid));
$monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled;
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, $restart?'restart':'' );
zmaControl( $monitor, 'start' );
zmaControl($monitor, 'stop');
zmcControl($monitor, $restart?'restart':'');
zmaControl($monitor, 'start');
}
$refreshParent = true;
}
} elseif ( $action == 'zone' && isset( $_REQUEST['zid'] ) ) {
} else if ( $action == 'zone' && isset($_REQUEST['zid']) ) {
$zid = validInt($_REQUEST['zid']);
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
if ( !empty($zid) ) {
$zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array( $mid, $zid ) );
$zone = dbFetchOne('SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array($mid, $zid));
} else {
$zone = array();
}
@ -365,74 +369,74 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
unset( $_REQUEST['newZone']['Points'] );
$types = array();
$changes = getFormChanges( $zone, $_REQUEST['newZone'], $types );
$changes = getFormChanges($zone, $_REQUEST['newZone'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( $zid > 0 ) {
dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) );
dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', array($mid, $zid));
} else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid));
}
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'restart' );
zmaControl( $monitor, 'start' );
zmaControl($monitor, 'stop');
zmcControl($monitor, 'restart');
zmaControl($monitor, 'start');
} else {
zmaControl( $mid, 'restart' );
zmaControl($monitor, 'restart');
}
}
if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) {
require_once( 'control_functions.php' );
sendControlCommand( $mid, 'quit' );
if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor['Controllable'] ) {
require_once('control_functions.php');
sendControlCommand($mid, 'quit');
}
$refreshParent = true;
}
$view = 'none';
} elseif ( $action == 'plugin' && isset($_REQUEST['pl'])) {
$sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?';
$pconfs=dbFetchAll( $sql, NULL, array( $mid, $_REQUEST['zid'], $_REQUEST['pl'] ) );
$changes=0;
foreach( $pconfs as $pconf ) {
$value=$_REQUEST['pluginOpt'][$pconf['Name']];
if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) {
dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array( $value, $pconf['Id'] ) );
} elseif ( $action == 'plugin' && isset($_REQUEST['pl']) ) {
$sql = 'SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?';
$pconfs=dbFetchAll($sql, NULL, array($mid, $_REQUEST['zid'], $_REQUEST['pl']));
$changes = 0;
foreach ( $pconfs as $pconf ) {
$value = $_REQUEST['pluginOpt'][$pconf['Name']];
if ( array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value'] != $value) ) {
dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array($value, $pconf['Id']));
$changes++;
}
}
if($changes>0) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
zmaControl( $mid, 'restart' );
if ( $changes > 0 ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
zmaControl($mid, 'restart');
}
$refreshParent = true;
}
$view = 'none';
} elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) {
} elseif ( ($action == 'sequence') && isset($_REQUEST['smid']) ) {
$smid = validInt($_REQUEST['smid']);
$monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) );
$smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid));
$smonitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($smid));
dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) );
dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) );
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($smonitor['Sequence'], $monitor['Id']));
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($monitor['Sequence'], $smonitor['Id']));
$refreshParent = true;
fixSequences();
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markZids']) ) {
$deletedZid = 0;
foreach( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) );
dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) );
foreach ( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne('SELECT * FROM Zones WHERE Id=?', NULL, array($markZid));
dbQuery('DELETE FROM Zones WHERE MonitorId=? AND Id=?', array($mid, $markZid));
$deletedZid = 1;
}
if ( $deletedZid ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $zone['Type'] == 'Privacy' ) {
zmaControl( $mid, 'stop' );
zmcControl( $mid, 'restart' );
zmaControl( $mid, 'start' );
zmaControl($mid, 'stop');
zmcControl($mid, 'restart');
zmaControl($mid, 'start');
} else {
zmaControl( $mid, 'restart' );
zmaControl($mid, 'restart');
}
} // end if daemonCheck()
$refreshParent = true;
@ -442,15 +446,15 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} // end if $mid and canEdit($mid)
// Monitor edit actions, monitor id derived, require edit permissions for that monitor
if ( canEdit( 'Monitors' ) ) {
if ( canEdit('Monitors') ) {
if ( $action == 'monitor' ) {
$mid = 0;
if ( !empty($_REQUEST['mid']) ) {
$mid = validInt($_REQUEST['mid']);
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
if ( ZM_OPT_X10 ) {
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) );
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid));
if ( !$x10Monitor )
$x10Monitor = array();
}
@ -476,18 +480,19 @@ if ( canEdit( 'Monitors' ) ) {
);
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] );
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne(
'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId'] );
if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
Logger::Debug("Auto selecting server to " . ZM_SERVER_ID);
Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID);
}
}
$columns = getTableColumns('Monitors');
$changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( $mid ) {
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
@ -495,19 +500,19 @@ if ( canEdit( 'Monitors' ) ) {
zmaControl($monitor, 'stop');
zmcControl($monitor, 'stop');
}
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
dbQuery('UPDATE Monitors SET '.implode(', ', $changes).' WHERE Id=?', array($mid));
// Groups will be added below
if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
$OldStorage = new Storage( $monitor['StorageId'] );
$saferOldName = basename( $monitor['Name'] );
if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) )
unlink( $OldStorage->Path().'/'.$saferOldName );
$OldStorage = new Storage($monitor['StorageId']);
$saferOldName = basename($monitor['Name']);
if ( file_exists($OldStorage->Path().'/'.$saferOldName) )
unlink($OldStorage->Path().'/'.$saferOldName);
$NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
if ( ! file_exists( $NewStorage->Path().'/'.$mid ) )
mkdir( $NewStorage->Path().'/'.$mid, 0755 );
$saferNewName = basename( $_REQUEST['newMonitor']['Name'] );
symlink( $mid, $NewStorage->Path().'/'.$saferNewName );
$NewStorage = new Storage($_REQUEST['newMonitor']['StorageId']);
if ( ! file_exists($NewStorage->Path().'/'.$mid) )
mkdir($NewStorage->Path().'/'.$mid, 0755);
$saferNewName = basename($_REQUEST['newMonitor']['Name']);
symlink($mid, $NewStorage->Path().'/'.$saferNewName);
}
if ( isset($changes['Width']) || isset($changes['Height']) ) {
$newW = $_REQUEST['newMonitor']['Width'];
@ -517,15 +522,15 @@ if ( canEdit( 'Monitors' ) ) {
$oldH = $monitor['Height'];
$oldA = $oldW * $oldH;
$zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid) );
$zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid));
foreach ( $zones as $zone ) {
$newZone = $zone;
$points = coordsToPoints( $zone['Coords'] );
$points = coordsToPoints($zone['Coords']);
for ( $i = 0; $i < count($points); $i++ ) {
$points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1));
$points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1));
}
$newZone['Coords'] = pointsToCoords( $points );
$newZone['Coords'] = pointsToCoords($points);
$newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA));
$newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA));
$newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA));
@ -534,41 +539,43 @@ if ( canEdit( 'Monitors' ) ) {
$newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA));
$newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA));
$changes = getFormChanges( $zone, $newZone, $types );
$changes = getFormChanges($zone, $newZone, $types);
if ( count( $changes ) ) {
dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) );
if ( count($changes) ) {
dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
array($mid, $zone['Id']));
}
}
}
} // end foreach zone
} // end if width and height
$restart = true;
} else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors
} else if ( ! $user['MonitorIds'] ) {
// Can only create new monitors if we are not restricted to specific monitors
# FIXME This is actually a race condition. Should lock the table.
$maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
$changes[] = 'Sequence = '.($maxSeq+1);
if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) {
if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) {
$mid = dbInsertId();
$zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height'];
dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
//$view = 'none';
$Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
mkdir( $Storage->Path().'/'.$mid, 0755 );
$Storage = new Storage($_REQUEST['newMonitor']['StorageId']);
mkdir($Storage->Path().'/'.$mid, 0755);
$saferName = basename($_REQUEST['newMonitor']['Name']);
symlink( $mid, $Storage->Path().'/'.$saferName );
symlink($mid, $Storage->Path().'/'.$saferName);
} else {
Error("Error saving new Monitor.");
Error('Error saving new Monitor.');
return;
}
} else {
Error("Users with Monitors restrictions cannot create new monitors.");
Error('Users with Monitors restrictions cannot create new monitors.');
return;
}
$restart = true;
} else {
Logger::Debug("No action due to no changes to Monitor");
Logger::Debug('No action due to no changes to Monitor');
} # end if count(changes)
if (
@ -589,21 +596,21 @@ if ( canEdit( 'Monitors' ) ) {
} // end if there has been a change of groups
if ( ZM_OPT_X10 ) {
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
$x10Changes = getFormChanges($x10Monitor, $_REQUEST['newX10Monitor']);
if ( count( $x10Changes ) ) {
if ( count($x10Changes) ) {
if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) {
dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) );
dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid));
} elseif ( !$user['MonitorIds'] ) {
if ( !$x10Monitor ) {
dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) );
dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid));
} else {
dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) );
dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid));
}
}
$restart = true;
}
}
} # end if has x10Changes
} # end if ZM_OPT_X10
if ( $restart ) {
@ -616,8 +623,8 @@ if ( canEdit( 'Monitors' ) ) {
}
if ( $new_monitor->Controllable() ) {
require_once( 'control_functions.php' );
sendControlCommand( $mid, 'quit' );
require_once('control_functions.php');
sendControlCommand($mid, 'quit');
}
// really should thump zmwatch and maybe zmtrigger too.
//daemonControl( 'restart', 'zmwatch.pl' );
@ -626,11 +633,11 @@ if ( canEdit( 'Monitors' ) ) {
$view = 'none';
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) {
require_once( 'Monitor.php' );
foreach( $_REQUEST['markMids'] as $markMid ) {
require_once('Monitor.php');
foreach ( $_REQUEST['markMids'] as $markMid ) {
if ( canEdit('Monitors', $markMid) ) {
// This could be faster as a select all
if ( $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid) ) ) {
if ( $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid)) ) {
$Monitor = new Monitor($monitor);
$Monitor->delete();
} // end if monitor found in db
@ -641,15 +648,17 @@ if ( canEdit( 'Monitors' ) ) {
}
// Device view actions
if ( canEdit( 'Devices' ) ) {
if ( canEdit('Devices') ) {
if ( $action == 'device' ) {
if ( !empty($_REQUEST['command']) ) {
setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] );
} elseif ( isset( $_REQUEST['newDevice'] ) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) ) {
dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
dbQuery('UPDATE Devices SET Name=?, KeyString=? WHERE Id=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
} else {
dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) );
dbQuery('INSERT INTO Devices SET Name=?, KeyString=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString']) );
}
$refreshParent = true;
$view = 'none';
@ -657,7 +666,7 @@ if ( canEdit( 'Devices' ) ) {
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markDids']) ) {
foreach( $_REQUEST['markDids'] as $markDid ) {
dbQuery( 'delete from Devices where Id=?', array($markDid) );
dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid));
$refreshParent = true;
}
}
@ -665,47 +674,59 @@ if ( canEdit( 'Devices' ) ) {
} // end if canedit devices
// Group view actions
if ( canView( 'Groups' ) && $action == 'setgroup' ) {
if ( canView('Groups') && ($action == 'setgroup') ) {
if ( !empty($_REQUEST['gid']) ) {
setcookie( 'zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10 );
setcookie('zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10);
} else {
setcookie( 'zmGroup', '', time()-3600*24*2 );
setcookie('zmGroup', '', time()-3600*24*2);
}
$refreshParent = true;
}
// Group edit actions
# Should probably verify that each monitor id is a valid monitor, that we have access to. However at the moment, you have to have System permissions to do this
if ( canEdit( 'Groups' ) ) {
# Should probably verify that each monitor id is a valid monitor, that we have access to.
# However at the moment, you have to have System permissions to do this
if ( canEdit('Groups') ) {
if ( $action == 'group' ) {
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$group_id = null;
if ( !empty($_POST['gid']) ) {
$group_id = $_POST['gid'];
dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) );
dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id) );
dbQuery(
'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
$group_id,
)
);
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id));
} else {
dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) );
$group_id=dbInsertId();
dbQuery(
'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
)
);
$group_id = dbInsertId();
}
if ( $group_id ) {
foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) );
dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid));
}
}
$view = 'none';
$refreshParent = true;
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['gid']) ) {
if ( is_array( $_REQUEST['gid'] ) ) {
foreach( $_REQUEST['gid'] as $gid ) {
$Group = new Group( $gid );
if ( is_array($_REQUEST['gid']) ) {
foreach ( $_REQUEST['gid'] as $gid ) {
$Group = new Group($gid);
$Group->delete();
}
} else {
$Group = new Group( $_REQUEST['gid'] );
$Group = new Group($_REQUEST['gid'] );
$Group->delete();
}
}
@ -714,43 +735,48 @@ if ( canEdit( 'Groups' ) ) {
} // end if can edit groups
// System edit actions
if ( canEdit( 'System' ) ) {
if ( isset( $_REQUEST['object'] ) ) {
if ( canEdit('System') ) {
if ( isset($_REQUEST['object']) ) {
if ( $_REQUEST['object'] == 'MontageLayout' ) {
require_once('MontageLayout.php');
if ( $action == 'Save' ) {
$Layout = null;
if ( $_REQUEST['Name'] != '' ) {
$Layout = new MontageLayout();
$Layout->Name( $_REQUEST['Name'] );
$Layout->Name($_REQUEST['Name']);
} else {
$Layout = new MontageLayout( $_REQUEST['zmMontageLayout'] );
$Layout = new MontageLayout($_REQUEST['zmMontageLayout']);
}
$Layout->Positions( $_REQUEST['Positions'] );
$Layout->Positions($_REQUEST['Positions']);
$Layout->save();
session_start();
$_SESSION['zmMontageLayout'] = $Layout->Id();
setcookie('zmMontageLayout', $Layout->Id(), 1 );
setcookie('zmMontageLayout', $Layout->Id(), 1);
session_write_close();
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview';
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montage';
} // end if save
} else if ( $_REQUEST['object'] == 'server' ) {
if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) )
$dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) );
else
if ( !empty($_REQUEST['id']) ) {
$dbServer = dbFetchOne(
'SELECT * FROM Servers WHERE Id=?',
NULL,
array($_REQUEST['id']) );
} else {
$dbServer = array();
}
$types = array();
$changes = getFormChanges( $dbServer, $_REQUEST['newServer'], $types );
$changes = getFormChanges($dbServer, $_REQUEST['newServer'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['id']) ) {
dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
dbQuery('UPDATE Servers SET '.implode(', ', $changes).' WHERE Id = ?',
array($_REQUEST['id']) );
} else {
dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Servers SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -758,27 +784,27 @@ if ( canEdit( 'System' ) ) {
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['markIds']) ) {
foreach( $_REQUEST['markIds'] as $Id )
dbQuery( 'DELETE FROM Servers WHERE Id=?', array($Id) );
dbQuery('DELETE FROM Servers WHERE Id=?', array($Id));
}
$refreshParent = true;
} else {
Error( "Unknown action $action in saving Server" );
Error("Unknown action $action in saving Server");
}
} else if ( $_REQUEST['object'] == 'storage' ) {
if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) )
$dbStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']) );
$dbStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']));
else
$dbStorage = array();
$types = array();
$changes = getFormChanges( $dbStorage, $_REQUEST['newStorage'], $types );
$changes = getFormChanges($dbStorage, $_REQUEST['newStorage'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['id']) ) {
dbQuery( 'UPDATE Storage SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
dbQuery('UPDATE Storage SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['id']));
} else {
dbQuery( 'INSERT INTO Storage set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Storage set '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -786,11 +812,11 @@ if ( canEdit( 'System' ) ) {
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['markIds']) ) {
foreach( $_REQUEST['markIds'] as $Id )
dbQuery( 'DELETE FROM Storage WHERE Id=?', array($Id) );
dbQuery('DELETE FROM Storage WHERE Id=?', array($Id));
}
$refreshParent = true;
} else {
Error( "Unknown action $action in saving Storage" );
Error("Unknown action $action in saving Storage");
}
} # end if isset($_REQUEST['object'] )
@ -804,7 +830,7 @@ if ( canEdit( 'System' ) ) {
}
case 'ignore' :
{
dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" );
dbQuery("UPDATE Config SET Value = '".ZM_DYN_LAST_VERSION."' WHERE Name = 'ZM_DYN_CURR_VERSION'");
break;
}
case 'hour' :
@ -819,12 +845,12 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
break;
}
case 'never' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_CHECK_FOR_UPDATES'");
break;
}
}
@ -852,30 +878,51 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
break;
}
case 'never' :
case 'already' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
break;
}
} // end switch option
}
if ( ($action == 'privacy') && isset($_REQUEST['option']) ) {
switch( $_REQUEST['option'] ) {
case 'decline' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
case 'accept' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
default: # Enable the privacy statement if we somehow submit something other than accept or decline
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_SHOW_PRIVACY'");
} // end switch option
return;
}
if ( $action == 'options' && isset($_REQUEST['tab']) ) {
$configCat = $configCats[$_REQUEST['tab']];
$changed = false;
foreach ( $configCat as $name=>$value ) {
unset( $newValue );
unset($newValue);
if ( $value['Type'] == 'boolean' && empty($_REQUEST['newConfig'][$name]) ) {
$newValue = 0;
} else if ( isset($_REQUEST['newConfig'][$name]) ) {
$newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) );
$newValue = preg_replace("/\r\n/", "\n", stripslashes($_REQUEST['newConfig'][$name]));
}
if ( isset($newValue) && ($newValue != $value['Value']) ) {
dbQuery( 'UPDATE Config SET Value=? WHERE Name=?', array( $newValue, $name ) );
dbQuery('UPDATE Config SET Value=? WHERE Name=?', array($newValue, $name));
$changed = true;
}
}
@ -901,29 +948,30 @@ if ( canEdit( 'System' ) ) {
}
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab'];
}
loadConfig( false );
loadConfig(false);
return;
} elseif ( $action == 'user' ) {
if ( !empty($_REQUEST['uid']) )
$dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) );
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
else
$dbUser = array();
$types = array();
$changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( $_REQUEST['newUser']['Password'] )
$changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
else
unset( $changes['Password'] );
unset($changes['Password']);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['uid']) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) );
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid']));
# If we are updating the logged in user, then update our session user data.
if ( $user and ( $dbUser['Username'] == $user['Username'] ) )
userLogin( $dbUser['Username'], $dbUser['Password'] );
userLogin($dbUser['Username'], $dbUser['Password']);
} else {
dbQuery( 'insert into Users set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -931,29 +979,28 @@ if ( canEdit( 'System' ) ) {
} elseif ( $action == 'state' ) {
if ( !empty($_REQUEST['runState']) ) {
//if ( $cookies ) session_write_close();
packageControl( $_REQUEST['runState'] );
packageControl($_REQUEST['runState']);
$refreshParent = true;
}
} elseif ( $action == 'save' ) {
if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) {
$sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id';
$definitions = array();
foreach( dbFetchAll( $sql ) as $monitor )
{
foreach( dbFetchAll($sql) as $monitor ) {
$definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled'];
}
$definition = join( ',', $definitions );
$definition = join(',', $definitions);
if ( $_REQUEST['newState'] )
$_REQUEST['runState'] = $_REQUEST['newState'];
dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) );
dbQuery('REPLACE INTO States SET Name=?, Definition=?', array($_REQUEST['runState'],$definition));
}
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['runState']) )
dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) );
dbQuery('DELETE FROM States WHERE Name=?', array($_REQUEST['runState']));
if ( isset($_REQUEST['markUids']) ) {
foreach( $_REQUEST['markUids'] as $markUid )
dbQuery( 'delete from Users where Id = ?', array($markUid) );
dbQuery('DELETE FROM Users WHERE Id = ?', array($markUid));
if ( $markUid == $user['Id'] )
userLogout();
}
@ -962,17 +1009,17 @@ if ( canEdit( 'System' ) ) {
if ( ZM_USER_SELF_EDIT && $action == 'user' ) {
$uid = $user['Id'];
$dbUser = dbFetchOne( 'SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid) );
$dbUser = dbFetchOne('SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid));
$types = array();
$changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( !empty($_REQUEST['newUser']['Password']) )
$changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
else
unset( $changes['Password'] );
if ( count( $changes ) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) );
unset($changes['Password']);
if ( count($changes) ) {
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
$refreshParent = true;
}
$view = 'none';
@ -981,8 +1028,8 @@ if ( canEdit( 'System' ) ) {
if ( $action == 'reset' ) {
session_start();
$_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB );
setcookie( 'zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 );
$_SESSION['zmEventResetTime'] = strftime(STRF_FMT_DATETIME_DB);
setcookie('zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10);
session_write_close();
}

View File

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

View File

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

View File

@ -227,7 +227,7 @@ function getImageStreamHTML( $id, $src, $width, $height, $title='' ) {
if ( canStreamIframe() ) {
return '<iframe id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" '.($width? ' width="'. validInt($width).'"' : '').($height?' height="'.validInt($height).'"' : '' ).'/>';
} else {
return '<img id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" style="'.($width? ' width:'.$width.';' : '' ).($height ? ' height:'. $height.';' : '' ).'"/>';
return '<img id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" style="'.($width? ' width:'.$width.'px;' : '' ).($height ? ' height:'. $height.'px;' : '' ).'"/>';
}
}
@ -432,20 +432,38 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
}
}
return "<select name=\"$name\" id=\"$name\"$behaviourText>".htmlOptions( $contents, $values ).'</select>';
return "<select name=\"$name\" id=\"$name\"$behaviourText>".htmlOptions($contents, $values).'</select>';
}
function htmlOptions( $contents, $values ) {
$html = '';
foreach ( $contents as $value=>$text ) {
if ( is_array( $text ) )
$text = $text['Name'];
else if ( is_object( $text ) )
$text = $text->Name();
$selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values);
$html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>";
function htmlOptions($contents, $values) {
$options_html = '';
foreach ( $contents as $value=>$option ) {
$disabled = 0;
$text = '';
if ( is_array($option) ) {
if ( isset($option['Name']) )
$text = $option['Name'];
else if ( isset($option['text']) )
$text = $option['text'];
if ( isset($option['disabled']) ) {
$disabled = $option['disabled'];
Error("Setting to disabled");
}
} else if ( is_object($option) ) {
$text = $option->Name();
} else {
$text = $option;
}
$selected = is_array($values) ? in_array($value, $values) : !strcmp($value, $values);
$options_html .= "<option value=\"$value\"".
($selected?' selected="selected"':'').
($disabled?' disabled="disabled"':'').
">$text</option>";
}
return $html;
return $options_html;
}
function truncText( $text, $length, $deslash=1 ) {
@ -2137,15 +2155,15 @@ function getStreamHTML( $monitor, $options = array() ) {
if ( isset($options['scale']) and $options['scale'] and ( $options['scale'] != 100 ) ) {
//Warning("Scale to " . $options['scale'] );
$options['width'] = reScale( $monitor->Width(), $options['scale'] ) . 'px';
$options['height'] = reScale( $monitor->Height(), $options['scale'] ) . 'px';
$options['width'] = reScale( $monitor->Width(), $options['scale'] );
$options['height'] = reScale( $monitor->Height(), $options['scale'] );
} else {
# scale is empty or 100
# There may be a fixed width applied though, in which case we need to leave the height empty
if ( ! ( isset($options['width']) and $options['width'] ) ) {
$options['width'] = $monitor->Width() . 'px';
$options['width'] = $monitor->Width();
if ( ! ( isset($options['height']) and $options['height'] ) ) {
$options['height'] = $monitor->Height() . 'px';
$options['height'] = $monitor->Height();
}
} else if ( ! isset($options['height']) ) {
$options['height'] = '';

View File

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

View File

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

View File

@ -18,17 +18,17 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
error_reporting( E_ALL );
error_reporting(E_ALL);
$debug = false;
if ( $debug ) {
// Use these for debugging, though not both at once!
phpinfo( INFO_VARIABLES );
phpinfo(INFO_VARIABLES);
//error_reporting( E_ALL );
}
// Use new style autoglobals where possible
if ( version_compare( phpversion(), '4.1.0', '<') ) {
if ( version_compare(phpversion(), '4.1.0', '<') ) {
$_SESSION = &$HTTP_SESSION_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
}
@ -36,20 +36,20 @@ if ( version_compare( phpversion(), '4.1.0', '<') ) {
// Useful debugging lines for mobile devices
if ( false ) {
ob_start();
phpinfo( INFO_VARIABLES );
$fp = fopen( '/tmp/env.html', 'w' );
fwrite( $fp, ob_get_contents() );
fclose( $fp );
phpinfo(INFO_VARIABLES);
$fp = fopen('/tmp/env.html', 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_end_clean();
}
require_once( 'includes/config.php' );
require_once( 'includes/logger.php' );
require_once( 'includes/Server.php' );
require_once( 'includes/Storage.php' );
require_once( 'includes/Event.php' );
require_once( 'includes/Group.php' );
require_once( 'includes/Monitor.php' );
require_once('includes/config.php');
require_once('includes/logger.php');
require_once('includes/Server.php');
require_once('includes/Storage.php');
require_once('includes/Event.php');
require_once('includes/Group.php');
require_once('includes/Monitor.php');
if (
@ -61,13 +61,13 @@ if (
} else {
$protocol = 'http';
}
define( 'ZM_BASE_PROTOCOL', $protocol );
define('ZM_BASE_PROTOCOL', $protocol);
// Absolute URL's are unnecessary and break compatibility with reverse proxies
// define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] );
// Use relative URL's instead
define( 'ZM_BASE_URL', '' );
define('ZM_BASE_URL', '');
// Check time zone is set
if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) {
@ -85,10 +85,10 @@ if ( isset($_GET['skin']) ) {
$skin = 'classic';
}
$skins = array_map( 'basename', glob('skins/*', GLOB_ONLYDIR ) );
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
if ( ! in_array( $skin, $skins ) ) {
Error( "Invalid skin '$skin' setting to " . $skins[0] );
if ( ! in_array($skin, $skins) ) {
Error("Invalid skin '$skin' setting to " . $skins[0]);
$skin = $skins[0];
}
@ -96,25 +96,25 @@ if ( isset($_GET['css']) ) {
$css = $_GET['css'];
} elseif ( isset($_COOKIE['zmCSS']) ) {
$css = $_COOKIE['zmCSS'];
} elseif (defined('ZM_CSS_DEFAULT')) {
} elseif ( defined('ZM_CSS_DEFAULT') ) {
$css = ZM_CSS_DEFAULT;
} else {
$css = 'classic';
}
$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) );
if ( ! in_array( $css, $css_skins ) ) {
Error( "Invalid skin css '$css' setting to " . $css_skins[0] );
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR));
if ( !in_array($css, $css_skins) ) {
Error("Invalid skin css '$css' setting to " . $css_skins[0]);
$css = $css_skins[0];
}
define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) );
define( 'ZM_SKIN_PATH', "skins/$skin" );
define( 'ZM_SKIN_NAME', $skin );
define('ZM_BASE_PATH', dirname($_SERVER['REQUEST_URI']));
define('ZM_SKIN_PATH', "skins/$skin");
define('ZM_SKIN_NAME', $skin);
$skinBase = array(); // To allow for inheritance of skins
if ( !file_exists( ZM_SKIN_PATH ) )
Fatal( "Invalid skin '$skin'" );
if ( !file_exists(ZM_SKIN_PATH) )
Fatal("Invalid skin '$skin'");
$skinBase[] = $skin;
$currentCookieParams = session_get_cookie_params();
@ -127,25 +127,25 @@ session_set_cookie_params(
true
);
ini_set( 'session.name', 'ZMSESSID' );
ini_set('session.name', 'ZMSESSID');
session_start();
if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) {
$_SESSION['skin'] = $skin;
setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 );
setcookie('zmSkin', $skin, time()+3600*24*30*12*10);
}
if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) {
$_SESSION['css'] = $css;
setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 );
setcookie('zmCSS', $css, time()+3600*24*30*12*10);
}
if ( ZM_OPT_USE_AUTH ) {
if ( isset( $_SESSION['user'] ) ) {
if ( isset($_SESSION['user']) ) {
$user = $_SESSION['user'];
} else {
unset( $user );
unset($user);
}
} else {
$user = $defaultUser;
@ -154,9 +154,9 @@ if ( ZM_OPT_USE_AUTH ) {
# Any file/page that sets session variables must re-open it.
session_write_close();
require_once( 'includes/lang.php' );
require_once( 'includes/functions.php' );
require_once( 'includes/auth.php' );
require_once('includes/lang.php');
require_once('includes/functions.php');
require_once('includes/auth.php');
# Running is global but only do the daemonCheck if it is actually needed
$running = null;
@ -179,20 +179,20 @@ $request = null;
if ( isset($_REQUEST['request']) )
$request = detaintPath($_REQUEST['request']);
foreach ( getSkinIncludes( 'skin.php' ) as $includeFile )
foreach ( getSkinIncludes('skin.php') as $includeFile )
require_once $includeFile;
if ( ZM_OPT_USE_AUTH ) {
if ( ZM_AUTH_HASH_LOGINS ) {
if ( empty($user) && ! empty($_REQUEST['auth']) ) {
if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) {
userLogin( $authUser['Username'], $authUser['Password'], true );
if ( $authUser = getAuthUser($_REQUEST['auth']) ) {
userLogin($authUser['Username'], $authUser['Password'], true);
}
}
}
if ( ! empty($user) ) {
if ( !empty($user) ) {
// generate it once here, while session is open. Value will be cached in session and return when called later on
generateAuthHash( ZM_AUTH_HASH_IPS );
generateAuthHash(ZM_AUTH_HASH_IPS);
}
}
@ -205,40 +205,52 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL;
isset($action) || $action = NULL;
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) {
Logger::Debug("View: $view Request: $request Action: $action");
if (
ZM_ENABLE_CSRF_MAGIC &&
( $action != 'login' ) &&
( $view != 'view_video' ) &&
( $view != 'image' ) &&
( $request != 'control' ) &&
( $view != 'frames' ) &&
( $view != 'archive' )
) {
require_once( 'includes/csrf/csrf-magic.php' );
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();
}
# Need to include actions because it does auth
require_once( 'includes/actions.php' );
require_once('includes/actions.php');
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
if ( ZM_OPT_USE_AUTH and ! isset($user) ) {
Logger::Debug("Redirecting to login" );
if ( ZM_OPT_USE_AUTH and !isset($user) ) {
Logger::Debug('Redirecting to login');
$view = 'login';
$request = null;
} else if ( ZM_SHOW_PRIVACY && ($action != 'privacy') && ($view !='options') && (!$request) && canEdit('System') ) {
Logger::Debug('Redirecting to privacy');
$view = 'privacy';
$request = null;
}
if ( $redirect ) {
header('Location: '.$redirect);
return;
}
if ( $request ) {
foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) {
if ( !file_exists( $includeFile ) )
Fatal( "Request '$request' does not exist" );
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
if ( !file_exists($includeFile) )
Fatal("Request '$request' does not exist");
require_once $includeFile;
}
return;
} else {
if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) {
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
foreach ( $includeFiles as $includeFile ) {
if ( !file_exists( $includeFile ) )
Fatal( "View '$view' does not exist" );
if ( !file_exists($includeFile) )
Fatal("View '$view' does not exist");
require_once $includeFile;
}
// If the view overrides $view to 'error', and the user is not logged in, then the
@ -246,14 +258,14 @@ if ( $request ) {
// The login view should handle redirecting to the correct location afterward.
if ( $view == 'error' && !isset($user) ) {
$view = 'login';
foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile )
foreach ( getSkinIncludes('views/login.php', true, true) as $includeFile )
require_once $includeFile;
}
}
// If the view is missing or the view still returned error with the user logged in,
// then it is not recoverable.
if ( !$includeFiles || $view == 'error' ) {
foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile )
foreach ( getSkinIncludes('views/error.php', true, true) as $includeFile )
require_once $includeFile;
}
}

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

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@ var popupSizes = {
'filter': { 'width': 900, 'height': 700 },
'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 200 },
'frames': { 'width': 600, 'height': 600 },
'function': { 'width': 350, 'height': 160 },
'function': { 'width': 350, 'height': 260 },
'group': { 'width': 760, 'height': 600 },
'groups': { 'width': 540, 'height': 420 },
'image': { 'addWidth': 48, 'addHeight': 80 },
@ -54,7 +54,7 @@ var popupSizes = {
'monitorselect':{ 'width': 160, 'height': 200 },
'montage': { 'width': -1, 'height': -1 },
'onvifprobe': { 'width': 700, 'height': 550 },
'optionhelp': { 'width': 400, 'height': 320 },
'optionhelp': { 'width': 400, 'height': 400 },
'options': { 'width': 1000, 'height': 660 },
'preset': { 'width': 300, 'height': 220 },
'server': { 'width': 600, 'height': 405 },

View File

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

View File

@ -18,7 +18,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView( 'Events' ) ) {
if ( !canView('Events') ) {
$view = 'error';
return;
}
@ -28,7 +28,7 @@ parseSort();
$filterNames = array( ''=>translate('ChooseFilter') );
$filter = NULL;
foreach ( dbFetchAll( 'SELECT * FROM Filters ORDER BY Name' ) as $row ) {
foreach ( dbFetchAll('SELECT * FROM Filters ORDER BY Name') as $row ) {
$filterNames[$row['Id']] = $row['Id'] . ' ' . $row['Name'];
if ( $row['Background'] )
$filterNames[$row['Id']] .= '*';
@ -410,25 +410,23 @@ if ( ZM_OPT_MESSAGE ) {
</div>
<hr/>
<div id="contentButtons">
<button type="button" onclick="submitToEvents(this);"> <?php echo translate('ListMatches') ?></button>
<button type="button" onclick="submitToExport(this);"> <?php echo translate('ExportMatches') ?></button>
<button type="button" name="executeButton" id="executeButton" onclick="executeFilter( this );"><?php echo translate('Execute') ?></button>
<button type="submit" onclick="submitToEvents(this);"><?php echo translate('ListMatches') ?></button>
<button type="submit" onclick="submitToExport(this);"><?php echo translate('ExportMatches') ?></button>
<button type="submit" name="executeButton" id="executeButton" onclick="executeFilter( this );"><?php echo translate('Execute') ?></button>
<?php
if ( canEdit( 'Events' ) ) {
if ( canEdit('Events') ) {
?>
<button type="button" name="Save" value="Save" onclick="saveFilter(this);"><?php echo translate('Save') ?></button>
<button type="button" name="SaveAs" value="SaveAs" onclick="saveFilter(this);"><?php echo translate('SaveAs') ?></button>
<button type="submit" name="Save" value="Save" onclick="saveFilter(this);"><?php echo translate('Save') ?></button>
<button type="submit" name="SaveAs" value="SaveAs" onclick="saveFilter(this);"><?php echo translate('SaveAs') ?></button>
<?php
if ( $filter->Id() ) {
?>
<button type="button" value="Delete " onclick="deleteFilter( this, '<?php echo $filter->Name() ?>' );">
<?php echo translate('Delete') ?>
</button>
<button type="button" value="Delete" onclick="deleteFilter(this, '<?php echo $filter->Name() ?>');"><?php echo translate('Delete') ?></button>
<?php
}
}
?>
<button value="<?php echo translate('Reset') ?>" onclick="resetFilter( this );"><?php echo translate('Reset') ?></button>
<button type="button" value="Reset" onclick="resetFilter( this );"><?php echo translate('Reset') ?></button>
</div>
</form>
</div><!--content-->

View File

@ -180,7 +180,7 @@ function changeScale() {
function changeReplayMode() {
var replayMode = $('replayMode').get('value');
Cookie.write( 'replayMode', replayMode, { duration: 10*365 });
Cookie.write('replayMode', replayMode, { duration: 10*365 });
refreshWindow();
}
@ -195,6 +195,8 @@ var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
console.log('Got an error from getCmdResponse');
console.log(respObj);
console.log(respText);
zmsBroke = true;
return;
}
@ -202,18 +204,24 @@ function getCmdResponse( respObj, respText ) {
zmsBroke = false;
if ( streamCmdTimer )
streamCmdTimer = clearTimeout( streamCmdTimer );
streamCmdTimer = clearTimeout(streamCmdTimer);
streamStatus = respObj.status;
if (streamStatus.progress >= Math.round(parseFloat(eventData.Length))) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
if ( streamStatus.progress >= Math.round(parseFloat(eventData.Length)) )
streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
var eventId = streamStatus.event;
if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax
eventQuery( eventId );
initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render
lastEventId = eventId;
if ( lastEventId ) {
if ( eventId != lastEventId ) {
//Doesn't run on first load, prevents a double hit on event and nearEvents ajax
eventQuery(eventId);
initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render
lastEventId = eventId;
}
} else {
lastEventId = eventId; //Only fires on first load.
}
if (lastEventId == 0) lastEventId = eventId; //Only fires on first load.
if ( streamStatus.paused == true ) {
streamPause( );
} else {
@ -239,18 +247,24 @@ function getCmdResponse( respObj, respText ) {
streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display
}
var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } );
var streamReq = new Request.JSON( {
url: thisUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onSuccess: getCmdResponse
} );
function pauseClicked() {
if (vid) {
if ( vid ) {
vid.pause();
} else {
streamReq.send( streamParms+"&command="+CMD_PAUSE );
streamReq.send(streamParms+"&command="+CMD_PAUSE);
streamPause();
}
}
function vjsPause () {
function vjsPause() {
stopFastRev();
streamPause();
}
@ -267,8 +281,8 @@ function streamPause( ) {
}
function playClicked( ) {
if (vid) {
if (vid.paused()) {
if ( vid ) {
if ( vid.paused() ) {
vid.play();
} else {
vjsPlay(); //handles fast forward and rewind
@ -279,7 +293,7 @@ function playClicked( ) {
}
}
function vjsPlay () { //catches if we change mode programatically
function vjsPlay() { //catches if we change mode programatically
stopFastRev();
$j('#rateValue').html(vid.playbackRate());
streamPlay();
@ -302,13 +316,14 @@ function streamFastFwd( action ) {
setButtonState( $('slowFwdBtn'), 'unavail' );
setButtonState( $('slowRevBtn'), 'unavail' );
setButtonState( $('fastRevBtn'), 'inactive' );
if (vid) {
if (revSpeed != .5) stopFastRev();
if ( vid ) {
if ( revSpeed != .5 ) stopFastRev();
vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100);
if (rates.indexOf(vid.playbackRate()*100)-1 == -1) setButtonState($('fastFwdBtn'), 'unavail');
if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 )
setButtonState($('fastFwdBtn'), 'unavail');
$j('#rateValue').html(vid.playbackRate());
} else {
streamReq.send( streamParms+"&command="+CMD_FASTFWD );
streamReq.send(streamParms+"&command="+CMD_FASTFWD);
}
}
@ -317,22 +332,22 @@ var intervalRewind;
var revSpeed = .5;
function streamSlowFwd( action ) {
if (vid) {
if ( vid ) {
vid.currentTime(vid.currentTime() + spf);
} else {
streamReq.send( streamParms+"&command="+CMD_SLOWFWD );
streamReq.send(streamParms+"&command="+CMD_SLOWFWD);
}
}
function streamSlowRev( action ) {
if (vid) {
if ( vid ) {
vid.currentTime(vid.currentTime() - spf);
} else {
streamReq.send( streamParms+"&command="+CMD_SLOWREV );
streamReq.send(streamParms+"&command="+CMD_SLOWREV);
}
}
function stopFastRev () {
function stopFastRev() {
clearInterval(intervalRewind);
vid.playbackRate(1);
revSpeed = .5;
@ -345,9 +360,9 @@ function streamFastRev( action ) {
setButtonState( $('slowFwdBtn'), 'unavail' );
setButtonState( $('slowRevBtn'), 'unavail' );
setButtonState( $('fastRevBtn'), 'active' );
if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back.
if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back.
revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100;
if (rates.indexOf(revSpeed*100) == 0) {
if ( rates.indexOf(revSpeed*100) == 0 ) {
setButtonState( $('fastRevBtn'), 'unavail' );
}
clearInterval(intervalRewind);
@ -367,9 +382,12 @@ function streamFastRev( action ) {
}
function streamPrev(action) {
if (action) {
if ( action ) {
$j(".vjsMessage").remove();
if (vid && PrevEventDefVideoPath.indexOf("view_video") > 0) {
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
return;
if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) {
CurEventDefVideoPath = PrevEventDefVideoPath;
eventQuery(prevEventId);
} else if (zmsBroke || (vid && PrevEventDefVideoPath.indexOf("view_video") < 0) || $j("#vjsMessage").length || PrevEventDefVideoPath.indexOf("view_video") > 0) {//zms broke, leaving videojs, last event, moving to videojs
@ -382,20 +400,25 @@ function streamPrev(action) {
}
function streamNext(action) {
if (action) {
if ( action ) {
$j(".vjsMessage").remove();//This shouldn't happen
if (nextEventId == 0) { //handles deleting last event.
if ( nextEventId == 0 ) { //handles deleting last event.
pauseClicked();
let hideContainer = $j('#eventVideo');
let hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height());
hideContainer.prepend('<p class="vjsMessage" style="height: ' + hideStream + 'px; line-height: ' + hideStream + 'px;">No more events</p>');
if (vid == null) zmsBroke = true;
if ( vid == null ) zmsBroke = true;
return;
}
if (vid && NextEventDefVideoPath.indexOf("view_video") > 0) { //on and staying with videojs
// We used to try to dynamically update all the bits in the page, which is really complex
// How about we just reload the page?
//
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
return;
if ( vid && ( NextEventDefVideoPath.indexOf("view_video") > 0 ) ) { //on and staying with videojs
CurEventDefVideoPath = NextEventDefVideoPath;
eventQuery(nextEventId);
} else if (zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs
} else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
} else {
streamReq.send(streamParms+"&command="+CMD_NEXT);

View File

@ -248,15 +248,13 @@ function changeSize() {
var monitor = monitors[x];
// Scale the frame
monitor_frame = $j('#monitorFrame'+monitor.id);
if ( ! monitor_frame ) {
console.log("Error finding frame for " + monitor.id );
continue;
}
if ( width )
monitor_frame.css('width',width+'px');
if ( height )
monitor_frame.css('height',height+'px');
monitor_frame = $j('#monitorFrame'+monitor.id);
if ( ! monitor_frame ) {
console.log("Error finding frame for " + monitor.id );
continue;
}
monitor_frame.css('width',width?width+'px':'');
monitor_frame.css('height',height?height+'px':'');
/*Stream could be an applet so can't use moo tools*/
var streamImg = $( 'liveStream'+monitor.id );
if ( streamImg ) {
@ -347,7 +345,7 @@ function edit_layout(button) {
for ( var i = 0; i < monitors.length; i++ ) {
var monitor = monitors[i];
monitor_feed = $j('#imageFeed'+monitor.id)[0];
monitor_feed.onclick='';
monitor_feed.click('');
};
$j('#monitors .monitorFrame').draggable({
@ -359,7 +357,7 @@ function edit_layout(button) {
} // end function edit_layout
function save_layout(button) {
var form=button.form;
var form = button.form;
// In fixed positioning, order doesn't matter. In floating positioning, it does.
var Positions = {};
for ( var i = 0; i < monitors.length; i++ ) {

View File

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

View File

@ -114,6 +114,7 @@ if ( monitorType != 'WebSite' ) {
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onError: getStreamCmdError,
onSuccess: getStreamCmdResponse,
onFailure: getStreamCmdFailure
} );
@ -122,22 +123,28 @@ if ( monitorType != 'WebSite' ) {
var streamStatus;
function getStreamCmdFailure(xhr) {
console.log(xhr);
function getStreamCmdError(text,error) {
console.log(error);
// Error are normally due to failed auth. reload the page.
window.location.reload();
}
function getStreamCmdResponse( respObj, respText ) {
function getStreamCmdFailure(xhr) {
console.log(xhr);
}
function getStreamCmdResponse(respObj, respText) {
watchdogOk("stream");
console.log('stream');
if ( streamCmdTimer )
streamCmdTimer = clearTimeout( streamCmdTimer );
streamCmdTimer = clearTimeout(streamCmdTimer);
if ( respObj.result == 'Ok' ) {
// The get status command can get backed up, in which case we won't be able to get the semaphore and will exit.
if ( respObj.status ) {
streamStatus = respObj.status;
$('fpsValue').set( 'text', streamStatus.fps );
$('fpsValue').set('text', streamStatus.fps);
setAlarmState( streamStatus.state );
setAlarmState(streamStatus.state);
$('levelValue').set( 'text', streamStatus.level );
$('levelValue').set('text', streamStatus.level);
if ( streamStatus.level > 95 )
$('levelValue').className = "alarm";
else if ( streamStatus.level > 80 )
@ -145,34 +152,34 @@ function getStreamCmdResponse( respObj, respText ) {
else
$('levelValue').className = "ok";
var delayString = secsToTime( streamStatus.delay );
var delayString = secsToTime(streamStatus.delay);
if ( streamStatus.paused == true ) {
$('modeValue').set( 'text', "Paused" );
$('rate').addClass( 'hidden' );
$('delayValue').set( 'text', delayString );
$('delay').removeClass( 'hidden' );
$('level').removeClass( 'hidden' );
streamCmdPause( false );
$('modeValue').set('text', 'Paused');
$('rate').addClass('hidden');
$('delayValue').set('text', delayString);
$('delay').removeClass('hidden');
$('level').removeClass('hidden');
streamCmdPause(false);
} else if ( streamStatus.delayed == true ) {
$('modeValue').set( 'text', "Replay" );
$('rateValue').set( 'text', streamStatus.rate );
$('rate').removeClass( 'hidden' );
$('delayValue').set( 'text', delayString );
$('delay').removeClass( 'hidden' );
$('level').removeClass( 'hidden' );
$('modeValue').set('text', 'Replay');
$('rateValue').set('text', streamStatus.rate);
$('rate').removeClass('hidden');
$('delayValue').set('text', delayString);
$('delay').removeClass('hidden');
$('level').removeClass('hidden');
if ( streamStatus.rate == 1 ) {
streamCmdPlay( false );
streamCmdPlay(false);
} else if ( streamStatus.rate > 0 ) {
if ( streamStatus.rate < 1 )
streamCmdSlowFwd( false );
streamCmdSlowFwd(false);
else
streamCmdFastFwd( false );
streamCmdFastFwd(false);
} else {
if ( streamStatus.rate > -1 )
streamCmdSlowRev( false );
streamCmdSlowRev(false);
else
streamCmdFastRev( false );
streamCmdFastRev(false);
} // rate
} else {
$('modeValue').set( 'text', "Live" );
@ -209,16 +216,19 @@ function getStreamCmdResponse( respObj, respText ) {
} // end if canEditMonitors
if ( streamStatus.auth ) {
console.log("Haev a new auth hash" + streamStatus.auth);
console.log("Have a new auth hash" + streamStatus.auth);
// Try to reload the image stream.
var streamImg = $('liveStream');
if ( streamImg )
streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth );
} // end if haev a new auth hash
streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
} // end if have a new auth hash
} // end if respObj.status
} else {
checkStreamForErrors("getStreamCmdResponse",respObj);//log them
checkStreamForErrors("getStreamCmdResponse", respObj);//log them
// Try to reload the image stream.
// If it's an auth error, we should reload the whole page.
window.location.reload();
if ( 0 ) {
var streamImg = $('liveStream'+monitorId);
if ( streamImg ) {
streamImg.src = streamImg.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
@ -226,6 +236,7 @@ function getStreamCmdResponse( respObj, respText ) {
} else {
console.log("Unable to find streamImg liveStream");
}
}
}
var streamCmdTimeout = statusRefreshTimeout;
@ -360,14 +371,14 @@ if ( monitorType != 'WebSite' ) {
var statusCmdTimer = null;
}
function getStatusCmdResponse( respObj, respText ) {
function getStatusCmdResponse(respObj, respText) {
watchdogOk("status");
if ( statusCmdTimer )
statusCmdTimer = clearTimeout( statusCmdTimer );
statusCmdTimer = clearTimeout(statusCmdTimer);
if ( respObj.result == 'Ok' ) {
$('fpsValue').set( 'text', respObj.monitor.FrameRate );
setAlarmState( respObj.monitor.Status );
$('fpsValue').set('text', respObj.monitor.FrameRate);
setAlarmState(respObj.monitor.Status);
} else
checkStreamForErrors("getStatusCmdResponse", respObj);
@ -401,19 +412,19 @@ function getAlarmCmdResponse( respObj, respText ) {
}
function cmdDisableAlarms() {
alarmCmdReq.send( alarmCmdParms+"&command=disableAlarms" );
alarmCmdReq.send(alarmCmdParms+"&command=disableAlarms");
}
function cmdEnableAlarms() {
alarmCmdReq.send( alarmCmdParms+"&command=enableAlarms" );
alarmCmdReq.send(alarmCmdParms+"&command=enableAlarms");
}
function cmdForceAlarm() {
alarmCmdReq.send( alarmCmdParms+"&command=forceAlarm" );
alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm");
}
function cmdCancelForcedAlarm() {
alarmCmdReq.send( alarmCmdParms+"&command=cancelForcedAlarm" );
alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm");
return false;
}
@ -429,7 +440,13 @@ function getActResponse( respObj, respText ) {
function deleteEvent( event, eventId ) {
var actParms = "view=request&request=event&action=delete&id="+eventId;
var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: 3000, data: actParms, onSuccess: getActResponse } );
var actReq = new Request.JSON( {
url: thisUrl,
method: 'post',
timeout: 3000,
data: actParms,
onSuccess: getActResponse
} );
actReq.send();
event.stop();
}
@ -444,7 +461,7 @@ if ( monitorType != 'WebSite' ) {
}
function highlightRow( row ) {
$(row).toggleClass( 'highlight' );
$(row).toggleClass('highlight');
}
function getEventCmdResponse( respObj, respText ) {
@ -649,7 +666,7 @@ var watchdogFunctions = {
//Make sure the various refreshes are still taking effect
function watchdogCheck( type ) {
if ( watchdogInactive[type] ) {
console.log( "Detected streamWatch of type: " + type + " stopped, restarting" );
console.log("Detected streamWatch of type: " + type + " stopped, restarting");
watchdogFunctions[type]();
watchdogInactive[type] = false;
} else {
@ -662,7 +679,7 @@ function watchdogOk( type ) {
}
function reloadWebSite() {
document.getElementById('imageFeed').innerHTML = document.getElementById('imageFeed').innerHTML;
document.getElementById('imageFeed').innerHTML = document.getElementById('imageFeed').innerHTML;
}
function initPage() {
@ -683,16 +700,16 @@ function initPage() {
if ( !streamImg )
streamImg = $('imageFeed').getElement('object');
if ( streamMode == "single" ) {
streamImg.addEvent( 'click', fetchImage.pass( streamImg ) );
fetchImage.pass( streamImg ).periodical( imageRefreshTimeout );
streamImg.addEvent('click', fetchImage.pass(streamImg));
fetchImage.pass(streamImg).periodical(imageRefreshTimeout);
} else
streamImg.addEvent( 'click', function( event ) { handleClick( event ); } );
streamImg.addEvent('click', function(event) { handleClick(event); });
}
if ( refreshApplet && appletRefreshTime )
appletRefresh.delay( appletRefreshTime*1000 );
if (scale == "auto") changeScale();
if (window.history.length == 1) {
appletRefresh.delay(appletRefreshTime*1000);
if ( scale == "auto" ) changeScale();
if ( window.history.length == 1 ) {
$j('#closeControl').html('');
}
} else if ( monitorRefresh > 0 ) {
@ -701,4 +718,4 @@ function initPage() {
}
// Kick everything off
window.addEvent( 'domready', initPage );
window.addEvent('domready', initPage);

View File

@ -924,7 +924,9 @@ if ( $monitor->Type() == 'Local' ) {
1 => 'X264 Encode',
);
if ($monitor->Type() == 'Ffmpeg' )
$videowriteropts[2]='H264 Camera Passthrough';
$videowriteropts[2] = 'H264 Camera Passthrough';
else
$videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1);
echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() );
?>
</td></tr>

View File

@ -202,7 +202,7 @@ foreach ( $monitors as $monitor ) {
onclick="createPopup('?view=watch&amp;mid=<?php echo $monitor->Id() ?>', 'zmWatch<?php echo $monitor->Id() ?>', 'watch', <?php echo reScale( $monitor->Width(), $monitor->PopupScale() ); ?>, <?php echo reScale( $monitor->Height(), $monitor->PopupScale() ); ?> );">
<?php
$monitor_options = $options;
if ( $Positions ) {
if (0 and $Positions ) {
$monitor_options['width'] = '100%';
$monitor_options['height'] = '100%';
if ( 0 ) {

View File

@ -260,8 +260,8 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<thead class="thead-highlight">
<tr>
<th class="colId"><?php echo translate('Id') ?></th>
<th class="colName"><?php echo translate('name') ?></th>
<th class="colPath"><?php echo translate('path') ?></th>
<th class="colName"><?php echo translate('Name') ?></th>
<th class="colPath"><?php echo translate('Path') ?></th>
<th class="colType"><?php echo translate('Type') ?></th>
<th class="colScheme"><?php echo translate('StorageScheme') ?></th>
<th class="colServer"><?php echo translate('Server') ?></th>

View File

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

View File

@ -40,7 +40,7 @@ require_once('includes/Event.php');
require_once('includes/Frame.php');
// Compatibility for PHP 5.4
if (!function_exists('imagescale')) {
if ( !function_exists('imagescale') ) {
function imagescale($image, $new_width, $new_height = -1, $mode = 0) {
$mode; // Not supported
@ -70,17 +70,18 @@ if ( empty($_REQUEST['path']) ) {
if ( !empty($_REQUEST['eid']) ) {
$Event = Event::find_one(array('Id'=>$_REQUEST['eid']));
if ( ! $Event ) {
if ( !$Event ) {
header('HTTP/1.0 404 Not Found');
Fatal('Event ' . $_REQUEST['eid'].' Not found');
Fatal('Event '.$_REQUEST['eid'].' Not found');
return;
}
if ( $_REQUEST['fid'] == 'snapshot' ) {
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Score'=>$Event->MaxScore()));
if ( ! $Frame )
if ( !$Frame )
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid']));
if ( ! $Frame ) {
if ( !$Frame ) {
Warning("No frame found for event " + $_REQUEST['eid']);
$Frame = new Frame();
$Frame->Delta(1);
$Frame->FrameId('snapshot');
@ -123,14 +124,14 @@ Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousB
} else {
# If we are only specifying fid, then the fid must be the primary key into the frames table. But when the event is specified, then it is the frame #
$Frame = Frame::find_one(array('Id'=>$_REQUEST['fid']));
if ( ! $Frame ) {
if ( !$Frame ) {
header('HTTP/1.0 404 Not Found');
Fatal('Frame ' . $_REQUEST['fid'] . ' Not Found');
return;
}
$Event = Event::find_one(array('Id'=>$Frame->EventId()));
if ( ! $Event ) {
if ( !$Event ) {
header('HTTP/1.0 404 Not Found');
Fatal('Event ' . $Frame->EventId() . ' Not Found');
return;
@ -138,11 +139,11 @@ Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousB
$path = $Event->Path().'/'.sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d',$Frame->FrameId()).'-'.$show.'.jpg';
} # end if have eid
if ( ! file_exists($path) ) {
if ( !file_exists($path) ) {
Logger::Debug("$path does not exist");
# Generate the frame JPG
if ( $show == 'capture' and $Event->DefaultVideo() ) {
if ( ! file_exists($Event->Path().'/'.$Event->DefaultVideo()) ) {
if ( ($show == 'capture') and $Event->DefaultVideo() ) {
if ( !file_exists($Event->Path().'/'.$Event->DefaultVideo()) ) {
header('HTTP/1.0 404 Not Found');
Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() );
}
@ -190,52 +191,52 @@ Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousB
} else {
$errorText = 'Invalid image path';
}
if ( ! file_exists($path) ) {
if ( !file_exists($path) ) {
header('HTTP/1.0 404 Not Found');
Fatal("Image not found at $path");
}
}
$scale=0;
$scale = 0;
if ( !empty($_REQUEST['scale']) ) {
if ( is_numeric($_REQUEST['scale']) ) {
$x = $_REQUEST['scale'];
if ( $x >= 1 and $x <= 400 )
$scale=$x;
$scale = $x;
}
}
$width=0;
$width = 0;
if ( !empty($_REQUEST['width']) ) {
if ( is_numeric($_REQUEST['width']) ) {
$x = $_REQUEST['width'];
if ( $x >= 10 and $x <= 8000 )
$width=$x;
$width = $x;
}
}
$height=0;
$height = 0;
if ( !empty($_REQUEST['height']) ) {
if ( is_numeric($_REQUEST['height']) ) {
$x = $_REQUEST['height'];
if ( $x >= 10 and $x <= 8000 )
$height=$x;
$height = $x;
}
}
header('Content-type: image/jpeg');
# This is so that Save Image As give a useful filename
if ( $Event ) {
$filename = $Event->MonitorId().'_'.$Event->Id().'_'.$Frame->FrameId().'.jpg';
header('Content-Disposition: inline; filename="' . $filename . '"');
}
ob_clean();
flush();
if ( $errorText ) {
Error($errorText);
} else {
# Clears the output buffer. Not sure what is there, but have had troubles.
ob_end_clean();
header('Content-type: image/jpeg');
if ( ( $scale==0 || $scale==100 ) && $width==0 && $height==0 ) {
# This is so that Save Image As give a useful filename
if ( $Event ) {
$filename = $Event->MonitorId().'_'.$Event->Id().'_'.$Frame->FrameId().'.jpg';
header('Content-Disposition: inline; filename="' . $filename . '"');
}
ob_clean();
flush();
if ( !readfile($path) ) {
Error('No bytes read from '. $path);
}
@ -254,17 +255,23 @@ if ( $errorText ) {
} elseif ( $width != 0 && $height == 0 ) {
$height = ($width * $oldHeight) / $oldWidth;
}
if ( $width == $oldWidth && $height == $oldHeight) {
if ( $width == $oldWidth && $height == $oldHeight ) {
Warning('No change to width despite scaling.');
}
}
# Slight optimisation, thumbnails always specify width and height, so we can cache them.
$scaled_path = preg_replace('/\.jpg$/', "-${width}x${height}.jpg", $path );
if ( ! file_exists($scaled_path) or ! readfile($scaled_path) ) {
$scaled_path = preg_replace('/\.jpg$/', "-${width}x${height}.jpg", $path);
if ( $Event ) {
$filename = $Event->MonitorId().'_'.$Event->Id().'_'.$Frame->FrameId()."-${width}x${height}.jpg";
header('Content-Disposition: inline; filename="' . $filename . '"');
}
//ob_clean();
//flush();
if ( !( file_exists($scaled_path) and readfile($scaled_path) ) ) {
Logger::Debug("Cached scaled image does not exist at $scaled_path or is no good.. Creating it");
ob_start();
if ( ! $i )
if ( !$i )
$i = imagecreatefromjpeg($path);
$iScale = imagescale($i, $width, $height);
imagejpeg($iScale);
@ -274,6 +281,15 @@ if ( $errorText ) {
file_put_contents($scaled_path, $scaled_jpeg_data);
ob_end_clean();
echo $scaled_jpeg_data;
} else {
Logger::Debug("Sending $scaled_path");
$bytes = readfile($scaled_path);
if ( !$bytes ) {
Error('No bytes read from '. $scaled_path);
} else {
Logger::Debug("$bytes sent");
}
}
}
}
exit();

View File

@ -25,7 +25,7 @@
// Does not support scaling at this time.
//
if ( !canView( 'Events' ) ) {
if ( !canView('Events') ) {
$view = 'error';
return;
}
@ -37,12 +37,12 @@ $path = '';
$Event = null;
if ( ! empty($_REQUEST['eid'] ) ) {
$Event = new Event( $_REQUEST['eid'] );
if ( ! empty($_REQUEST['eid']) ) {
$Event = new Event($_REQUEST['eid']);
$path = $Event->Path().'/'.$Event->DefaultVideo();
Logger::Debug("Path: $path");
} else if ( ! empty($_REQUEST['event_id'] ) ) {
$Event = new Event( $_REQUEST['event_id'] );
} else if ( ! empty($_REQUEST['event_id']) ) {
$Event = new Event($_REQUEST['event_id']);
$path = $Event->Path().'/'.$Event->DefaultVideo();
Logger::Debug("Path: $path");
} else {
@ -50,7 +50,7 @@ if ( ! empty($_REQUEST['eid'] ) ) {
}
if ( $errorText ) {
Error( $errorText );
Error($errorText);
header('HTTP/1.0 404 Not Found');
die();
}
@ -67,12 +67,12 @@ $begin = 0;
$end = $size-1;
$length = $size;
if ( isset( $_SERVER['HTTP_RANGE'] ) ) {
Logger::Debug("Using Range " . $_SERVER['HTTP_RANGE'] );
if ( preg_match( '/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches) ) {
$begin = intval( $matches[1] );
if ( ! empty( $matches[2]) ) {
$end = intval( $matches[2] );
if ( isset($_SERVER['HTTP_RANGE']) ) {
Logger::Debug('Using Range ' . $_SERVER['HTTP_RANGE']);
if ( preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches) ) {
$begin = intval($matches[1]);
if ( ! empty($matches[2]) ) {
$end = intval($matches[2]);
}
$length = $end - $begin + 1;
Logger::Debug("Using Range $begin $end size: $size, length: $length");
@ -102,10 +102,10 @@ ob_clean();
flush();
$cur = $begin;
fseek( $fh, $begin, 0 );
fseek($fh, $begin, 0);
while( $length && ( ! feof( $fh ) ) && ( connection_status() == 0 ) ) {
$amount = min( 1024*16, $length );
while( $length && ( !feof($fh) ) && ( connection_status() == 0 ) ) {
$amount = min(1024*16, $length);
print fread( $fh, $amount );
$length -= $amount;
@ -114,5 +114,5 @@ while( $length && ( ! feof( $fh ) ) && ( connection_status() == 0 ) ) {
flush();
}
fclose( $fh );
fclose($fh);
exit();