Merge branch 'master' into feature-h264-videostorage

This commit is contained in:
Isaac Connor 2016-06-17 11:09:47 -04:00
commit 26bba58c56
115 changed files with 3802 additions and 3236 deletions

View File

@ -4,7 +4,7 @@
# #
cmake_minimum_required (VERSION 2.6) cmake_minimum_required (VERSION 2.6)
project (zoneminder) project (zoneminder)
set(zoneminder_VERSION "1.29.2") file (STRINGS "version" zoneminder_VERSION)
# make API version a minor of ZM version # make API version a minor of ZM version
set(zoneminder_API_VERSION "${zoneminder_VERSION}.1") set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")

View File

@ -373,7 +373,7 @@ UPDATE States SET IsActive = '1' WHERE Name = 'default';
-- If duplicate states existed while upgrading, that is -- If duplicate states existed while upgrading, that is
-- very likely an error that ZM allowed earlier, so -- very likely an error that ZM allowed earlier, so
-- we are picking up the first one and deleting the others -- we are picking up the first one and deleting the others
ALTER IGNORE TABLE States ADD UNIQUE (Name); ALTER TABLE States ADD UNIQUE (Name);
SET @s = (SELECT IF( SET @s = (SELECT IF(
(SELECT COUNT(*) (SELECT COUNT(*)

5
db/zm_update-1.30.0.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.29.1 database to 1.30.0
--
-- No changes required
--

View File

@ -1,7 +1,6 @@
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
php_flag register_globals off
Options Indexes FollowSymLinks Options Indexes FollowSymLinks
<IfModule mod_dir.c> <IfModule mod_dir.c>
DirectoryIndex index.php DirectoryIndex index.php

View File

@ -17,6 +17,14 @@ Build-Depends: debhelper (>= 9), cmake
, libvlccore-dev, libvlc-dev , libvlccore-dev, libvlc-dev
, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev , libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev
, libgcrypt11-dev, libpolkit-gobject-1-dev , libgcrypt11-dev, libpolkit-gobject-1-dev
, libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl
, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl
Standards-Version: 3.9.4 Standards-Version: 3.9.4
Package: zoneminder Package: zoneminder
@ -32,6 +40,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl , libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl , libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl
, libpcre3 , libpcre3
, libav-tools, libavdevice53 , libav-tools, libavdevice53
, rsyslog | system-log-daemon , rsyslog | system-log-daemon

View File

@ -49,10 +49,9 @@ New installs
Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous
step. step.
Additionally, you must also edit This version of zoneminder no longer requires you to make a similar change
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner. to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
Scroll down and change login and password to the values you created in the This now happens dynamically. Do *not* make any changes to this file.
previous step.
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -113,10 +112,9 @@ Upgrades
Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf
contains any new config settings that may be in zm.conf.rpmnew. contains any new config settings that may be in zm.conf.rpmnew.
Additionally, you must also edit This version of zoneminder no longer requires you to make a similar change
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner. to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
Scroll down and change login and password to the values you used This now happens dynamically. Do *not* make any changes to this file.
previsouly.
2. Verify permissions of the zmuser account. 2. Verify permissions of the zmuser account.

View File

@ -40,11 +40,6 @@ New installs
other than zmuser/zmpass then you must now edit /etc/zm.conf. Change other than zmuser/zmpass then you must now edit /etc/zm.conf. Change
ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step. ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step.
Additionally, you must also edit
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner.
Scroll down and change login and password to the values you created in the
previous step.
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
incorrectly, and these complaints will show up in the zoneminder logging incorrectly, and these complaints will show up in the zoneminder logging
@ -89,9 +84,8 @@ New installs
Then point your web browser to http://<machine name or ip>/zm Then point your web browser to http://<machine name or ip>/zm
================================================================================ Upgrades
UPGRADES ========
================================================================================
1. Verify /etc/zm.conf. 1. Verify /etc/zm.conf.
@ -105,11 +99,6 @@ New installs
Compare /etc/zm.conf to /etc/zm.conf.rpmnew. Verify that zm.conf Compare /etc/zm.conf to /etc/zm.conf.rpmnew. Verify that zm.conf
contains any new config settings that may be in zm.conf.rpmnew. contains any new config settings that may be in zm.conf.rpmnew.
Additionally, you must also edit
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner.
Scroll down and change login and password to the values you used
previsouly.
2. Verify permissions of the zmuser account. 2. Verify permissions of the zmuser account.
Over time, the database account permissions required for normal operation Over time, the database account permissions required for normal operation

View File

@ -47,10 +47,9 @@ New installs
Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous
step. step.
Additionally, you must also edit This version of zoneminder no longer requires you to make a similar change
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner. to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
Scroll down and change login and password to the values you created in the This now happens dynamically. Do *not* make any changes to this file.
previous step.
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -111,10 +110,9 @@ Upgrades
Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf
contains any new config settings that may be in zm.conf.rpmnew. contains any new config settings that may be in zm.conf.rpmnew.
Additionally, you must also edit This version of zoneminder no longer requires you to make a similar change
/usr/share/zoneminder/www/api/app/Config/database.php in a similar manner. to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
Scroll down and change login and password to the values you used This now happens dynamically. Do *not* make any changes to this file.
previsouly.
2. Verify permissions of the zmuser account. 2. Verify permissions of the zmuser account.

View File

@ -5,17 +5,23 @@ Maintainer: Dmitry Smirnov <onlyjob@debian.org>
Uploaders: Vagrant Cascadian <vagrant@debian.org> Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake ,cmake
,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev
,libbz2-dev ,libbz2-dev
,libgcrypt-dev ,libgcrypt-dev
,libcurl4-gnutls-dev ,libcurl4-gnutls-dev
,libgnutls-openssl-dev ,libgnutls-openssl-dev
,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, ,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev,
,libmysqlclient-dev ,libmysqlclient-dev
,libpcre3-dev ,libpcre3-dev
,libpolkit-gobject-1-dev ,libpolkit-gobject-1-dev
,libv4l-dev (>= 0.8.3) [!hurd-any] ,libv4l-dev (>= 0.8.3) [!hurd-any]
,libvlc-dev ,libvlc-dev
,libdate-manip-perl
,libdbd-mysql-perl
,libphp-serialization-perl
,libsys-mmap-perl [!hurd-any]
,libwww-perl
,libdata-uuid-perl
# Unbundled (dh_linktree): # Unbundled (dh_linktree):
,libjs-jquery ,libjs-jquery
,libjs-mootools ,libjs-mootools
@ -37,14 +43,15 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libmodule-load-conditional-perl ,libmodule-load-conditional-perl
,libnet-sftp-foreign-perl ,libnet-sftp-foreign-perl
# ,libzoneminder-perl (= ${source:Version}) # ,libzoneminder-perl (= ${source:Version})
,libarchive-zip-perl ,libarchive-zip-perl
,libdbd-mysql-perl ,libdbd-mysql-perl
,libdevice-serialport-perl ,libdevice-serialport-perl
,libimage-info-perl ,libimage-info-perl
,libjson-any-perl ,libjson-any-perl
,libsys-mmap-perl [!hurd-any] ,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl ,liburi-encode-perl
,libwww-perl ,libwww-perl
,libdata-uuid-perl
,mysql-client | virtual-mysql-client ,mysql-client | virtual-mysql-client
,perl-modules ,perl-modules
,php5-mysql, php5-gd ,php5-mysql, php5-gd

View File

@ -1,7 +1,6 @@
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
php_flag register_globals off
Options Indexes FollowSymLinks Options Indexes FollowSymLinks
<IfModule mod_dir.c> <IfModule mod_dir.c>
DirectoryIndex index.php DirectoryIndex index.php

View File

@ -1,9 +1,43 @@
zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium zoneminder (1.29.0+dfsg-1) unstable; urgency=low
* include api, switch to cmake build * New upstream release [February 2016] (Closes: #788317, #770851).
-- Isaac Connor <iconnor@connortechnology.com> Mon, 17 Aug 2015 10:29:23 -0400 [ Dmitry Smirnov <onlyjob@debian.org> ]
* copyright/Files-Excluded += "onvif/*" due to licensing uncertainty.
* Fixed FTBFS when built with dpkg-buildpackage -A (Closes: #806126).
* FFmpeg 2.9 support. Thanks, Andreas Cadhalpun. (Closes: #803850).
* Use "ffmpeg" instead of "avconv":
+ "libav_path.patch" replaced with "default_ffmpeg_path.patch".
* zoneminder/Depends:
- perl-modules (package-relation-with-perl-modules)
- libav-tools
* zoneminder/Recommends:
+ ffmpeg | libav-tools
* Updated Vcs URLs.
* Build/install new man pages.
* Removed obsolete lintian-overrides.
* README: grant "index" right to DB user.
* systemd: start after MySQL but do not require the latter.
* Added new patch with spelling corrections.
* Removed obsolete patches:
- 783.patch
- 980-fix-image-size.patch
- cmake-fix-confpath.patch
- cmake.patch
- cmake-gnutls.patch
- fix-html-export.patch
- format-hardening.patch
- libv4l1-videodev.h.patch
- pod_man_fixes.patch
- pod_name_fixes.patch
- pod_zmupdate-to-pod2usage.patch
- respect-privacy.patch
- zmtrigger-plus.patch
[ Vagrant Cascadian <vagrant@debian.org> ]
* Remove myself from Uploaders.
-- Dmitry Smirnov <onlyjob@debian.org> Tue, 09 Feb 2016 15:40:32 +1100
zoneminder (1.28.1-8) unstable; urgency=medium zoneminder (1.28.1-8) unstable; urgency=medium

View File

@ -8,7 +8,6 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
php_flag register_globals off
Options Indexes FollowSymLinks Options Indexes FollowSymLinks
<IfModule mod_dir.c> <IfModule mod_dir.c>
DirectoryIndex index.php DirectoryIndex index.php

View File

@ -5,7 +5,11 @@ Maintainer: Dmitry Smirnov <onlyjob@debian.org>
Uploaders: Vagrant Cascadian <vagrant@debian.org> Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake ,cmake
,libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libavdevice-ffmpeg-dev ,libavdevice-dev (>= 6:10~)
,libavcodec-dev (>= 6:10~)
,libavformat-dev (>= 6:10~)
,libavutil-dev (>= 6:10~)
,libswscale-dev (>= 6:10~)
,libbz2-dev ,libbz2-dev
,libgcrypt-dev ,libgcrypt-dev
,libcurl4-gnutls-dev ,libcurl4-gnutls-dev
@ -21,6 +25,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libphp-serialization-perl ,libphp-serialization-perl
,libsys-mmap-perl [!hurd-any] ,libsys-mmap-perl [!hurd-any]
,libwww-perl ,libwww-perl
,libdata-uuid-perl
# Unbundled (dh_linktree): # Unbundled (dh_linktree):
,libjs-jquery ,libjs-jquery
,libjs-mootools ,libjs-mootools
@ -41,30 +46,32 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libphp-serialization-perl ,libphp-serialization-perl
,libmodule-load-conditional-perl ,libmodule-load-conditional-perl
,libnet-sftp-foreign-perl ,libnet-sftp-foreign-perl
,libarchive-zip-perl ,libarchive-zip-perl
,libdbd-mysql-perl ,libdbd-mysql-perl
,libdevice-serialport-perl ,libdevice-serialport-perl
,libimage-info-perl ,libimage-info-perl
,libjson-any-perl ,libjson-any-perl
,libsys-mmap-perl [!hurd-any] ,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl ,liburi-encode-perl
,libwww-perl ,libwww-perl
,libdata-dump-perl ,libdata-dump-perl
,libclass-std-fast-perl ,libclass-std-fast-perl
,libsoap-wsdl-perl ,libsoap-wsdl-perl
,libio-socket-multicast-perl ,libio-socket-multicast-perl
,libdigest-sha-perl ,libdigest-sha-perl
,libsys-cpu-perl, libsys-meminfo-perl ,libsys-cpu-perl, libsys-meminfo-perl
,libdata-uuid-perl
,mysql-client | virtual-mysql-client ,mysql-client | virtual-mysql-client
,perl-modules ,perl-modules
,php5-mysql, php5-gd ,php5-mysql | php-mysql, php5-gd | php-gd
,policykit-1 ,policykit-1
,rsyslog | system-log-daemon ,rsyslog | system-log-daemon
,zip ,zip
Recommends: ${misc:Recommends} Recommends: ${misc:Recommends}
,libapache2-mod-php5 | php5-fpm ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | virtual-mysql-server ,mysql-server | virtual-mysql-server
,zoneminder-doc (>= ${source:Version}) ,zoneminder-doc (>= ${source:Version})
,ffmpeg
Suggests: fcgiwrap, logrotate Suggests: fcgiwrap, logrotate
Description: video camera security and surveillance solution Description: video camera security and surveillance solution
ZoneMinder is intended for use in single or multi-camera video security ZoneMinder is intended for use in single or multi-camera video security

View File

@ -130,6 +130,28 @@ depend on it.
curl -XDELETE http://server/zm/api/monitors/1.json curl -XDELETE http://server/zm/api/monitors/1.json
Arm/Disarm monitors
^^^^^^^^^^^^^^^^^^^^
This command will force an alarm on Monitor 1:
::
curl http://server/zm/api/monitors/alarm/id:1/command:on.json
This command will disable the alarm on Monitor 1:
::
curl http://server/zm/api/monitors/alarm/id:1/command:off.json
This command will report the status of the alarm Monitor 1:
::
curl http://server/zm/api/monitors/alarm/id:1/command:status.json
Return a list of all events Return a list of all events
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -216,6 +238,26 @@ Return a list of events for all monitors within a specified date/time range
curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json" curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json"
Return event count based on times and conditions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The API also supports a handy mechanism to return a count of events for a period of time.
This returns number of events per monitor that were recorded in the last one hour
::
curl "http://server/zm/api/events/consoleEvents/1%20hour.json"
This returns number of events per monitor that were recorded in the last day where there were atleast 10 frames that were alarms"
::
curl "http://server/zm/api/events/consoleEvents/1%20day.json/AlarmFrames >=: 10.json"
Configuration Apis Configuration Apis
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^

View File

@ -61,16 +61,19 @@ sub _notify_response
} }
sub send_multi() { sub send_multi() {
my ($self, $address, $port, $data) = @_; my ($self, $address, $port, $utf8_string) = @_;
my $destination = $address . ':' . $port; my $destination = $address . ':' . $port;
my $socket = IO::Socket::Multicast->new(PROTO => 'udp', my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
LocalPort=>$port, PeerAddr=>$destination, ReuseAddr=>1) LocalPort=>$port, PeerAddr=>$destination, ReuseAddr=>1)
or die 'Cannot open multicast socket to ' . ${address} . ':' . ${port}; or die 'Cannot open multicast socket to ' . ${address} . ':' . ${port};
my $bytes = $utf8_string;
utf8::encode($bytes);
$socket->mcast_ttl(1); $socket->mcast_ttl(1);
$socket->send($data); $socket->send($bytes);
} }
sub receive_multi() { sub receive_multi() {

View File

@ -0,0 +1,55 @@
package WSDiscovery10::Elements::Header;
use strict;
use warnings;
__PACKAGE__->_set_element_form_qualified(0);
sub get_xmlns { 'http://schemas.xmlsoap.org/soap/envelope/' };
our $XML_ATTRIBUTE_CLASS;
undef $XML_ATTRIBUTE_CLASS;
sub __get_attr_class {
return $XML_ATTRIBUTE_CLASS;
}
use Class::Std::Fast::Storable constructor => 'none';
use base qw(SOAP::WSDL::XSD::Typelib::ComplexType);
Class::Std::initialize();
{ # BLOCK to scope variables
my %Action_of :ATTR(:get<Action>);
my %MessageID_of :ATTR(:get<MessageID>);
my %ReplyTo_of :ATTR(:get<ReplyTo>);
my %To_of :ATTR(:get<To>);
__PACKAGE__->_factory(
[ qw( Action MessageID ReplyTo To ) ],
{
'Action' => \%Action_of,
'MessageID' => \%MessageID_of,
'ReplyTo' => \%ReplyTo_of,
'To' => \%To_of,
},
{
'Action' => 'WSDiscovery10::Elements::Action',
'MessageID' => 'WSDiscovery10::Elements::MessageID',
'ReplyTo' => 'WSDiscovery10::Elements::ReplyTo',
'To' => 'WSDiscovery10::Elements::To',
},
{
'Action' => '',
'MessageID' => '',
'ReplyTo' => '',
'To' => '',
}
);
} # end BLOCK
1;

View File

@ -34,7 +34,11 @@ sub ProbeOp {
}, },
header => { header => {
'use' => 'literal',
namespace => 'http://schemas.xmlsoap.org/ws/2004/08/addressing',
encodingStyle => '',
parts => [qw( WSDiscovery10::Elements::Header )],
}, },
headerfault => { headerfault => {

View File

@ -23,7 +23,12 @@ our $typemap_1 = {
'ProbeMatches/ProbeMatch/Types' => 'WSDiscovery10::Types::QNameListType', 'ProbeMatches/ProbeMatch/Types' => 'WSDiscovery10::Types::QNameListType',
'ProbeMatches/ProbeMatch/EndpointReference' => 'WSDiscovery10::Types::EndpointReferenceType', 'ProbeMatches/ProbeMatch/EndpointReference' => 'WSDiscovery10::Types::EndpointReferenceType',
'ProbeMatches/ProbeMatch/EndpointReference/ReferenceProperties' => 'WSDiscovery10::Types::ReferencePropertiesType', 'ProbeMatches/ProbeMatch/EndpointReference/ReferenceProperties' => 'WSDiscovery10::Types::ReferencePropertiesType',
'ProbeMatches/ProbeMatch/EndpointReference/PortType' => 'WSDiscovery10::Types::AttributedQName' 'ProbeMatches/ProbeMatch/EndpointReference/PortType' => 'WSDiscovery10::Types::AttributedQName',
'MessageID' => '__SKIP__',
'RelatesTo' => '__SKIP__',
'To' => '__SKIP__',
'Action' => '__SKIP__',
'AppSequence' => '__SKIP__',
}; };
; ;

View File

@ -7,8 +7,10 @@ __PACKAGE__->_set_element_form_qualified(0);
sub get_xmlns { 'http://schemas.xmlsoap.org/ws/2005/04/discovery' }; sub get_xmlns { 'http://schemas.xmlsoap.org/ws/2005/04/discovery' };
our $XML_ATTRIBUTE_CLASS; our $XML_ATTRIBUTE_CLASS = 'WSDiscovery10::Types::ProbeType::_ProbeType::XmlAttr';
undef $XML_ATTRIBUTE_CLASS;
#our $XML_ATTRIBUTE_CLASS;
#undef $XML_ATTRIBUTE_CLASS;
sub __get_attr_class { sub __get_attr_class {
return $XML_ATTRIBUTE_CLASS; return $XML_ATTRIBUTE_CLASS;
@ -49,11 +51,55 @@ __PACKAGE__->_factory(
} # end BLOCK } # end BLOCK
package WSDiscovery10::Types::ProbeType::_ProbeType::XmlAttr;
#use base qw(SOAP::WSDL::XSD::Typelib::ComplexType);
use Class::Std::Fast::Storable constructor => 'none', cache => 1;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
{ # BLOCK to scope variables
my %Attribs_of :ATTR(:get<Attribs>);
sub new
{
my $self = pop @{ Class::Std::Fast::OBJECT_CACHE_REF()->{ $_[0] } };
$self = bless \(my $o = Class::Std::Fast::ID()), $_[0]
if not defined $self;
$self->BUILD(${$self}, $_[1]);
return $self;
}
sub BUILD
{
my ($self, $ident, $arg_ref) = @_;
$Attribs_of{$ident} = $arg_ref;
}
# without this no attributes are serialized
# SOAP::WSDL::XSD::Typelib::CompexType sub serialize_attr()
sub as_bool :BOOLIFY { 1 }
sub serialize()
{
my $ident = ${ $_[0] };
my $option_ref = $_[1];
my $attr_str = "";
foreach my $attr (keys %{$Attribs_of{$ident}})
{
my $value = %{$Attribs_of{$ident}}{$attr};
$attr_str .= " $attr=\"$value\"";
}
return $attr_str;
}
} # end BLOCK
1; 1;

View File

@ -25,13 +25,14 @@
# #
use Getopt::Std; use Getopt::Std;
use Data::UUID;
require ONVIF::Client; require ONVIF::Client;
require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort; require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort;
require WSDiscovery10::Elements::Header;
require WSDiscovery10::Elements::Types; require WSDiscovery10::Elements::Types;
require WSDiscovery10::Elements::Scopes; require WSDiscovery10::Elements::Scopes;
require WSDiscovery10::Elements::To;
require WSDiscovery::TransportUDP; require WSDiscovery::TransportUDP;
@ -169,6 +170,8 @@ sub discover
## try both soap versions ## try both soap versions
my %services; my %services;
my $uuid_gen = Data::UUID->new();
if($verbose) { if($verbose) {
print "Probing for SOAP 1.1\n" print "Probing for SOAP 1.1\n"
} }
@ -177,12 +180,18 @@ sub discover
}); });
$svc_discover->set_soap_version('1.1'); $svc_discover->set_soap_version('1.1');
my $uuid = $uuid_gen->create_str();
my $result = $svc_discover->ProbeOp( my $result = $svc_discover->ProbeOp(
{ # WSDiscovery::Types::ProbeType { # WSDiscovery::Types::ProbeType
Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType
Scopes => { value => '' }, Scopes => { value => '' },
}, },
WSDiscovery10::Elements::To->new({ value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }) WSDiscovery10::Elements::Header->new({
Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' },
MessageID => { value => "urn:uuid:$uuid" },
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
})
); );
# print $result . "\n"; # print $result . "\n";
@ -197,12 +206,22 @@ sub discover
}); });
$svc_discover->set_soap_version('1.2'); $svc_discover->set_soap_version('1.2');
# copies of the same Probe message must have the same MessageID.
# This is not a copy. So we generate a new uuid.
$uuid = $uuid_gen->create_str();
$result = $svc_discover->ProbeOp( $result = $svc_discover->ProbeOp(
{ # WSDiscovery::Types::ProbeType { # WSDiscovery::Types::ProbeType
Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType xmlattr => { 'xmlns:dn' => 'http://www.onvif.org/ver10/network/wsdl',
'xmlns:tds' => 'http://www.onvif.org/ver10/device/wsdl', },
Types => 'dn:NetworkVideoTransmitter tds:Device', # QNameListType
Scopes => { value => '' }, Scopes => { value => '' },
}, },
WSDiscovery10::Elements::To->new({ value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }) WSDiscovery10::Elements::Header->new({
Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' },
MessageID => { value => "urn:uuid:$uuid" },
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
})
); );
# print $result . "\n"; # print $result . "\n";

View File

@ -30,6 +30,7 @@ use warnings;
require Exporter; require Exporter;
require ZoneMinder::Base; require ZoneMinder::Base;
use ZoneMinder::ConfigData qw(:all);
our @ISA = qw(Exporter ZoneMinder::Base); our @ISA = qw(Exporter ZoneMinder::Base);
@ -44,7 +45,12 @@ use vars qw( %Config );
our @EXPORT_CONFIG = qw( %Config ); # Get populated by BEGIN our @EXPORT_CONFIG = qw( %Config ); # Get populated by BEGIN
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (
'constants' => [ qw( functions => [ qw(
zmConfigLoad
loadConfigFromDB
saveConfigToDB
) ],
constants => [ qw(
ZM_PID ZM_PID
) ] ) ]
); );
@ -96,20 +102,134 @@ BEGIN
} }
$sth->finish(); $sth->finish();
#$dbh->disconnect(); #$dbh->disconnect();
if ( ! exists $Config{ZM_SERVER_ID} ) {
$Config{ZM_SERVER_ID} = undef;
$sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' );
if ( $Config{ZM_SERVER_NAME} ) {
$res = $sth->execute( $Config{ZM_SERVER_NAME} );
my $result = $sth->fetchrow_hashref();
$Config{ZM_SERVER_ID} = $$result{Id};
} elsif ( $Config{ZM_SERVER_HOST} ) {
$res = $sth->execute( $Config{ZM_SERVER_HOST} );
my $result = $sth->fetchrow_hashref();
$Config{ZM_SERVER_ID} = $$result{Id};
}
$sth->finish();
}
}
if ( ! exists $Config{ZM_SERVER_ID} ) { sub loadConfigFromDB {
$Config{ZM_SERVER_ID} = undef; print( "Loading config from DB\n" );
$sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' ); my $dbh = ZoneMinder::Database::zmDbConnect();
if ( $Config{ZM_SERVER_NAME} ) { if ( !$dbh ) {
$res = $sth->execute( $Config{ZM_SERVER_NAME} ); print( "Error: unable to load options from database: $DBI::errstr\n" );
my $result = $sth->fetchrow_hashref(); return( 0 );
$Config{ZM_SERVER_ID} = $$result{Id}; }
} elsif ( $Config{ZM_SERVER_HOST} ) { my $sql = "select * from Config";
$res = $sth->execute( $Config{ZM_SERVER_HOST} ); my $sth = $dbh->prepare_cached( $sql )
my $result = $sth->fetchrow_hashref(); or croak( "Can't prepare '$sql': ".$dbh->errstr() );
$Config{ZM_SERVER_ID} = $$result{Id}; my $res = $sth->execute()
} or croak( "Can't execute: ".$sth->errstr() );
} my $option_count = 0;
while( my $config = $sth->fetchrow_hashref() ) {
my ( $name, $value ) = ( $config->{Name}, $config->{Value} );
#print( "Name = '$name'\n" );
my $option = $options_hash{$name};
if ( !$option ) {
warn( "No option '$name' found, removing" );
next;
}
#next if ( $option->{category} eq 'hidden' );
if ( defined($value) ) {
if ( $option->{type} == $types{boolean} ) {
$option->{value} = $value?"yes":"no";
} else {
$option->{value} = $value;
}
}
$option_count++;;
}
$sth->finish();
return( $option_count );
}
sub saveConfigToDB {
print( "Saving config to DB\n" );
my $dbh = ZoneMinder::Database::zmDbConnect();
if ( !$dbh )
{
print( "Error: unable to save options to database: $DBI::errstr\n" );
return( 0 );
}
my $ac = $dbh->{AutoCommit};
$dbh->{AutoCommit} = 0;
$dbh->do('LOCK TABLE Config WRITE')
or croak( "Can't lock Config table: " . $dbh->errstr() );
my $sql = "delete from Config";
my $res = $dbh->do( $sql )
or croak( "Can't do '$sql': ".$dbh->errstr() );
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?";
my $sth = $dbh->prepare_cached( $sql )
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
foreach my $option ( @options )
{
#next if ( $option->{category} eq 'hidden' );
#print( $option->{name}."\n" ) if ( !$option->{category} );
$option->{db_type} = $option->{type}->{db_type};
$option->{db_hint} = $option->{type}->{hint};
$option->{db_pattern} = $option->{type}->{pattern};
$option->{db_format} = $option->{type}->{format};
if ( $option->{db_type} eq "boolean" )
{
$option->{db_value} = ($option->{value} eq "yes")
? "1"
: "0"
;
}
else
{
$option->{db_value} = $option->{value};
}
if ( my $requires = $option->{requires} )
{
$option->{db_requires} = join( ";",
map {
my $value = $_->{value};
$value = ($value eq "yes")
? 1
: 0
if ( $options_hash{$_->{name}}->{db_type} eq "boolean" )
; ( "$_->{name}=$value" )
} @$requires
);
}
else
{
}
my $res = $sth->execute(
$option->{id},
$option->{name},
$option->{db_value},
$option->{db_type},
$option->{default},
$option->{db_hint},
$option->{db_pattern},
$option->{db_format},
$option->{description},
$option->{help},
$option->{category},
$option->{readonly} ? 1 : 0,
$option->{db_requires}
) or croak( "Can't execute: ".$sth->errstr() );
}
$sth->finish();
$dbh->do('UNLOCK TABLES');
$dbh->{AutoCommit} = $ac;
} }
1; 1;
@ -121,7 +241,7 @@ ZoneMinder::Config - ZoneMinder configuration module.
=head1 SYNOPSIS =head1 SYNOPSIS
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
=head1 DESCRIPTION =head1 DESCRIPTION
@ -136,7 +256,25 @@ namespace of the calling program or module.
Once the configuration has been imported then configuration variables are Once the configuration has been imported then configuration variables are
defined as constants and can be accessed directory by name, e.g. defined as constants and can be accessed directory by name, e.g.
$lang = $Config{ZM_LANG_DEFAULT}; $lang = $Config{ZM_LANG_DEFAULT};
=head1 METHODS
=over 4
=item loadConfigFromDB ();
Loads existing configuration from the database (if any) and merges it with
the definitions held in this module. This results in the merging of any new
configuration and the removal of any deprecated configuration while
preserving the existing values of every else.
=item saveConfigToDB ();
Saves configuration held in memory to the database. The act of loading and
saving configuration is a convenient way to ensure that the configuration
held in the database corresponds with the most recent definitions and that
all components are using the same set of configuration.
=head2 EXPORT =head2 EXPORT
@ -159,7 +297,7 @@ Copyright (C) 2001-2008 Philip Coombes
This library is free software; you can redistribute it and/or modify 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, 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. at your option, any later version of Perl 5 you may have available.
=cut =cut

View File

@ -1,276 +0,0 @@
# ==========================================================================
#
# ZoneMinder Config Admin Module, $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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the debug definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::ConfigAdmin;
use 5.006;
use strict;
use warnings;
require Exporter;
require ZoneMinder::Base;
our @ISA = qw(Exporter ZoneMinder::Base);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
loadConfigFromDB
saveConfigToDB
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'functions'} } );
our @EXPORT = qw();
our $VERSION = $ZoneMinder::Base::VERSION;
# ==========================================================================
#
# Configuration Administration
#
# ==========================================================================
use ZoneMinder::Config qw(:all);
use ZoneMinder::ConfigData qw(:all);
use Carp;
sub loadConfigFromDB
{
print( "Loading config from DB\n" );
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
,$Config{ZM_DB_USER}
,$Config{ZM_DB_PASS}
);
if ( !$dbh )
{
print( "Error: unable to load options from database: $DBI::errstr\n" );
return( 0 );
}
my $sql = "select * from Config";
my $sth = $dbh->prepare_cached( $sql )
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or croak( "Can't execute: ".$sth->errstr() );
my $option_count = 0;
while( my $config = $sth->fetchrow_hashref() )
{
my ( $name, $value ) = ( $config->{Name}, $config->{Value} );
#print( "Name = '$name'\n" );
my $option = $options_hash{$name};
if ( !$option )
{
warn( "No option '$name' found, removing" );
next;
}
#next if ( $option->{category} eq 'hidden' );
if ( defined($value) )
{
if ( $option->{type} == $types{boolean} )
{
$option->{value} = $value?"yes":"no";
}
else
{
$option->{value} = $value;
}
}
$option_count++;;
}
$sth->finish();
$dbh->disconnect();
return( $option_count );
}
sub saveConfigToDB
{
print( "Saving config to DB\n" );
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.";host=".$Config{ZM_DB_HOST}
,$Config{ZM_DB_USER}
,$Config{ZM_DB_PASS}
);
if ( !$dbh )
{
print( "Error: unable to save options to database: $DBI::errstr\n" );
return( 0 );
}
my $ac = $dbh->{AutoCommit};
$dbh->{AutoCommit} = 0;
$dbh->do('LOCK TABLE Config WRITE')
or croak( "Can't lock Config table: " . $dbh->errstr() );
my $sql = "delete from Config";
my $res = $dbh->do( $sql )
or croak( "Can't do '$sql': ".$dbh->errstr() );
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?";
my $sth = $dbh->prepare_cached( $sql )
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
foreach my $option ( @options )
{
#next if ( $option->{category} eq 'hidden' );
#print( $option->{name}."\n" ) if ( !$option->{category} );
$option->{db_type} = $option->{type}->{db_type};
$option->{db_hint} = $option->{type}->{hint};
$option->{db_pattern} = $option->{type}->{pattern};
$option->{db_format} = $option->{type}->{format};
if ( $option->{db_type} eq "boolean" )
{
$option->{db_value} = ($option->{value} eq "yes")
? "1"
: "0"
;
}
else
{
$option->{db_value} = $option->{value};
}
if ( my $requires = $option->{requires} )
{
$option->{db_requires} = join( ";",
map {
my $value = $_->{value};
$value = ($value eq "yes")
? 1
: 0
if ( $options_hash{$_->{name}}->{db_type} eq "boolean" )
; ( "$_->{name}=$value" )
} @$requires
);
}
else
{
}
my $res = $sth->execute(
$option->{id},
$option->{name},
$option->{db_value},
$option->{db_type},
$option->{default},
$option->{db_hint},
$option->{db_pattern},
$option->{db_format},
$option->{description},
$option->{help},
$option->{category},
$option->{readonly} ? 1 : 0,
$option->{db_requires}
) or croak( "Can't execute: ".$sth->errstr() );
}
$sth->finish();
$dbh->do('UNLOCK TABLES');
$dbh->{AutoCommit} = $ac;
$dbh->disconnect();
}
1;
__END__
=head1 NAME
ZoneMinder::ConfigAdmin - ZoneMinder Configuration Administration module
=head1 SYNOPSIS
use ZoneMinder::ConfigAdmin;
use ZoneMinder::ConfigAdmin qw(:all);
loadConfigFromDB();
saveConfigToDB();
=head1 DESCRIPTION
The ZoneMinder:ConfigAdmin module contains the master definition of the
ZoneMinder configuration options as well as helper methods. This module is
intended for specialist confguration management and would not normally be
used by end users.
The configuration held in this module, which was previously in zmconfig.pl,
includes the name, default value, description, help text, type and category
for each option, as well as a number of additional fields in a small number
of cases.
=head1 METHODS
=over 4
=item loadConfigFromDB ();
Loads existing configuration from the database (if any) and merges it with
the definitions held in this module. This results in the merging of any new
configuration and the removal of any deprecated configuration while
preserving the existing values of every else.
=item saveConfigToDB ();
Saves configuration held in memory to the database. The act of loading and
saving configuration is a convenient way to ensure that the configuration
held in the database corresponds with the most recent definitions and that
all components are using the same set of configuration.
=back
=head2 EXPORT
None by default.
The :data tag will export the various configuration data structures
The :functions tag will export the helper functions.
The :all tag will export all above symbols.
=head1 SEE ALSO
http://www.zoneminder.com
=head1 AUTHOR
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2008 Philip Coombes
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

@ -1709,6 +1709,17 @@ our @options =
type => $types{boolean}, type => $types{boolean},
category => "config", category => "config",
}, },
{
name => 'ZM_LD_PRELOAD',
default => '',
description => "Path to library to preload before launching daemons",
help => qqq("Some older cameras require the use of the v4l1 compat
library. This setting allows the setting of the path
to the library, so that it can be loaded by zmdc.pl
before launching zmc."),
type => $types{abs_path},
category => 'config',
},
{ {
name => "ZM_SIGNAL_CHECK_POINTS", name => "ZM_SIGNAL_CHECK_POINTS",
default => "10", default => "10",

View File

@ -75,6 +75,10 @@ $| = 1;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
if ( $Config{ZM_LD_PRELOAD} ) {
Debug("Adding ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD}");
$ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD};
}
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my @daemons = ( my @daemons = (
@ -88,7 +92,7 @@ my @daemons = (
'zmwatch.pl', 'zmwatch.pl',
'zmupdate.pl', 'zmupdate.pl',
'zmtrack.pl', 'zmtrack.pl',
'zmtelemetry.pl', 'zmtelemetry.pl'
); );
my $command = shift @ARGV; my $command = shift @ARGV;
@ -258,7 +262,7 @@ sub run
killAll( 1 ); killAll( 1 );
socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" );
unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE ); unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE );
bind( SERVER, $saddr ) or Fatal( "Can't bind to " . main::SOCK_FILE . ": $!" ); bind( SERVER, $saddr ) or Fatal( "Can't bind to " . main::SOCK_FILE . ": $!" );
listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" );
@ -370,8 +374,8 @@ sub run
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
unlink( main::SOCK_FILE ); unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE );
unlink( ZM_PID ); unlink( ZM_PID ) or Error( "Unable to unlink " . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID );
exit(); exit();
} }
@ -756,8 +760,8 @@ sub shutdownAll
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
unlink( main::SOCK_FILE ); unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE );
unlink( ZM_PID ); unlink( ZM_PID ) or Error( "Unable to unlink " . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID );
close( CLIENT ); close( CLIENT );
close( SERVER ); close( SERVER );
exit(); exit();

View File

@ -226,7 +226,12 @@ if ( $command =~ /^(?:start|restart)$/ )
zmMemTidy(); zmMemTidy();
runCommand( "zmdc.pl startup" ); runCommand( "zmdc.pl startup" );
Info( "Starting up services" . ( $Config{ZM_SERVER_ID} ? " for server $Config{ZM_SERVER_ID}\n" : "\n" ) ); if ( $Config{ZM_SERVER_ID} ) {
Info( "Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}\n");
} else {
Info( "Single server configuration detected. Starting up services." );
}
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors'; my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );

View File

@ -71,7 +71,7 @@ if ( $Config{ZM_TELEMETRY_DATA} )
{ {
print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
my $lastCheck = $Config{ZM_TELEMETRY_LAST_CHECK}; my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
while( 1 ) { while( 1 ) {
my $now = time(); my $now = time();

View File

@ -72,7 +72,6 @@ use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::General qw(:all); use ZoneMinder::General qw(:all);
use ZoneMinder::Database qw(:all); use ZoneMinder::Database qw(:all);
use ZoneMinder::ConfigAdmin qw( :functions );
use POSIX; use POSIX;
use DBI; use DBI;
use Getopt::Long; use Getopt::Long;
@ -155,6 +154,7 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} )
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'"; my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
} }
while( 1 ) while( 1 )
@ -184,10 +184,12 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} )
my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'"; my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'";
my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() ); my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() );
my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() ); my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() );
$lv_sth->finish();
my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'"; my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'";
my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() ); my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() );
my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() ); my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() );
$lc_sth->finish();
} }
else else
{ {
@ -239,14 +241,14 @@ if ( $zoneFix )
} }
$sth->finish(); $sth->finish();
$sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?";
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
foreach my $zone ( @zones ) foreach my $zone ( @zones )
{ {
my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100;
my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100; my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100;
my $zone_area = $zone_width * $zone_height; my $zone_area = $zone_width * $zone_height;
my $monitor_area = $zone->{MonitorWidth} * $zone->{MonitorHeight}; my $monitor_area = $zone->{MonitorWidth} * $zone->{MonitorHeight};
my $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( my $res = $sth->execute(
($zone->{MinAlarmPixels}*$monitor_area)/$zone_area, ($zone->{MinAlarmPixels}*$monitor_area)/$zone_area,
($zone->{MaxAlarmPixels}*$monitor_area)/$zone_area, ($zone->{MaxAlarmPixels}*$monitor_area)/$zone_area,
@ -257,6 +259,7 @@ if ( $zoneFix )
$zone->{Id} $zone->{Id}
) or die( "Can't execute: ".$sth->errstr() ); ) or die( "Can't execute: ".$sth->errstr() );
} }
$sth->finish();
} }
if ( $migrateEvents ) if ( $migrateEvents )
{ {
@ -318,11 +321,13 @@ if ( $migrateEvents )
symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" ); symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" );
rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" ); rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" );
} }
$sth->finish();
print( "Updating configuration.\n" ); print( "Updating configuration.\n" );
$sql = "update Config set Value = ? where Name = 'ZM_USE_DEEP_STORAGE'"; $sql = "update Config set Value = ? where Name = 'ZM_USE_DEEP_STORAGE'";
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
$res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() ); $res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
print( "All events converted.\n\n" ); print( "All events converted.\n\n" );
} }
@ -334,8 +339,8 @@ if ( $migrateEvents )
if ( $freshen ) if ( $freshen )
{ {
print( "\nFreshening configuration in database\n" ); print( "\nFreshening configuration in database\n" );
loadConfigFromDB(); ZoneMinder::Config::loadConfigFromDB();
saveConfigToDB(); ZoneMinder::Config::saveConfigToDB();
} }
# Don't do innoDB upgrade if not interactive # Don't do innoDB upgrade if not interactive
@ -360,10 +365,10 @@ if ( $interactive ) {
$dbh->do(q|SET sql_mode='traditional'|); # Elevate warnings to errors $dbh->do(q|SET sql_mode='traditional'|); # Elevate warnings to errors
print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; print "\nConverting MyISAM tables to InnoDB. Please wait.\n";
foreach (@MyISAM_Tables) { foreach (@MyISAM_Tables) {
my $sql = "ALTER TABLE $_ ENGINE = InnoDB"; my $sql = "ALTER TABLE $_ ENGINE = InnoDB";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
$sth->finish(); $sth->finish();
} }
$dbh->do(q|SET sql_mode=''|); # Set mode back to default $dbh->do(q|SET sql_mode=''|); # Set mode back to default
} }
@ -494,8 +499,8 @@ if ( $version )
print( "\nUpgrading database to version ".ZM_VERSION."\n" ); print( "\nUpgrading database to version ".ZM_VERSION."\n" );
# Update config first of all # Update config first of all
loadConfigFromDB(); ZoneMinder::Config::loadConfigFromDB();
saveConfigToDB(); ZoneMinder::Config::saveConfigToDB();
my $cascade = undef; my $cascade = undef;
if ( $cascade || $version eq "1.19.0" ) if ( $cascade || $version eq "1.19.0" )
@ -1064,6 +1069,7 @@ if ( $version )
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( "$installed_version", "ZM_DYN_DB_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute( "$installed_version", "ZM_DYN_DB_VERSION" ) or die( "Can't execute: ".$sth->errstr() );
$res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); $res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
} }
else else
{ {

View File

@ -35,6 +35,8 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/param.h> #include <sys/param.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <arpa/inet.h> // for debug output
#include <stdio.h> // for snprintf
#ifdef SOLARIS #ifdef SOLARIS
#include <sys/filio.h> // define FIONREAD #include <sys/filio.h> // define FIONREAD
@ -516,6 +518,166 @@ bool Socket::setNoDelay( bool nodelay )
return( true ); return( true );
} }
bool InetSocket::connect( const char *host, const char *serv )
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int s;
char buf[255];
mAddressFamily = AF_UNSPEC;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = getType();
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
s = getaddrinfo(host, serv, &hints, &result);
if (s != 0) {
Error( "connect(): getaddrinfo: %s", gai_strerror(s) );
return( false );
}
/* getaddrinfo() returns a list of address structures.
* Try each address until we successfully connect(2).
* If socket(2) (or connect(2)) fails, we (close the socket
* and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
if (mSd != -1) {
if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1)
break; /* Success */
continue;
}
memset(&buf, 0, sizeof(buf));
if (rp->ai_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1);
}
else if (rp->ai_family == AF_INET6) {
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1);
}
else {
strncpy(buf, "n/a", sizeof(buf)-1);
}
Debug( 1, "connect(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol);
mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (mSd == -1)
continue;
int val = 1;
(void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) );
(void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) );
mAddressFamily = rp->ai_family; /* save AF_ for ctrl and data connections */
if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1)
break; /* Success */
::close(mSd);
}
if (rp == NULL) { /* No address succeeded */
Error( "connect(), Could not connect" );
mAddressFamily = AF_UNSPEC;
return( false );
}
freeaddrinfo(result); /* No longer needed */
mState = CONNECTED;
return( true );
}
bool InetSocket::connect( const char *host, int port )
{
char serv[8];
snprintf(serv, sizeof(serv), "%d", port);
return connect( host, serv );
}
bool InetSocket::bind( const char * host, const char * serv )
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int s;
char buf[255];
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = getType();
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
s = getaddrinfo(host, serv, &hints, &result);
if (s != 0) {
Error( "bind(): getaddrinfo: %s", gai_strerror(s) );
return( false );
}
/* getaddrinfo() returns a list of address structures.
* Try each address until we successfully bind(2).
* If socket(2) (or bind(2)) fails, we (close the socket
* and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
memset(&buf, 0, sizeof(buf));
if (rp->ai_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1);
}
else if (rp->ai_family == AF_INET6) {
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1);
}
else {
strncpy(buf, "n/a", sizeof(buf)-1);
}
Debug( 1, "bind(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol);
mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (mSd == -1)
continue;
mState = DISCONNECTED;
if (::bind(mSd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
::close(mSd);
mSd = -1;
}
if (rp == NULL) { /* No address succeeded */
Error( "bind(), Could not bind" );
return( false );
}
freeaddrinfo(result); /* No longer needed */
return( true );
}
bool InetSocket::bind( const char * serv )
{
return bind( NULL, serv);
}
bool InetSocket::bind( const char * host, int port )
{
char serv[8];
snprintf(serv, sizeof(serv), "%d", port);
return bind( host, serv );
}
bool InetSocket::bind( int port )
{
char serv[8];
snprintf(serv, sizeof(serv), "%d", port);
return bind( NULL, serv );
}
bool TcpInetServer::listen() bool TcpInetServer::listen()
{ {
return( Socket::listen() ); return( Socket::listen() );

View File

@ -399,103 +399,27 @@ public:
class InetSocket : virtual public Socket class InetSocket : virtual public Socket
{ {
protected:
int mAddressFamily;
public: public:
int getDomain() const int getDomain() const
{ {
return( AF_INET ); return( mAddressFamily );
} }
virtual socklen_t getAddrSize() const virtual socklen_t getAddrSize() const
{ {
return( SockAddrInet::addrSize() ); return( SockAddrInet::addrSize() );
} }
protected: protected:
bool resolveLocal( const char *host, const char *serv, const char *proto ) bool connect( const char *host, const char *serv );
{ bool connect( const char *host, int port );
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( host, serv, proto ) );
}
bool resolveLocal( const char *host, int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( host, port, proto ) );
}
bool resolveLocal( const char *serv, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( serv, proto ) );
}
bool resolveLocal( int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( port, proto ) );
}
bool resolveRemote( const char *host, const char *serv, const char *proto ) bool bind( const char *host, const char *serv );
{ bool bind( const char *host, int port );
SockAddrInet *addr = new SockAddrInet; bool bind( const char *serv );
mRemoteAddr = addr; bool bind( int port );
return( addr->resolve( host, serv, proto ) );
}
bool resolveRemote( const char *host, int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mRemoteAddr = addr;
return( addr->resolve( host, port, proto ) );
}
protected:
bool bind( const SockAddrInet &addr )
{
mLocalAddr = new SockAddrInet( addr );
return( Socket::bind() );
}
bool bind( const char *host, const char *serv )
{
if ( !resolveLocal( host, serv, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( const char *host, int port )
{
if ( !resolveLocal( host, port, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( const char *serv )
{
if ( !resolveLocal( serv, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( int port )
{
if ( !resolveLocal( port, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool connect( const SockAddrInet &addr )
{
mRemoteAddr = new SockAddrInet( addr );
return( Socket::connect() );
}
bool connect( const char *host, const char *serv )
{
if ( !resolveRemote( host, serv, getProtocol() ) )
return( false );
return( Socket::connect() );
}
bool connect( const char *host, int port )
{
if ( !resolveRemote( host, port, getProtocol() ) )
return( false );
return( Socket::connect() );
}
}; };
class UnixSocket : virtual public Socket class UnixSocket : virtual public Socket
@ -591,10 +515,6 @@ public:
class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket
{ {
public: public:
bool bind( const SockAddrInet &addr )
{
return( InetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv ) bool bind( const char *host, const char *serv )
{ {
return( InetSocket::bind( host, serv ) ); return( InetSocket::bind( host, serv ) );
@ -612,10 +532,6 @@ public:
return( InetSocket::bind( port ) ); return( InetSocket::bind( port ) );
} }
bool connect( const SockAddrInet &addr )
{
return( InetSocket::connect( addr ) );
}
bool connect( const char *host, const char *serv ) bool connect( const char *host, const char *serv )
{ {
return( InetSocket::connect( host, serv ) ); return( InetSocket::connect( host, serv ) );
@ -642,33 +558,7 @@ public:
class UdpInetClient : public UdpInetSocket class UdpInetClient : public UdpInetSocket
{ {
protected:
bool bind( const SockAddrInet &addr )
{
return( UdpInetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv )
{
return( UdpInetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( UdpInetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( UdpInetSocket::bind( serv ) );
}
bool bind( int port )
{
return( UdpInetSocket::bind( port ) );
}
public: public:
bool connect( const SockAddrInet &addr )
{
return( UdpInetSocket::connect( addr ) );
}
bool connect( const char *host, const char *serv ) bool connect( const char *host, const char *serv )
{ {
return( UdpInetSocket::connect( host, serv ) ); return( UdpInetSocket::connect( host, serv ) );
@ -697,10 +587,6 @@ public:
class UdpInetServer : public UdpInetSocket class UdpInetServer : public UdpInetSocket
{ {
public: public:
bool bind( const SockAddrInet &addr )
{
return( UdpInetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv ) bool bind( const char *host, const char *serv )
{ {
return( UdpInetSocket::bind( host, serv ) ); return( UdpInetSocket::bind( host, serv ) );
@ -812,18 +698,6 @@ public:
class TcpInetServer : public TcpInetSocket class TcpInetServer : public TcpInetSocket
{ {
public: public:
bool bind( const char *host, const char *serv )
{
return( TcpInetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( TcpInetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( TcpInetSocket::bind( serv ) );
}
bool bind( int port ) bool bind( int port )
{ {
return( TcpInetSocket::bind( port ) ); return( TcpInetSocket::bind( port ) );

View File

@ -133,12 +133,11 @@ void zmLoadConfig()
} else { } else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
} }
if ( staticConfig.SERVER_ID ) {
} Debug( 3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID );
if ( ! staticConfig.SERVER_ID ) { } else {
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." ); Debug( 3, "Single server configuration assumed because no Server ID or Name was specified." );
} else { }
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
} }
} }

View File

@ -170,12 +170,20 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
#endif #endif
/* Check the buffer sizes */ /* Check the buffer sizes */
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
size_t insize = av_image_get_buffer_size(in_pf, width, height,1);
#else
size_t insize = avpicture_get_size(in_pf, width, height); size_t insize = avpicture_get_size(in_pf, width, height);
#endif
if(insize != in_buffer_size) { if(insize != in_buffer_size) {
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
return -4; return -4;
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
size_t outsize = av_image_get_buffer_size(out_pf, width, height,1);
#else
size_t outsize = avpicture_get_size(out_pf, width, height); size_t outsize = avpicture_get_size(out_pf, width, height);
#endif
if(outsize < out_buffer_size) { if(outsize < out_buffer_size) {
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
return -5; return -5;

View File

@ -48,6 +48,10 @@ extern "C" {
#else #else
#include <libavcodec/opt.h> #include <libavcodec/opt.h>
#endif #endif
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
#include <libavutil/imgutils.h>
#endif
#elif HAVE_FFMPEG_AVUTIL_H #elif HAVE_FFMPEG_AVUTIL_H
#include <ffmpeg/avutil.h> #include <ffmpeg/avutil.h>
#include <ffmpeg/base64.h> #include <ffmpeg/base64.h>

View File

@ -192,9 +192,14 @@ int FfmpegCamera::Capture( Image &image )
if ( frameComplete ) if ( frameComplete )
{ {
Debug( 3, "Got frame %d", frameCount ); Debug( 3, "Got frame %d", frameCount );
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); av_image_fill_arrays(mFrame->data, mFrame->linesize,
directbuffer, imagePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)mFrame, directbuffer,
imagePixFormat, width, height);
#endif
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
if(mConvertContext == NULL) { if(mConvertContext == NULL) {
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
@ -392,8 +397,13 @@ int FfmpegCamera::OpenFfmpeg() {
Fatal( "Unable to allocate frame for %s", mPath.c_str() ); Fatal( "Unable to allocate frame for %s", mPath.c_str() );
Debug ( 1, "Allocated frames" ); Debug ( 1, "Allocated frames" );
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
#else
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size( imagePixFormat, width, height );
#endif
if( (unsigned int)pSize != imagesize) { if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
} }

View File

@ -643,8 +643,12 @@ LocalCamera::LocalCamera(
#endif #endif
if ( !tmpPicture ) if ( !tmpPicture )
Fatal( "Could not allocate temporary picture" ); Fatal( "Could not allocate temporary picture" );
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
#else
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size( imagePixFormat, width, height );
#endif
if( (unsigned int)pSize != imagesize) { if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
} }
@ -877,7 +881,18 @@ void LocalCamera::Initialise()
#endif #endif
if ( !capturePictures[i] ) if ( !capturePictures[i] )
Fatal( "Could not allocate picture" ); Fatal( "Could not allocate picture" );
avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height ); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(capturePictures[i]->data,
capturePictures[i]->linesize,
(uint8_t*)v4l2_data.buffers[i].start,capturePixFormat,
v4l2_data.fmt.fmt.pix.width,
v4l2_data.fmt.fmt.pix.height, 1);
#else
avpicture_fill( (AVPicture *)capturePictures[i],
(uint8_t*)v4l2_data.buffers[i].start, capturePixFormat,
v4l2_data.fmt.fmt.pix.width,
v4l2_data.fmt.fmt.pix.height );
#endif
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
} }
@ -1035,7 +1050,16 @@ void LocalCamera::Initialise()
#endif #endif
if ( !capturePictures[i] ) if ( !capturePictures[i] )
Fatal( "Could not allocate picture" ); Fatal( "Could not allocate picture" );
avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height ); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(capturePictures[i]->data,
capturePictures[i]->linesize,
(unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i],
capturePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)capturePictures[i],
(unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i],
capturePixFormat, width, height );
#endif
} }
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
@ -2128,10 +2152,17 @@ int LocalCamera::Capture( Image &image )
} }
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
if(conversion_type == 1) { if(conversion_type == 1) {
Debug( 9, "Calling sws_scale to perform the conversion" ); Debug( 9, "Calling sws_scale to perform the conversion" );
/* Use swscale to convert the image directly into the shared memory */ /* Use swscale to convert the image directly into the shared memory */
avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(tmpPicture->data,
tmpPicture->linesize, directbuffer,
imagePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)tmpPicture, directbuffer,
imagePixFormat, width, height );
#endif
sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize );
} }
#endif #endif

View File

@ -36,6 +36,11 @@
#include <linux/videodev2.h> #include <linux/videodev2.h>
#endif // HAVE_LINUX_VIDEODEV2_H #endif // HAVE_LINUX_VIDEODEV2_H
// Required on systems with v4l1 but without v4l2 headers
#ifndef VIDEO_MAX_FRAME
#define VIDEO_MAX_FRAME 32
#endif
#include "zm_ffmpeg.h" #include "zm_ffmpeg.h"
// //

View File

@ -332,8 +332,14 @@ void VideoStream::OpenStream( )
{ {
Panic( "Could not allocate opicture" ); Panic( "Could not allocate opicture" );
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( c->pix_fmt, c->width,
c->height, 1 );
#else
int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); int size = avpicture_get_size( c->pix_fmt, c->width, c->height );
#endif
uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *opicture_buf = (uint8_t *)av_malloc( size );
if ( !opicture_buf ) if ( !opicture_buf )
{ {
@ -344,7 +350,13 @@ void VideoStream::OpenStream( )
#endif #endif
Panic( "Could not allocate opicture_buf" ); Panic( "Could not allocate opicture_buf" );
} }
avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height ); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(opicture->data, opicture->linesize,
opicture_buf, c->pix_fmt, c->width, c->height, 1);
#else
avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt,
c->width, c->height );
#endif
/* if the output format is not identical to the input format, then a temporary /* if the output format is not identical to the input format, then a temporary
picture is needed too. It is then converted to the required picture is needed too. It is then converted to the required
@ -361,7 +373,12 @@ void VideoStream::OpenStream( )
{ {
Panic( "Could not allocate tmp_opicture" ); Panic( "Could not allocate tmp_opicture" );
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( pf, c->width,
c->height,1 );
#else
int size = avpicture_get_size( pf, c->width, c->height ); int size = avpicture_get_size( pf, c->width, c->height );
#endif
uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size );
if ( !tmp_opicture_buf ) if ( !tmp_opicture_buf )
{ {
@ -372,7 +389,14 @@ void VideoStream::OpenStream( )
#endif #endif
Panic( "Could not allocate tmp_opicture_buf" ); Panic( "Could not allocate tmp_opicture_buf" );
} }
avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height ); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(tmp_opicture->data,
tmp_opicture->linesize, tmp_opicture_buf, pf,
c->width, c->height, 1);
#else
avpicture_fill( (AVPicture *)tmp_opicture,
tmp_opicture_buf, pf, c->width, c->height );
#endif
} }
} }
@ -678,14 +702,14 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
#endif #endif
if ( got_packet ) if ( got_packet )
{ {
if ( c->coded_frame->key_frame ) // if ( c->coded_frame->key_frame )
{ // {
#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) //#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2)
pkt->flags |= AV_PKT_FLAG_KEY; // pkt->flags |= AV_PKT_FLAG_KEY;
#else //#else
pkt->flags |= PKT_FLAG_KEY; // pkt->flags |= PKT_FLAG_KEY;
#endif //#endif
} // }
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) if ( pkt->pts != (int64_t)AV_NOPTS_VALUE )
{ {

View File

@ -220,8 +220,13 @@ int RemoteCameraRtsp::PrimeCapture()
if(mRawFrame == NULL || mFrame == NULL) if(mRawFrame == NULL || mFrame == NULL)
Fatal( "Unable to allocate frame(s)"); Fatal( "Unable to allocate frame(s)");
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int pSize = av_image_get_buffer_size( imagePixFormat, width, height, 1 );
#else
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size( imagePixFormat, width, height );
#endif
if( (unsigned int)pSize != imagesize) { if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
} }
@ -452,7 +457,13 @@ int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* even
Debug( 3, "Got frame %d", frameCount ); Debug( 3, "Got frame %d", frameCount );
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(mFrame->data, mFrame->linesize,
directbuffer, imagePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)mFrame, directbuffer,
imagePixFormat, width, height);
#endif
//Video recording //Video recording
if ( recording && !wasRecording ) { if ( recording && !wasRecording ) {
@ -524,8 +535,6 @@ int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* even
return 0; return 0;
} }
} else { } else {
Debug( 4, "Not storing audio" );
}
} }
} // end if video or audio packet } // end if video or audio packet

View File

@ -279,20 +279,17 @@ int RtpCtrlThread::run()
UdpInetSocket rtpCtrlServer; UdpInetSocket rtpCtrlServer;
if ( mRtpSource.getLocalHost() != "" ) if ( mRtpSource.getLocalHost() != "" )
{ {
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" ); if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) )
if ( !rtpCtrlServer.bind( localAddr ) )
Fatal( "Failed to bind RTCP server" ); Fatal( "Failed to bind RTCP server" );
sendReports = false; sendReports = false;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
} }
else else
{ {
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" ); if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) )
if ( !rtpCtrlServer.bind( localAddr ) )
Fatal( "Failed to bind RTCP server" ); Fatal( "Failed to bind RTCP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" ); if ( !rtpCtrlServer.connect( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ) )
if ( !rtpCtrlServer.connect( remoteAddr ) )
Fatal( "Failed to connect RTCP server" ); Fatal( "Failed to connect RTCP server" );
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
sendReports = true; sendReports = true;

View File

@ -123,7 +123,7 @@ private:
} sdes; } sdes;
// BYE // BYE
struct Bye struct
{ {
uint32_t srcN[]; // list of sources uint32_t srcN[]; // list of sources
// can't express trailing text for reason (what does this mean? it's not even english!) // can't express trailing text for reason (what does this mean? it's not even english!)

View File

@ -67,13 +67,17 @@ int RtpDataThread::run()
SockAddrInet localAddr; SockAddrInet localAddr;
UdpInetServer rtpDataSocket; UdpInetServer rtpDataSocket;
if ( mRtpSource.getLocalHost() != "" ) if ( mRtpSource.getLocalHost() != "" ) {
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" ); if ( !rtpDataSocket.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ) )
Fatal( "Failed to bind RTP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
}
else else
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" ); {
if ( !rtpDataSocket.bind( localAddr ) ) if ( !rtpDataSocket.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalDataPort() ) )
Fatal( "Failed to bind RTP server" ); Fatal( "Failed to bind RTP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
}
Select select( 3 ); Select select( 3 );
select.addReader( &rtpDataSocket ); select.addReader( &rtpDataSocket );

View File

@ -234,7 +234,7 @@ int RtspThread::run()
response.reserve( ZM_NETWORK_BUFSIZ ); response.reserve( ZM_NETWORK_BUFSIZ );
if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) )
Fatal( "Unable to connect RTSP socket" ); Fatal( "Unable to connect RTSP socket" );
//Select select( 0.25 ); //Select select( 0.25 );
//select.addReader( &mRtspSocket ); //select.addReader( &mRtspSocket );
@ -248,7 +248,7 @@ int RtspThread::run()
bool authTried = false; bool authTried = false;
if ( mMethod == RTP_RTSP_HTTP ) if ( mMethod == RTP_RTSP_HTTP )
{ {
if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) )
Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); Fatal( "Unable to connect auxiliary RTSP/HTTP socket" );
//Select select( 0.25 ); //Select select( 0.25 );
//select.addReader( &mRtspSocket2 ); //select.addReader( &mRtspSocket2 );
@ -289,13 +289,13 @@ int RtspThread::run()
{ {
if ( isalnum(response[0]) ) if ( isalnum(response[0]) )
{ {
Error( "Response parse failure in '%s'", response.c_str() ); Error( "Response parse failure in '%s'", response.c_str() );
} }
else else
{ {
Error( "Response parse failure, %zd bytes follow", response.size() ); Error( "Response parse failure, %zd bytes follow", response.size() );
if ( response.size() ) if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
} }
return( -1 ); return( -1 );
} }
@ -306,7 +306,7 @@ int RtspThread::run()
mAuthenticator->checkAuthResponse(response); mAuthenticator->checkAuthResponse(response);
Debug(2, "Processed 401 response"); Debug(2, "Processed 401 response");
mRtspSocket.close(); mRtspSocket.close();
if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) )
Fatal( "Unable to reconnect RTSP socket" ); Fatal( "Unable to reconnect RTSP socket" );
Debug(2, "connection should be reopened now"); Debug(2, "connection should be reopened now");
} }

View File

@ -138,6 +138,10 @@ public:
{ {
return( mStop ); return( mStop );
} }
int getAddressFamily ()
{
return mRtspSocket.getDomain();
}
}; };
#endif // ZM_RTSP_H #endif // ZM_RTSP_H

View File

@ -112,7 +112,7 @@ SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
if ( mNetworkType != "IN" ) if ( mNetworkType != "IN" )
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
mAddressType = tokens[1]; mAddressType = tokens[1];
if ( mAddressType != "IP4" ) if ( mAddressType != "IP4" && mAddressType != "IP6" )
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
StringVector addressTokens = split( tokens[2], "/" ); StringVector addressTokens = split( tokens[2], "/" );
if ( addressTokens.size() < 1 ) if ( addressTokens.size() < 1 )

View File

@ -291,18 +291,21 @@ void StreamBase::openComms()
if ( connkey > 0 ) if ( connkey > 0 )
{ {
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey); unsigned int length = snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated.");
length = sizeof(sock_path_lock)-1;
}
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR); lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 ) if ( lock_fd <= 0 )
{ {
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) ); Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
lock_fd = 0; lock_fd = 0;
} }
else if ( flock(lock_fd, LOCK_EX) != 0 ) else if ( flock(lock_fd, LOCK_EX) != 0 )
{ {
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) ); Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
close(lock_fd); close(lock_fd);
lock_fd = 0; lock_fd = 0;
} }
@ -311,19 +314,25 @@ void StreamBase::openComms()
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
} }
sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
if ( sd < 0 ) if ( sd < 0 )
{ {
Fatal( "Can't create socket: %s", strerror(errno) ); Fatal( "Can't create socket: %s", strerror(errno) );
} }
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); length = snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1;
}
unlink( loc_sock_path ); unlink( loc_sock_path );
if ( sizeof(loc_addr.sun_path) < length ) {
Error("Not enough space %d in loc_addr.sun_path for socket file %s", sizeof(loc_addr.sun_path), loc_sock_path );
}
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
loc_addr.sun_family = AF_UNIX; loc_addr.sun_family = AF_UNIX;
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 ) if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1 ) < 0 )
{ {
Fatal( "Can't bind: %s", strerror(errno) ); Fatal( "Can't bind: %s", strerror(errno) );
} }
@ -331,7 +340,7 @@ void StreamBase::openComms()
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
rem_addr.sun_family = AF_UNIX; rem_addr.sun_family = AF_UNIX;
} } // end if connKey > 0
} }
void StreamBase::closeComms() void StreamBase::closeComms()

View File

@ -49,7 +49,7 @@ git submodule update --init --recursive
if [ $DISTRO == "trusty" ]; then if [ $DISTRO == "trusty" ]; then
ln -sf distros/ubuntu1204 debian ln -sf distros/ubuntu1204 debian
else else
ln -sf distros/ubuntu1504 debian ln -sf distros/ubuntu1604 debian
fi; fi;
# Auto-install all ZoneMinder's depedencies using the Debian control file # Auto-install all ZoneMinder's depedencies using the Debian control file

View File

@ -1 +1 @@
1.29.2 1.30.0

View File

@ -1,5 +1,4 @@
<?php <?php
define( "MSG_TIMEOUT", 2.0 ); define( "MSG_TIMEOUT", 2.0 );
define( "MSG_DATA_SIZE", 4+256 ); define( "MSG_DATA_SIZE", 4+256 );
@ -35,8 +34,7 @@ if ( canEdit( 'Monitors' ) )
} }
} }
ajaxResponse( exec( escapeshellcmd( $zmuCommand ) ) ); ajaxResponse( exec( escapeshellcmd( $zmuCommand ) ) );
} else {
ajaxError( 'Insufficient permissions' );
} }
ajaxError( 'Unrecognised action or insufficient permissions' );
?> ?>

View File

@ -394,7 +394,7 @@ tr.log-dbg td {
} }
$exportFile = "zm-log.$exportExt"; $exportFile = "zm-log.$exportExt";
$exportPath = "temp/zm-log-$exportKey.$exportExt"; $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt";
header( "Pragma: public" ); header( "Pragma: public" );
header( "Expires: 0" ); header( "Expires: 0" );

View File

@ -134,8 +134,10 @@ function loadConfigFile() {
continue; continue;
elseif ( preg_match( '/^\s*#/', $str )) elseif ( preg_match( '/^\s*#/', $str ))
continue; continue;
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) {
Configure::write("$matches[1]", "$matches[2]"); Configure::write( $matches[1], $matches[2] );
define( $matches[1], $matches[2] );
}
} }
fclose( $cfg ); fclose( $cfg );
} }

View File

@ -67,10 +67,9 @@ class DATABASE_CONFIG {
public $default = array( public $default = array(
'datasource' => 'Database/Mysql', 'datasource' => 'Database/Mysql',
'persistent' => false, 'persistent' => false,
'host' => '@ZM_DB_HOST@', 'login' => ZM_DB_USER,
'login' => '@ZM_DB_USER@', 'password' => ZM_DB_PASS,
'password' => '@ZM_DB_PASS@', 'database' => ZM_DB_NAME,
'database' => '@ZM_DB_NAME@',
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8', //'encoding' => 'utf8',
); );
@ -85,4 +84,18 @@ class DATABASE_CONFIG {
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8', //'encoding' => 'utf8',
); );
public function __construct() {
if (strpos(ZM_DB_HOST, ':')):
$array = explode(':', ZM_DB_HOST, 2);
if (is_numeric($array[1])):
$this->default['host'] = $array[0];
$this->default['port'] = $array[1];
else:
$this->default['unix_socket'] = $array[1];
endif;
else:
$this->default['host'] = ZM_DB_HOST;
endif;
}
} }

View File

@ -253,11 +253,20 @@ public function beforeFilter() {
} }
// format expected:
// you can changed AlarmFrames to any other named params
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
public function consoleEvents($interval = null) { public function consoleEvents($interval = null) {
$this->Event->recursive = -1; $this->Event->recursive = -1;
$results = array(); $results = array();
$query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE StartTime >= (DATE_SUB(NOW(), interval $interval)) GROUP BY MonitorId;"); $moreconditions ="";
foreach ($this->request->params['named'] as $name => $param) {
$moreconditions = $moreconditions . " AND ".$name.$param;
}
$query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
foreach ($query as $result) { foreach ($query as $result) {
$results[$result['Events']['MonitorId']] = $result[0]['Count']; $results[$result['Events']['MonitorId']] = $result[0]['Count'];

View File

@ -103,7 +103,7 @@ public function beforeFilter() {
$this->Monitor->create(); $this->Monitor->create();
if ($this->Monitor->save($this->request->data)) { if ($this->Monitor->save($this->request->data)) {
$this->daemonControl($this->Monitor->id, 'start', $this->request->data); $this->daemonControl($this->Monitor->id, 'start');
return $this->flash(__('The monitor has been saved.'), array('action' => 'index')); return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
} }
} }
@ -138,7 +138,8 @@ public function beforeFilter() {
'_serialize' => array('message') '_serialize' => array('message')
)); ));
// - restart this monitor after change // - restart this monitor after change
$this->daemonControl($this->Monitor->id, 'restart', $this->request->data); // We don't pass the request data as the monitor object because it may be a subset of the full monitor array
$this->daemonControl( $this->Monitor->id, 'restart' );
} }
/** /**
@ -183,6 +184,86 @@ public function beforeFilter() {
)); ));
} }
// arm/disarm alarms
// expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json
// where M=monitorId
// where C=on|off|status
public function alarm()
{
$id = $this->request->params['named']['id'];
$cmd = strtolower($this->request->params['named']['command']);
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status')
{
throw new BadRequestException(__('Invalid command'));
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
switch ($cmd)
{
case "on":
$q = '-a';
$verbose = "-v";
break;
case "off":
$q = "-c";
$verbose = "-v";
break;
case "status":
$verbose = ""; // zmu has a bug - gives incorrect verbose output in this case
$q = "-s";
break;
}
// form auth key based on auth credentials
$this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value'];
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$config = $this->Config->find('first', $options);
$zmAuthRelay = $config['Config']['Value'];
$auth="";
if ($zmOptAuth)
{
if ($zmAuthRelay == 'hashed')
{
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
$config = $this->Config->find('first', $options);
$zmAuthHashSecret = $config['Config']['Value'];
$time = localtime();
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
$ak = md5($ak);
$auth = " -A ".$ak;
}
elseif ($zmAuthRelay == 'plain')
{
$auth = " -U " .$this->Session->Read('username')." -P ".$this->Session->Read('password');
}
elseif ($zmAuthRelay == 'none')
{
$auth = " -U " .$this->Session->Read('username');
}
}
$shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
$status = exec ($shellcmd);
$this->set(array(
'status' => $status,
'_serialize' => array('status'),
));
}
// Check if a daemon is running for the monitor id // Check if a daemon is running for the monitor id
public function daemonStatus() { public function daemonStatus() {
$id = $this->request->params['named']['id']; $id = $this->request->params['named']['id'];

View File

@ -0,0 +1,155 @@
<?php
App::uses('AppController', 'Controller');
/**
* Servers Controller
*
* @property Server $Server
* @property PaginatorComponent $Paginator
*/
class ServersController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('Paginator', 'RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
$canView = $this->Session->Read('streamPermission');
if ($canView =='None') {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/**
* index method
*
* @return void
*/
public function index() {
$this->Server->recursive = 0;
$options='';
$servers = $this->Server->find('all',$options);
$this->set(array(
'servers' => $servers,
'_serialize' => array('servers')
));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
$this->Server->recursive = 0;
if (!$this->Server->exists($id)) {
throw new NotFoundException(__('Invalid server'));
}
$restricted = '';
$options = array('conditions' => array(
array('Server.' . $this->Server->primaryKey => $id),
$restricted
)
);
$server = $this->Server->find('first', $options);
$this->set(array(
'server' => $server,
'_serialize' => array('server')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Server->create();
if ($this->Server->save($this->request->data)) {
# Might be nice to send it a start request
#$this->daemonControl($this->Server->id, 'start', $this->request->data);
return $this->flash(__('The server has been saved.'), array('action' => 'index'));
}
}
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Server->id = $id;
if (!$this->Server->exists($id)) {
throw new NotFoundException(__('Invalid server'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ($this->Server->save($this->request->data)) {
$message = 'Saved';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
// - restart this server after change
#$this->daemonControl($this->Server->id, 'restart', $this->request->data);
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->Server->id = $id;
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->request->allowMethod('post', 'delete');
#$this->daemonControl($this->Server->id, 'stop');
if ($this->Server->delete()) {
return $this->flash(__('The server has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The server could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
}

View File

@ -0,0 +1,74 @@
<?php
App::uses('AppModel', 'Model');
/**
* Server Model
*
* @property Event $Event
* @property Zone $Zone
*/
class Server extends AppModel {
/**
* Use table
*
* @var mixed False or table name
*/
public $useTable = 'Servers';
/**
* Primary key field
*
* @var string
*/
public $primaryKey = 'Id';
/**
* Display field
*
* @var string
*/
public $displayField = 'Name';
public $recursive = -1;
/**
* Validation rules
*
* @var array
*/
public $validate = array(
'Id' => array(
'numeric' => array(
'rule' => array('numeric'),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasMany associations
*
* @var array
*/
public $hasMany = array(
'Monitor' => array(
'className' => 'Monitor',
'foreignKey' => 'ServerId',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}

View File

@ -0,0 +1,2 @@
echo json_encode($message);
echo json_encode($server);

View File

@ -0,0 +1 @@
echo json_encode($servers);

View File

@ -0,0 +1 @@
echo json_encode($server);

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $message));
echo $xml->asXML();

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $monitors));
echo $xml->asXML();

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $monitor));
echo $xml->asXML();

152
web/includes/Event.php Normal file
View File

@ -0,0 +1,152 @@
<?php
require_once( 'database.php' );
require_once( 'Storage.php' );
class Event {
public function __construct( $IdOrRow ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error("Unable to load Event record for Id=" . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
$row = $IdOrRow;
} else {
Error("Unknown argument passed to Event Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
} else {
Error("No row for Event " . $IdOrRow );
}
} // end function __construct
public function Storage() {
return new Storage( $this->{'StorageId'} );
}
public function __call( $fn, array $args){
if(isset($this->{$fn})){
return $this->{$fn};
#array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args);
}
}
public function Time() {
if ( ! isset( $this->{'Time'} ) ) {
$this->{'Time'} = strtotime($this->{'StartTime'});
}
return $this->{'Time'};
}
public function Path() {
$Storage = $this->Storage();
return $Storage->Path().'/'.$this->Relative_Path();
}
public function Relative_Path() {
$event_path = "";
if ( ZM_USE_DEEP_STORAGE )
{
$event_path =
$this->{'MonitorId'}
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
$this->Time()
)
;
}
else
{
$event_path =
$this->{'MonitorId'}
.'/'.$this->{'Id'}
;
}
return( $event_path );
}
public function LinkPath() {
if ( ZM_USE_DEEP_STORAGE ) {
return $this->{'MonitorId'} .'/'.strftime( "%y/%m/%d/.", $this->Time()).$this->{'Id'};
}
Error("Calling Link_Path when not using deep storage");
return '';
}
public function delete() {
dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) );
if ( !ZM_OPT_FAST_DELETE ) {
dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) );
dbQuery( 'DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}) );
if ( ZM_USE_DEEP_STORAGE ) {
# Assumption: All events haev a start time
$start_date = date_parse( $this->{'StartTime'} );
$start_date['year'] = $start_date['year'] % 100;
$Storage = $this->Storage();
# So this is because ZM creates a link under teh day pointing to the time that the event happened.
$eventlink_path = $Storage->Path().'/'.$this->Link_Path();
if ( $id_files = glob( $eventlink_path ) ) {
# I know we are using arrays here, but really there can only ever be 1 in the array
$eventPath = preg_replace( '/\.'.$event['Id'].'$/', readlink($id_files[0]), $id_files[0] );
deletePath( $eventPath );
deletePath( $id_files[0] );
$pathParts = explode( '/', $eventPath );
for ( $i = count($pathParts)-1; $i >= 2; $i-- ) {
$deletePath = join( '/', array_slice( $pathParts, 0, $i ) );
if ( !glob( $deletePath."/*" ) ) {
deletePath( $deletePath );
}
}
} else {
Warning( "Found no event files under $eventlink_path" );
} # end if found files
} else {
$eventPath = $this->Path();
deletePath( $eventPath );
} # USE_DEEP_STORAGE OR NOT
} # ! ZM_OPT_FAST_DELETE
} # end Event->delete
public function getStreamSrc( $args, $querySep='&amp;' ) {
return ZM_BASE_URL.'/index.php?view=view_video&eid='.$this->{'Id'};
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS;
$args[] = "source=event&event=".$this->{'Id'};
if ( ZM_OPT_USE_AUTH ) {
if ( ZM_AUTH_RELAY == "hashed" ) {
$args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS );
} elseif ( ZM_AUTH_RELAY == "plain" ) {
$args[] = "user=".$_SESSION['username'];
$args[] = "pass=".$_SESSION['password'];
} elseif ( ZM_AUTH_RELAY == "none" ) {
$args[] = "user=".$_SESSION['username'];
}
}
if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) {
$args[] = "connkey=".$GLOBALS['connkey'];
}
if ( ZM_RAND_STREAM ) {
$args[] = "rand=".time();
}
if ( count($args) ) {
$streamSrc .= "?".join( $querySep, $args );
}
return( $streamSrc );
} // end function getStreamSrc
} # end class
?>

103
web/includes/Frame.php Normal file
View File

@ -0,0 +1,103 @@
<?php
require_once( 'database.php' );
require_once( 'Event.php' );
class Frame {
public function __construct( $IdOrRow ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Frames WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error("Unable to load Frame record for Id=" . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
$row = $IdOrRow;
} else {
Error("Unknown argument passed to Frame Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
} else {
Error("No row for Frame " . $IdOrRow );
}
} // end function __construct
public function Storage() {
return $this->Event()->Storage();
}
public function Event() {
return new Event( $this->{'EventId'} );
}
public function __call( $fn, array $args){
if(isset($this->{$fn})){
return $this->{$fn};
#array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args);
}
}
public function Path() {
$Storage = $this->Storage();
return $Storage->Path().'/'.$this->Relative_Path();
}
public function Relative_Path() {
$event_path = "";
if ( ZM_USE_DEEP_STORAGE )
{
$event_path =
$this->{'MonitorId'}
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
$this->Time()
)
;
}
else
{
$event_path =
$this->{'MonitorId'}
.'/'.$this->{'Id'}
;
}
return( $event_path );
}
public function getImageSrc( ) {
return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'};
} // end function getImageSrc
public static function find( $parameters = array(), $limit = NULL ) {
$sql = 'SELECT * FROM Frames';
$values = array();
if ( sizeof($parameters) ) {
$sql .= ' WHERE ' . implode( ' AND ', array_map(
function($v){ return $v.'=?'; },
array_keys( $parameters )
) );
$values = array_values( $parameters );
}
if ( $limit ) {
$sql .= ' LIMIT ' . $limit;
}
$results = dbFetchAll( $sql, NULL, $values );
if ( $results ) {
return array_map( function($id){ return new Frame($id); }, $results );
}
}
public static function find_one( $parameters = array() ) {
$results = Frame::find( $parameters, 1 );
if ( ! sizeof( $results ) ) {
return;
}
return $results[0];
}
} # end class
?>

View File

@ -1,28 +1,28 @@
<?php <?php
require_once( 'database.php' ); require_once( 'database.php' );
class Server {
public function __construct( $IdOrRow = NULL ) { class Server {
$row = NULL; public function __construct( $IdOrRow = NULL ) {
if ( $IdOrRow ) { $row = NULL;
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { if ( $IdOrRow ) {
$row = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array( $IdOrRow ) ); if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
if ( ! $row ) { $row = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array( $IdOrRow ) );
Error("Unable to load Server record for Id=" . $IdOrRow ); if ( ! $row ) {
} Error("Unable to load Server record for Id=" . $IdOrRow );
} elseif ( is_array( $IdOrRow ) ) { }
$row = $IdOrRow; } elseif ( is_array( $IdOrRow ) ) {
} $row = $IdOrRow;
} # end if isset($IdOrRow) }
if ( $row ) { } # end if isset($IdOrRow)
foreach ($row as $k => $v) { if ( $row ) {
$this->{$k} = $v; foreach ($row as $k => $v) {
} $this->{$k} = $v;
} else { }
$this->{'Name'} = ''; } else {
$this->{'Hostname'} = ''; $this->{'Name'} = '';
} $this->{'Hostname'} = '';
} }
}
public static function find_all() { public static function find_all() {
$servers = array(); $servers = array();
$result = dbQuery( 'SELECT * FROM Servers ORDER BY Name'); $result = dbQuery( 'SELECT * FROM Servers ORDER BY Name');
@ -47,11 +47,38 @@ class Server {
return $this->{'Name'}; return $this->{'Name'};
} }
public function __call( $fn, array $args= NULL){ public function __call( $fn, array $args= NULL){
if(isset($this->{$fn})){ if(isset($this->{$fn})){
return $this->{$fn}; return $this->{$fn};
#array_unshift($args, $this); #array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args); #call_user_func_array( $this->{$fn}, $args);
}
} }
}
public static function find( $parameters = array(), $limit = NULL ) {
$sql = 'SELECT * FROM Servers';
$values = array();
if ( sizeof($parameters) ) {
$sql .= ' WHERE ' . implode( ' AND ', array_map(
function($v){ return $v.'=?'; },
array_keys( $parameters )
) );
$values = array_values( $parameters );
}
if ( $limit ) {
$sql .= ' LIMIT ' . $limit;
}
$results = dbFetchAll( $sql, NULL, $values );
if ( $results ) {
return array_map( function($id){ return new Server($id); }, $results );
}
}
public static function find_one( $parameters = array() ) {
$results = Server::find( $parameters, 1 );
if ( ! sizeof( $results ) ) {
return;
}
return $results[0];
}
} }
?> ?>

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