Merge ../ZoneMinder.connortechnology.bad into storageareas
This commit is contained in:
commit
520c41da23
|
@ -39,6 +39,8 @@ sub AUTOLOAD
|
|||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
## This seems odd... if the method existed would we even be here?
|
||||
## https://perldoc.perl.org/perlsub.html#Autoloading
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
|
@ -46,9 +48,17 @@ sub AUTOLOAD
|
|||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
# FIXME: Do we really have to open a new connection every time?
|
||||
|
||||
#Digest usernbme="bdmin", reblm="Login to 4K05DB3PAJE98BE", nonae="1720242756",
|
||||
#uri="/agi-bin/ptz.agi?bation=getStbtus&ahbnnel=1", response="10dd925b26ebd559353734635b859b8b",
|
||||
#opbque="1a99677524b4ae63bbe3a132b2e9b38e3b163ebd", qop=buth, na=00000001, anonae="ab1bb5d43aa5d542"
|
||||
|
||||
sub open
|
||||
{
|
||||
#Debug("&open invoked by: " . (caller(1))[3]);
|
||||
my $self = shift;
|
||||
my $cgi = shift || '/cgi-bin/configManager.cgi?action=getConfig&name=Ptz';
|
||||
$self->loadMonitor();
|
||||
|
||||
# The Dahua camera firmware API supports the concept of having multiple
|
||||
|
@ -73,60 +83,55 @@ sub open
|
|||
}
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua} = LWP::UserAgent->new(keep_alive => 1);
|
||||
$self->{ua}->agent("ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION);
|
||||
$self->{state} = 'closed';
|
||||
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
||||
Debug("sendCmd credentials control address:'".$ADDRESS
|
||||
."' realm:'" . $REALM
|
||||
. "' username:'" . $USERNAME
|
||||
. "' password:'".$PASSWORD
|
||||
."'"
|
||||
);
|
||||
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||
|
||||
# Detect REALM
|
||||
my $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz";
|
||||
my $req = HTTP::Request->new(GET=>$get_config_url);
|
||||
my $url = $PROTOCOL . $ADDRESS . $cgi;
|
||||
my $req = HTTP::Request->new(GET=>$url);
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ($res->is_success) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||
my $headers = $res->headers();
|
||||
foreach my $k (keys %$headers) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
}
|
||||
|
||||
if ($$headers{'www-authenticate'}) {
|
||||
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||
Debug("Tokens: " . $tokens);
|
||||
## FIXME: This is necessary because the Dahua spec does not match reality
|
||||
if ($tokens =~ /\w+="([^"]+)"/i) {
|
||||
if ($REALM ne $1) {
|
||||
$REALM = $1;
|
||||
Debug("Changing REALM to '" . $REALM . "'");
|
||||
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||
my $req = HTTP::Request->new(GET=>$get_config_url);
|
||||
my $req = HTTP::Request->new(GET=>$url);
|
||||
$res = $self->{ua}->request($req);
|
||||
|
||||
if ($res->is_success()) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
Debug('Authentication succeeded...');
|
||||
return 1;
|
||||
}
|
||||
Debug('Authentication still failed after updating REALM' . $res->status_line);
|
||||
$headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
} # end foreach
|
||||
} else {
|
||||
Error('Authentication failed, not a REALM problem');
|
||||
} else { ## NOTE: Each of these else conditions is fatal as the command will not be
|
||||
## executed. No use going further.
|
||||
Fatal('Authentication failed: Check username and password.');
|
||||
}
|
||||
} else {
|
||||
Error('Failed to match realm in tokens');
|
||||
Fatal('Authentication failed: Incorrect realm.');
|
||||
} # end if
|
||||
} else {
|
||||
Error('No WWW-Authenticate Header');
|
||||
Fatal('Authentication failed: No www-authenticate header returned.');
|
||||
} # end if headers
|
||||
} # end if $res->status_line() eq '401 Unauthorized'
|
||||
}
|
||||
|
@ -146,45 +151,40 @@ sub printMsg
|
|||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendGetRequest {
|
||||
sub _sendGetRequest {
|
||||
my $self = shift;
|
||||
my $url_path = shift;
|
||||
|
||||
my $result = undef;
|
||||
# Attempt to reuse the connection
|
||||
|
||||
# FIXME: I think we need some sort of keepalive/heartbeat sent to the camera
|
||||
# in order to keep the session alive. As it is, it appears that the
|
||||
# ua's authentication times out or some such.
|
||||
#
|
||||
# This might be of some use:
|
||||
# {"method":"global.keepAlive","params":{"timeout":300,"active":false},"id":1518,"session":"dae233a51c0693519395209b271411b6"}[!http]
|
||||
# The web browser interface POSTs commands as JSON using js
|
||||
|
||||
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
||||
my $req = HTTP::Request->new(GET => $url);
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ($res->is_success) {
|
||||
$result = !undef;
|
||||
return 1;
|
||||
} else {
|
||||
if ($res->status_line() eq '401 Unauthorized') {
|
||||
Debug("Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD);
|
||||
Debug("Content was " . $res->content() );
|
||||
my $res = $self->{ua}->request($req);
|
||||
if ($res->is_success) {
|
||||
$result = !undef;
|
||||
} else {
|
||||
Error("Content was " . $res->content() );
|
||||
return($self->open($url_path)); # if we have to, open a new connection
|
||||
}
|
||||
}
|
||||
if ( ! $result ) {
|
||||
Error("Error check failed: '".$res->status_line());
|
||||
}
|
||||
}
|
||||
return($result);
|
||||
}
|
||||
|
||||
sub sendPtzCommand
|
||||
sub _sendPtzCommand
|
||||
{
|
||||
my $self = shift;
|
||||
my $action = shift;
|
||||
my $command_code = shift;
|
||||
my $arg1 = shift;
|
||||
my $arg2 = shift;
|
||||
my $arg3 = shift;
|
||||
my $arg1 = shift || 0;
|
||||
my $arg2 = shift || 0;
|
||||
my $arg3 = shift || 0;
|
||||
my $arg4 = shift || 0;
|
||||
|
||||
my $channel = $self->{dahua_channel_number};
|
||||
|
||||
|
@ -194,10 +194,12 @@ sub sendPtzCommand
|
|||
$url_path .= "code=" . $command_code . "&";
|
||||
$url_path .= "arg1=" . $arg1 . "&";
|
||||
$url_path .= "arg2=" . $arg2 . "&";
|
||||
$url_path .= "arg3=" . $arg3;
|
||||
$self->sendGetRequest($url_path);
|
||||
$url_path .= "arg3=" . $arg3 . "&";
|
||||
$url_path .= "arg4=" . $arg4;
|
||||
return $self->_sendGetRequest($url_path);
|
||||
}
|
||||
sub sendMomentaryPtzCommand
|
||||
|
||||
sub _sendMomentaryPtzCommand
|
||||
{
|
||||
my $self = shift;
|
||||
my $command_code = shift;
|
||||
|
@ -206,92 +208,195 @@ sub sendMomentaryPtzCommand
|
|||
my $arg3 = shift;
|
||||
my $duration_ms = shift;
|
||||
|
||||
$self->sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
|
||||
$self->_sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
|
||||
my $duration_ns = $duration_ms * 1000;
|
||||
usleep($duration_ns);
|
||||
$self->sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
|
||||
$self->_sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
|
||||
}
|
||||
|
||||
sub _sendAbsolutePositionCommand
|
||||
{
|
||||
my $self = shift;
|
||||
my $arg1 = shift;
|
||||
my $arg2 = shift;
|
||||
my $arg3 = shift;
|
||||
my $arg4 = shift;
|
||||
|
||||
$self->_sendPtzCommand("start", "PositionABS", $arg1, $arg2, $arg3, $arg4);
|
||||
}
|
||||
|
||||
sub moveConLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up Left");
|
||||
$self->_sendMomentaryPtzCommand("Left", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Right" );
|
||||
$self->_sendMomentaryPtzCommand("Right", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConUp
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Up" );
|
||||
$self->_sendMomentaryPtzCommand("Up", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConDown
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Down" );
|
||||
$self->_sendMomentaryPtzCommand("Down", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConUpRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Up Right" );
|
||||
$self->_sendMomentaryPtzCommand("RightUp", 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConDownRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Down Right" );
|
||||
$self->_sendMomentaryPtzCommand("RightDown", 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConUpLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Up Left" );
|
||||
$self->_sendMomentaryPtzCommand("LeftUp", 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveConDownLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Up Right" );
|
||||
$self->_sendMomentaryPtzCommand("LeftDown", 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub zoomConTele
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Zoom Tele" );
|
||||
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub zoomConWide
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Zoom Wide" );
|
||||
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
sub moveRelUpLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up Left");
|
||||
$self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelUp
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up");
|
||||
$self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelUpRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up Right");
|
||||
$self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Left");
|
||||
$self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Right");
|
||||
$self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDownLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down Left");
|
||||
$self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDown
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down");
|
||||
$self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDownRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down Right");
|
||||
$self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub zoomRelTele
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Zoom Relative Tele");
|
||||
$self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
||||
}
|
||||
|
||||
sub zoomRelWide
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Zoom Relative Wide");
|
||||
$self->sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
|
||||
}
|
||||
|
||||
sub focusRelNear
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $response = $self->_sendPtzCommand("start", "FocusNear", 0, 1, 0, 0);
|
||||
Debug("focusRelNear response: " . $response);
|
||||
}
|
||||
|
||||
sub focusRelFar
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $response = $self->_sendPtzCommand("start", "FocusFar", 0, 1, 0, 0);
|
||||
Debug("focusRelFar response: " . $response);
|
||||
}
|
||||
|
||||
sub moveStop
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Stop" );
|
||||
# The command does not matter here, just the stop...
|
||||
$self->_sendPtzCommand("stop", "Up", 0, 0, 1, 0);
|
||||
}
|
||||
|
||||
sub presetClear
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset_id = $self->getParam($params, 'preset');
|
||||
$self->sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
|
||||
$self->_sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
|
||||
}
|
||||
|
||||
|
||||
sub presetSet
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -308,8 +413,8 @@ sub presetSet
|
|||
my $control_preset_row = $sth->fetchrow_hashref();
|
||||
my $new_label_name = $control_preset_row->{'Label'};
|
||||
|
||||
$self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
||||
$self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
||||
$self->_sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
||||
$self->_sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
||||
}
|
||||
|
||||
sub presetGoto
|
||||
|
@ -318,12 +423,39 @@ sub presetGoto
|
|||
my $params = shift;
|
||||
my $preset_id = $self->getParam($params, 'preset');
|
||||
|
||||
$self->sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
|
||||
$self->_sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
|
||||
}
|
||||
|
||||
sub presetHome
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->_sendAbsolutePositionCommand( 0, 0, 0, 1 );
|
||||
}
|
||||
|
||||
sub reset
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Camera Reset" );
|
||||
$self->_sendPtzCommand("Reset", 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
sub reboot
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Camera Reboot" );
|
||||
my $cmd = "cgi-bin/magicBox.cgi?action=reboot";
|
||||
$self->_sendGetRequest($cmd);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
ZoneMinder::Control::Dahua - Perl module for Dahua cameras
|
||||
|
@ -337,10 +469,6 @@ place this in /usr/share/perl5/ZoneMinder/Control
|
|||
|
||||
This module is an implementation of the Dahua IP camera HTTP control API.
|
||||
|
||||
=head2 EXPORT
|
||||
|
||||
None by default.
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2018 ZoneMinder LLC
|
||||
|
@ -359,4 +487,138 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
=head1 Private Methods
|
||||
|
||||
Methods intended for use internally but documented here for future developers.
|
||||
|
||||
=head2 _sendAbsolutePositionCommand( $arg1, $arg2, $arg3, $arg4 )
|
||||
|
||||
Where:
|
||||
|
||||
$arg1 = Horizontal angle 0° to 360°
|
||||
$arg2 = Vertical angle 0° to -90°
|
||||
$arg3 = Zoom multiplier
|
||||
$arg4 = Speed 1 to 8
|
||||
|
||||
This is an private method used to send an absolute position command to the
|
||||
camera.
|
||||
|
||||
=head1 Public Methods
|
||||
|
||||
Methods made available to control.pl via ZoneMinder::Control
|
||||
|
||||
=head2 Notes:
|
||||
|
||||
=over 1
|
||||
|
||||
Which methods are invoked depends on which types of movement are selected in
|
||||
the camera control type. For example: if the 'Can Move Continuous' option is
|
||||
checked, then methods including 'Con' in their names are invoked. Likewise if
|
||||
the 'Can Move Relative" option is checked, then methods including 'Rel' in
|
||||
their names are invoked.
|
||||
|
||||
|
||||
At present, these types of movement are prioritized and exclusive. This applies
|
||||
to all types of movement, not just PTZ, but focus, iris, etc. as well. The options
|
||||
are tested in the following order:
|
||||
|
||||
1. Continuous
|
||||
|
||||
2. Relative
|
||||
|
||||
3. Absolute
|
||||
|
||||
These types are exclusive meaning that the first one that matches is the one
|
||||
ZoneMinder will use to control with. It would be nice to allow the user to
|
||||
select the type used given that some cameras support all three types of
|
||||
movement.
|
||||
|
||||
=back
|
||||
|
||||
=head2 new
|
||||
|
||||
This method instantiates a new control object based upon this control module
|
||||
and sets the 'id' attribute to the value passed in.
|
||||
|
||||
=head2 open
|
||||
|
||||
This method opens an HTTP connection to the camera. It handles authentication,
|
||||
etc. Upon success it sets the 'state' attribute to 'open.'
|
||||
|
||||
=head2 close
|
||||
|
||||
This method effectively closes the HTTP connection to the camera. It sets the
|
||||
'state' attribute to 'close.'
|
||||
|
||||
=head2 printMsg
|
||||
|
||||
This method appears to be used for debugging.
|
||||
|
||||
=head2 moveCon<direction>
|
||||
|
||||
This set of methods invoke continuous movement in the direction indicated by
|
||||
the <direction> portion of their name. They accept no arguments and move the
|
||||
camera at a speed of 1 for 0ms. The speed index of 1 is the lowest of the
|
||||
accepted range of 1-8.
|
||||
|
||||
NOTE:
|
||||
|
||||
This is not true continuous movmement as currently implemented.
|
||||
|
||||
=head2 focusCon<range>
|
||||
|
||||
This set of methods invoke continuous focus in the range direction indicated
|
||||
by the <range> portion of their name. They accept no arguments.
|
||||
|
||||
NOTE:
|
||||
|
||||
This is not true continuous movmement as currently implemented.
|
||||
|
||||
=head2 moveRel<direction>
|
||||
|
||||
This set of methods invoke relatvie movement in the direction indicated by
|
||||
the <direction> portion of their name. They accept no arguments and move the
|
||||
camera at a speed of 4 for 500ms. The speed index of 4 is half-way between
|
||||
the accepted range of 1-8.
|
||||
|
||||
=head2 focusRel<range>
|
||||
|
||||
This set of methods invoke realtive focus in the range direction indicated by
|
||||
the <range> portion of their name. They accept no arguments.
|
||||
|
||||
NOTE:
|
||||
|
||||
This only just does work. The Dahua API specifies "multiples" as the input.
|
||||
We pass in a 1 for that as it does not seem to matter what number (0-8) is
|
||||
provided, the camera focus behaves the same.
|
||||
|
||||
=head2 moveStop
|
||||
|
||||
This method attempts to stop the camera. The problem is that if continuous
|
||||
motion is occurring in multiple directions, this will only stop the motion
|
||||
in the 'Up' direction. Dahua does not support an "all-stop" command.
|
||||
|
||||
=head2 presetHome
|
||||
|
||||
This method "homes" the camera to a preset position. It accepts no arguments.
|
||||
When either continuous or relative movement is enabled, pressing the center
|
||||
button on the movement controls invokes this method.
|
||||
|
||||
NOTE:
|
||||
|
||||
The Dahua protocol does not appear to support a preset Home feature. We could
|
||||
allow the user to assign a preset slot as the "home" slot. Dahua does appear
|
||||
to support naming presets which may lend itself to this sort of thing. At
|
||||
this point, we'll just send the camera back to center and zoom wide. (0°,0°,0)
|
||||
|
||||
=head2 reset
|
||||
|
||||
This method will reset the PTZ controls to their "default." It is not clear
|
||||
what that is.
|
||||
|
||||
=head2 reboot
|
||||
|
||||
This method performs a reboot of the camera. This will take the camera offline
|
||||
for the time it takes to reboot.
|
||||
|
||||
=cut
|
||||
|
|
|
@ -248,15 +248,19 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
|||
|
||||
//Function to check Username length
|
||||
bool checkUser ( const char *username) {
|
||||
if ( strlen(username) > 32) {
|
||||
if ( ! username )
|
||||
return false;
|
||||
}
|
||||
if ( strlen(username) > 32 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//Function to check password length
|
||||
bool checkPass (const char *password) {
|
||||
if ( strlen(password) > 64) {
|
||||
if ( !password )
|
||||
return false;
|
||||
}
|
||||
if ( strlen(password) > 64 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
32
src/zmu.cpp
32
src/zmu.cpp
|
@ -394,7 +394,7 @@ int main(int argc, char *argv[]) {
|
|||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end getopt loop
|
||||
|
||||
if ( optind < argc ) {
|
||||
fprintf(stderr, "Extraneous options, ");
|
||||
|
@ -425,23 +425,27 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
if ( config.opt_use_auth ) {
|
||||
if ( strcmp(config.auth_relay, "none") == 0 ) {
|
||||
if ( !checkUser(username)) {
|
||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
if ( !username ) {
|
||||
fprintf(stderr, "Error, username must be supplied\n");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
|
||||
if ( username ) {
|
||||
user = zmLoadUser(username);
|
||||
if ( !checkUser(username)) {
|
||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
|
||||
user = zmLoadUser(username);
|
||||
} else {
|
||||
|
||||
if ( !(username && password) && !auth ) {
|
||||
fprintf(stderr, "Error, username and password or auth string must be supplied\n");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
if ( auth ) {
|
||||
user = zmLoadAuthUser(auth, false);
|
||||
}
|
||||
if ( username && password ) {
|
||||
if ( !checkUser(username)) {
|
||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
||||
exit_zmu(-1);
|
||||
|
@ -450,19 +454,9 @@ int main(int argc, char *argv[]) {
|
|||
fprintf(stderr, "Error, password greater than allowed 64 characters\n");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||
{
|
||||
if ( auth ) {
|
||||
user = zmLoadAuthUser(auth, false);
|
||||
}
|
||||
}
|
||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||
{
|
||||
if ( username && password ) {
|
||||
user = zmLoadUser(username, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end if username && password
|
||||
} // end if relay or not
|
||||
if ( !user ) {
|
||||
fprintf(stderr, "Error, unable to authenticate user\n");
|
||||
return exit_zmu(-1);
|
||||
|
|
|
@ -1,14 +1,88 @@
|
|||
<?php
|
||||
App::uses('Component', 'Controller');
|
||||
|
||||
class FilterComponent extends Component {
|
||||
|
||||
/**
|
||||
* Valid MySQL operands that can be used from namedParams with two operands
|
||||
* where the right-hand side (RHS) is a literal.
|
||||
* These came from https://dev.mysql.com/doc/refman/8.0/en/non-typed-operators.html
|
||||
*/
|
||||
public $twoOperandSQLOperands = array(
|
||||
'AND',
|
||||
'&&',
|
||||
'=',
|
||||
//':=',
|
||||
//'BETWEEN ... AND ...',
|
||||
//'BINARY',
|
||||
'&',
|
||||
'~',
|
||||
//'|',
|
||||
'^',
|
||||
//'CASE',
|
||||
'DIV',
|
||||
'/',
|
||||
'=',
|
||||
'<=>',
|
||||
'>',
|
||||
'>=',
|
||||
'IS',
|
||||
'IS NOT',
|
||||
//'IS NOT NULL',
|
||||
//'IS NULL',
|
||||
//'->',
|
||||
//'->>',
|
||||
'<<',
|
||||
'<',
|
||||
'<=',
|
||||
'LIKE',
|
||||
'-',
|
||||
'%',
|
||||
'MOD',
|
||||
//'NOT',
|
||||
//'!',
|
||||
//'NOT BETWEEN ... AND ...',
|
||||
'!=',
|
||||
'<>',
|
||||
'NOT LIKE',
|
||||
'NOT REGEXP',
|
||||
// `or` operators aren't safe as they can
|
||||
// be used to skip an existing condition
|
||||
// enforcing access to only certain
|
||||
// monitors/events.
|
||||
//'||',
|
||||
//'OR',
|
||||
'+',
|
||||
'REGEXP',
|
||||
'>>',
|
||||
'RLIKE',
|
||||
'SOUNDS LIKE',
|
||||
//'*',
|
||||
'-',
|
||||
//'XOR',
|
||||
);
|
||||
|
||||
// Build a CakePHP find() condition based on the named parameters
|
||||
// that are passed in
|
||||
public function buildFilter($namedParams) {
|
||||
if ($namedParams) {
|
||||
$conditions = array();
|
||||
|
||||
if ($namedParams) {
|
||||
foreach ($namedParams as $attribute => $value) {
|
||||
// We need to sanitize $attribute to avoid SQL injection.
|
||||
$lhs = trim($attribute);
|
||||
$matches = NULL;
|
||||
if (preg_match('/^(?P<field>[a-z0-9]+)(?P<operator>.+)?$/i', $lhs, $matches) !== 1) {
|
||||
throw new Exception('Invalid argument before `:`: ' . $lhs);
|
||||
}
|
||||
$operator = trim($matches['operator']);
|
||||
|
||||
// Only allow operators on our allow list. No operator
|
||||
// specified defaults to `=` by cakePHP.
|
||||
if ($operator != '' && !in_array($operator, $this->twoOperandSQLOperands)) {
|
||||
throw new Exception('Invalid operator: ' . $operator);
|
||||
}
|
||||
|
||||
$lhs = '`' . $matches['field'] . '` ' . $operator;
|
||||
// If the named param contains an array, we want to turn it into an IN condition
|
||||
// Otherwise, we add it right into the $conditions array
|
||||
if (is_array($value)) {
|
||||
|
@ -18,10 +92,10 @@ class FilterComponent extends Component {
|
|||
array_push($array, $term);
|
||||
}
|
||||
|
||||
$query = array($attribute => $array);
|
||||
$query = array($lhs => $array);
|
||||
array_push($conditions, $query);
|
||||
} else {
|
||||
$conditions[$attribute] = $value;
|
||||
$conditions[$lhs] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,9 +44,8 @@ class EventsController extends AppController {
|
|||
}
|
||||
|
||||
if ( $this->request->params['named'] ) {
|
||||
//$this->FilterComponent = $this->Components->load('Filter');
|
||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
$conditions = $this->request->params['named'];
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
} else {
|
||||
$conditions = array();
|
||||
}
|
||||
|
@ -234,18 +233,34 @@ class EventsController extends AppController {
|
|||
|
||||
public function search() {
|
||||
$this->Event->recursive = -1;
|
||||
// Unmodified conditions to pass to find()
|
||||
$find_conditions = array();
|
||||
// Conditions to be filtered by buildFilter
|
||||
$conditions = array();
|
||||
|
||||
foreach ($this->params['named'] as $param_name => $value) {
|
||||
// Transform params into mysql
|
||||
if ( preg_match('/interval/i', $value, $matches) ) {
|
||||
$condition = array("$param_name >= (date_sub(now(), $value))");
|
||||
// Transform params into conditions
|
||||
if ( preg_match('/^\s?interval\s?/i', $value) ) {
|
||||
if (preg_match('/^[a-z0-9]+$/i', $param_name) !== 1) {
|
||||
throw new Exception('Invalid field name: ' . $param_name);
|
||||
}
|
||||
$matches = NULL;
|
||||
$value = preg_replace('/^\s?interval\s?/i', '', $value);
|
||||
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($value), $matches) !== 1) {
|
||||
throw new Exception('Invalid interval: ' . $value);
|
||||
}
|
||||
$expr = trim($matches['expr']);
|
||||
$unit = trim($matches['unit']);
|
||||
array_push($find_conditions, "$param_name >= DATE_SUB(NOW(), INTERVAL $expr $unit)");
|
||||
} else {
|
||||
$condition = array($param_name => $value);
|
||||
$conditions[$param_name] = $value;
|
||||
}
|
||||
array_push($conditions, $condition);
|
||||
}
|
||||
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
$conditions = $this->FilterComponent->buildFilter($conditions);
|
||||
array_push($conditions, $find_conditions);
|
||||
|
||||
$results = $this->Event->find('all', array(
|
||||
'conditions' => $conditions
|
||||
));
|
||||
|
@ -261,18 +276,32 @@ class EventsController extends AppController {
|
|||
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
|
||||
|
||||
public function consoleEvents($interval = null) {
|
||||
$matches = NULL;
|
||||
// https://dev.mysql.com/doc/refman/5.5/en/expressions.html#temporal-intervals
|
||||
// Examples: `'1-1' YEAR_MONTH`, `'-1 10' DAY_HOUR`, `'1.999999' SECOND_MICROSECOND`
|
||||
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($interval), $matches) !== 1) {
|
||||
throw new Exception('Invalid interval: ' . $interval);
|
||||
}
|
||||
$expr = trim($matches['expr']);
|
||||
$unit = trim($matches['unit']);
|
||||
|
||||
$this->Event->recursive = -1;
|
||||
$results = array();
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
$conditions = $this->FilterComponent->buildFilter($conditions);
|
||||
array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)"));
|
||||
|
||||
$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;");
|
||||
$query = $this->Event->find('all', array(
|
||||
'fields' => array(
|
||||
'MonitorId',
|
||||
'COUNT(*) AS Count',
|
||||
),
|
||||
'conditions' => $conditions,
|
||||
'group' => 'MonitorId',
|
||||
));
|
||||
|
||||
foreach ($query as $result) {
|
||||
$results[$result['Events']['MonitorId']] = $result[0]['Count'];
|
||||
$results[$result['Event']['MonitorId']] = $result[0]['Count'];
|
||||
}
|
||||
|
||||
$this->set(array(
|
||||
|
|
|
@ -40,8 +40,7 @@ class MonitorsController extends AppController {
|
|||
|
||||
if ( $this->request->params['named'] ) {
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
$conditions = $this->request->params['named'];
|
||||
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
} else {
|
||||
$conditions = array();
|
||||
}
|
||||
|
@ -318,6 +317,10 @@ class MonitorsController extends AppController {
|
|||
throw new NotFoundException(__('Invalid monitor'));
|
||||
}
|
||||
|
||||
if (preg_match('/^[a-z]+$/i', $daemon) !== 1) {
|
||||
throw new BadRequestException(__('Invalid command'));
|
||||
}
|
||||
|
||||
$monitor = $this->Monitor->find('first', array(
|
||||
'fields' => array('Id', 'Type', 'Device'),
|
||||
'conditions' => array('Id' => $id)
|
||||
|
|
|
@ -49,6 +49,10 @@ if ( $action == 'donate' && isset($_REQUEST['option']) ) {
|
|||
case 'already' :
|
||||
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
|
||||
break;
|
||||
default :
|
||||
Warning("Unknown value for option in donate: $option");
|
||||
break;
|
||||
} // end switch option
|
||||
$view = 'none';
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -211,7 +211,7 @@ global $user;
|
|||
if ( ZM_OPT_USE_AUTH ) {
|
||||
$close_session = 0;
|
||||
if ( !is_session_started() ) {
|
||||
session_start();
|
||||
zm_session_start();
|
||||
$close_session = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
namespace ZM;
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
|
@ -194,7 +193,7 @@ isset($view) || $view = NULL;
|
|||
isset($request) || $request = NULL;
|
||||
isset($action) || $action = NULL;
|
||||
|
||||
Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
|
||||
ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
|
||||
if (
|
||||
ZM_ENABLE_CSRF_MAGIC &&
|
||||
( $action != 'login' ) &&
|
||||
|
@ -205,17 +204,17 @@ if (
|
|||
( $view != 'archive' )
|
||||
) {
|
||||
require_once( 'includes/csrf/csrf-magic.php' );
|
||||
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
||||
#ZM\Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
||||
csrf_check();
|
||||
}
|
||||
|
||||
# Need to include actions because it does auth
|
||||
if ( $action ) {
|
||||
if ( file_exists('includes/actions/'.$view.'.php') ) {
|
||||
Logger::Debug("Including includes/actions/$view.php");
|
||||
ZM\Logger::Debug("Including includes/actions/$view.php");
|
||||
require_once('includes/actions/'.$view.'.php');
|
||||
} else {
|
||||
Warning("No includes/actions/$view.php for action $action");
|
||||
ZM\Warning("No includes/actions/$view.php for action $action");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +226,7 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
|||
header('HTTP/1.1 401 Unauthorized');
|
||||
exit;
|
||||
}
|
||||
Logger::Debug('Redirecting to login');
|
||||
ZM\Logger::Debug('Redirecting to login');
|
||||
$view = 'none';
|
||||
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
|
||||
$request = null;
|
||||
|
@ -240,7 +239,7 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
|||
CSPHeaders($view, $cspNonce);
|
||||
|
||||
if ( $redirect ) {
|
||||
Logger::Debug("Redirecting to $redirect");
|
||||
ZM\Logger::Debug("Redirecting to $redirect");
|
||||
header('Location: '.$redirect);
|
||||
return;
|
||||
}
|
||||
|
@ -248,7 +247,7 @@ if ( $redirect ) {
|
|||
if ( $request ) {
|
||||
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
|
||||
if ( !file_exists($includeFile) )
|
||||
Fatal("Request '$request' does not exist");
|
||||
ZM\Fatal("Request '$request' does not exist");
|
||||
require_once $includeFile;
|
||||
}
|
||||
return;
|
||||
|
@ -257,7 +256,7 @@ if ( $request ) {
|
|||
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
|
||||
foreach ( $includeFiles as $includeFile ) {
|
||||
if ( !file_exists($includeFile) )
|
||||
Fatal("View '$view' does not exist");
|
||||
ZM\Fatal("View '$view' does not exist");
|
||||
require_once $includeFile;
|
||||
}
|
||||
// If the view overrides $view to 'error', and the user is not logged in, then the
|
||||
|
|
|
@ -33,7 +33,7 @@ var popupSizes = {
|
|||
'cycle': {'addWidth': 32, 'minWidth': 384, 'addHeight': 62},
|
||||
'device': {'width': 260, 'height': 150},
|
||||
'devices': {'width': 400, 'height': 240},
|
||||
'donate': {'width': 500, 'height': 280},
|
||||
'donate': {'width': 500, 'height': 480},
|
||||
'download': {'width': 350, 'height': 215},
|
||||
'event': {'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540},
|
||||
'eventdetail': {'width': 600, 'height': 420},
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
if ( !canEdit( 'System' ) )
|
||||
{
|
||||
$view = "error";
|
||||
if ( !canEdit('System') ) {
|
||||
$view = 'error';
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -46,17 +45,17 @@ xhtmlHeaders(__FILE__, translate('Donate') );
|
|||
</div>
|
||||
<div id="content">
|
||||
<form name="contentForm" id="contentForm" method="post" action="?">
|
||||
<input type="hidden" name="view" value="none"/>
|
||||
<input type="hidden" name="view" value="donate"/>
|
||||
<input type="hidden" name="action" value="donate"/>
|
||||
<p>
|
||||
<?php echo translate('DonateEnticement') ?>
|
||||
</p>
|
||||
<p>
|
||||
<?php echo buildSelect( "option", $options ); ?>
|
||||
<?php echo buildSelect('option', $options); ?>
|
||||
</p>
|
||||
<div id="contentButtons">
|
||||
<input type="submit" value="<?php echo translate('Apply') ?>" data-on-click-this="submitForm">
|
||||
<input type="button" value="<?php echo translate('Close') ?>" data-on-click="closeWindow">
|
||||
<button type="submit"><?php echo translate('Apply') ?></button>
|
||||
<button type="button" data-on-click="closeWindow"><?php echo translate('Close') ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -26,10 +26,11 @@ function cycleNext() {
|
|||
window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout);
|
||||
}
|
||||
function cyclePrev() {
|
||||
if ( monIdx )
|
||||
if (monIdx) {
|
||||
monIdx -= 1;
|
||||
else
|
||||
} else {
|
||||
monIdx = monitorData.length - 1;
|
||||
}
|
||||
|
||||
window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,3 @@
|
|||
function submitForm( element ) {
|
||||
var form = element.form;
|
||||
if ( form.option.selectedIndex == 0 ) {
|
||||
form.view.value = currentView;
|
||||
} else {
|
||||
form.view.value = 'none';
|
||||
}
|
||||
form.submit();
|
||||
}
|
||||
|
||||
if ( action == "donate" && option == "go" ) {
|
||||
zmWindow();
|
||||
}
|
||||
|
|
|
@ -448,10 +448,16 @@ function cmdEnableAlarms() {
|
|||
|
||||
function cmdForceAlarm() {
|
||||
alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm");
|
||||
if (window.event) {
|
||||
window.event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function cmdCancelForcedAlarm() {
|
||||
alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm");
|
||||
if (window.event) {
|
||||
window.event.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue