Merge branch 'master' into zma_to_thread

This commit is contained in:
Isaac Connor 2020-10-20 16:20:29 -04:00
commit 2eda49333f
183 changed files with 3626 additions and 3416 deletions

83
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,83 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 3 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['cpp', 'javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Clean install dependencies and build
run: |
git submodule init
git submodule update --init --recursive
sudo apt-get update
sudo apt-get install libx264-dev libmp4v2-dev libavdevice-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev
sudo apt-get install libbz2-dev libgcrypt20-dev libcurl4-gnutls-dev libjpeg-turbo8-dev libturbojpeg0-dev
sudo apt-get install default-libmysqlclient-dev libpcre3-dev libpolkit-gobject-1-dev libv4l-dev libvlc-dev
sudo apt-get install libdate-manip-perl libdbd-mysql-perl libphp-serialization-perl libsys-mmap-perl
sudo apt-get install libwww-perl libdata-uuid-perl libssl-dev libcrypt-eksblowfish-perl libdata-entropy-perl
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl-
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -808,7 +808,7 @@ find_package(
Getopt::Long Time::HiRes Date::Manip LWP::UserAgent
ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE})
if(NOT PERLMODULES_FOUND)
message(FATAL_ERROR
message(WARNING
"Not all required perl modules were found on your system")
endif(NOT PERLMODULES_FOUND)
@ -844,7 +844,7 @@ if(WITH_SYSTEMD)
# Check for polkit
find_package(Polkit)
if(NOT POLKIT_FOUND)
message(FATAL_ERROR
message(WARNING
"Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.")
endif(NOT POLKIT_FOUND)
endif(WITH_SYSTEMD)

View File

@ -285,6 +285,7 @@ CREATE TABLE `Filters` (
`UserId` int(10) unsigned,
`Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoUnarchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
`AutoEmail` tinyint(3) unsigned NOT NULL default '0',
@ -537,6 +538,8 @@ CREATE TABLE `Monitors` (
`ArchivedEventDiskSpace` bigint default NULL,
`ZoneCount` TINYINT NOT NULL DEFAULT 0,
`Refresh` int(10) unsigned default NULL,
`Latitude` DECIMAL(10,8),
`Longitude` DECIMAL(10,8),
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@ -550,7 +553,7 @@ CREATE TABLE `Monitor_Status` (
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
`CaptureBandwidth` INT NOT NULL default 0,
PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY;
) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Table structure for table `States`
-- PP - Added IsActive to track custom run states
@ -1023,6 +1026,13 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('3 Wide', '{ "default":{
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"24.5%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
CREATE TABLE Sessions (
id char(32) not null,
access INT(10) UNSIGNED DEFAULT NULL,
data text,
PRIMARY KEY(id)
) ENGINE=InnoDB;
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @PKGDATADIR@/db/triggers.sql
--

15
db/zm_update-1.35.10.sql Normal file
View File

@ -0,0 +1,15 @@
--
-- Add AutoUnarchive action to Filters
--
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'AutoUnarchive'
) > 0,
"SELECT 'Column AutoUunarchive already exists in Filters'",
"ALTER TABLE Filters ADD `AutoUnarchive` tinyint(3) unsigned NOT NULL default '0' AFTER `AutoArchive`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

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

@ -0,0 +1,23 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'Latitude'
) > 0,
"SELECT 'Column Latitude already exists in Monitors'",
"ALTER TABLE `Monitors` ADD `Latitude` DECIMAL(10,8) AFTER `Refresh`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'Longitude'
) > 0,
"SELECT 'Column Longitude already exists in Monitors'",
"ALTER TABLE `Monitors` ADD `Longitude` DECIMAL(10,8) AFTER `Latitude`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.35.8.sql Normal file
View File

@ -0,0 +1,12 @@
/* The MEMORY TABLE TYPE IS BAD! Switch to regular InnoDB */
DROP TABLE IF EXISTS `Monitor_Status`;
CREATE TABLE `Monitor_Status` (
`MonitorId` int(10) unsigned NOT NULL,
`Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
`CaptureBandwidth` INT NOT NULL default 0,
PRIMARY KEY (`MonitorId`)
) ENGINE=InnoDB;

21
db/zm_update-1.35.9.sql Normal file
View File

@ -0,0 +1,21 @@
--
-- This adds Sessions Table
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Sessions'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Sessions table exists'",
"CREATE TABLE Sessions (
id char(32) not null,
access INT(10) UNSIGNED DEFAULT NULL,
data text,
PRIMARY KEY(id)
) ENGINE=InnoDB;"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

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

View File

@ -17,6 +17,7 @@ Build-Depends: debhelper, dh-systemd, sphinx-doc, python3-sphinx, dh-linktree, d
,libbz2-dev
,libgcrypt20-dev
,libcurl4-gnutls-dev
,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev
,libturbojpeg0-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev
@ -32,6 +33,7 @@ Build-Depends: debhelper, dh-systemd, sphinx-doc, python3-sphinx, dh-linktree, d
,libssl-dev
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncserver-dev
# Unbundled (dh_linktree):
,libjs-jquery
,libjs-mootools
@ -80,6 +82,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libpcre3
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncclient1|libvncclient0
Recommends: ${misc:Recommends}
,libapache2-mod-php | php-fpm
,mysql-server | mariadb-server | virtual-mysql-server

View File

@ -16,17 +16,20 @@ create_db () {
else
echo "Db exists."
fi
}
create_update_user () {
USER_EXISTS="$(mysql --defaults-file=/etc/mysql/debian.cnf -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$ZM_DB_USER')")"
if [ $USER_EXISTS -ne 1 ]; then
echo "Creating zm user $ZM_DB_USER"
# This creates the user.
echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi
echo "Updating permissions"
echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
}
update_db () {
echo "Updating permissions"
echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
@ -94,6 +97,7 @@ if [ "$1" = "configure" ]; then
# Make sure systemctl status exit code is 0; i.e. the DB is running
if systemctl is-active --quiet "$DBSERVICE"; then
create_db
create_update_user
update_db
else
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
@ -108,6 +112,7 @@ if [ "$1" = "configure" ]; then
fi
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
create_db
create_update_user
update_db
else
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'

View File

@ -479,6 +479,33 @@ our @options = (
type => $types{string},
category => 'system',
},
{
name => 'ZM_OPT_USE_GEOLOCATION',
description => 'Add geolocation features to ZoneMinder.',
help => 'Whether or not to enable Latitude/Longitude settings on Monitors and enable mapping options.',
type => $types{boolean},
category => 'system',
},
{
name => 'ZM_OPT_GEOLOCATION_TILE_PROVIDER',
description => 'Tile provider to use for maps.',
help => 'OpenStreetMaps does not itself provide the images to use in the map. There are many to choose from. Mapbox.com is one example that offers free tiles and has been tested during development of this feature.',
requires => [
{name=>'ZM_OPT_USE_GEOLOCATION', value=>'yes'}
],
type => $types{string},
category => 'system',
},
{
name => 'ZM_OPT_GEOLOCATION_ACCESS_TOKEN',
description => 'Access Token for the tile provider used for maps.',
help => 'OpenStreetMaps does not itself provide the images to use in the map. There are many to choose from. Mapbox.com is one example that offers free tiles and has been tested during development of this feature. You must go to mapbox.com and sign up and get an access token and cutnpaste it here.',
requires => [
{name=>'ZM_OPT_USE_GEOLOCATION', value=>'yes'}
],
type => $types{string},
category => 'system',
},
{
name => 'ZM_SYSTEM_SHUTDOWN',
default => 'true',

View File

@ -58,6 +58,7 @@ Id
Name
Query_json
AutoArchive
AutoUnarchive
AutoVideo
AutoUpload
AutoEmail
@ -201,28 +202,25 @@ sub Sql {
$self->{Sql} .= 'extract( hour_second from E.EndTime )';
} elsif ( $term->{attr} eq 'EndWeekday' ) {
$self->{Sql} .= "weekday( E.EndTime )";
#
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
push @{$self->{PostSQLConditions}}, $term;
} elsif ( $term->{attr} eq 'DiskSpace' ) {
$self->{Sql} .= 'E.DiskSpace';
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
} elsif ( $term->{attr} eq 'DiskPercent' ) {
$self->{Sql} .= 'zmDiskPercent';
$self->{HasDiskPercent} = !undef;
$self->{HasPreCondition} = !undef;
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
$self->{Sql} .= 'zmDiskBlocks';
$self->{HasDiskBlocks} = !undef;
$self->{HasPreCondition} = !undef;
} elsif ( $term->{attr} eq 'SystemLoad' ) {
$self->{Sql} .= 'zmSystemLoad';
$self->{HasSystemLoad} = !undef;
$self->{HasPreCondition} = !undef;
} else {
$self->{Sql} .= 'E.'.$term->{attr};
}
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
# PostCondition, so no further SQL
} else {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
@ -297,11 +295,12 @@ sub Sql {
push @value_list, $value;
} # end foreach temp_value
} # end if has an attr
if ( $term->{op} ) {
if ( $term->{op} eq '=~' ) {
$self->{Sql} .= " regexp $value";
$self->{Sql} .= ' REGEXP '.$value;
} elsif ( $term->{op} eq '!~' ) {
$self->{Sql} .= " not regexp $value";
$self->{Sql} .= ' NOT REGEXP '.$value;
} elsif ( $term->{op} eq 'IS' ) {
if ( $value eq 'Odd' ) {
$self->{Sql} .= ' % 2 = 1';
@ -311,21 +310,22 @@ sub Sql {
$self->{Sql} .= " IS $value";
}
} elsif ( $term->{op} eq 'EXISTS' ) {
$self->{Sql} .= " EXISTS $value";
$self->{Sql} .= ' EXISTS '.$value;
} elsif ( $term->{op} eq 'IS NOT' ) {
$self->{Sql} .= " IS NOT $value";
} elsif ( $term->{op} eq '=[]' ) {
$self->{Sql} .= ' IS NOT '.$value;
} elsif ( $term->{op} eq '=[]' or $term->{op} eq 'IN' ) {
$self->{Sql} .= ' IN ('.join(',', @value_list).')';
} elsif ( $term->{op} eq '!~' ) {
} elsif ( $term->{op} eq '![]' ) {
$self->{Sql} .= ' NOT IN ('.join(',', @value_list).')';
} elsif ( $term->{op} eq 'LIKE' ) {
$self->{Sql} .= " LIKE $value";
$self->{Sql} .= ' LIKE '.$value;
} elsif ( $term->{op} eq 'NOT LIKE' ) {
$self->{Sql} .= " NOT LIKE $value";
$self->{Sql} .= ' NOT LIKE '.$value;
} else {
$self->{Sql} .= ' '.$term->{op}.' '.$value;
}
} # end if has an operator
} # end if Pre/Post or SQL
if ( exists($term->{cbr}) ) {
$self->{Sql} .= ' '.str_repeat(')', $term->{cbr}).' ';
}
@ -341,6 +341,9 @@ sub Sql {
if ( $self->{AutoArchive} ) {
push @auto_terms, 'E.Archived = 0';
}
if ( $self->{AutoUnarchive} ) {
push @auto_terms, 'E.Archived = 1';
}
# Don't do this, it prevents re-generation and concatenation.
# If the file already exists, then the video won't be re-recreated
if ( $self->{AutoVideo} ) {
@ -394,7 +397,7 @@ sub Sql {
$sort_column = 'E.StartTime';
}
my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC';
$sql .= ' ORDER BY '.$sort_column." ".$sort_order;
$sql .= ' ORDER BY '.$sort_column.' '.$sort_order;
if ( $filter_expr->{limit} ) {
$sql .= ' LIMIT 0,'.$filter_expr->{limit};
}

View File

@ -121,6 +121,24 @@ $serial = $primary_key = 'Id';
WebColour
Exif
Sequence
TotalEvents
TotalEventDiskSpace
HourEvents
HourEventDiskSpace
DayEvents
DayEventDiskSpace
WeekEvents
WeekEventDiskSpace
MonthEvents
MonthEventDiskSpace
ArchivedEvents
ArchivedEventDiskSpace
ZoneCount
Refresh
DefaultCodec
GroupIds
Latitude
Longitude
);
%defaults = (
@ -201,6 +219,23 @@ $serial = $primary_key = 'Id';
WebColour => '#ff0000',
Exif => 0,
Sequence => undef,
TotalEvents => undef,
TotalEventDiskSpace => undef,
HourEvents => undef,
HourEventDiskSpace => undef,
DayEvents => undef,
DayEventDiskSpace => undef,
WeekEvents => undef,
WeekEventDiskSpace => undef,
MonthEvents => undef,
MonthEventDiskSpace => undef,
ArchivedEvents => undef,
ArchivedEventDiskSpace => undef,
ZoneCount => 0,
Refresh => undef,
DefaultCodec => 'auto',
Latitude => undef,
Longitude => undef,
);
sub Server {
@ -211,6 +246,30 @@ sub Storage {
return new ZoneMinder::Storage( $_[0]{StorageId} );
} # end sub Storage
sub control {
my $monitor = shift;
my $command = shift;
my $process = shift;
if ( $command eq 'stop' or $command eq 'restart' ) {
if ( $process ) {
`/usr/bin/zmdc.pl stop $process -m $$monitor{Id}`;
} else {
`/usr/bin/zmdc.pl stop zma -m $$monitor{Id}`;
`/usr/bin/zmdc.pl stop zmc -m $$monitor{Id}`;
}
}
if ( $command eq 'start' or $command eq 'restart' ) {
if ( $process ) {
`/usr/bin/zmdc.pl start $process -m $$monitor{Id}`;
} else {
`/usr/bin/zmdc.pl start zmc -m $$monitor{Id}`;
`/usr/bin/zmdc.pl start zma -m $$monitor{Id}`;
} # end if
}
} # end sub control
1;
__END__

View File

@ -0,0 +1,105 @@
# ==========================================================================
#
# ZoneMinder Zone Module
# Copyright (C) 2020 ZoneMinder LLC
#
# 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.
#
# ==========================================================================
package ZoneMinder::Zone;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Object;
#our @ISA = qw(Exporter ZoneMinder::Base);
use parent qw(ZoneMinder::Object);
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
$table = 'Zones';
$serial = $primary_key = 'Id';
%fields = map { $_ => $_ } qw(
Id
Name
MonitorId
Type
Units
CheckMethod
MinPixelThreshold
MaxPixelThreshold
MinAlarmPixels
MaxAlarmPixels
FilterX
FilterY
MinFilterPixels
MaxFilterPixels
MinBlobPixels
MaxBlobPixels
MinBlobs
MaxBlobs
OverloadFrames
ExtendAlarmFrames
);
%defaults = (
Name => '',
Type => 'Active',
Units => 'Pixels',
CheckMethod => 'Blobs',
MinPixelThreshold => undef,
MaxPixelThreshold => undef,
MinAlarmPixels => undef,
MaxAlarmPixels => undef,
FilterX => undef,
FilterY => undef,
MinFilterPixels => undef,
MaxFilterPixels => undef,
MinBlobPixels => undef,
MaxBlobPixels => undef,
MinBlobs => undef,
MaxBlobs => undef,
OverloadFrames => 0,
ExtendAlarmFrames => 0,
);
1;
__END__
=head1 NAME
ZoneMinder::Zone - Perl Class for Zones
=head1 SYNOPSIS
use ZoneMinder::Zone;
=head1 AUTHOR
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2017 ZoneMinder LLC
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

@ -98,6 +98,7 @@ 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.
@ -236,6 +237,7 @@ sub getFilters {
$sql .= ' `Background` = 1 AND';
}
$sql .= '( `AutoArchive` = 1
or `AutoUnarchive` = 1
or `AutoVideo` = 1
or `AutoUpload` = 1
or `AutoEmail` = 1
@ -282,6 +284,7 @@ sub checkFilter {
join(', ',
($filter->{AutoDelete}?'delete':()),
($filter->{AutoArchive}?'archive':()),
($filter->{AutoUnarchive}?'unarchive':()),
($filter->{AutoVideo}?'video':()),
($filter->{AutoUpload}?'upload':()),
($filter->{AutoEmail}?'email':()),
@ -299,7 +302,7 @@ sub checkFilter {
last if $zm_terminate;
my $Event = new ZoneMinder::Event($$event{Id}, $event);
Debug("Checking event $Event->{Id}");
Debug('Checking event '.$Event->{Id});
my $delete_ok = !undef;
$dbh->ping();
if ( $filter->{AutoArchive} ) {
@ -311,6 +314,15 @@ sub checkFilter {
my $res = $sth->execute($Event->{Id})
or Error("Unable to execute '$sql': ".$dbh->errstr());
}
if ( $filter->{AutoUnarchive} ) {
Info("Unarchiving event $Event->{Id}");
# Do it individually to avoid locking up the table for new events
my $sql = 'UPDATE `Events` SET `Archived` = 0 WHERE `Id` = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Error("Unable to execute '$sql': ".$dbh->errstr());
}
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) {
if ( !$Event->{Videoed} ) {
$delete_ok = undef if !generateVideo($filter, $Event);

View File

@ -39,6 +39,7 @@
//#define USE_PREPARED_SQL 1
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
char frame_insert_sql[ZM_SQL_LGE_BUFSIZ] = "INSERT INTO `Frames` (`EventId`, `FrameId`, `Type`, `TimeStamp`, `Delta`, `Score`) VALUES ";
int Event::pre_alarm_count = 0;
@ -109,9 +110,11 @@ Event::Event(
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert event: %s. sql was (%s)", mysql_error(&dbconn), sql);
db_mutex.unlock();
Error("Can't insert event: %s. sql was (%s)", mysql_error(&dbconn), sql);
return;
} else {
Debug(2, "Created new event with %s", sql);
}
id = mysql_insert_id(&dbconn);
@ -219,8 +222,8 @@ Event::Event(
video_name = stringtf("%" PRIu64 "-%s.%s", id, "video", container.c_str());
snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql);
db_mutex.unlock();
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql);
return;
}
video_file = path + "/" + video_name;
@ -479,8 +482,9 @@ void Event::AddFrames(int n_frames, Image **images, struct timeval **timestamps)
}
void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, struct timeval **timestamps) {
static char sql[ZM_SQL_LGE_BUFSIZ];
strncpy(sql, "INSERT INTO `Frames` (`EventId`, `FrameId`, `TimeStamp`, `Delta`) VALUES ", sizeof(sql));
char *frame_insert_values = (char *)&frame_insert_sql + 90; // 90 == strlen(frame_insert_sql);
//static char sql[ZM_SQL_LGE_BUFSIZ];
//strncpy(sql, "INSERT INTO `Frames` (`EventId`, `FrameId`, `TimeStamp`, `Delta`) VALUES ", sizeof(sql));
int frameCount = 0;
for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) {
if ( timestamps[i]->tv_sec <= 0 ) {
@ -520,21 +524,24 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
delta_time.sec = 0;
}
int sql_len = strlen(sql);
snprintf(sql+sql_len, sizeof(sql)-sql_len, "( %" PRIu64 ", %d, from_unixtime(%ld), %s%ld.%02ld ), ",
frame_insert_values += snprintf(frame_insert_values,
sizeof(frame_insert_sql)-(frame_insert_values-(char *)&frame_insert_sql),
"\n( %" PRIu64 ", %d, 'Normal', from_unixtime(%ld), %s%ld.%02ld, 0 ),",
id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec);
frameCount++;
} // end foreach frame
if ( frameCount ) {
Debug(1, "Adding %d/%d frames to DB", frameCount, n_frames);
*(sql+strlen(sql)-2) = '\0';
*(frame_insert_values-1) = '\0';
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frames: %s, sql was (%s)", mysql_error(&dbconn), sql);
}
int rc = mysql_query(&dbconn, frame_insert_sql);
db_mutex.unlock();
if ( rc ) {
Error("Can't insert frames: %s, sql was (%s)", mysql_error(&dbconn), frame_insert_sql);
} else {
Debug(1, "INSERT %d/%d frames sql %s", frameCount, n_frames, frame_insert_sql);
}
last_db_frame = frames;
} else {
Debug(1, "No valid pre-capture frames to add");
@ -559,16 +566,15 @@ void Event::AddPacket(ZMPacket *packet, int score, Image *alarm_image) {
}
void Event::WriteDbFrames() {
static char sql[ZM_SQL_LGE_BUFSIZ];
char * sql_ptr = (char *)&sql;
sql_ptr += snprintf(sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) VALUES "
);
char *frame_insert_values_ptr = (char *)&frame_insert_sql + 90; // 90 == strlen(frame_insert_sql);
while ( frame_data.size() ) {
Frame *frame = frame_data.front();
frame_data.pop();
sql_ptr += snprintf(sql_ptr, sizeof(sql)-(sql_ptr-(char *)&sql), "( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d ), ",
id, frame->frame_id, frame_type_names[frame->type],
frame_insert_values_ptr += snprintf(frame_insert_values_ptr,
sizeof(frame_insert_sql)-(frame_insert_values_ptr-(char *)&frame_insert_sql),
"\n( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d ),",
id, frame->frame_id,
frame_type_names[frame->type],
frame->timestamp.tv_sec,
frame->delta.positive?"":"-",
frame->delta.sec,
@ -576,14 +582,17 @@ void Event::WriteDbFrames() {
frame->score);
delete frame;
}
*(sql_ptr-2) = '\0';
*(frame_insert_values_ptr-1) = '\0'; // The -2 is for the extra , added for values above
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
int rc = mysql_query(&dbconn, frame_insert_sql);
db_mutex.unlock();
Error("Can't insert frames: %s, sql was %s", mysql_error(&dbconn), sql);
if ( rc ) {
Error("Can't insert frames: %s, sql was %s", mysql_error(&dbconn), frame_insert_sql);
return;
} else {
Debug(1, "INSERT FRAMES: sql was %s", frame_insert_sql);
}
db_mutex.unlock();
} // end void Event::WriteDbFrames()
// Subtract an offset time from frames deltas to match with video start time
@ -666,20 +675,18 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
static char sql[ZM_SQL_MED_BUFSIZ];
struct DeltaTimeval delta_time;
DELTA_TIMEVAL(delta_time, timestamp, start_time, DT_PREC_2);
Debug(1, "Frame delta is %d.%d - %d.%d = %d.%d",
start_time.tv_sec, start_time.tv_usec, timestamp.tv_sec, timestamp.tv_usec, delta_time.sec, delta_time.fsec);
// The idea is to write out 1/sec
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
double fps = monitor->get_capture_fps();
if ( write_to_db || ( fps && (frame_data.size() > fps) ) ) {
Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > fps %f",
if ( write_to_db or ( monitor->get_fps() and (frame_data.size() > monitor->get_fps())) or frame_type==BULK ) {
Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > capture fps %f or BULK",
frame_data.size(), write_to_db, fps);
WriteDbFrames();
last_db_frame = frames;
Debug(1, "Adding %d frames to DB, done", frame_data.size());
}
// We are writing a Bulk frame
if ( frame_type == BULK ) {
snprintf(sql, sizeof(sql),
"UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
( delta_time.positive?"":"-" ),
@ -703,5 +710,4 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
} // end if db_frame
end_time = timestamp;
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)

View File

@ -117,6 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
snprintf(sql, sizeof(sql),
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
"unix_timestamp( `EndTime` ) AS EndTimestamp, "
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
@ -150,9 +151,10 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]);
event_data->start_time = atoi(dbrow[3]);
event_data->duration = dbrow[4] ? atof(dbrow[4]) : 0.0;
strncpy(event_data->video_file, dbrow[5], sizeof(event_data->video_file)-1);
std::string scheme_str = std::string(dbrow[6]);
event_data->end_time = dbrow[4] ? atoi(dbrow[4]) : 0;
event_data->duration = dbrow[5] ? atof(dbrow[5]) : 0.0;
strncpy(event_data->video_file, dbrow[6], sizeof(event_data->video_file)-1);
std::string scheme_str = std::string(dbrow[7]);
if ( scheme_str == "Deep" ) {
event_data->scheme = Storage::DEEP;
} else if ( scheme_str == "Medium" ) {
@ -160,8 +162,8 @@ bool EventStream::loadEventData(uint64_t event_id) {
} else {
event_data->scheme = Storage::SHALLOW;
}
event_data->SaveJPEGs = dbrow[7] == nullptr ? 0 : atoi(dbrow[7]);
event_data->Orientation = (Monitor::Orientation)(dbrow[8] == nullptr ? 0 : atoi(dbrow[8]));
event_data->SaveJPEGs = dbrow[8] == nullptr ? 0 : atoi(dbrow[8]);
event_data->Orientation = (Monitor::Orientation)(dbrow[9] == nullptr ? 0 : atoi(dbrow[9]));
mysql_free_result(result);
if ( !monitor ) {
@ -223,8 +225,6 @@ bool EventStream::loadEventData(uint64_t event_id) {
}
updateFrameRate((double)event_data->frame_count/event_data->duration);
Debug(3, "fps set by frame_count(%d)/duration(%f)",
event_data->frame_count, event_data->duration);
snprintf(sql, sizeof(sql), "SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
@ -284,11 +284,13 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->frames[id-1].in_db
);
}
// Incomplete events might not have any frame data
event_data->last_frame_id = last_id;
if ( mysql_errno(&dbconn) ) {
Error("Can't fetch row: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
mysql_free_result(result);
if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) {
@ -296,9 +298,10 @@ bool EventStream::loadEventData(uint64_t event_id) {
snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4");
}
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
//char filepath[PATH_MAX];
//snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
Debug(1, "Loading video file from %s", filepath.c_str());
if ( ffmpeg_input )
delete ffmpeg_input;
ffmpeg_input = new FFmpeg_Input();
if ( 0 > ffmpeg_input->Open(filepath.c_str()) ) {
Warning("Unable to open ffmpeg_input %s", filepath.c_str());
@ -307,14 +310,15 @@ bool EventStream::loadEventData(uint64_t event_id) {
}
}
// Not sure about this
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
if ( replay_rate > 0 )
curr_stream_time = event_data->frames[0].timestamp;
else
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
curr_stream_time = event_data->frames[event_data->last_frame_id-1].timestamp;
}
Debug(2, "Event:%" PRIu64 ", Frames:%ld, Duration: %.2f",
event_data->event_id, event_data->frame_count, event_data->duration);
Debug(2, "Event:%" PRIu64 ", Frames:%ld, Last Frame ID(%ld, Duration: %.2f",
event_data->event_id, event_data->frame_count, event_data->last_frame_id, event_data->duration);
return true;
} // bool EventStream::loadEventData( int event_id )
@ -341,12 +345,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
if (
(mode == MODE_SINGLE || mode == MODE_NONE)
&&
((unsigned int)curr_frame_id == event_data->frame_count)
((unsigned int)curr_frame_id == event_data->last_frame_id)
) {
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
curr_frame_id = 1;
} else {
Debug(1, "mode is %s, current frame is %d, frame count is %d",
Debug(1, "mode is %s, current frame is %ld, frame count is %ld, last frame id is %ld",
(mode == MODE_SINGLE ? "single" : "not single"),
curr_frame_id, event_data->frame_count );
}
@ -359,6 +363,13 @@ void EventStream::processCommand(const CmdMsg *msg) {
paused = false;
}
replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
if ( replay_rate > 50 * ZM_RATE_BASE ) {
Warning("requested replay rate (%d) is too high. We only support up to 50x", replay_rate);
replay_rate = 50 * ZM_RATE_BASE;
} else if ( replay_rate < -50*ZM_RATE_BASE ) {
Warning("requested replay rate (%d) is too low. We only support up to -50x", replay_rate);
replay_rate = -50 * ZM_RATE_BASE;
}
break;
case CMD_STOP :
Debug(1, "Got STOP command");
@ -394,7 +405,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
paused = true;
replay_rate = ZM_RATE_BASE;
step = 1;
if ( (unsigned int)curr_frame_id < event_data->frame_count )
if ( (unsigned int)curr_frame_id < event_data->last_frame_id )
curr_frame_id += 1;
Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id);
break;
@ -411,6 +422,8 @@ void EventStream::processCommand(const CmdMsg *msg) {
paused = false;
// Set play rate
switch ( replay_rate ) {
case -1 * ZM_RATE_BASE :
replay_rate = -2 * ZM_RATE_BASE;
case -2 * ZM_RATE_BASE :
replay_rate = -5 * ZM_RATE_BASE;
break;
@ -425,7 +438,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
replay_rate = -50 * ZM_RATE_BASE;
break;
default :
replay_rate = -2 * ZM_RATE_BASE;
replay_rate = -1 * ZM_RATE_BASE;
break;
}
break;
@ -489,14 +502,14 @@ void EventStream::processCommand(const CmdMsg *msg) {
if ( replay_rate >= 0 )
curr_frame_id = 0;
else
curr_frame_id = event_data->frame_count+1;
curr_frame_id = event_data->last_frame_id+1;
paused = false;
forceEventChange = true;
break;
case CMD_NEXT :
Debug(1, "Got NEXT command");
if ( replay_rate >= 0 )
curr_frame_id = event_data->frame_count+1;
curr_frame_id = event_data->last_frame_id+1;
else
curr_frame_id = 0;
paused = false;
@ -505,9 +518,33 @@ void EventStream::processCommand(const CmdMsg *msg) {
case CMD_SEEK :
{
// offset is in seconds
int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration);
Debug(1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id);
int int_part = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
int dec_part = ((unsigned char)msg->msg_data[5]<<24)|((unsigned char)msg->msg_data[6]<<16)|((unsigned char)msg->msg_data[7]<<8)|(unsigned char)msg->msg_data[8];
double offset = (double)int_part + (double)(dec_part / (double)1000000);
if ( offset < 0.0 ) {
Warning("Invalid offset, not seeking");
break;
}
// This should get us close, but not all frames will have the same duration
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration)+1;
if ( event_data->frames[curr_frame_id-1].offset > offset ) {
while ( (curr_frame_id --) && ( event_data->frames[curr_frame_id-1].offset > offset ) ) {
}
} else if ( event_data->frames[curr_frame_id-1].offset < offset ) {
while ( (curr_frame_id ++) && ( event_data->frames[curr_frame_id-1].offset > offset ) ) {
}
}
if ( curr_frame_id < 1 ) {
curr_frame_id = 1;
} else if ( curr_frame_id > event_data->last_frame_id ) {
curr_frame_id = event_data->last_frame_id;
}
curr_stream_time = event_data->frames[curr_frame_id-1].timestamp;
Debug(1, "Got SEEK command, to %f (new current frame id: %d offset %f)",
offset, curr_frame_id, event_data->frames[curr_frame_id-1].offset);
send_frame = true;
break;
}
@ -524,19 +561,22 @@ void EventStream::processCommand(const CmdMsg *msg) {
struct {
uint64_t event_id;
int progress;
double duration;
double progress;
int rate;
int zoom;
bool paused;
} status_data;
status_data.event_id = event_data->event_id;
status_data.progress = (int)event_data->frames[curr_frame_id-1].offset;
status_data.duration = event_data->duration;
status_data.progress = event_data->frames[curr_frame_id-1].offset;
status_data.rate = replay_rate;
status_data.zoom = zoom;
status_data.paused = paused;
Debug(2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d",
Debug(2, "Event:%" PRIu64 ", Duration %f, Paused:%d, progress:%f Rate:%d, Zoom:%d",
status_data.event_id,
status_data.duration,
status_data.paused,
status_data.progress,
status_data.rate,
@ -568,7 +608,14 @@ bool EventStream::checkEventLoaded() {
snprintf(sql, sizeof(sql),
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
event_data->monitor_id, event_data->event_id);
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
} else if ( (unsigned int)curr_frame_id > event_data->last_frame_id ) {
if ( !event_data->end_time ) {
// We are viewing an in-process event, so just reload it.
loadEventData(event_data->event_id);
if ( (unsigned int)curr_frame_id > event_data->last_frame_id )
curr_frame_id = event_data->last_frame_id;
return false;
}
snprintf(sql, sizeof(sql),
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` > %" PRIu64 " ORDER BY `Id` ASC LIMIT 1",
event_data->monitor_id, event_data->event_id);
@ -581,6 +628,7 @@ bool EventStream::checkEventLoaded() {
// Event change required.
if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) {
Debug(1, "Checking for next event %s", sql);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
@ -591,6 +639,9 @@ bool EventStream::checkEventLoaded() {
Error("Can't use query result: %s", mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
if ( mysql_num_rows(result) != 1 ) {
Debug(1, "No rows returned for %s", sql);
}
MYSQL_ROW dbrow = mysql_fetch_row(result);
if ( mysql_errno(&dbconn)) {
@ -605,7 +656,7 @@ bool EventStream::checkEventLoaded() {
loadEventData(event_id);
if ( replay_rate < 0 ) // rewind
curr_frame_id = event_data->frame_count;
curr_frame_id = event_data->last_frame_id;
else
curr_frame_id = 1;
Debug(2, "New frame id = %d", curr_frame_id);
@ -626,7 +677,7 @@ bool EventStream::checkEventLoaded() {
if ( curr_frame_id <= 0 )
curr_frame_id = 1;
else
curr_frame_id = event_data->frame_count;
curr_frame_id = event_data->last_frame_id;
paused = true;
}
return false;
@ -683,9 +734,6 @@ bool EventStream::sendFrame(int delta_us) {
#endif // HAVE_LIBAVCODEC
{
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
int img_buffer_size = 0;
uint8_t *img_buffer = temp_img_buffer;
bool send_raw = (type == STREAM_JPEG) && ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
@ -748,6 +796,9 @@ bool EventStream::sendFrame(int delta_us) {
}
Image *send_image = prepareImage(image);
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
int img_buffer_size = 0;
uint8_t *img_buffer = temp_img_buffer;
switch ( type ) {
case STREAM_JPEG :
@ -776,7 +827,7 @@ bool EventStream::sendFrame(int delta_us) {
} // end if stream MPEG or other
fputs("\r\n\r\n", stdout);
fputs("\r\n", stdout);
fflush(stdout);
last_frame_sent = TV_2_FLOAT(now);
return true;
@ -795,13 +846,11 @@ void EventStream::runStream() {
exit(0);
}
Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration);
updateFrameRate((double)event_data->frame_count/event_data->duration);
gettimeofday(&start, nullptr);
uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec;
uint64_t last_frame_offset = 0;
bool in_event = true;
double time_to_event = 0;
while ( !zm_terminate ) {
@ -843,8 +892,8 @@ void EventStream::runStream() {
send_frame = true;
} else if ( !send_frame ) {
// We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > MAX_STREAM_DELAY ) {
double time_since_last_send = TV_2_FLOAT(now) - last_frame_sent;
if ( time_since_last_send > MAX_STREAM_DELAY ) {
// Send keepalive
Debug(2, "Sending keepalive frame");
send_frame = true;
@ -852,21 +901,19 @@ void EventStream::runStream() {
} // end if streaming stepping or doing nothing
// time_to_event > 0 means that we are not in the event
if ( time_to_event > 0 ) {
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
Debug(1, "Actual delta time = %f = %f - %f", actual_delta_time, TV_2_FLOAT(now), last_frame_sent);
// > 1 second
if ( actual_delta_time > 1 ) {
Debug(1, "Sending time to next event frame");
if ( ( time_to_event > 0 ) and ( mode == MODE_ALL ) ) {
double time_since_last_send = TV_2_FLOAT(now) - last_frame_sent;
Debug(1, "Time since last send = %f = %f - %f", time_since_last_send, TV_2_FLOAT(now), last_frame_sent);
if ( time_since_last_send > 1 /* second */ ) {
static char frame_text[64];
snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event);
snprintf(frame_text, sizeof(frame_text), "Time to %s event = %d seconds",
(replay_rate > 0 ? "next" : "previous" ), (int)time_to_event);
if ( !sendTextFrame(frame_text) )
zm_terminate = true;
} else {
Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time);
send_frame = false; // In case keepalive was set
}
//else
//{
// FIXME ICON But we are not paused. We are somehow still in the event?
double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
//double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
@ -976,6 +1023,7 @@ void EventStream::runStream() {
//if ( step != 0 )// Adding 0 is cheaper than an if 0
// curr_frame_id starts at 1 though, so we might skip the first frame?
curr_frame_id += step;
} // end if !paused
// Detects when we hit end of event and will load the next event or previous event
if ( checkEventLoaded() ) {
@ -996,9 +1044,7 @@ void EventStream::runStream() {
Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f",
replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp);
} // end if forward or reverse
} // end if checkEventLoaded
} // end if !paused
} // end while ! zm_terminate
#if HAVE_LIBAVCODEC
if ( type == STREAM_MPEG )
@ -1010,6 +1056,7 @@ void EventStream::runStream() {
bool EventStream::send_file(const char * filepath) {
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
int rc;
int img_buffer_size = 0;
uint8_t *img_buffer = temp_img_buffer;
@ -1020,47 +1067,50 @@ bool EventStream::send_file(const char * filepath) {
Error("Can't open %s: %s", filepath, strerror(errno));
return false;
}
bool size_sent = false;
#if HAVE_SENDFILE
static struct stat filestat;
if ( fstat(fileno(fdj), &filestat) < 0 ) {
fclose(fdj); /* Close the file handle */
Error("Failed getting information about file %s: %s", filepath, strerror(errno));
return false;
}
if ( !filestat.st_size ) {
fclose(fdj); /* Close the file handle */
Info("File size is zero. Unable to send raw frame %u: %s", curr_frame_id);
return false;
}
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size) ) {
fclose(fdj); /* Close the file handle */
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
size_sent = true;
if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) {
rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size);
if ( rc == (int)filestat.st_size ) {
// Success
fclose(fdj); /* Close the file handle */
return true;
}
Warning("Unable to send raw frame %u: %s rc %d", curr_frame_id, strerror(errno), rc);
#endif
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
if ( !size_sent ) {
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) ) {
fclose(fdj); /* Close the file handle */
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
}
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
if ( !img_buffer_size ) {
Info("Unable to read raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
fclose(fdj); /* Close the file handle */
return true;
return send_buffer(img_buffer, img_buffer_size);
} // end bool EventStream::send_file(const char * filepath)
bool EventStream::send_buffer(uint8_t* buffer, int size) {
fprintf(stdout, "Content-Length: %d\r\n\r\n", size);
if ( fwrite(buffer, size, 1, stdout) != 1 ) {
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", size) ) {
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
int rc = fwrite(buffer, size, 1, stdout);
if ( 1 != rc ) {
Error("Unable to send raw frame %u: %s %d", curr_frame_id, strerror(errno), rc);
return false;
}
return true;

View File

@ -54,11 +54,13 @@ class EventStream : public StreamBase {
uint64_t event_id;
unsigned int monitor_id;
unsigned long storage_id;
unsigned long frame_count;
unsigned long frame_count; // Value of Frames column in Event
unsigned long last_frame_id; // Highest frame id known about. Can be < frame_count in incomplete events
time_t start_time;
time_t end_time;
double duration;
char path[PATH_MAX];
int n_frames;
int n_frames; // # of frame rows returned from database
FrameData *frames;
char video_file[PATH_MAX];
Storage::Schemes scheme;
@ -74,7 +76,7 @@ class EventStream : public StreamBase {
StreamMode mode;
bool forceEventChange;
int curr_frame_id;
unsigned long curr_frame_id;
double curr_stream_time;
bool send_frame;
struct timeval start; // clock time when started the event

View File

@ -20,7 +20,7 @@ FFmpeg_Input::~FFmpeg_Input() {
if ( streams ) {
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
avcodec_close(streams[i].context);
streams[i].context = nullptr;
avcodec_free_context(&streams[i].context);
}
delete[] streams;
streams = nullptr;
@ -262,6 +262,9 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
// Have to grab a frame to update our current frame to know where we are
get_frame(stream_id);
zm_dump_frame(frame, "frame->pts > seek_target, got");
} else if ( last_seek_request == seek_target ) {
// paused case, sending keepalives
return frame;
} // end if frame->pts > seek_target
last_seek_request = seek_target;

View File

@ -1859,7 +1859,7 @@ void Image::Delta(const Image &image, Image* targetimage) const {
#endif
}
const Coord Image::centreCoord( const char *text ) const {
const Coord Image::centreCoord( const char *text, int size=1 ) const {
int index = 0;
int line_no = 0;
int text_len = strlen(text);
@ -1878,8 +1878,8 @@ const Coord Image::centreCoord( const char *text ) const {
line = text+index;
line_no++;
}
int x = (width - (max_line_len * ZM_CHAR_WIDTH) ) / 2;
int y = (height - (line_no * LINE_HEIGHT) ) / 2;
int x = (width - (max_line_len * ZM_CHAR_WIDTH * size) ) / 2;
int y = (height - (line_no * LINE_HEIGHT * size) ) / 2;
return Coord(x, y);
}

View File

@ -285,7 +285,7 @@ public:
//Image *Delta( const Image &image ) const;
void Delta( const Image &image, Image* targetimage) const;
const Coord centreCoord(const char *p_text) const;
const Coord centreCoord(const char *text, const int size) const;
void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 );
void Annotate( const char *p_text, const Coord &coord, const unsigned int size=1, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK );
Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 );

View File

@ -1766,9 +1766,7 @@ bool Monitor::Analyse() {
int motion_score = last_motion_score;
if ( !(analysis_image_count % (motion_frame_skip+1) ) ) {
if ( snap->image ) {
// Get new score.
Debug(3, "before DetectMotion packet index is %d", snap->image_index);
motion_score = DetectMotion(*snap_image, zoneSet);
Debug(3, "After motion detection, last_motion_score(%d), new motion score(%d)", last_motion_score, motion_score);
@ -1780,11 +1778,9 @@ bool Monitor::Analyse() {
}
if ( motion_score ) {
score += motion_score;
if ( !event ) {
if ( cause.length() )
cause += ", ";
cause += MOTION_CAUSE;
}
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
//shared_data->active = signal; // unneccessary active gets set on signal change
@ -1887,7 +1883,6 @@ bool Monitor::Analyse() {
( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length
);
}
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) {
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause = "";
@ -1947,6 +1942,8 @@ Debug(1, "alarm frame count so SavePreAlarmFrames");
} else if ( state == PREALARM ) {
// Back to IDLE
shared_data->state = state = function != MOCORD ? IDLE : TAPE;
} else {
Debug(1, "Not leaving ALERT beacuse image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%d) - recording.tv_src(%d) >= min_section_length(%d)", image_count, last_alarm_count, post_event_count, timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
}
if ( Event::PreAlarmCount() )
Event::EmptyPreAlarmFrames();

View File

@ -502,6 +502,8 @@ public:
inline void setStartupTime( time_t p_time ) { shared_data->startup_time = p_time; }
void get_ref_image();
int LabelSize() { return label_size; }
void actionReload();
void actionEnable();
void actionDisable();

View File

@ -39,7 +39,7 @@ StreamBase::~StreamBase() {
closeComms();
if ( monitor ) {
delete monitor;
monitor = NULL;
monitor = nullptr;
}
}
@ -252,7 +252,8 @@ bool StreamBase::sendTextFrame(const char *frame_text) {
monitor->Width(), monitor->Height(), scale, frame_text);
Image image(monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
image.Annotate(frame_text, image.centreCoord(frame_text));
image.Clear();
image.Annotate(frame_text, image.centreCoord(frame_text, monitor->LabelSize()), monitor->LabelSize());
if ( scale != 100 ) {
image.Scale(scale);

View File

@ -116,7 +116,7 @@ protected:
public:
StreamBase():
monitor_id(0),
monitor(0),
monitor(nullptr),
type(DEFAULT_TYPE),
format(""),
replay_rate(DEFAULT_RATE),

View File

@ -239,63 +239,31 @@ void hwcaps_detect() {
neonversion = 0;
sse_version = 0;
#if (defined(__i386__) || defined(__x86_64__))
/* x86 or x86-64 processor */
uint32_t r_edx, r_ecx, r_ebx;
__builtin_cpu_init();
#ifdef __x86_64__
__asm__ __volatile__(
"push %%rbx\n\t"
"mov $0x0,%%ecx\n\t"
"mov $0x7,%%eax\n\t"
"cpuid\n\t"
"push %%rbx\n\t"
"mov $0x1,%%eax\n\t"
"cpuid\n\t"
"pop %%rax\n\t"
"pop %%rbx\n\t"
: "=d" (r_edx), "=c" (r_ecx), "=a" (r_ebx)
:
:
);
#else
__asm__ __volatile__(
"push %%ebx\n\t"
"mov $0x0,%%ecx\n\t"
"mov $0x7,%%eax\n\t"
"cpuid\n\t"
"push %%ebx\n\t"
"mov $0x1,%%eax\n\t"
"cpuid\n\t"
"pop %%eax\n\t"
"pop %%ebx\n\t"
: "=d" (r_edx), "=c" (r_ecx), "=a" (r_ebx)
:
:
);
#endif
if ( r_ebx & 0x00000020 ) {
if ( __builtin_cpu_supports("avx2") ) {
sse_version = 52; /* AVX2 */
Debug(1, "Detected a x86\\x86-64 processor with AVX2");
} else if ( r_ecx & 0x10000000 ) {
} else if ( __builtin_cpu_supports("avx") ) {
sse_version = 51; /* AVX */
Debug(1, "Detected a x86\\x86-64 processor with AVX");
} else if ( r_ecx & 0x00100000 ) {
} else if ( __builtin_cpu_supports("sse4.2") ) {
sse_version = 42; /* SSE4.2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.2");
} else if ( r_ecx & 0x00080000 ) {
} else if ( __builtin_cpu_supports("sse4.1") ) {
sse_version = 41; /* SSE4.1 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.1");
} else if ( r_ecx & 0x00000200 ) {
} else if ( __builtin_cpu_supports("ssse3") ) {
sse_version = 35; /* SSSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
} else if ( r_ecx & 0x00000001 ) {
} else if ( __builtin_cpu_supports("sse3") ) {
sse_version = 30; /* SSE3 */
Debug(1, "Detected a x86\\x86-64 processor with SSE3");
} else if ( r_edx & 0x04000000 ) {
} else if ( __builtin_cpu_supports("sse2") ) {
sse_version = 20; /* SSE2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE2");
} else if ( r_edx & 0x02000000 ) {
} else if ( __builtin_cpu_supports("sse") ) {
sse_version = 10; /* SSE */
Debug(1, "Detected a x86\\x86-64 processor with SSE");
} else {

View File

@ -126,6 +126,7 @@ void Zone::Setup(
Zone::~Zone() {
delete[] label;
if ( image )
delete image;
delete pg_image;
delete[] ranges;
@ -158,6 +159,7 @@ void Zone::SetScore(unsigned int nScore) {
} // end void Zone::SetScore(unsigned int nScore)
void Zone::SetAlarmImage(const Image* srcImage) {
if ( image )
delete image;
image = new Image(*srcImage);
} // end void Zone::SetAlarmImage( const Image* srcImage )
@ -205,6 +207,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
if ( image )
delete image;
// Get the difference image
Image *diff_image = image = new Image(*delta_image);
@ -734,14 +737,12 @@ bool Zone::CheckAlarms(const Image *delta_image) {
// Only need to delete this when 'image' becomes detached and points somewhere else
delete diff_image;
} else {
delete image;
image = 0;
}
diff_image = nullptr;
} // end if ( (type < PRECLUSIVE) && (check_method >= BLOBS) && (monitor->GetOptSaveJPEGs() > 1)
Debug(1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d",
Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score);
}
} // end if score
return true;
}

View File

@ -1 +1 @@
1.35.6
1.35.10

View File

@ -27,11 +27,11 @@ function probe( &$url_bits ) {
$cam_list_html = file_get_contents('http://'.$url_bits['host'].':5000/monitoring/');
if ( $cam_list_html ) {
ZM\Logger::Debug("Have content at port 5000/monitoring");
ZM\Debug("Have content at port 5000/monitoring");
$matches_count = preg_match_all(
'/<a href="http:\/\/([.[:digit:]]+):([[:digit:]]+)\/\?action=stream" target="_blank">([^<]+)<\/a>/',
$cam_list_html, $cam_list );
ZM\Logger::Debug(print_r($cam_list,true));
ZM\Debug(print_r($cam_list,true));
}
if ( $matches_count ) {
for( $index = 0; $index < $matches_count; $index ++ ) {
@ -41,7 +41,7 @@ function probe( &$url_bits ) {
if ( ! isset($new_stream['scheme'] ) )
$new_stream['scheme'] = 'http';
$available_streams[] = $new_stream;
ZM\Logger::Debug("Have new stream " . print_r($new_stream,true) );
ZM\Debug("Have new stream " . print_r($new_stream,true) );
}
} else {
ZM\Info('No matches');

33
web/ajax/controlcaps.php Normal file
View File

@ -0,0 +1,33 @@
<?php
if ( !canEdit('Control') ) {
ZM\Warning('Need Control permissions to edit control capabilities');
return;
}
if ( empty($_REQUEST['action']) ) {
ajaxError('Action Not Provided');
return;
} else {
$action = $_REQUEST['action'];
}
if ( isset($_REQUEST['cids']) ) {
$cids = $_REQUEST['cids'];
} else {
ajaxError('At least one Control Id must be Provided.');
return;
}
if ( $action == 'delete' ) {
foreach( $cids as $cid ) {
dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($cid));
dbQuery('DELETE FROM Controls WHERE Id = ?', array($cid));
}
} else {
ajaxError('Unrecognised action ' .$_REQUEST['action']);
return;
}
ajaxResponse();
return;
?>

47
web/ajax/device.php Normal file
View File

@ -0,0 +1,47 @@
<?php
//
// ZoneMinder web action file
// Copyright (C) 2019 ZoneMinder LLC
//
// 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.
//
// Device view actions
if ( !canEdit('Devices') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
if ( $action == 'device' ) {
if ( !empty($_REQUEST['command']) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) && $_REQUEST['did'] ) {
ZM\Warning('did value is: '.$_REQUEST['did']);
ZM\Warning('newDevice array value is: '.print_r($_REQUEST['newDevice'],true));
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']) );
}
}
ajaxResponse();
} else {
ajaxError('Unrecognised action '.$_REQUEST['action']);
} // end if action
?>

38
web/ajax/devices.php Normal file
View File

@ -0,0 +1,38 @@
<?php
//
// ZoneMinder web action file
// Copyright (C) 2019 ZoneMinder LLC
//
// 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.
//
// Device view actions
if ( !canEdit('Devices') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
if ( $action == 'delete' ) {
if ( isset($_REQUEST['markDids']) ) {
foreach( $_REQUEST['markDids'] as $markDid ) {
dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid));
}
}
ajaxResponse();
} else {
ajaxError('Unrecognised action '.$_REQUEST['action']);
}
?>

View File

@ -1,28 +1,107 @@
<?php
ini_set('display_errors', '0');
if ( empty($_REQUEST['eids']) ) {
ajaxError('No event id(s) supplied');
$message = '';
$data = array();
//
// INITIALIZE AND CHECK SANITY
//
if ( !canEdit('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
if ( empty($_REQUEST['task']) ) {
$message = 'Must specify a task';
} else {
$task = $_REQUEST['task'];
}
if ( canView('Events') ) {
} // end if canView('Events')
if ( empty($_REQUEST['eids']) ) {
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != "query" ) $message = 'No event id(s) supplied';
} else {
$eids = $_REQUEST['eids'];
}
if ( canEdit('Events') ) {
$message = array();
if ( $message ) {
ajaxError($message);
return;
}
foreach ( $_REQUEST['eids'] as $eid ) {
// Search contains a user entered string to search on
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
switch ( $_REQUEST['action'] ) {
// Advanced search contains an array of "column name" => "search text" pairs
// Bootstrap table sends json_ecoded array, which we must decode
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
// Sort specifies the name of the column to sort on
$sort = 'StartTime';
if ( isset($_REQUEST['sort']) ) {
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
} else {
$sort = $_REQUEST['sort'];
//if ( $sort == 'DateTime' ) $sort = 'TimeKey';
}
}
// Offset specifies the starting row to return, used for pagination
$offset = 0;
if ( isset($_REQUEST['offset']) ) {
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
} else {
$offset = $_REQUEST['offset'];
}
}
// Order specifies the sort direction, either asc or desc
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
// Limit specifies the number of rows to return
$limit = 100;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
} else {
$limit = $_REQUEST['limit'];
}
}
//
// MAIN LOOP
//
switch ( $task ) {
case 'archive' :
case 'unarchive' :
$archiveVal = ($_REQUEST['action'] == 'archive') ? 1 : 0;
foreach ( $eids as $eid ) archiveRequest($task, $eid);
break;
case 'delete' :
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
break;
case 'query' :
$data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit);
break;
default :
ZM\Fatal("Unrecognised task '$task'");
} // end switch task
ajaxResponse($data);
//
// FUNCTION DEFINITIONS
//
function archiveRequest($task, $eid) {
$archiveVal = ($task == 'archive') ? 1 : 0;
dbQuery(
'UPDATE Events SET Archived = ? WHERE Id = ?',
array($archiveVal, $eid)
);
break;
case 'delete' :
}
function deleteRequest($eid) {
$message = array();
$event = new ZM\Event($eid);
if ( !$event->Id() ) {
$message[] = array($eid=>'Event not found.');
@ -31,11 +110,105 @@ if ( canEdit('Events') ) {
} else {
$event->delete();
}
break;
} // end switch action
} // end foreach
ajaxResponse($message);
} // end if canEdit('Events')
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user '.$user['Username']);
return $message;
}
function queryRequest($search, $advsearch, $sort, $offset, $order, $limit) {
// Put server pagination code here
// The table we want our data from
$table = 'Events';
// The names of the dB columns in the log table we are interested in
$columns = array('Id', 'MonitorId', 'StorageId', 'Name', 'Cause', 'StartTime', 'EndTime', 'Length', 'Frames', 'AlarmFrames', 'TotScore', 'AvgScore', 'MaxScore', 'Archived', 'Emailed', 'Notes', 'DiskSpace');
// The names of columns shown in the log view that are NOT dB columns in the database
$col_alt = array('Monitor', 'Storage');
$col_str = implode(', ', $columns);
$data = array();
$query = array();
$query['values'] = array();
$likes = array();
$where = '';
// There are two search bars in the log view, normal and advanced
// Making an exuctive decision to ignore the normal search, when advanced search is in use
// Alternatively we could try to do both
if ( count($advsearch) ) {
foreach ( $advsearch as $col=>$text ) {
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
ZM\Error("'$col' is not a sortable column name");
continue;
}
$text = '%' .$text. '%';
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
} else if ( $search != '' ) {
$search = '%' .$search. '%';
foreach ( $columns as $col ) {
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $search);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
}
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
array_push($query['values'], $offset, $limit);
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
if ( $search != '' || count($advsearch) ) {
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
} else {
$data['total'] = $data['totalNotFiltered'];
}
$storage_areas = ZM\Storage::find();
$StorageById = array();
foreach ( $storage_areas as $S ) {
$StorageById[$S->Id()] = $S;
}
$monitor_names = ZM\Monitor::find();
$MonitorById = array();
foreach ( $monitor_names as $S ) {
$MonitorById[$S->Id()] = $S;
}
$rows = array();
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
$event = new ZM\Event($row['Id']);
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
$imgSrc = $event->getThumbnailSrc(array(),'&amp;');
$streamSrc = $event->getStreamSrc(array(
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
// Modify the row data as needed
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="' .validHtmlStr('Event ' .$event->Id()). '" style="width:' .validInt($event->ThumbnailWidth()). 'px;height:' .validInt($event->ThumbnailHeight()).'px;" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$row['Name'] = validHtmlStr($row['Name']);
$row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No');
$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No');
$row['Monitor'] = ( $row['MonitorId'] and isset($MonitorById[$row['MonitorId']]) ) ? $MonitorById[$row['MonitorId']]->Name() : '';
$row['Cause'] = validHtmlStr($row['Cause']);
$row['StartTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime']));
$row['EndTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime']));
$row['Length'] = gmdate('H:i:s', $row['Length'] );
$row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default';
$row['Notes'] = htmlspecialchars($row['Notes']);
$row['DiskSpace'] = human_filesize($row['DiskSpace']);
$rows[] = $row;
}
$data['rows'] = $rows;
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
return $data;
}
?>

View File

@ -1,111 +1,112 @@
<?php
ini_set('display_errors', '0');
global $Servers;
# Moved up here because it is used in several spots.
# These are the valid columns that you can filter on.
$filterFields = array('Component', 'ServerId', 'Pid', 'Level', 'File', 'Line');
if ( !canView('System') ) {
ajaxError('Insufficient permissions to view log entries');
return;
}
function buildLogQuery($action) {
global $filterFields;
// Only the query task is supported at the moment
if ( !isset($_REQUEST['task']) or $_REQUEST['task'] != 'query' ) {
ajaxError('Unrecognised action');
return;
}
// The table we want our data from
$table = 'Logs';
$minTime = isset($_REQUEST['minTime']) ? $_REQUEST['minTime'] : NULL;
$maxTime = isset($_REQUEST['maxTime']) ? $_REQUEST['maxTime'] : NULL;
// The names of the dB columns in the log table we are interested in
$columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line');
// The names of columns shown in the log view that are NOT dB columns in the database
$col_alt = array('DateTime', 'Server');
// Search contains a user entered string to search on
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
// Advanced search contains an array of "column name" => "search text" pairs
// Bootstrap table sends json_ecoded array, which we must decode
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
// Sort specifies the name of the column to sort on
$sort = 'TimeKey';
if ( isset($_REQUEST['sort']) ) {
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
} else {
$sort = $_REQUEST['sort'];
if ( $sort == 'DateTime' ) $sort = 'TimeKey';
}
}
// Offset specifies the starting row to return, used for pagination
$offset = 0;
if ( isset($_REQUEST['offset']) ) {
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
} else {
$offset = $_REQUEST['offset'];
}
}
// Order specifies the sort direction, either asc or desc
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
// Limit specifies the number of rows to return
$limit = 100;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_integer($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
ZM\Error('Invalid value for limit ' . $_REQUEST['limit']);
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
ZM\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' ) ) {
ZM\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();
$sql = $action.' FROM Logs';
$where = array();
$values = array();
if ( $minTime ) {
$where[] = 'TimeKey > ?';
$values[] = $minTime;
} elseif ( $maxTime ) {
$where[] = 'TimeKey < ?';
$values[] = $maxTime;
}
$col_str = implode(', ', $columns);
$data = array();
$query = array();
$query['values'] = array();
$likes = array();
$where = '';
// There are two search bars in the log view, normal and advanced
// Making an exuctive decision to ignore the normal search, when advanced search is in use
// Alternatively we could try to do both
if ( count($advsearch) ) {
foreach ( $filter as $field=>$value ) {
if ( !in_array($field, $filterFields) ) {
ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true));
foreach ( $advsearch as $col=>$text ) {
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
ZM\Error("'$col' is not a sortable column name");
continue;
}
if ( $field == 'Level' ) {
$where[] = $field.' <= ?';
$values[] = $value;
$text = '%' .$text. '%';
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
} else if ( $search != '' ) {
$search = '%' .$search. '%';
foreach ( $columns as $col ) {
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $search);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
}
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
array_push($query['values'], $offset, $limit);
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
if ( $search != '' || count($advsearch) ) {
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
} else {
$where[] = $field.' = ?';
$values[] = $value;
}
}
if ( count($where) )
$sql.= ' WHERE '.join(' AND ', $where);
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
return array('sql'=>$sql, 'values'=>$values);
} # function buildLogQuery($action)
switch ( $_REQUEST['task'] ) {
case 'create' :
{
// Silently ignore bogus requests
if ( !empty($_POST['level']) && !empty($_POST['message']) ) {
ZM\logInit(array('id'=>'web_js'));
$string = $_POST['message'];
$file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : '';
if ( !empty($_POST['line']) ) {
$line = validInt($_POST['line']);
} else {
$line = NULL;
$data['total'] = $data['totalNotFiltered'];
}
$levels = array_flip(ZM\Logger::$codes);
if ( !isset($levels[$_POST['level']]) ) {
ZM\Panic('Unexpected logger level '.$_POST['level']);
}
$level = $levels[$_POST['level']];
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
} else {
ZM\Error('Invalid log create: '.print_r($_POST, true));
}
ajaxResponse();
break;
}
case 'delete' :
{
if ( !canEdit('System') )
ajaxError('Insufficient permissions to delete log entries');
$query = buildLogQuery('DELETE');
$result = dbQuery($query['sql'], $query['values']);
ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount()));
}
case 'query' :
{
if ( !canView('System') )
ajaxError('Insufficient permissions to view log entries');
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
$query = buildLogQuery('SELECT *');
global $Servers;
if ( !$Servers )
$Servers = ZM\Server::find();
$servers_by_Id = array();
@ -114,353 +115,18 @@ switch ( $_REQUEST['task'] ) {
$servers_by_Id[$server->Id()] = $server;
}
$logs = array();
$options = array();
$rows = array();
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
$row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey']));
$row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : '';
// First strip out any html tags
// Second strip out all characters that are not ASCII 32-126 (yes, 126)
$row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message']));
$rows[] = $row;
}
$data['rows'] = $rows;
$data['logstate'] = logState();
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) {
ajaxResponse($data);
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
foreach ( $filterFields as $field ) {
if ( !isset($options[$field]) )
$options[$field] = array();
$value = $log[$field];
if ( $field == 'Level' ) {
if ( $value <= ZM\Logger::INFO )
$options[$field][$value] = ZM\Logger::$codes[$value];
else
$options[$field][$value] = 'DB'.$value;
} else if ( $field == 'ServerId' ) {
$options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : '';
} else if ( isset($log[$field]) ) {
$options[$field][$log[$field]] = $value;
}
}
$logs[] = $log;
} # end foreach log db row
foreach ( $options as $field => $values ) {
asort($options[$field]);
}
$available = count($logs);
ajaxResponse(array(
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG),
'total' => $total,
'available' => isset($available) ? $available : $total,
'logs' => $logs,
'state' => logState(),
'options' => $options,
));
break;
}
case 'export' :
{
if ( !canView('System') )
ajaxError('Insufficient permissions to export logs');
$minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL;
$maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL;
if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) {
$tempTime = $minTime;
$minTime = $maxTime;
$maxTime = $tempTime;
}
//$limit = isset($_POST['limit'])?$_POST['limit']:1000;
$filter = isset($_POST['filter'])?$_POST['filter']:array();
$sortField = 'TimeKey';
if ( isset($_POST['sortField']) ) {
if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) {
ZM\Error('Invalid sort field '.$_POST['sortField']);
} else {
$sortField = $_POST['sortField'];
}
}
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc';
global $Servers;
if ( !$Servers )
$Servers = ZM\Server::find();
$servers_by_Id = array();
# There is probably a better way to do this.
foreach ( $Servers as $server ) {
$servers_by_Id[$server->Id()] = $server;
}
$sql = 'SELECT * FROM Logs';
$where = array();
$values = array();
if ( $minTime ) {
if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) {
# This handles sub second precision
$minTime = strtotime($matches[1]).$matches[2];
} else {
$minTime = strtotime($minTime);
}
$where[] = 'TimeKey >= ?';
$values[] = $minTime;
}
if ( $maxTime ) {
if ( preg_match('/(.+)(\.\d+)/', $maxTime, $matches) ) {
$maxTime = strtotime($matches[1]).$matches[2];
} else {
$maxTime = strtotime($maxTime);
}
$where[] = 'TimeKey <= ?';
$values[] = $maxTime;
}
foreach ( $filter as $field=>$value ) {
if ( $value != '' ) {
if ( $field == 'Level' ) {
$where[] = $field.' <= ?';
$values[] = $value;
} else {
$where[] = $field.' = ?';
$values[] = $value;
}
}
}
if ( count($where) )
$sql.= ' WHERE '.join(' AND ', $where);
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder;
//$sql .= " limit ".dbEscape($limit);
$format = isset($_POST['format']) ? $_POST['format'] : 'text';
switch ( $format ) {
case 'text' :
$exportExt = 'txt';
break;
case 'tsv' :
$exportExt = 'tsv';
break;
case 'html' :
$exportExt = 'html';
break;
case 'xml' :
$exportExt = 'xml';
break;
default :
ZM\Fatal("Unrecognised log export format '$format'");
}
$exportKey = substr(md5(rand()), 0, 8);
$exportFile = 'zm-log.'.$exportExt;
// mkdir will generate a warning if it exists, but that is ok
error_reporting(0);
if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) {
ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\'');
}
$exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt;
ZM\Logger::Debug("Exporting to $exportPath");
if ( !($exportFP = fopen($exportPath, 'w')) )
ZM\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']);
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$logs[] = $log;
}
ZM\Logger::Debug(count($logs).' lines being exported by '.$sql.implode(',', $values));
switch( $format ) {
case 'text' :
{
foreach ( $logs as $log ) {
if ( $log['Line'] )
fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n",
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']);
else
fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n",
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']);
}
break;
}
case 'tsv' :
{
# This line doesn't need fprintf, it could use fwrite
fprintf($exportFP, join("\t",
translate('DateTime'),
translate('Component'),
translate('Server'),
translate('Pid'),
translate('Level'),
translate('Message'),
translate('File'),
translate('Line')
)."\n");
foreach ( $logs as $log ) {
fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n",
$log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
}
break;
}
case 'html' :
{
fwrite($exportFP,
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>'.translate('ZoneMinderLog').'</title>
<style type="text/css">
body, h3, p, table, td {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 11px;
}
table {
border-collapse: collapse;
width: 100%;
}
th {
font-weight: bold;
}
th, td {
border: 1px solid #888888;
padding: 1px 2px;
}
tr.log-fat td {
background-color:#ffcccc;
font-weight: bold;
font-style: italic;
}
tr.log-err td {
background-color:#ffcccc;
}
tr.log-war td {
background-color: #ffe4b5;
}
tr.log-dbg td {
color: #666666;
font-style: italic;
}
</style>
</head>
<body>
<h3>'.translate('ZoneMinderLog').'</h3>
<p>'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'</p>
<p>'.count($logs).' '.translate('Logs').'</p>
<table>
<tbody>
<tr><th>'.translate('DateTime').'</th><th>'.translate('Component').'</th><th>'.translate('Server').'</th><th>'.translate('Pid').'</th><th>'.translate('Level').'</th><th>'.translate('Message').'</th><th>'.translate('File').'</th><th>'.translate('Line').'</th></tr>
');
foreach ( $logs as $log ) {
$classLevel = $log['Level'];
if ( $classLevel < ZM\Logger::FATAL ) {
$classLevel = ZM\Logger::FATAL;
} else if ( $classLevel > ZM\Logger::DEBUG ) {
$classLevel = ZM\Logger::DEBUG;
}
$logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]);
fprintf($exportFP, ' <tr class="%s"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>
', $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
}
fwrite($exportFP,
' </tbody>
</table>
</body>
</html>');
break;
}
case 'xml' :
{
fwrite($exportFP,
'<?xml version="1.0" encoding="utf-8"?>
<logexport title="'.translate('ZoneMinderLog').'" date="'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'">
<selector>'.$_POST['selector'].'</selector>');
foreach ( $filter as $field=>$value )
if ( $value != '' )
fwrite( $exportFP,
' <filter>
<'.strtolower($field).'>'.htmlspecialchars($value).'</'.strtolower($field).'>
</filter>' );
fwrite( $exportFP,
'
<columns>
<column field="datetime">'.translate('DateTime').'</column>
<column field="component">'.translate('Component').'</column>
<column field="server">'.translate('Server').'</column>
<column field="pid">'.translate('Pid').'</column>
<column field="level">'.translate('Level').'</column>
<column field="message">'.translate('Message').'</column>
<column field="file">'.translate('File').'</column>
<column field="line">'.translate('Line').'</column>
</columns>
<logs count="'.count($logs).'">
' );
foreach ( $logs as $log ) {
fprintf( $exportFP,
' <log>
<datetime>%s</datetime>
<component>%s</component>
<server>%s</server>
<pid>%d</pid>
<level>%s</level>
<message><![CDATA[%s]]></message>
<file>%s</file>
<line>%d</line>
</log>
', $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] );
}
fwrite( $exportFP,
' </logs>
</logexport>' );
break;
}
$exportExt = 'xml';
break;
}
fclose( $exportFP );
ajaxResponse( array(
'key' => $exportKey,
'format' => $format,
) );
break;
}
case 'download' :
{
if ( !canView('System') )
ajaxError('Insufficient permissions to download logs');
if ( empty($_REQUEST['key']) )
ZM\Fatal('No log export key given');
$exportKey = $_REQUEST['key'];
if ( empty($_REQUEST['format']) )
ZM\Fatal('No log export format given');
$format = $_REQUEST['format'];
switch ( $format ) {
case 'text' :
$exportExt = 'txt';
break;
case 'tsv' :
$exportExt = 'tsv';
break;
case 'html' :
$exportExt = 'html';
break;
case 'xml' :
$exportExt = 'xml';
break;
default :
ZM\Fatal("Unrecognised log export format '$format'");
}
$exportFile = 'zm-log.'.$exportExt;
$exportPath = ZM_DIR_EXPORTS.'/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);
break;
}
} // end switch ( $_REQUEST['task'] )
ajaxError('Unrecognised action or insufficient permissions');
?>

View File

@ -8,7 +8,7 @@ if ( empty($_REQUEST['modal']) ) {
$modal = validJsStr($_REQUEST['modal']);
$data = array();
ZM\Logger::Debug("Including modals/$modal.php");
ZM\Debug("Including modals/$modal.php");
# Shouldn't be necessary but at the moment we have last .conf file contents
ob_start();
@$result = include('modals/'.$modal.'.php');

View File

@ -0,0 +1,55 @@
<?php
if ( !canEdit('Monitors') ) return;
$monitor = dbFetchOne('SELECT C.*,M.* FROM Monitors AS M INNER JOIN Controls AS C ON (M.ControlId = C.Id ) WHERE M.Id = ?', NULL, array( $_REQUEST['mid']) );
$labels = array();
foreach( dbFetchAll( 'SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array( $monitor['Id'] ) ) as $row ) {
$labels[$row['Preset']] = $row['Label'];
}
$presets = array();
for ( $i = 1; $i <= $monitor['NumPresets']; $i++ ) {
$presets[$i] = translate('Preset').' '.$i;
if ( !empty($labels[$i]) ) {
$presets[$i] .= ' ('.validHtmlStr($labels[$i]).')';
}
}
?>
<div class="modal" id="ctrlPresetModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo translate('SetPreset') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form name="contentForm" id="ctrlPresetForm" method="post" action="?">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="control"/>
<input type="hidden" name="mid" value="<?php echo $monitor['Id'] ?>"/>
<input type="hidden" name="action" value="control"/>
<input type="hidden" name="control" value="presetSet"/>
<input type="hidden" name="showControls" value="1"/>
<p><?php echo buildSelect('preset', $presets) ?></p>
<p>
<label for="newLabel"><?php echo translate('NewLabel') ?></label>
<input type="text" name="newLabel" id="newLabel" value=""/>
</p>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" id="cPresetSubmitModal" value="Save"><?php echo translate('Save') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,18 +1,20 @@
<?php
// This is the HTML representing the Delete confirmation modal on the Events page
// This is the HTML representing the Delete confirmation modal on the Events page and other pages
$delTextKey = isset($_REQUEST['key']) ? $_REQUEST['key'] : 'ConfirmDeleteEvents';
?>
<div id="deleteConfirm" class="modal fade" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Confirmation</h5>
<h5 class="modal-title"><?php echo translate('ConfirmDeleteTitle') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p><?php echo translate('ConfirmDeleteEvents') ?></p>
<p><?php echo translate($delTextKey) ?></p>
</div>
<div class="modal-footer">
<button id="delCancelBtn" type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>

View File

@ -0,0 +1,54 @@
<?php
if ( !canEdit( 'Devices' ) ) return;
if ( !empty($_REQUEST['did']) ) {
$newDevice = dbFetchOne( 'SELECT * FROM Devices WHERE Id = ?', NULL, array($_REQUEST['did']) );
} else {
$newDevice = array(
"Id" => "",
"Name" => "New Device",
"KeyString" => ""
);
}
?>
<div class="modal fade" id="deviceModal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel"><?php echo translate('Device')." - ".validHtmlStr($newDevice['Name']) ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="deviceModalForm" name="contentForm" method="get" action="?view=device&action=device">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="device"/>
<input type="hidden" name="action" value="device"/>
<input type="hidden" name="did" value="<?php echo $newDevice['Id'] ?>"/>
<table class="table-sm table-borderless" cellspacing="0">
<tbody>
<tr>
<th scope="row" class="text-right pr-2"><?php echo translate('Name') ?></th>
<td><input type="text" name="newDevice[Name]" value="<?php echo validHtmlStr($newDevice['Name']) ?>"/></td>
</tr>
<tr>
<th scope="row" class="text-right pr-2"><?php echo translate('KeyString') ?></th>
<td><input type="text" name="newDevice[KeyString]" value="<?php echo validHtmlStr($newDevice['KeyString']) ?>"/></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="submit" id="deviceSaveBtn" class="btn btn-primary"><?php echo translate('Save') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,119 @@
<?php
// This is the HTML representing the Download event modal on the Events page and other pages
function getDLEventHTML($eid, $eids) {
$result = '';
if ( !empty($eid) ) {
$result .= '<input type="hidden" name="id" value="' .validInt($eid). '"/>'.PHP_EOL;
$Event = new ZM\Event($eid);
if ( !$Event->Id() ) {
ZM\Error('Invalid event id');
$result .= '<div class="error">Invalid event id</div>'.PHP_EOL;
} else {
$result .= 'Downloading event ' . $Event->Id . '. Resulting file should be approximately ' . human_filesize( $Event->DiskSpace() ).PHP_EOL;
}
} else if ( !empty($eids) ) {
$total_size = 0;
foreach ( $eids as $eid ) {
if ( !validInt($eid) ) {
ZM\Warning("Invalid event id in eids[] $eid");
continue;
}
$Event = new ZM\Event($eid);
$total_size += $Event->DiskSpace();
$result .= '<input type="hidden" name="eids[]" value="' .validInt($eid). '"/>'.PHP_EOL;
}
unset($eid);
$result .= 'Downloading ' . count($eids) . ' events. Resulting file should be approximately ' . human_filesize($total_size).PHP_EOL;
} else {
$result .= '<div class="warning">There are no events found. Resulting download will be empty.</div>';
}
return $result;
}
if ( !canView('Events') ) {
$view = 'error';
return;
}
$eid = isset($_REQUEST['eid']) ? $_REQUEST['eid'] : '';
$eids = isset($_REQUEST['eids']) ? $_REQUEST['eids'] : array();
$generated = isset($_REQUEST['generated']) ? $_REQUEST['generated'] : '';
$total_size = 0;
if ( isset($_SESSION['montageReviewFilter']) and !$eids ) {
# Handles montageReview filter
$eventsSql = 'SELECT E.Id, E.DiskSpace FROM Events AS E WHERE 1';
$eventsSql .= $_SESSION['montageReviewFilter']['sql'];
$results = dbQuery($eventsSql);
while ( $event_row = dbFetchNext( $results ) ) {
array_push($eids, $event_row['Id']);
$total_size += $event_row['DiskSpace'];
}
if ( ! count($eids) ) {
ZM\Error("No events found for download using $eventsSql");
}
#session_start();
#unset($_SESSION['montageReviewFilter']);
#session_write_close();
#} else {
#Debug("NO montageReviewFilter");
}
$exportFormat = '';
if ( isset($_REQUEST['exportFormat']) ) {
if ( !in_array($_REQUEST['exportFormat'], array('zip', 'tar')) ) {
ZM\Error('Invalid exportFormat: '.$_REQUEST['exportFormat']);
} else {
$exportFormat = $_REQUEST['exportFormat'];
}
}
$focusWindow = true;
$connkey = isset($_REQUEST['connkey']) ? validInt($_REQUEST['connkey']) : generateConnKey();
?>
<div id="downloadModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo translate('Download') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form name="contentForm" id="downloadForm" method="post" action="?">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="connkey" value="<?php echo $connkey; ?>"/>
<input type="hidden" name="exportVideo" value="1"/>
<div>
<?php echo getDLEventHTML($eid, $eids) ?>
</div>
<div class="font-weight-bold py-2">
<span class="pr-3"><?php echo translate('ExportFormat') ?></span>
<input type="radio" id="exportFormatTar" name="exportFormat" value="tar"/>
<label for="exportFormatTar"><?php echo translate('ExportFormatTar') ?></label>
<input type="radio" id="exportFormatZip" name="exportFormat" value="zip" checked="checked"/>
<label for="exportFormatZip"><?php echo translate('ExportFormatZip') ?></label>
</div>
<button type="button" id="exportButton" name="exportButton" value="GenerateDownload"><?php echo translate('GenerateDownload') ?></button>
</form>
</div>
<h2 id="exportProgress" class="text-warning invisible">
<span class="spinner-grow" role="status" aria-hidden="true"></span>
Exporting...
</h2>
<h3><a id="downloadLink" href="#"></a></h3>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

135
web/ajax/modals/group.php Normal file
View File

@ -0,0 +1,135 @@
<?php
// This is the HTML representing the Group modal accessed from the Groups (plural) view
//
// DEFINE SUPPORTING FUNCTIONS
//
function get_Id( $G ) {
return $G->Id();
}
function get_children($Group) {
global $children;
$kids = array();
if ( isset( $children[$Group->Id()] ) ) {
$kids += array_map('get_Id', $children[$Group->Id()]);
foreach ( $children[$Group->Id()] as $G ) {
foreach ( get_children($G) as $id ) {
$kids[] = $id;
}
}
}
return $kids;
}
function parentGrpSelect($newGroup) {
$Groups = array();
foreach ( ZM\Group::find() as $Group ) {
$Groups[$Group->Id()] = $Group;
}
# This array is indexed by parent_id
$children = array();
foreach ( $Groups as $id=>$Group ) {
if ( $Group->ParentId() != null ) {
if ( ! isset( $children[$Group->ParentId()] ) )
$children[$Group->ParentId()] = array();
$children[$Group->ParentId()][] = $Group;
}
}
$kids = get_children($newGroup);
if ( $newGroup->Id() )
$kids[] = $newGroup->Id();
$sql = 'SELECT Id,Name FROM `Groups`'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids)).')' : '').' ORDER BY Name';
$options = array(''=>'None');
foreach ( dbFetchAll($sql, null, $kids) as $option ) {
$options[$option['Id']] = str_repeat('&nbsp;&nbsp;', $Groups[$option['Id']]->depth()) . $option['Name'];
}
return htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('data-on-change'=>'configModalBtns'));
}
function monitorList($newGroup) {
$result = '';
$monitors = dbFetchAll('SELECT Id,Name FROM Monitors ORDER BY Sequence ASC');
$monitorIds = $newGroup->MonitorIds();
foreach ( $monitors as $monitor ) {
if ( visibleMonitor($monitor['Id']) ) {
$result .= '<option value="' .$monitor['Id']. '"' .( in_array( $monitor['Id'], $monitorIds ) ? ' selected="selected"' : ''). '>' .validHtmlStr($monitor['Name']). '</option>'.PHP_EOL;
}
}
return $result;
}
//
// INITIAL SANITY CHECKS
//
if ( !canEdit('Groups') ) {
$view = 'error';
return;
}
if ( !empty($_REQUEST['gid']) ) {
$newGroup = new ZM\Group($_REQUEST['gid']);
} else {
$newGroup = new ZM\Group();
}
//
// BEGIN HTML
//
?>
<div id="groupModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo translate('Group').($newGroup->Name() ? ' - ' .validHtmlStr($newGroup->Name()) : '') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="groupForm" name="groupForm" method="post" action="?view=group&action=save">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="group"/>
<input type="hidden" name="gid" value="<?php echo $newGroup->Id() ?>"/>
<table id="groupModalTable" class="table-sm table-borderless">
<tbody>
<tr>
<th class="text-right pr-3" scope="row"><?php echo translate('Name') ?></th>
<td><input type="text" name="newGroup[Name]" value="<?php echo validHtmlStr($newGroup->Name()) ?>" data-on-input="configModalBtns"/></td>
</tr>
<tr>
<th class="text-right pr-3" scope="row"><?php echo translate('ParentGroup') ?></th>
<td><?php echo parentGrpSelect($newGroup) ?></td>
</tr>
<tr>
<th class="text-right pr-3" scope="row"><?php echo translate('Monitor') ?></th>
<td>
<select name="newGroup[MonitorIds][]" class="chosen" multiple="multiple" data-on-change="configModalBtns">
<?php echo monitorList($newGroup) ?>
</select>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" name="action" id="grpModalSaveBtn" value="save"<?php $newGroup->Id() ? '' : ' disabled="disabled"'?>><?php echo translate('Save') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
</div>
</form>
</div>
</div>
</div>

View File

@ -30,14 +30,73 @@ global $CLANG;
</div>
<div class="modal-body">
<p><?php echo sprintf( $CLANG['CurrentLogin'], $user['Username'] ) ?></p>
<?php if ( canView('System') ) { ?>
<p>Other logged in users:<br/>
<table class="table table-striped">
<thead>
<tr>
<th class="text-left"><?php echo translate('Username') ?></th>
<th class="text-left"><?php echo translate('IPAddress') ?></th>
<th class="text-left"><?php echo translate('Last Access') ?></th>
</tr>
</thead>
<tbody>
<?php
require_once('includes/User.php');
$result = dbQuery('SELECT * FROM Sessions ORDER BY access DESC');
if ( ! $result ) return;
$current_session = $_SESSION;
zm_session_start();
$user_cache = array();
while ( $row = $result->fetch(PDO::FETCH_ASSOC) ) {
$_SESSION = array();
if ( ! session_decode($row['data']) ) {
ZM\Warning('Failed to decode ' . $row['data']);
continue;
}
ZM\Debug(print_r($_SESSION, true));
if ( isset($_SESSION['last_time']) ) {
# This is a dead session
continue;
}
if ( !isset($_SESSION['username']) ) {
# Not logged in
continue;
}
if ( isset($user_cache[$_SESSION['username']]) ) {
$user = $user_cache[$_SESSION['username']];
} else {
$user = ZM\User::find_one(array('Username'=>$_SESSION['username']));
if ( ! $user ) {
ZM\Debug('User not found for ' . $_SESSION['username']);
continue;
}
$user_cache[$_SESSION['username']] = $user;
}
echo '
<tr>
<td>'.$user->Username().'</td>
<td>'.$_SESSION['remoteAddr'].'</td>
<td>'.strftime(STRF_FMT_DATETIME_SHORTER, $row['access']).'</td>
</tr>
';
} # end while
session_abort();
$_SESSION = $current_session;
?>
</tbody>
</table>
<?php } # end if canView(System) ?>
</div>
<div class="modal-footer">
<form name="logoutForm" id="logoutForm" method="post" action="?">
<form name="logoutForm" id="logoutForm" method="post" action="?view=logout">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="logout"/>
<button type="submit" name="action" value="logout"><?php echo translate('Logout') ?></button>
<?php if ( ZM_USER_SELF_EDIT ) echo '<button type="submit" name="action" value="config">'.translate('Config').'</button>'.PHP_EOL; ?>
<button type="button" data-dismiss="modal"><?php echo translate('Cancel') ?></button>

View File

@ -1,27 +1,6 @@
<?php
//
// ZoneMinder web settings 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 ( !canView('Control') ) return;
if ( !canView('Control') ) {
$view = 'error';
return;
}
$monitor = ZM\Monitor::find_one(array('Id'=>$_REQUEST['mid']));
$zmuCommand = getZmuCommand(' -m '.escapeshellarg($_REQUEST['mid']).' -B -C -H -O');
@ -35,17 +14,22 @@ if ( $zmuOutput ) {
$monitor->Colour($colour);
}
$focusWindow = true;
xhtmlHeaders(__FILE__, validHtmlStr($monitor->Name()).' - '.translate('Settings'));
?>
<body>
<div id="page">
<div id="header">
<h2><?php echo validHtmlStr($monitor->Name()) ?> - <?php echo translate('Settings') ?></h2>
<div class="modal" id="settingsModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo validHtmlStr($monitor->Name()) ?> - <?php echo translate('Settings') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="content">
<form name="contentForm" id="contentForm" method="post" action="?">
<div class="modal-body">
<form name="contentForm" id="settingsForm" method="post" action="?">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="action" value="settings"/>
<input type="hidden" name="mid" value="<?php echo validInt($_REQUEST['mid']) ?>"/>
@ -69,11 +53,12 @@ xhtmlHeaders(__FILE__, validHtmlStr($monitor->Name()).' - '.translate('Settings'
</tr>
</tbody>
</table>
<div id="contentButtons">
<button type="submit" value="Save"<?php echo canView('Control') ? '' : ' disabled="disabled"' ?>><?php echo translate('Save') ?></button>
<button type="button" value="Close" data-on-click="closeWindow"/><?php echo translate('Close') ?></button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" id="settingsSubmitModal" value="Save"<?php echo canView('Control') ? '' : ' disabled="disabled"' ?>><?php echo translate('Save') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
<?php xhtmlFooter() ?>
</div>

View File

@ -0,0 +1,47 @@
<?php
$error='';
if ( !canEdit('System') ) {
$error = 'Insufficient permissions';
} else if ( !defined('ZM_PATH_SHUTDOWN') or ZM_PATH_SHUTDOWN == '' ) {
$error = 'ZM_PATH_SHUTDOWN is not defined. This is normally configured in /etc/zm/conf.d/01-system-paths.conf';
} else if ( !file_exists(ZM_PATH_SHUTDOWN) ) {
$error = 'Path does not exist for ZM_PATH_SHUTDOWN. Current value is '.ZM_PATH_SHUTDOWN;
}
if ( $error ) {
ZM\Error($error);
return;
}
?>
<div class="modal" id="shutdownModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo translate('Shutdown').' '.translate('Restart') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p class="warning"><h2>Warning</h2>
This command will either shutdown or restart all ZoneMinder Servers<br/>
</p>
<p>
<input type="radio" name="when" value="now" id="whennow"/><label for="whennow">Now</label>
<input type="radio" name="when" value="1min" id="when1min" checked="checked"/><label for="when1min">1 Minute</label>
</p>
<p id="respText" class="invisible">
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-command="cancel" data-on-click-this="manageShutdownBtns" id="cancelBtn" disabled><?php echo translate('Cancel') ?></button>
<button type="button" class="btn btn-primary" data-command="restart" data-on-click-this="manageShutdownBtns"><?php echo translate('Restart') ?></button>
<button type="button" class="btn btn-primary" data-command="shutdown" data-on-click-this="manageShutdownBtns"><?php echo translate('Shutdown') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Close') ?></button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,132 +0,0 @@
<?php
global $Servers;
if ( !canView('System') ) {
ajaxError('Insufficient permissions to view log entries');
return;
}
// Only the query task is supported at the moment
if ( !isset($_REQUEST['task']) or $_REQUEST['task'] != 'query' ) {
ajaxError('Unrecognised action');
return;
}
// The table we want our data from
$table = 'Logs';
// The names of the dB columns in the log table we are interested in
$columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line');
// The names of columns shown in the log view that are NOT dB columns in the database
$col_alt = array('DateTime', 'Server');
// Search contains a user entered string to search on
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
// Advanced search contains an array of "column name" => "search text" pairs
// Bootstrap table sends json_ecoded array, which we must decode
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
// Sort specifies the name of the column to sort on
$sort = 'TimeKey';
if ( isset($_REQUEST['sort']) ) {
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
} else {
$sort = $_REQUEST['sort'];
if ( $sort == 'DateTime' ) $sort = 'TimeKey';
}
}
// Offset specifies the starting row to return, used for pagination
$offset = 0;
if ( isset($_REQUEST['offset']) ) {
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
} else {
$offset = $_REQUEST['offset'];
}
}
// Order specifies the sort direction, either asc or desc
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
// Limit specifies the number of rows to return
$limit = 100;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
} else {
$limit = $_REQUEST['limit'];
}
}
$col_str = implode(', ', $columns);
$data = array();
$query = array();
$query['values'] = array();
$likes = array();
$where = '';
// There are two search bars in the log view, normal and advanced
// Making an exuctive decision to ignore the normal search, when advanced search is in use
// Alternatively we could try to do both
if ( count($advsearch) ) {
foreach ( $advsearch as $col=>$text ) {
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
ZM\Error("'$col' is not a sortable column name");
continue;
}
$text = '%' .$text. '%';
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
} else if ( $search != '' ) {
$search = '%' .$search. '%';
foreach ( $columns as $col ) {
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $search);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
}
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
array_push($query['values'], $offset, $limit);
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
if ( $search != '' || count($advsearch) ) {
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
} else {
$data['total'] = $data['totalNotFiltered'];
}
if ( !$Servers )
$Servers = ZM\Server::find();
$servers_by_Id = array();
# There is probably a better way to do this.
foreach ( $Servers as $server ) {
$servers_by_Id[$server->Id()] = $server;
}
$rows = array();
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
$row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey']));
$row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : '';
// First strip out any html tags
// Second strip out all characters that are not ASCII 32-126 (yes, 126)
$row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message']));
$rows[] = $row;
}
$data['rows'] = $rows;
$data['logstate'] = logState();
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
ajaxResponse($data);

58
web/ajax/shutdown.php Normal file
View File

@ -0,0 +1,58 @@
<?php
//
// ZoneMinder web action file
// Copyright (C) 2019 ZoneMinder LLC
//
// 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.
//
$message = '';
if ( !canEdit('System') ) {
$message = 'Need System permissions to shutdown server';
} else if ( !isset($_REQUEST['command']) ) {
$message = 'A command is required. Cannot continue';
}
if ( $message ) {
ZM\Warning($message);
ajaxError($message);
return;
}
$data = array();
$when = isset($_REQUEST['when']) and $_REQUEST['when'] == 'now' ? 'now' : '+1';
$command = $_REQUEST['command'];
if ( $command == 'shutdown' ) {
exec('sudo -n '.ZM_PATH_SHUTDOWN." -P $when 2>&1", $data['output'], $data['rc']);
#exec('sudo -n /bin/systemctl poweroff -i 2>&1', $data['output'], $data['rc']);
ZM\Debug('Shutdown output ' .$data['rc'].' '.implode("\n",$data['output']));
#ZM\Debug("Shutdown output " . shell_exec('/bin/systemctl poweroff -i 2>&1'));
} else if ( $command == 'restart' ) {
$data['output'] = array();
exec('sudo -n '.ZM_PATH_SHUTDOWN." -r $when 2>&1", $data['output'], $data['rc']);
#exec('sudo -n /bin/systemctl reboot -i 2>&1', $data['output'], $data['rc']);
ZM\Debug("Shutdown output " . implode("\n",$data['output']));
} else if ( $command == 'cancel' ) {
$data['output'] = array();
exec('sudo '.ZM_PATH_SHUTDOWN.' -c 2>&1', $data['output'], $data['rc']);
} else {
ajaxError('Unknwn command:'.$command);
return;
}
ajaxResponse($data);
return;
?>

View File

@ -336,7 +336,7 @@ function collectData() {
}
}
}
#ZM\Logger::Debug(print_r($data, true));
#ZM\Debug(print_r($data, true));
return $data;
}
@ -411,7 +411,10 @@ function getNearEvents() {
global $user, $sortColumn, $sortOrder;
$eventId = $_REQUEST['id'];
$NearEvents = array( 'EventId'=>$eventId );
$event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, array($eventId));
if ( !$event ) return $NearEvents;
if ( isset($_REQUEST['filter']) )
parseFilter( $_REQUEST['filter'] );
@ -424,10 +427,10 @@ function getNearEvents() {
# When listing, it may make sense to list them in descending order. But when viewing Prev should timewise earlier and Next should be after.
if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartTime' ) {
$sortOrder = 'asc';
$sortOrder = 'ASC';
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id<'.$event['Id'] . " ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc');
$sql = 'SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn.' '.($sortOrder=='ASC'?'<=':'>=').' \''.$event[$_REQUEST['sort_field']].'\''.$_REQUEST['filter']['sql'].$midSql.' AND E.Id<'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'DESC':'ASC');
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id DESC';
@ -436,7 +439,7 @@ function getNearEvents() {
$result = dbQuery($sql);
$prevEvent = dbFetchNext($result);
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id>'.$event['Id'] . " ORDER BY $sortColumn $sortOrder";
$sql = 'SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn .' '.($sortOrder=='ASC'?'>=':'<=').' \''.$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id>'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'ASC':'DESC');
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id ASC';
@ -445,22 +448,21 @@ function getNearEvents() {
$result = dbQuery($sql);
$nextEvent = dbFetchNext($result);
$result = array( 'EventId'=>$eventId );
if ( $prevEvent ) {
$result['PrevEventId'] = $prevEvent['Id'];
$result['PrevEventStartTime'] = $prevEvent['StartTime'];
$result['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']);
$NearEvents['PrevEventId'] = $prevEvent['Id'];
$NearEvents['PrevEventStartTime'] = $prevEvent['StartTime'];
$NearEvents['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']);
} else {
$result['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0;
$NearEvents['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0;
}
if ( $nextEvent ) {
$result['NextEventId'] = $nextEvent['Id'];
$result['NextEventStartTime'] = $nextEvent['StartTime'];
$result['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']);
$NearEvents['NextEventId'] = $nextEvent['Id'];
$NearEvents['NextEventStartTime'] = $nextEvent['StartTime'];
$NearEvents['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']);
} else {
$result['NextEventId'] = $result['NextEventStartTime'] = $result['NextEventDefVideoPath'] = 0;
$NearEvents['NextEventId'] = $NearEvents['NextEventStartTime'] = $NearEvents['NextEventDefVideoPath'] = 0;
}
return $result;
return $NearEvents;
}
?>

View File

@ -31,27 +31,29 @@ if ( sem_acquire($semaphore,1) !== false ) {
switch ( $_REQUEST['command'] ) {
case CMD_VARPLAY :
ZM\Logger::Debug('Varplaying to '.$_REQUEST['rate']);
ZM\Debug('Varplaying to '.$_REQUEST['rate']);
$msg = pack('lcn', MSG_CMD, $_REQUEST['command'], $_REQUEST['rate']+32768);
break;
case CMD_ZOOMIN :
ZM\Logger::Debug('Zooming to '.$_REQUEST['x'].','.$_REQUEST['y']);
ZM\Debug('Zooming to '.$_REQUEST['x'].','.$_REQUEST['y']);
$msg = pack('lcnn', MSG_CMD, $_REQUEST['command'], $_REQUEST['x'], $_REQUEST['y']);
break;
case CMD_PAN :
ZM\Logger::Debug('Panning to '.$_REQUEST['x'].','.$_REQUEST['y']);
ZM\Debug('Panning to '.$_REQUEST['x'].','.$_REQUEST['y']);
$msg = pack('lcnn', MSG_CMD, $_REQUEST['command'], $_REQUEST['x'], $_REQUEST['y']);
break;
case CMD_SCALE :
ZM\Logger::Debug('Scaling to '.$_REQUEST['scale']);
ZM\Debug('Scaling to '.$_REQUEST['scale']);
$msg = pack('lcn', MSG_CMD, $_REQUEST['command'], $_REQUEST['scale']);
break;
case CMD_SEEK :
ZM\Logger::Debug('Seeking to '.$_REQUEST['offset']);
$msg = pack('lcN', MSG_CMD, $_REQUEST['command'], $_REQUEST['offset']);
# Pack int two 32 bit integers instead of trying to deal with floats
$msg = pack('lcNN', MSG_CMD, $_REQUEST['command'],
intval($_REQUEST['offset']),
1000000*( $_REQUEST['offset']-intval($_REQUEST['offset'])));
break;
default :
ZM\Logger::Debug('Sending command ' . $_REQUEST['command']);
ZM\Debug('Sending command ' . $_REQUEST['command']);
$msg = pack('lc', MSG_CMD, $_REQUEST['command']);
break;
}
@ -65,7 +67,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
// WHY? We will just send another one...
// ANSWER: Because otherwise we get a log of errors logged
//ZM\Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' );
//ZM\Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' );
usleep(1000);
}
@ -124,20 +126,20 @@ if ( sem_acquire($semaphore,1) !== false ) {
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
$data['auth'] = $auth_hash;
ZM\Logger::Debug("including nw auth hash " . $data['auth']);
ZM\Debug("including nw auth hash " . $data['auth']);
} else {
ZM\Logger::Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash);
ZM\Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash);
}
}
ajaxResponse(array('status'=>$data));
break;
case MSG_DATA_EVENT :
if ( version_compare( phpversion(), '5.6.0', '<') ) {
ZM\Logger::Debug('Using old unpack methods to handle 64bit event id');
$data = unpack('ltype/ieventlow/ieventhigh/iprogress/irate/izoom/Cpaused', $msg);
ZM\Debug('Using old unpack methods to handle 64bit event id');
$data = unpack('ltype/ieventlow/ieventhigh/dduration/dprogress/irate/izoom/Cpaused', $msg);
$data['event'] = $data['eventhigh'] << 32 | $data['eventlow'];
} else {
$data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg);
$data = unpack('ltype/Qevent/dduration/dprogress/irate/izoom/Cpaused', $msg);
}
$data['rate'] /= RATE_BASE;
$data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
@ -154,7 +156,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
}
sem_release($semaphore);
} else {
ZM\Logger::Debug('Couldn\'t get semaphore');
ZM\Debug('Couldn\'t get semaphore');
ajaxResponse(array());
}

View File

@ -105,7 +105,7 @@ class AppController extends Controller {
if ( ! is_session_started() )
zm_session_start();
ZM\Logger::Debug(print_r($_SESSION, true));
ZM\Debug(print_r($_SESSION, true));
$user = userFromSession();
session_write_close();
}

View File

@ -50,10 +50,10 @@ class HostController extends AppController {
$cred_depr = [];
if ( $username && $password ) {
ZM\Logger::Debug('Username and password provided, generating access and refresh tokens');
ZM\Debug('Username and password provided, generating access and refresh tokens');
$cred = $this->_getCredentials(true, '', $username); // generate refresh
} else {
ZM\Logger::Debug('Only generating access token');
ZM\Debug('Only generating access token');
$cred = $this->_getCredentials(false, $token); // don't generate refresh
}
@ -72,7 +72,7 @@ class HostController extends AppController {
$login_array['credentials'] = $cred_depr[0];
$login_array['append_password'] = $cred_depr[1];
} else {
ZM\Logger::Debug('Legacy Auth is disabled, not generating auth= credentials');
ZM\Debug('Legacy Auth is disabled, not generating auth= credentials');
}
$login_array['version'] = $ver[0];
@ -203,7 +203,7 @@ class HostController extends AppController {
if ( $mid ) {
// Get disk usage for $mid
ZM\Logger::Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
ZM\Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
$usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'");
} else {
$monitors = $this->Monitor->find('all', array(

View File

@ -178,7 +178,7 @@ class MonitorsController extends AppController {
)
) {
if ( !defined('ZM_SERVER_ID')) {
ZM\Logger::Debug("Not defined ZM_SERVER_ID");
ZM\Debug("Not defined ZM_SERVER_ID");
}
$this->daemonControl($this->Monitor->id, 'start');
}
@ -386,7 +386,7 @@ class MonitorsController extends AppController {
}
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
ZM\Logger::Debug("Command $shellcmd");
ZM\Debug("Command $shellcmd");
$status = exec($shellcmd);
$status_text .= $status."\n";
}

View File

@ -133,10 +133,10 @@ class Event extends AppModel {
if ( file_exists($this->Path().'/'.$event['DefaultVideo']) ) {
return 1;
} else {
ZM\Logger::Debug('File does not exist at ' . $this->Path().'/'.$event['DefaultVideo'] );
ZM\Debug('File does not exist at ' . $this->Path().'/'.$event['DefaultVideo'] );
}
} else {
ZM\Logger::Debug('No DefaultVideo in Event' . $this->Event);
ZM\Debug('No DefaultVideo in Event' . $this->Event);
return 0;
}
} // end function fileExists($event)

View File

@ -1,7 +1,7 @@
/**
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
*
* @version v1.18.0
* @version v1.17.1
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT

View File

@ -1,7 +1,7 @@
/**
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
*
* @version v1.18.0
* @version v1.17.1
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT

View File

@ -291,7 +291,7 @@ class Event extends ZM_Object {
# The idea here is that we don't really want to use the analysis jpeg as the thumbnail.
# The snapshot image will be generated during capturing
if ( file_exists($this->Path().'/snapshot.jpg') ) {
Logger::Debug("snapshot exists");
Debug("snapshot exists");
$frame = null;
} else {
# Load the frame with the highest score to use as a thumbnail
@ -396,14 +396,14 @@ class Event extends ZM_Object {
if ( $frame and !is_array($frame) ) {
# Must be an Id
Logger::Debug("Assuming that $frame is an Id");
Debug("Assuming that $frame is an Id");
$frame = array('FrameId'=>$frame, 'Type'=>'', 'Delta'=>0);
}
if ( ( !$frame ) and file_exists($eventPath.'/snapshot.jpg') ) {
# No frame specified, so look for a snapshot to use
$captImage = 'snapshot.jpg';
Logger::Debug('Frame not specified, using snapshot');
Debug('Frame not specified, using snapshot');
$frame = array('FrameId'=>'snapshot', 'Type'=>'', 'Delta'=>0);
} else {
$captImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId']);
@ -421,11 +421,11 @@ class Event extends ZM_Object {
#$command ='ffmpeg -v 0 -i '.$videoPath.' -vf "select=gte(n\\,'.$frame['FrameId'].'),setpts=PTS-STARTPTS" '.$eventPath.'/'.$captImage;
$command ='ffmpeg -ss '. $frame['Delta'] .' -i '.$videoPath.' -frames:v 1 '.$eventPath.'/'.$captImage;
Logger::Debug('Running '.$command);
Debug('Running '.$command);
$output = array();
$retval = 0;
exec($command, $output, $retval);
Logger::Debug("Retval: $retval, output: " . implode("\n", $output));
Debug("Retval: $retval, output: " . implode("\n", $output));
} else {
Error('Can\'t create frame images from video because there is no video file for event '.$Event->Id().' at ' .$Event->Path());
}
@ -529,7 +529,7 @@ class Event extends ZM_Object {
return;
}
}
Logger::Debug("sending command to $url");
Debug("sending command to $url");
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
@ -545,7 +545,7 @@ class Event extends ZM_Object {
Error("Error restarting zmc using $url");
}
$event_data = json_decode($result,true);
Logger::Debug(print_r($event_data['event']['Event'],1));
Debug(print_r($event_data['event']['Event'],1));
return $event_data['event']['Event']['fileExists'];
} catch ( Exception $e ) {
Error("Except $e thrown trying to get event data");
@ -577,7 +577,7 @@ class Event extends ZM_Object {
return;
}
}
Logger::Debug("sending command to $url");
Debug("sending command to $url");
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
@ -593,7 +593,7 @@ class Event extends ZM_Object {
Error("Error restarting zmc using $url");
}
$event_data = json_decode($result,true);
Logger::Debug(print_r($event_data['event']['Event'], 1));
Debug(print_r($event_data['event']['Event'], 1));
return $event_data['event']['Event']['fileSize'];
} catch ( Exception $e ) {
Error("Except $e thrown trying to get event data");

View File

@ -16,6 +16,7 @@ class Filter extends ZM_Object {
'EmailBody' => '',
'AutoDelete' => 0,
'AutoArchive' => 0,
'AutoUnarchive' => 0,
'AutoVideo' => 0,
'AutoUpload' => 0,
'AutoMessage' => 0,
@ -55,6 +56,9 @@ class Filter extends ZM_Object {
foreach ( $this->FilterTerms() as $term ) {
$this->_querystring .= $term->querystring($separator);
} # end foreach term
if ( $this->Id() ) {
$this->_querystring .= $separator.'filter[Id]='.$this->Id();
}
}
return $this->_querystring;
}
@ -226,7 +230,7 @@ class Filter extends ZM_Object {
if ( (!defined('ZM_SERVER_ID')) or (!$Server->Id()) or (ZM_SERVER_ID==$Server->Id()) ) {
# Local
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
Debug("Controlling filter locally $command for server ".$Server->Id());
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
} else {
# Remote case
@ -243,7 +247,7 @@ class Filter extends ZM_Object {
}
}
$url .= '&view=filter&object=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
Logger::Debug("sending command to $url");
Debug("sending command to $url");
$data = array();
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
require_once( 'includes/csrf/csrf-magic.php' );
@ -274,7 +278,7 @@ class Filter extends ZM_Object {
public function execute() {
$command = ZM_PATH_BIN.'/zmfilter.pl --filter_id='.escapeshellarg($this->Id());
$result = exec($command, $output, $status);
Logger::Debug("$command status:$status output:".implode("\n", $output));
Debug("$command status:$status output:".implode("\n", $output));
return $status;
}

View File

@ -329,14 +329,14 @@ class FilterTerm {
public function test($event=null) {
if ( !isset($event) ) {
# Is a Pre Condition
Logger::Debug("Testing " . $this->attr);
Debug("Testing " . $this->attr);
if ( $this->attr == 'DiskPercent' ) {
# The logic on this is really ugly. We are going to treat it as an OR
foreach ( $this->filter->get_StorageAreas() as $storage ) {
$string_to_eval = 'return $storage->disk_usage_percent() '.$this->op.' '.$this->val.';';
try {
$ret = eval($string_to_eval);
Logger::Debug("Evalled $string_to_eval = $ret");
Debug("Evalled $string_to_eval = $ret");
if ( $ret )
return true;
} catch ( Throwable $t ) {
@ -348,7 +348,7 @@ class FilterTerm {
$string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';';
try {
$ret = eval($string_to_eval);
Logger::Debug("Evaled $string_to_eval = $ret");
Debug("Evaled $string_to_eval = $ret");
if ( $ret )
return true;
} catch ( Throwable $t ) {
@ -374,7 +374,7 @@ class FilterTerm {
$string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';';
try {
$ret = eval($string_to_eval);
Logger::Debug("Evalled $string_to_eval = $ret");
Debug("Evalled $string_to_eval = $ret");
if ( $ret )
return true;
} catch ( Throwable $t ) {
@ -385,7 +385,7 @@ class FilterTerm {
$string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';';
try {
$ret = eval($string_to_eval);
Logger::Debug("Evalled $string_to_eval = $ret");
Debug("Evalled $string_to_eval = $ret");
if ( $ret )
return true;
} catch ( Throwable $t ) {

View File

@ -130,6 +130,8 @@ class Monitor extends ZM_Object {
'Refresh' => null,
'DefaultCodec' => 'auto',
'GroupIds' => array('default'=>array(), 'do_not_update'=>1),
'Latitude' => null,
'Longitude' => null,
);
private $status_fields = array(
'Status' => null,
@ -180,7 +182,7 @@ class Monitor extends ZM_Object {
FROM `Monitor_Status` WHERE `MonitorId`=?';
$row = dbFetchOne($sql, NULL, array($this->{'Id'}));
if ( !$row ) {
Error('Unable to load Monitor record for Id='.$this->{'Id'});
Warning('Unable to load Monitor status record for Id='.$this->{'Id'}.' using '.$sql);
foreach ( $this->status_fields as $k => $v ) {
$this->{$k} = $v;
}
@ -292,7 +294,7 @@ class Monitor extends ZM_Object {
}
function zmcControl( $mode=false ) {
if ( ! $this->{'Id'} ) {
if ( !(property_exists($this,'Id') and $this->{'Id'}) ) {
Warning('Attempt to control a monitor with no Id');
return;
}
@ -328,7 +330,7 @@ class Monitor extends ZM_Object {
return;
}
}
Logger::Debug('sending command to '.$url);
Debug('sending command to '.$url);
$context = stream_context_create();
try {
@ -345,7 +347,7 @@ class Monitor extends ZM_Object {
} // end function zmcControl
function zmaControl($mode=false) {
if ( !$this->{'Id'} ) {
if ( ! (property_exists($this, 'Id') and $this->{'Id'}) ) {
Warning('Attempt to control a monitor with no Id');
return;
}
@ -388,7 +390,7 @@ class Monitor extends ZM_Object {
return;
}
}
Logger::Debug("sending command to $url");
Debug("sending command to $url");
$context = stream_context_create();
try {
@ -537,7 +539,7 @@ class Monitor extends ZM_Object {
if ( $command == 'quit' or $command == 'start' or $command == 'stop' ) {
# These are special as we now run zmcontrol as a daemon through zmdc.
$status = daemonStatus('zmcontrol.pl', array('--id', $this->{'Id'}));
Logger::Debug("Current status $status");
Debug("Current status $status");
if ( $status or ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) ) {
daemonControl($command, 'zmcontrol.pl', '--id '.$this->{'Id'});
return;
@ -551,10 +553,10 @@ class Monitor extends ZM_Object {
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
# Local
Logger::Debug('Trying to send options ' . print_r($options, true));
Debug('Trying to send options ' . print_r($options, true));
$optionString = jsonEncode($options);
Logger::Debug("Trying to send options $optionString");
Debug("Trying to send options $optionString");
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
if ( $socket < 0 ) {
@ -588,7 +590,7 @@ class Monitor extends ZM_Object {
$url .= '?user='.$_SESSION['username'];
}
}
Logger::Debug("sending command to $url");
Debug("sending command to $url");
$context = stream_context_create();
try {

View File

@ -94,7 +94,7 @@ class Storage extends ZM_Object {
}
$used = $this->disk_used_space();
$usage = round(($used / $total) * 100);
//Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )");
//Debug("Used $usage = round( ( $used / $total ) * 100 )");
return $usage;
}

View File

@ -36,6 +36,6 @@ if ( $action == 'control' ) {
$ctrlCommand = buildControlCommand($monitor);
$monitor->sendControlCommand($ctrlCommand);
$view = 'none';
$redirect = $_SERVER['HTTP_REFERER'];
}
?>

View File

@ -89,7 +89,6 @@ if ( $action == 'controlcap' ) {
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
$Control->save($_REQUEST['newControl']);
$refreshParent = true;
$view = 'none';
$redirect = '?view=controlcaps';
} // end if action
?>

View File

@ -28,15 +28,15 @@ if ( $action == 'device' ) {
if ( !empty($_REQUEST['command']) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) ) {
if ( isset($_REQUEST['did']) && $_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']) );
}
$refreshParent = true;
$view = 'none';
$redirect = '?view=devices';
}
} else {
ZM\Error('Unknown action in device');

View File

@ -32,7 +32,7 @@ if ( !canEdit('Events') ) {
if ( $action == 'archive' ) {
$dbConn->beginTransaction();
$eids = getAffectedIds('eids');
ZM\Logger::Debug("E IDS" . print_r($eids, true));
ZM\Debug("E IDS" . print_r($eids, true));
foreach ( $eids as $markEid ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(1, $markEid));
}
@ -41,7 +41,7 @@ if ( $action == 'archive' ) {
} else if ( $action == 'unarchive' ) {
$dbConn->beginTransaction();
$eids = getAffectedIds('eids');
ZM\Logger::Debug("E IDS" . print_r($eids, true));
ZM\Debug("E IDS" . print_r($eids, true));
foreach ( $eids as $markEid ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(0, $markEid));
}

View File

@ -64,7 +64,7 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
$_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
$_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
$changes = $filter->changes($_REQUEST['filter']);
ZM\Logger::Debug('Changes: ' . print_r($changes,true));
ZM\Debug('Changes: ' . print_r($changes,true));
if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
if ( $filter->Background() )

View File

@ -32,7 +32,7 @@ if ( !canEdit('Monitors', $mid) ) {
if ( $action == 'function' ) {
$monitor = new ZM\Monitor($mid);
if ( !$monitor ) {
if ( !$monitor->Id() ) {
ZM\Error("Monitor not found with Id=$mid");
return;
}
@ -46,14 +46,14 @@ if ( $action == 'function' ) {
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled));
if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
zmaControl($monitor, 'stop');
zmcControl($monitor, ($newFunction != 'None') ? 'restart' : 'stop');
$monitor->zmaControl('stop');
$monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop');
if ( $newFunction != 'None' && $newFunction != 'NoDect' )
zmaControl($monitor, 'start');
$monitor->zmaControl('start');
}
} else {
ZM\Logger::Debug('No change to function, not doing anything.');
ZM\Debug('No change to function, not doing anything.');
}
} // end if action
$view = 'console';
$redirect = '?view=console';
?>

View File

@ -26,26 +26,24 @@ if ( !canEdit('Groups') ) {
return;
}
if ( $action == 'Save' ) {
if ( $action == 'save' ) {
$group_id = null;
if ( !empty($_POST['gid']) )
$group_id = $_POST['gid'];
if ( !empty($_REQUEST['gid']) )
$group_id = $_REQUEST['gid'];
$group = new ZM\Group($group_id);
$group->save(
array(
'Name'=> $_POST['newGroup']['Name'],
'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
'Name'=> $_REQUEST['newGroup']['Name'],
'ParentId'=>( $_REQUEST['newGroup']['ParentId'] == '' ? null : $_REQUEST['newGroup']['ParentId'] ),
)
);
dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id));
$group_id = $group->Id();
if ( $group_id ) {
foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
foreach ( $_REQUEST['newGroup']['MonitorIds'] as $mid ) {
dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid));
}
}
$view = 'none';
$refreshParent = true;
$closePopup = true;
$redirect = '?view=groups';
}
?>

View File

@ -22,9 +22,8 @@
if ( $action == 'logout' ) {
userLogout();
$view = 'login';
ZM\Logger::Debug("User: " . print_r($user,true));
} elseif ( $action == 'config' ) {
$redirect = '?view=user&uid='.$user['Id'];
$redirect = '?view=user&prev=console&uid='.$user['Id'];
}
?>

View File

@ -75,10 +75,10 @@ if ( $action == 'save' ) {
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');
ZM\Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId']);
ZM\Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId']);
if ( ( !$_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
ZM\Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID);
ZM\Debug('Auto selecting server to ' . ZM_SERVER_ID);
}
}
@ -235,7 +235,7 @@ if ( $action == 'save' ) {
$restart = true;
} else {
ZM\Logger::Debug('No action due to no changes to Monitor');
ZM\Debug('No action due to no changes to Monitor');
} # end if count(changes)
if ( !$mid ) {

View File

@ -1,44 +0,0 @@
<?php
//
// ZoneMinder web action file
// Copyright (C) 2019 ZoneMinder LLC
//
// 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') ) {
ZM\Warning('Need System permissions to shutdown server');
return;
}
if ( $action ) {
$when = isset($_POST['when']) and $_POST['when'] == 'now' ? 'now' : '+1';
if ( $action == 'shutdown' ) {
$output = array();
$rc = 0;
exec('sudo -n '.ZM_PATH_SHUTDOWN." -P $when 2>&1", $output, $rc);
#exec('sudo -n /bin/systemctl poweroff -i 2>&1', $output, $rc);
ZM\Logger::Debug("Shutdown output $rc " . implode("\n",$output));
#ZM\Logger::Debug("Shutdown output " . shell_exec('/bin/systemctl poweroff -i 2>&1'));
} else if ( $action == 'restart' ) {
$output = array();
exec('sudo -n '.ZM_PATH_SHUTDOWN." -r $when 2>&1", $output);
#exec('sudo -n /bin/systemctl reboot -i 2>&1', $output);
ZM\Logger::Debug("Shutdown output " . implode("\n",$output));
} else if ( $action == 'cancel' ) {
$output = array();
exec('sudo '.ZM_PATH_SHUTDOWN.' -c 2>&1', $output);
}
} # end if action
?>

View File

@ -34,7 +34,6 @@ if ( $action == 'Save' ) {
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( isset($_REQUEST['newUser']['Password']) ) {
if ( function_exists('password_hash') ) {
$pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"';
@ -67,9 +66,7 @@ if ( $action == 'Save' ) {
} else {
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
}
$refreshParent = true;
}
$view = 'none';
} # end if changes
} else if ( ZM_USER_SELF_EDIT and ( $_REQUEST['uid'] == $user['Id'] ) ) {
$uid = $user['Id'];
@ -100,9 +97,7 @@ if ( $action == 'Save' ) {
zm_session_start();
generateAuthHash(ZM_AUTH_HASH_IPS, true);
session_write_close();
$refreshParent = true;
}
$view = 'none';
}
} // end if $action == user
?>

View File

@ -70,9 +70,8 @@ if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) {
if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor->Controllable() ) {
$monitor->sendControlCommand('quit');
}
$refreshParent = true;
} // end if changes
$view = 'none';
$redirect = $_SERVER['HTTP_REFERER'];
} // end if action
} // end if $mid and canEdit($mid)
?>

View File

@ -72,12 +72,12 @@ function validateUser($username='', $password='') {
// We assume we don't need to support mysql < 4.1
// Starting MY SQL 4.1, mysql concats a '*' in front of its password hash
// https://blog.pythian.com/hashing-algorithm-in-mysql-password-2/
ZM\Logger::Debug('Saved password is using MYSQL password function');
ZM\Debug('Saved password is using MYSQL password function');
$input_password_hash = '*'.strtoupper(sha1(sha1($password, true)));
$password_correct = ($user['Password'] == $input_password_hash);
break;
case 'bcrypt' :
ZM\Logger::Debug('bcrypt signature found, assumed bcrypt password');
ZM\Debug('bcrypt signature found, assumed bcrypt password');
$password_correct = password_verify($password, $user['Password']);
break;
case 'mysql+bcrypt' :
@ -85,10 +85,10 @@ function validateUser($username='', $password='') {
// this is done so that we don't spend cycles doing two bcrypt password_verify calls
// for every wrong password entered. This will only be invoked for passwords zmupdate.pl has
// overlay hashed
ZM\Logger::Debug("Detected bcrypt overlay hashing for $username");
ZM\Debug("Detected bcrypt overlay hashing for $username");
$bcrypt_hash = substr($user['Password'], 4);
$mysql_encoded_password = '*'.strtoupper(sha1(sha1($password, true)));
ZM\Logger::Debug("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash");
ZM\Debug("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash");
$password_correct = password_verify($mysql_encoded_password, $bcrypt_hash);
break;
default:
@ -130,7 +130,7 @@ function validateToken($token, $allowed_token_type='access') {
return array(false, 'Incorrect token type');
}
} else {
ZM\Logger::Debug('Not comparing token types as [any] was passed');
ZM\Debug('Not comparing token types as [any] was passed');
}
$username = $jwt_payload['user'];
@ -210,7 +210,7 @@ function generateAuthHash($useRemoteAddr, $force=false) {
} else {
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
}
#ZM\Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
#ZM\Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
$auth = md5($authKey);
$_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth;
$_SESSION['AuthHashGeneratedAt'] = $time;
@ -248,14 +248,14 @@ function userFromSession() {
if ( isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) )
$user = getAuthUser($_SESSION['AuthHash'.$_SESSION['remoteAddr']]);
else
ZM\Logger::Debug("No auth hash in session, there should have been");
ZM\Debug("No auth hash in session, there should have been");
} else {
# Need to refresh permissions and validate that the user still exists
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
$user = dbFetchOne($sql, NULL, array($_SESSION['username']));
}
} else {
ZM\Logger::Debug('No username in session');
ZM\Debug('No username in session');
}
return $user;
}

View File

@ -193,21 +193,21 @@ function csrf_check($fatal = true) {
$tokens = '';
do {
if (!isset($_POST[$name])) {
#Logger::Debug("POST[$name] is not set");
#Debug("POST[$name] is not set");
break;
#} else {
#Logger::Debug("POST[$name] is set as " . $_POST[$name] );
#Debug("POST[$name] is set as " . $_POST[$name] );
}
// we don't regenerate a token and check it because some token creation
// schemes are volatile.
$tokens = $_POST[$name];
if (!csrf_check_tokens($tokens)) {
#Logger::Debug("Failed checking tokens");
#Debug("Failed checking tokens");
break;
#} else {
#Logger::Debug("Token passed");
#Debug("Token passed");
}
$ok = true;
} while (false);
@ -296,7 +296,7 @@ function csrf_callback($tokens) {
// Don't make it too easy for users to inflict a CSRF attack on themselves.
echo "<p><strong>Only try again if you weren't sent to this page by someone as this is potentially a sign of an attack.</strong></p>";
echo "<form method='post' action=''>$data<input type='submit' value='Try again' /></form>";
ZM\Logger::Debug("Failed csrf check");
ZM\Debug("Failed csrf check");
}
echo "<p>Debug: $tokens</p></body></html>
";
@ -318,27 +318,27 @@ function csrf_check_tokens($tokens) {
* Checks if a token is valid.
*/
function csrf_check_token($token) {
#Logger::Debug("Checking CSRF token $token");
#Debug("Checking CSRF token $token");
if (strpos($token, ':') === false) {
#Logger::Debug("Checking CSRF token $token bad because no :");
#Debug("Checking CSRF token $token bad because no :");
return false;
}
list($type, $value) = explode(':', $token, 2);
if (strpos($value, ',') === false) {
#Logger::Debug("Checking CSRF token $token bad because no ,");
#Debug("Checking CSRF token $token bad because no ,");
return false;
}
list($x, $time) = explode(',', $token, 2);
if ($GLOBALS['csrf']['expires']) {
if (time() > $time + $GLOBALS['csrf']['expires']) {
#Logger::Debug("Checking CSRF token $token bad because expired");
#Debug("Checking CSRF token $token bad because expired");
return false;
}
}
switch ($type) {
case 'sid':
{
#Logger::Debug("Checking sid: $value === " . csrf_hash(session_id(), $time) );
#Debug("Checking sid: $value === " . csrf_hash(session_id(), $time) );
return $value === csrf_hash(session_id(), $time);
}
case 'cookie':
@ -348,10 +348,10 @@ return false;
return $value === csrf_hash($_COOKIE[$n], $time);
case 'key':
if (!$GLOBALS['csrf']['key']) {
Logger::Debug("Checking key: no key set" );
Debug("Checking key: no key set" );
return false;
}
#Logger::Debug("Checking sid: $value === " . csrf_hash($GLOBALS['csrf']['key'], $time) );
#Debug("Checking sid: $value === " . csrf_hash($GLOBALS['csrf']['key'], $time) );
return $value === csrf_hash($GLOBALS['csrf']['key'], $time);
// We could disable these 'weaker' checks if 'key' was set, but
// that doesn't make me feel good then about the cookie-based

View File

@ -105,7 +105,7 @@ function dbLog($sql, $update=false) {
global $dbLogLevel;
$noExecute = $update && ($dbLogLevel >= DB_LOG_DEBUG);
if ( $dbLogLevel > DB_LOG_OFF )
ZM\Logger::Debug( "SQL-LOG: $sql".($noExecute?' (not executed)':'') );
ZM\Debug( "SQL-LOG: $sql".($noExecute?' (not executed)':'') );
return( $noExecute );
}
@ -146,7 +146,7 @@ function dbQuery($sql, $params=NULL) {
}
} else {
if ( defined('ZM_DB_DEBUG') ) {
ZM\Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):''));
ZM\Debug("SQL: $sql values:" . ($params?implode(',',$params):''));
}
$result = $dbConn->query($sql);
if ( ! $result ) {
@ -155,7 +155,7 @@ function dbQuery($sql, $params=NULL) {
}
}
if ( defined('ZM_DB_DEBUG') ) {
ZM\Logger::Debug('SQL: '.$sql.' '.($params?implode(',',$params):'').' rows: '.$result->rowCount());
ZM\Debug('SQL: '.$sql.' '.($params?implode(',',$params):'').' rows: '.$result->rowCount());
}
} catch(PDOException $e) {
ZM\Error("SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):''));

View File

@ -104,7 +104,7 @@ function CORSHeaders() {
# Only need CORSHeaders in the event that there are multiple servers in use.
# ICON: Might not be true. multi-port?
if ( ZM_MIN_STREAMING_PORT ) {
ZM\Logger::Debug('Setting default Access-Control-Allow-Origin from ' . $_SERVER['HTTP_ORIGIN']);
ZM\Debug('Setting default Access-Control-Allow-Origin from ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Headers: x-requested-with,x-request');
}
@ -117,7 +117,7 @@ function CORSHeaders() {
preg_match('/^(https?:\/\/)?'.preg_quote($Server->Name(),'/').'/i', $_SERVER['HTTP_ORIGIN'])
) {
$valid = true;
ZM\Logger::Debug('Setting Access-Control-Allow-Origin from '.$_SERVER['HTTP_ORIGIN']);
ZM\Debug('Setting Access-Control-Allow-Origin from '.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Headers: x-requested-with,x-request');
break;
@ -392,7 +392,7 @@ function getEventDefaultVideoPath($event) {
}
function deletePath( $path ) {
ZM\Logger::Debug('Deleting '.$path);
ZM\Debug('Deleting '.$path);
if ( is_dir($path) ) {
system(escapeshellcmd('rm -rf '.$path));
} else if ( file_exists($path) ) {
@ -424,6 +424,9 @@ function deleteEvent($event) {
} # CAN EDIT
}
/**
* $label must be already escaped. It can't be done here since it sometimes contains HTML tags.
*/
function makeLink($url, $label, $condition=1, $options='') {
$string = '';
if ( $condition ) {
@ -443,50 +446,6 @@ function makeHelpLink($ohndx) {
return $string;
}
/**
* $label must be already escaped. It can't be done here since it sometimes contains HTML tags.
*/
function makePopupLink($url, $winName, $winSize, $label, $condition=1, $options='') {
// Avoid double-encoding since some consumers incorrectly pass a pre-escaped URL.
$string = '<a';
if ( $condition ) {
$string .= ' class="popup-link" href="' . htmlspecialchars($url, ENT_COMPAT | ENT_HTML401, ini_get('default_charset'), false) . '"';
$string .= ' data-window-name="' . htmlspecialchars($winName) . '"';
if ( is_array( $winSize ) ) {
$string .= ' data-window-tag="' . htmlspecialchars($winSize[0]) . '"';
$string .= ' data-window-width="' . htmlspecialchars($winSize[1]) . '"';
$string .= ' data-window-height="' . htmlspecialchars($winSize[2]) . '"';
} else {
$string .= ' data-window-tag="' . htmlspecialchars($winSize) . '"';
}
$string .= ($options ? (' ' . $options ) : '') . '>';
} else {
$string .= '>';
}
$string .= $label;
$string .= '</a>';
return $string;
}
function makePopupButton($url, $winName, $winSize, $buttonValue, $condition=1, $options='') {
$string = '<input type="button" class="popup-link" value="' . htmlspecialchars($buttonValue) . '"';
$string .= ' data-url="' . htmlspecialchars($url, ENT_COMPAT | ENT_HTML401, ini_get("default_charset"), false) . '"';
$string .= ' data-window-name="' . htmlspecialchars($winName) . '"';
if ( is_array($winSize) ) {
$string .= ' data-window-tag="' . htmlspecialchars($winSize[0]) . '"';
$string .= ' data-window-width="' . htmlspecialchars($winSize[1]) . '"';
$string .= ' data-window-height="' . htmlspecialchars($winSize[2]) . '"';
} else {
$string .= ' data-window-tag="' . htmlspecialchars($winSize) . '"';
}
if (!$condition) {
$string .= ' disabled="disabled"';
}
$string .= ($options ? (' ' . $options) : '') . '/>';
return $string;
}
function makeButton($url, $buttonValue, $condition=1, $options='') {
$string = '<button type="button" data-on-click-this="'.$buttonValue.'"';
$string .= ' data-url="' .$url. '"';
@ -509,7 +468,7 @@ function htmlSelect($name, $contents, $values, $behaviours=false) {
}
}
return '<select name="'.$name.'" '.$behaviourText.'>'.htmlOptions($contents, $values).'</select>';
return '<select name="'.$name.'" '.$behaviourText.'>'.PHP_EOL.htmlOptions($contents, $values).'</select>';
}
function htmlOptions($options, $values) {
@ -540,11 +499,10 @@ function htmlOptions($options, $values) {
$options_html .= '<option value="'.htmlspecialchars($value, ENT_COMPAT | ENT_HTML401, ini_get('default_charset'), false).'"'.
($selected?' selected="selected"':'').
($disabled?' disabled="disabled"':'').
'>'.htmlspecialchars($text, ENT_COMPAT | ENT_HTML401, ini_get('default_charset'), false).'</option>
';
'>'.htmlspecialchars($text, ENT_COMPAT | ENT_HTML401, ini_get('default_charset'), false).'</option>'.PHP_EOL;
} # end foreach options
if ( $values and ! $has_selected ) {
ZM\Warning('Specified value '.$values.' not in contents: '.print_r($options, true));
if ( $values and ((!is_array($values)) or count($values) ) and ! $has_selected ) {
ZM\Warning('Specified value '.print_r($values, true).' not in contents: '.print_r($options, true));
}
return $options_html;
} # end function htmlOptions
@ -582,7 +540,7 @@ function buildSelect($name, $contents, $behaviours=false) {
$behaviourText .= ' '.$event.'="'.$action.'"';
}
} else {
$behaviourText = ' onchange="'.$behaviours.'"';
$behaviourText = ' data-on-change-this="'.$behaviours.'"';
}
}
?>
@ -832,7 +790,7 @@ function daemonControl($command, $daemon=false, $args=false) {
}
$string = escapeshellcmd($string);
#$string .= ' 2>/dev/null >&- <&- >/dev/null';
ZM\Logger::Debug('daemonControl '.$string);
ZM\Debug('daemonControl '.$string);
exec($string);
}
@ -973,7 +931,7 @@ function createVideo($event, $format, $rate, $scale, $overwrite=false) {
$command .= ' -o';
$command = escapeshellcmd($command);
$result = exec($command, $output, $status);
ZM\Logger::Debug("generating Video $command: result($result outptu:(".implode("\n", $output )." status($status");
ZM\Debug("generating Video $command: result($result outptu:(".implode("\n", $output )." status($status");
return $status ? '' : rtrim($result);
}
@ -1676,17 +1634,17 @@ function coordsToPoints($coords) {
function limitPoints(&$points, $min_x, $min_y, $max_x, $max_y) {
foreach ( $points as &$point ) {
if ( $point['x'] < $min_x ) {
ZM\Logger::Debug('Limiting point x'.$point['x'].' to min_x '.$min_x);
ZM\Debug('Limiting point x'.$point['x'].' to min_x '.$min_x);
$point['x'] = $min_x;
} else if ( $point['x'] > $max_x ) {
ZM\Logger::Debug('Limiting point x'.$point['x'].' to max_x '.$max_x);
ZM\Debug('Limiting point x'.$point['x'].' to max_x '.$max_x);
$point['x'] = $max_x;
}
if ( $point['y'] < $min_y ) {
ZM\Logger::Debug('Limiting point y'.$point['y'].' to min_y '.$min_y);
ZM\Debug('Limiting point y'.$point['y'].' to min_y '.$min_y);
$point['y'] = $min_y;
} else if ( $point['y'] > $max_y ) {
ZM\Logger::Debug('Limiting point y'.$point['y'].' to max_y '.$max_y);
ZM\Debug('Limiting point y'.$point['y'].' to max_y '.$max_y);
$point['y'] = $max_y;
}
} // end foreach point
@ -2111,7 +2069,7 @@ function getStreamHTML($monitor, $options = array()) {
if ( $scale < $options['scale'] )
$options['scale'] = $scale;
} else {
Warning('Invalid value for width: '.$options['width']);
ZM\Warning('Invalid value for width: '.$options['width']);
}
}
}
@ -2211,7 +2169,7 @@ function check_timezone() {
'TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),\'%H%i\')'
));
#Logger::Debug("System timezone offset determine to be: $sys_tzoffset,\x20
#Debug("System timezone offset determine to be: $sys_tzoffset,\x20
#PHP timezone offset determine to be: $php_tzoffset,\x20
#Mysql timezone offset determine to be: $mysql_tzoffset
#");
@ -2450,4 +2408,11 @@ function zm_random_bytes($length = 32) {
}
ZM\Error('No random_bytes function found.');
}
function i18n() {
$string = explode('_', ZM_LANG_DEFAULT, 2);
$string[1] = strtoupper($string[1]);
return implode('-', $string);
}
?>

View File

@ -158,7 +158,7 @@ class Logger {
$this->initialised = true;
//Logger::Debug( "LogOpts: level=".self::$codes[$this->level]."/".self::$codes[$this->effectiveLevel].", screen=".self::$codes[$this->termLevel].", database=".self::$codes[$this->databaseLevel].", logfile=".self::$codes[$this->fileLevel]."->".$this->logFile.", weblog=".self::$codes[$this->weblogLevel].", syslog=".self::$codes[$this->syslogLevel] );
//Debug( "LogOpts: level=".self::$codes[$this->level]."/".self::$codes[$this->effectiveLevel].", screen=".self::$codes[$this->termLevel].", database=".self::$codes[$this->databaseLevel].", logfile=".self::$codes[$this->fileLevel]."->".$this->logFile.", weblog=".self::$codes[$this->weblogLevel].", syslog=".self::$codes[$this->syslogLevel] );
}
private function terminate() {
@ -199,9 +199,6 @@ class Logger {
return self::$instance;
}
public static function Debug( $string ) {
Logger::fetch()->logPrint( Logger::DEBUG, $string );
}
public function id( $id=NULL ) {
if ( isset($id) && $this->id != $id ) {
@ -459,6 +456,10 @@ function Dump( &$var, $label='VAR' ) {
Logger::fetch()->logPrint( Logger::DEBUG, ob_get_clean() );
}
function Debug( $string ) {
Logger::fetch()->logPrint( Logger::DEBUG, $string );
}
function Info( $string ) {
Logger::fetch()->logPrint( Logger::INFO, $string );
}

View File

@ -25,7 +25,7 @@ function zm_session_start() {
}
ini_set('session.name', 'ZMSESSID');
ZM\Logger::Debug('Setting cookie parameters to '.print_r($currentCookieParams, true));
ZM\Debug('Setting cookie parameters to '.print_r($currentCookieParams, true));
}
session_start();
$_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking
@ -37,7 +37,7 @@ function zm_session_start() {
session_start();
} else if ( !empty($_SESSION['generated_at']) ) {
if ( $_SESSION['generated_at']<($now-(ZM_COOKIE_LIFETIME/2)) ) {
ZM\Logger::Debug('Regenerating session because generated_at ' . $_SESSION['generated_at'] . ' < ' . $now . '-'.ZM_COOKIE_LIFETIME.'/2 = '.($now-ZM_COOKIE_LIFETIME/2));
ZM\Debug('Regenerating session because generated_at ' . $_SESSION['generated_at'] . ' < ' . $now . '-'.ZM_COOKIE_LIFETIME.'/2 = '.($now-ZM_COOKIE_LIFETIME/2));
zm_session_regenerate_id();
}
}
@ -87,4 +87,69 @@ function zm_session_clear() {
session_write_close();
session_start();
} // function zm_session_clear()
class Session {
private $db;
public function __construct() {
global $dbConn;
$this->db = $dbConn;
// Set handler to overide SESSION
session_set_save_handler(
array($this, '_open'),
array($this, '_close'),
array($this, '_read'),
array($this, '_write'),
array($this, '_destroy'),
array($this, '_gc')
);
// Start the session
//zm_session_start();
}
public function _open() {
return $this->db ? true : false;
}
public function _close(){
// The example code closed the db connection.. I don't think we care to.
return true;
}
public function _read($id){
$sth = $this->db->prepare('SELECT data FROM Sessions WHERE id = :id');
$sth->bindParam(':id', $id, PDO::PARAM_STR, 32);
if ( $sth->execute() and ( $row = $sth->fetch(PDO::FETCH_ASSOC) ) ) {
return $row['data'];
}
// Return an empty string
return '';
}
public function _write($id, $data){
// Create time stamp
$access = time();
$sth = $this->db->prepare('REPLACE INTO Sessions VALUES (:id, :access, :data)');
$sth->bindParam(':id', $id, PDO::PARAM_STR, 32);
$sth->bindParam(':access', $access, PDO::PARAM_INT);
$sth->bindParam(':data', $data);
return $sth->execute() ? true : false;
}
public function _destroy($id) {
$sth = $this->db->prepare('DELETE FROM Sessions WHERE Id = :id');
$sth->bindParam(':id', $id, PDO::PARAM_STR, 32);
return $sth->execute() ? true : false;
}
public function _gc($max) {
// Calculate what is to be deemed old
$old = time() - $max;
$sth = $this->db->prepare('DELETE * FROM Sessions WHERE access < :old');
$sth->bindParam(':old', $old, PDO::PARAM_INT);
return $sth->execute() ? true : false;
}
} # end class Session
$session = new Session;
?>

View File

@ -56,7 +56,7 @@ require_once('includes/Monitor.php');
if ( 0 and ZM\Logger::fetch()->debugOn() ) {
ob_start();
phpinfo(INFO_VARIABLES);
ZM\Logger::Debug(ob_get_contents());
ZM\Debug(ob_get_contents());
ob_end_clean();
}
@ -82,7 +82,7 @@ define('ZM_BASE_URL', '');
require_once('includes/functions.php');
if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) {
ZM\Logger::Debug('OPTIONS Method, only doing CORS');
ZM\Debug('OPTIONS Method, only doing CORS');
# Add Cross domain access headers
CORSHeaders();
return;
@ -183,6 +183,9 @@ $user = null;
if ( isset($_REQUEST['view']) )
$view = detaintPath($_REQUEST['view']);
if ( isset($_REQUEST['redirect']) )
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
# Add CSP Headers
$cspNonce = bin2hex(zm_random_bytes(16));
@ -218,7 +221,7 @@ if ( (!$view and !$request) or ($view == 'console') ) {
check_timezone();
}
ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
ZM\Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
if (
ZM_ENABLE_CSRF_MAGIC &&
( $action != 'login' ) &&
@ -229,14 +232,14 @@ if (
( $view != 'archive' ) // returns data
) {
require_once('includes/csrf/csrf-magic.php');
#ZM\Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
#ZM\Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();
}
# Need to include actions because it does auth
if ( $action and !$request ) {
if ( file_exists('includes/actions/'.$view.'.php') ) {
ZM\Logger::Debug("Including includes/actions/$view.php");
ZM\Debug("Including includes/actions/$view.php");
require_once('includes/actions/'.$view.'.php');
} else {
ZM\Warning("No includes/actions/$view.php for action $action");
@ -251,7 +254,7 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n
header('HTTP/1.1 401 Unauthorized');
exit;
}
ZM\Logger::Debug('Redirecting to login');
ZM\Debug('Redirecting to login');
$view = 'none';
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
if ( ! $request ) {
@ -268,7 +271,7 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n
if ( $redirect ) {
ZM\Logger::Debug("Redirecting to $redirect");
ZM\Debug("Redirecting to $redirect");
header('Location: '.$redirect);
return;
}

View File

@ -72,14 +72,10 @@ function MonitorStream(monitorData) {
this.onclick = function(evt) {
var el = evt.currentTarget;
var tag = 'watch';
var id = el.getAttribute("data-monitor-id");
var width = el.getAttribute("data-width");
var height = el.getAttribute("data-height");
var url = '?view=watch&mid='+id;
var name = 'zmWatch'+id;
evt.preventDefault();
createPopup(url, name, tag, width, height);
window.location.assign(url);
};
this.setup_onclick = function() {

View File

@ -989,7 +989,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -951,7 +951,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -947,7 +947,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -949,7 +949,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -973,7 +973,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -261,7 +261,10 @@ $SLANG = array(
'ConfigType' => 'Config Type',
'ConfiguredFor' => 'Configured for',
'ConfigURL' => 'Config Base URL',
'ConfirmDeleteControl' => 'Warning, deleting a control will reset all monitors that use it to be uncontrollable.<br><br>Are you sure you wish to delete?',
'ConfirmDeleteDevices' => 'Are you sure you wish to delete the selected devices?',
'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?',
'ConfirmDeleteTitle' => 'Delete Confirmation',
'ConfirmPassword' => 'Confirm Password',
'ConjAnd' => 'and',
'ConjOr' => 'or',
@ -320,6 +323,7 @@ $SLANG = array(
'DuplicateMonitorName' => 'Duplicate Monitor Name',
'Duration' => 'Duration',
'Edit' => 'Edit',
'EditControl' => 'Edit Control',
'EditLayout' => 'Edit Layout',
'Email' => 'Email',
'EnableAlarms' => 'Enable Alarms',
@ -363,6 +367,7 @@ $SLANG = array(
'Ffmpeg' => 'Ffmpeg',
'File' => 'File',
'FilterArchiveEvents' => 'Archive all matches',
'FilterUnarchiveEvents' => 'Unarchive all matches',
'FilterUpdateDiskSpace' => 'Update used disk space',
'FilterDeleteEvents' => 'Delete all matches',
'FilterCopyEvents' => 'Copy all matches',
@ -1070,7 +1075,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -953,7 +953,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -946,7 +946,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -990,7 +990,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -953,7 +953,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -948,7 +948,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -948,7 +948,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -942,7 +942,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -887,7 +887,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -957,7 +957,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -65,9 +65,9 @@
//
// Examples
// setlocale( 'LC_ALL', 'en_GB' ); All locale settings pre-4.3.0
setlocale( LC_ALL, 'cn_ZH' ); //All locale settings 4.3.0 and after
setlocale( LC_CTYPE, 'cn_ZH' ); //Character class settings 4.3.0 and after
setlocale( LC_TIME, 'cn_ZH' ); //Date and time formatting 4.3.0 and after
setlocale( LC_ALL, 'zh_CN' ); //All locale settings 4.3.0 and after
setlocale( LC_CTYPE, 'zh_CN' ); //Character class settings 4.3.0 and after
setlocale( LC_TIME, 'zh_CN' ); //Date and time formatting 4.3.0 and after
// Simple String Replacements
$SLANG = array(
@ -989,7 +989,7 @@ $OLANG = array(
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"
// 'Help' => "This is some new help for this option which will be displayed in the window when the ? is clicked"
// ),
);

View File

@ -508,6 +508,7 @@ th.table-th-sort-rev span.table-th-sort-span {
margin: 0 auto 8px auto;
line-height: 130%;
text-align: center;
clear: both;
}
#content p {

View File

@ -1,19 +1,3 @@
#logSummary {
font-size:10px;
}
#logSummary tr {
margin: 0;
padding: 0;
}
#logSummary td {
border: 1px solid #7f7fb2;
padding: 0 6px;
font-size: 10px;
line-height: 15px;
}
tr.log-fat td {
background-color:#ffcccc;
font-weight: bold;
@ -32,20 +16,3 @@ tr.log-dbg td {
font-style: italic;
}
#exportLog label {
vertical-align: middle;
}
#exportLog input[type=radio] {
margin-right: 4px;
vertical-align: middle;
}
#exportError {
display: none;
color: #dc143c;
margin-bottom: 8px;
}
#exportErrorText {
}

View File

@ -1,18 +0,0 @@
tr.log-fat td {
background-color:#ffcccc;
font-weight: bold;
}
tr.log-err td {
background-color:#ffcccc;
}
tr.log-war td {
background-color: #ffe4b5;
}
tr.log-dbg td {
color: #666666;
font-style: italic;
}

View File

@ -27,7 +27,6 @@
}
#definitionPanel {
float: left;
margin: 2px;
text-align: center;
}

View File

@ -19,15 +19,23 @@
//
$rates = array(
'10000' => '100x',
'5000' => '50x',
'2500' => '25x',
'1000' => '10x',
'400' => '4x',
'200' => '2x',
'100' => translate('Real'),
'50' => '1/2x',
'25' => '1/4x',
-5000 => '-50x',
-2500 => '-25x',
-1000 => '-10x',
-500 => '-5x',
-200 => '-2x',
-100 => '-1x',
-50 => '-1/2x',
-25 => '-1/4x',
0 => translate('Stop'),
25 => '1/4x',
50 => '1/2x',
100 => '1x',
200 => '2x',
500 => '5x',
1000 => '10x',
2500 => '25x',
5000 => '50x'
);
$scales = array(

View File

@ -176,7 +176,7 @@ function controlPresets($monitor, $cmds) {
}
if ( canEdit('Monitors') && $control->CanSetPresets() ) {
?>
<button type="button" class="ptzTextBtn popup-link" value="Set" data-url="?view=controlpreset&amp;mid=<?php echo $monitor->Id() ?>" data-window-name="zmPreset" data-window-tag="preset"><?php echo translate('Set') ?></button>
<button type="button" class="ptzTextBtn" value="Set" data-toggle="modal" data-target="#ctrlPresetModal"><?php echo translate('Set') ?></button>
<?php
}
?>

Some files were not shown because too many files have changed in this diff Show More