Update to add new api credentials, get_config, set_config, reboot, ping and parse ip/auth from Monitor->Path

pull/3884/head
Isaac Connor 2024-03-21 09:44:03 -04:00
parent 7e26498b80
commit c6e71b19dc
1 changed files with 115 additions and 18 deletions

View File

@ -42,6 +42,10 @@ our @ISA = qw(ZoneMinder::Control);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
our $username = '';
our $password = '';
our $realm = '';
sub new {
my $class = shift;
my $id = shift;
@ -50,32 +54,74 @@ sub new {
return $self;
}
sub credentials {
my $self = shift;
($username, $password) = @_;
}
sub open {
my $self = shift;
$self->loadMonitor();
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
# Has no scheme at the beginning, so won't parse as a URI
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
}
my $uri = URI->new($self->{Monitor}->{ControlAddress});
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
my ( $username, $password );
my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice};
if ( $self->{Monitor}->{ControlAddress} ) {
( $username, $password ) = $uri->authority() =~ /^(.*):(.*)@(.*)$/;
$$self{address} = $uri->host_port();
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
# Testing seems to show that we need the username/password in each url as well as credentials
if ($self->{Monitor}->{ControlAddress}
and
$self->{Monitor}{ControlAddress} ne 'user:pass@ip'
and
$self->{Monitor}{ControlAddress} ne 'user:port@ip'
) {
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
# Has no scheme at the beginning, so won't parse as a URI
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
}
my $uri = URI->new($self->{Monitor}->{ControlAddress});
$realm = 'Login to ' . $self->{Monitor}->{ControlDevice};
if ($self->{Monitor}->{ControlAddress}) {
if ( $uri->userinfo()) {
( $username, $password ) = $uri->userinfo() =~ /^(.*):(.*)$/;
} else {
$username = $self->{Monitor}->{User};
$password = $self->{Monitor}->{Pass};
}
$$self{address} = $uri->host_port();
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
# Testing seems to show that we need the username/password in each url as well as credentials
$$self{base_url} = $uri->canonical();
Debug('Using initial credentials for '.$uri->host_port().", $realm, $username, $password, base_url: $$self{base_url} auth:".$uri->authority());
}
} elsif ( $self->{Monitor}{Path}) {
my $uri = URI->new($self->{Monitor}{Path});
Debug("Using Path for credentials: $self->{Monitor}{Path} " . $uri->userinfo());
if ( $uri->userinfo()) {
( $username, $password ) = $uri->userinfo() =~ /^(.*):(.*)$/;
} else {
$username = $self->{Monitor}->{User};
$password = $self->{Monitor}->{Pass};
$uri->userinfo($username.':'.$password);
}
$uri->scheme('http');
$uri->port(80);
$uri->path_query('');
$$self{base_url} = $uri->canonical();
Debug('Using initial credentials for '.$uri->host_port().", $realm, $username, $password, base_url: $$self{base_url} auth:".$uri->authority());
$$self{address} = $uri->host_port();
Debug("User auth $username $password " . $uri->authority() . ' ' . $uri->host_port());
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
chomp $$self{base_url};
Debug("Base_url is ".$$self{base_url});
} else {
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
}
my $url = $$self{base_url}.'cgi-bin/magicBox.cgi?action=getDeviceType';
# Detect REALM, has to be /cgi-bin/ptz.cgi because just / accepts no auth
my $res = $self->{ua}->get($$self{base_url}.'cgi-bin/magicBox.cgi?action=getDeviceType');
my $res = $self->get($url);
if ( $res->is_success ) {
$self->{state} = 'open';
@ -96,14 +142,14 @@ sub open {
$realm = $1;
Debug("Changing REALM to ($realm)");
$self->{ua}->credentials($$self{address}, $realm, $username, $password);
$res = $self->{ua}->get($$self{base_url}.'cgi-bin/ptz.cgi');
$res = $self->get($url);
if ( $res->is_success() ) {
$self->{state} = 'open';
return;
return !undef;
} elsif ( $res->status_line eq '400 Bad Request' ) {
# In testing, this second request fails with Bad Request, I assume because we didn't actually give it a command.
$self->{state} = 'open';
return;
return !undef;
} else {
Error('Authentication still failed after updating REALM' . $res->status_line);
$headers = $res->headers();
@ -133,6 +179,15 @@ sub close {
$self->{state} = 'closed';
}
sub get {
my $self = shift;
my $url = shift;
Debug("Getting $url");
my $response = $self->{ua}->get($url);
Debug('Response: '. $response->status_line . ' ' . $response->content);
return $response;
}
sub sendCmd {
my $self = shift;
my $cmd = shift;
@ -364,6 +419,48 @@ sub zoomConWide {
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
}
my %config_urls = (
caps => 'cgi-bin/encode.cgi?action=getCaps',
encode1 => 'cgi-bin/encode.cgi?action=getConfigCaps&channel=1',
);
sub get_config {
my $self = shift;
my %config;
foreach my $cat ( keys %config_urls ) {
my $url = $$self{base_url}.$config_urls{$cat};
my $response = $self->get($url);
if ($response->is_success()) {
my $resp = $response->decoded_content;
$config{$cat} = ZoneMinder::General::parseNameEqualsValueToHash($resp);
}
Warning("Failed to get config from $url: " . $response->status_line());
} # end foreach
return keys %config ? \%config : undef;
} # end sub get_config
sub set_config {
my $self = shift;
my $diff = shift;
my $url = $$self{base_url}.'/cgi-bin/configManager.cgi?action=setConfig'.
join('&', map { $_.'='.uri_encode($$diff{$_}) } keys %$diff);
my $response = $self->{ua}->get($url);
Debug($response->content);
return $response->is_success();
}
sub reboot {
my $self = shift;
my $response = $self->{ua}->post( $$self{base_url}.'/cgi-bin/setparam.cgi', {
system_reset => 1
});
return $response->is_success();
}
1;
__END__