diff --git a/conf.d/01-system-paths.conf.in b/conf.d/01-system-paths.conf.in index 9f45abdbd..e97433ba2 100644 --- a/conf.d/01-system-paths.conf.in +++ b/conf.d/01-system-paths.conf.in @@ -50,4 +50,4 @@ ZM_PATH_SWAP=@ZM_TMPDIR@ # Full path to optional arp binary # ZoneMinder will find the arp binary automatically on most systems -ZM_PATH_ARP=@ZM_PATH_ARP@ +ZM_PATH_ARP="@ZM_PATH_ARP@" diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 966fa9023..0b811ada0 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -581,7 +581,7 @@ DROP TABLE IF EXISTS `Stats`; CREATE TABLE `Stats` ( `MonitorId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0', - `EventId` int(10) unsigned NOT NULL default '0', + `EventId` BIGINT UNSIGNED NOT NULL, `FrameId` int(10) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0', @@ -780,6 +780,7 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0 INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -- -- Add some monitor preset values -- @@ -821,6 +822,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,' INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); +INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video','0',255,'http','simple','','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); diff --git a/db/zm_update-1.31.46.sql b/db/zm_update-1.31.46.sql new file mode 100644 index 000000000..8c9767e75 --- /dev/null +++ b/db/zm_update-1.31.46.sql @@ -0,0 +1,2 @@ +ALTER TABLE Stats MODIFY COLUMN EventId bigint unsigned NOT NULL; + diff --git a/db/zm_update-1.31.47.sql b/db/zm_update-1.31.47.sql new file mode 100644 index 000000000..155fb29cc --- /dev/null +++ b/db/zm_update-1.31.47.sql @@ -0,0 +1,2 @@ +ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL; + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index 7a86b51a8..d48747703 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -149,7 +149,7 @@ BEGIN { foreach my $str ( <$CONFIG> ) { next if ( $str =~ /^\s*$/ ); next if ( $str =~ /^\s*#/ ); - my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/; if ( ! $name ) { print( STDERR "Warning, bad line in $config_file: $str\n" ); next; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm index e345e5eab..efffd2d19 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm @@ -74,377 +74,351 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); -sub open -{ - my $self = shift; +sub open { + my $self = shift; - $self->loadMonitor(); + $self->loadMonitor(); - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); - $self->{state} = 'open'; + $self->{state} = 'open'; } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); +sub printMsg { + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); - Debug( $msg."[".$msg_len."]" ); + Debug($msg.'['.$msg_len.']'); } -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - my $msg = shift; - my $content_type = shift; - my $result = undef; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $msg = shift; + my $content_type = shift; + my $result = undef; - printMsg( $cmd, "Tx" ); + printMsg($cmd, 'Tx'); - my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/$cmd"; - my $req = HTTP::Request->new( POST => $server_endpoint ); - $req->header('content-type' => $content_type); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); - $req->header('content-length' => length($msg)); - $req->header('accept-encoding' => 'gzip, deflate'); - $req->header('connection' => 'Close'); - $req->content($msg); + my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd; + my $req = HTTP::Request->new(POST => $server_endpoint); + $req->header('content-type' => $content_type); + $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'Close'); + $req->content($msg); - my $res = $self->{ua}->request($req); + my $res = $self->{ua}->request($req); - if ( $res->is_success ) { - $result = !undef; - } else { - Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" ); + if ( $res->is_success ) { + $result = !undef; + } else { + Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'"); + } + return $result; +} + +sub getCamParams { + my $self = shift; + my $msg = '000'; + my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging'; + my $req = HTTP::Request->new(POST => $server_endpoint); + $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); + $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'Close'); + $req->content($msg); + + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + # We should really use an xml or soap library to parse the xml tags + my $content = $res->decoded_content; + + if ( $content =~ /.*(.+)<\/tt:Brightness>.*/ ) { + $CamParams{$1} = $2; } - return( $result ); -} - -sub getCamParams -{ - my $self = shift; - my $msg = '000'; - my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging"; - my $req = HTTP::Request->new( POST => $server_endpoint ); - $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); - $req->header('content-length' => length($msg)); - $req->header('accept-encoding' => 'gzip, deflate'); - $req->header('connection' => 'Close'); - $req->content($msg); - - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) { - # We should really use an xml or soap library to parse the xml tags - my $content = $res->decoded_content; - - if ($content =~ /.*(.+)<\/tt:Brightness>.*/) { - $CamParams{$1} = $2; - } - if ($content =~ /.*(.+)<\/tt:Contrast>.*/) { - $CamParams{$1} = $2; - } - } - else - { - Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" ); + if ( $content =~ /.*(.+)<\/tt:Contrast>.*/ ) { + $CamParams{$1} = $2; } + } else { + Error("Unable to retrieve camera image settings:'".$res->status_line()."'"); + } } #autoStop #This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab -sub autoStop -{ - my $self = shift; - my $autostop = shift; +sub autoStop { + my $self = shift; + my $autostop = shift; - if( $autostop ) { - Debug( "Auto Stop" ); - my $cmd = 'onvif/PTZ'; - my $msg = '000truefalse'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - usleep( $autostop ); - $self->sendCmd( $cmd, $msg, $content_type ); - } + if ( $autostop ) { + Debug('Auto Stop'); + my $cmd = 'onvif/PTZ'; + my $msg = '000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + usleep($autostop); + $self->sendCmd($cmd, $msg, $content_type); + } } # Reset the Camera -sub reset -{ - Debug( "Camera Reset" ); - my $self = shift; - my $cmd = ""; - my $msg = ''; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; - $self->sendCmd( $cmd, $msg, $content_type ); +sub reset { + Debug('Camera Reset'); + my $self = shift; + my $cmd = ''; + my $msg = ''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; + $self->sendCmd($cmd, $msg, $content_type); } #Up Arrow -sub moveConUp -{ - Debug( "Move Up" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConUp { + Debug('Move Up'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Down Arrow -sub moveConDown -{ - Debug( "Move Down" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConDown { + Debug('Move Down'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Left Arrow -sub moveConLeft -{ - Debug( "Move Left" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConLeft { + Debug('Move Left'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Right Arrow -sub moveConRight -{ - Debug( "Move Right" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConRight { + Debug('Move Right'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Zoom In -sub zoomConTele -{ - Debug( "Zoom Tele" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub zoomConTele { + Debug('Zoom Tele'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Zoom Out -sub zoomConWide -{ - Debug( "Zoom Wide" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub zoomConWide { + Debug('Zoom Wide'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Diagonally Up Right Arrow #This camera does not have builtin diagonal commands so we emulate them -sub moveConUpRight -{ - Debug( "Move Diagonally Up Right" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConUpRight { + Debug('Move Diagonally Up Right'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Diagonally Down Right Arrow #This camera does not have builtin diagonal commands so we emulate them -sub moveConDownRight -{ - Debug( "Move Diagonally Down Right" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConDownRight { + Debug('Move Diagonally Down Right'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Diagonally Up Left Arrow #This camera does not have builtin diagonal commands so we emulate them -sub moveConUpLeft -{ - Debug( "Move Diagonally Up Left" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConUpLeft { + Debug('Move Diagonally Up Left'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Diagonally Down Left Arrow #This camera does not have builtin diagonal commands so we emulate them -sub moveConDownLeft -{ - Debug( "Move Diagonally Down Left" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); - $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +sub moveConDownLeft { + Debug('Move Diagonally Down Left'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); + $self->autoStop($self->{Monitor}->{AutoStopTimeout}); } #Stop -sub moveStop -{ - Debug( "Move Stop" ); - my $self = shift; - my $cmd = 'onvif/PTZ'; - my $msg ='000truefalse'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; - $self->sendCmd( $cmd, $msg, $content_type ); +sub moveStop { + Debug('Move Stop'); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd($cmd, $msg, $content_type); } #Set Camera Preset -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; - $self->sendCmd( $cmd, $msg, $content_type ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Set Preset $preset"); + my $cmd = 'onvif/PTZ'; + my $msg ='000'.$preset.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; + $self->sendCmd($cmd, $msg, $content_type); } #Recall Camera Preset -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; - $self->sendCmd( $cmd, $msg, $content_type ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Goto Preset $preset"); + my $cmd = 'onvif/PTZ'; + my $msg ='000'.$preset.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; + $self->sendCmd( $cmd, $msg, $content_type ); } #Horizontal Patrol #To be determined if this camera supports this feature -sub horizontalPatrol -{ - Debug( "Horizontal Patrol" ); - my $self = shift; - my $cmd = ''; - my $msg =''; - my $content_type = ''; -# $self->sendCmd( $cmd, $msg, $content_type ); - Error( "PTZ Command not implemented in control script." ); +sub horizontalPatrol { + Debug('Horizontal Patrol'); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; + # $self->sendCmd( $cmd, $msg, $content_type ); + Error('PTZ Command not implemented in control script.'); } #Horizontal Patrol Stop #To be determined if this camera supports this feature -sub horizontalPatrolStop -{ - Debug( "Horizontal Patrol Stop" ); - my $self = shift; - my $cmd = ''; - my $msg =''; - my $content_type = ''; -# $self->sendCmd( $cmd, $msg, $content_type ); - Error( "PTZ Command not implemented in control script." ); +sub horizontalPatrolStop { + Debug('Horizontal Patrol Stop'); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; + # $self->sendCmd( $cmd, $msg, $content_type ); + Error('PTZ Command not implemented in control script.'); } # Increase Brightness -sub irisAbsOpen -{ - Debug( "Iris $CamParams{'Brightness'}" ); - my $self = shift; - my $params = shift; - $self->getCamParams() unless($CamParams{'Brightness'}); - my $step = $self->getParam( $params, 'step' ); - my $max = 100; +sub irisAbsOpen { + Debug("Iris $CamParams{Brightness}"); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{Brightness}); + my $step = $self->getParam($params, 'step'); + my $max = 100; - $CamParams{'Brightness'} += $step; - $CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max); + $CamParams{Brightness} += $step; + $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max); - my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{'Brightness'}.'true'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; - $self->sendCmd( $cmd, $msg, $content_type ); + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{Brightness}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); } # Decrease Brightness sub irisAbsClose { - Debug( "Iris $CamParams{'Brightness'}" ); - my $self = shift; - my $params = shift; - $self->getCamParams() unless($CamParams{'brightness'}); - my $step = $self->getParam( $params, 'step' ); - my $min = 0; + Debug( "Iris $CamParams{Brightness}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{brightness}); + my $step = $self->getParam( $params, 'step' ); + my $min = 0; - $CamParams{'Brightness'} -= $step; - $CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min); + $CamParams{Brightness} -= $step; + $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min); - my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{'Brightness'}.'true'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; - $self->sendCmd( $cmd, $msg, $content_type ); + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{Brightness}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); } # Increase Contrast -sub whiteAbsIn -{ - Debug( "Iris $CamParams{'Contrast'}" ); - my $self = shift; - my $params = shift; - $self->getCamParams() unless($CamParams{'Contrast'}); - my $step = $self->getParam( $params, 'step' ); - my $max = 100; +sub whiteAbsIn { + Debug("Iris $CamParams{Contrast}"); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{Contrast}); + my $step = $self->getParam( $params, 'step' ); + my $max = 100; - $CamParams{'Contrast'} += $step; - $CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max); + $CamParams{Contrast} += $step; + $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max); - my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{'Contrast'}.'true'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{Contrast}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } # Decrease Contrast -sub whiteAbsOut -{ - Debug( "Iris $CamParams{'Contrast'}" ); - my $self = shift; - my $params = shift; - $self->getCamParams() unless($CamParams{'Contrast'}); - my $step = $self->getParam( $params, 'step' ); - my $min = 0; +sub whiteAbsOut { + Debug("Iris $CamParams{Contrast}"); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{Contrast}); + my $step = $self->getParam($params, 'step'); + my $min = 0; - $CamParams{'Contrast'} -= $step; - $CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min); + $CamParams{Contrast} -= $step; + $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min); - my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{'Contrast'}.'true'; - my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{Contrast}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } 1; - +__END__ diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm deleted file mode 100644 index 827540dce..000000000 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm +++ /dev/null @@ -1,504 +0,0 @@ -# ========================================================================= -# -# ZoneMinder Trendnet TV-IP862IC IP Control Protocol Module, $Date: $, $Revision: $ -# Copyright (C) 2014 Vincent Giovannone -# -# -# ========================================================================== -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# ========================================================================== -# -# This module contains the implementation of the Trendnet TV-IP672PI IP camera control -# protocol. Also works or TV-IP862IC -# -# For Zoneminder 1.26+ -# -# Under control capability: -# -# * Main: name it (suggest TVIP672PI), type is FFMPEG (or remote if you're using MJPEG), protocol is TVIP672PI -# * Main (more): Can wake, can sleep, can reset -# * Move: Can move, can move diagonally, can move mapped, can move relative -# * Pan: Can pan -# * Tilt: Can tilt -# * Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.) -# -# Under control tab in the monitor itself: -# -# * Controllable -# * Control type is the name you gave it in control capability above -# * Control device is the password you use to authenticate to the camera (see further below if you need to change the username from "admin") -# * Control address is the camera's ip address AND web port. example: 192.168.1.1:80 -# -# -# If using with anything but a TV-IP672PI (ex: TV-IP672WI), YOU MUST MATCH THE REALM TO MATCH YOUR CAMERA FURTHER DOWN! -# -# -# Due to how the TVIP672 represents presets internally, you MUST define the presets in order... i.e. 1,2,3,4... not 1,10,3,4. -# (see much further down for why, if you care...) -# - - -package ZoneMinder::Control::TVIP862; - -use 5.006; -use strict; -use warnings; - -require ZoneMinder::Base; -require ZoneMinder::Control; - -our @ISA = qw(ZoneMinder::Control); - -# -# ******** YOU MUST CHANGE THE FOLLOWING LINES TO MATCH YOUR CAMERA! ********** -# -# I assume that "TV-IP672WI" would work for the TV-IP672WI, but can't test since I don't own one. -# -# TV-IP672PI works for the PI version, of course. -# -# Finally, the username is the username you'd like to authenticate as. -# -our $REALM = 'TV-IP862IC'; -our $USERNAME = 'admin'; -our $PASSWORD = ''; -our $ADDRESS = ''; - -# ========================================================================== -# -# Trendnet TV-IP672PI Control Protocol -# -# ========================================================================== - -use ZoneMinder::Logger qw(:all); -use ZoneMinder::Config qw(:all); - -sub open -{ - my $self = shift; - $self->loadMonitor(); - - my ( $protocol, $username, $password, $address ) - = $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/; - if ( $username ) { - $USERNAME = $username; - $PASSWORD = $password; - $ADDRESS = $address; - } else { - Error( "Failed to parse auth from address"); - $ADDRESS = $self->{Monitor}->{ControlAddress}; - } - if ( ! $ADDRESS =~ /:/ ) { - Error( "You generally need to also specify the port. I will append :80" ); - $ADDRESS .= ':80'; - } - - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION ); - $self->{state} = 'open'; -# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string) - Debug ( "sendCmd credentials control address:'".$ADDRESS - ."' realm:'" . $REALM - . "' username:'" . $USERNAME - . "' password:'".$PASSWORD - ."'" - ); - $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); - - # Detect REALM - my $req = HTTP::Request->new( GET=>"http://".$ADDRESS."/cgi/ptdc.cgi" ); - my $res = $self->{ua}->request($req); - - if ( ! $res->is_success ) { - Debug("Need newer REALM"); - if ( $res->status_line() eq '401 Unauthorized' ) { - my $headers = $res->headers(); - foreach my $k ( keys %$headers ) { - Debug("Initial Header $k => $$headers{$k}"); - } # end foreach - if ( $$headers{'www-authenticate'} ) { - my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; - if ( $tokens =~ /\w+="([^"]+)"/i ) { - $REALM = $1; - Debug( "Changing REALM to $REALM" ); - $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); - } # end if - } else { - Debug("No headers line"); - } # end if headers - } # end if $res->status_line() eq '401 Unauthorized' - } # end if ! $res->is_success -} - -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); -} - -sub sendCmd -{ - -# This routine is used for all moving, which are all GET commands... - - my $self = shift; - my $cmd = shift; - - my $result = undef; - - my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd; - my $req = HTTP::Request->new( GET=>$url ); - - Debug ("sendCmd command: " . $url ); - - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) { - $result = !undef; - } else { - if ( $res->status_line() eq '401 Unauthorized' ) { - Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); - Error("Content was " . $res->content() ); - my $res = $self->{ua}->request($req); - if ( $res->is_success ) { - $result = !undef; - } else { - Error("Content was " . $res->content() ); - } - } - if ( ! $result ) { - Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); - } - } - - return( $result ); -} - - - -sub sendCmdPost -{ - -# -# This routine is used for setting/clearing presets and IR commands, which are POST commands... -# - - my $self = shift; - my $url = shift; - my $cmd = shift; - - my $result = undef; - - if ($url eq undef) - { - Error ("url passed to sendCmdPost is undefined."); - return(-1); - } - - Debug ("sendCmdPost url: " . $url . " cmd: " . $cmd); - - my $req = HTTP::Request->new(POST => "http://".$ADDRESS.$url); - $req->content_type('application/x-www-form-urlencoded'); - $req->content($cmd); - - Debug ( "sendCmdPost credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'"); - - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "sendCmdPost Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); - if ( $res->status_line() eq '401 Unauthorized' ) { - Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); - } else { - Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); - } # endif - } - - return( $result ); -} - - - -sub move -{ - my $self = shift; - my $panSteps = shift; - my $tiltSteps = shift; - - my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps"; - $self->sendCmd( $cmd ); -} - -sub moveRelUpLeft -{ - my $self = shift; - Debug( "Move Up Left" ); - $self->move(-3, 3); -} - -sub moveRelUp -{ - my $self = shift; - Debug( "Move Up" ); - $self->move(0, 3); -} - -sub moveRelUpRight -{ - my $self = shift; - Debug( "Move Up Right" ); - $self->move(3, 3); -} - -sub moveRelLeft -{ - my $self = shift; - Debug( "Move Left" ); - $self->move(-3, 0); -} - -sub moveRelRight -{ - my $self = shift; - Debug( "Move Right" ); - $self->move(3, 0); -} - -sub moveRelDownLeft -{ - my $self = shift; - Debug( "Move Down Left" ); - $self->move(-3, -3); -} - -sub moveRelDown -{ - my $self = shift; - Debug( "Move Down" ); - $self->move(0, -3); -} - -sub moveRelDownRight -{ - my $self = shift; - Debug( "Move Down Right" ); - $self->move(3, -3); -} - - -# moves the camera to center on the point that the user clicked on in the video image. -# This isn't mega accurate but good enough for most purposes - -sub moveMap -{ - - # If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!) - # They scale the movement and are here to compensate for manufacturing variation. - # It's never going to be perfect, so just get somewhere in the ballpark and call it a day. - # (Don't forget to kill the zmcontrol process while tweaking!) - - # 1280x800 - my $hscale = 31; - my $vscale = 25; - - # 1280x800 with fisheye - #my $hscale = 15; - #my $vscale = 15; - - # 640x400 - #my $hscale = 14; - #my $vscale = 12; - - - my $self = shift; - my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); - - my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale; - my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale; - - $hor = int($hor); - $ver = -1 * int($ver); - - Debug( "Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver" ); - $self->move( $hor, $ver ); -} - - -# **** PRESETS **** -# -# OK, presets work a little funky but they DO work, provided you define them in order and don't skip any. -# -# The problem is that when you load the web page for this camera, it gives a list of preset names tied to index numbers. -# So let's say you have four presets... A, B, C, and D, and defined them in that order. -# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell the camera to go to a preset, you actually tell it by number, not by name. -# (So "Go to D" is really "go to index 3".) -# -# Now let's say somebody deletes C via the camera's web GUI. The camera re-numbers the existing presets A=0, B=1, D=2. -# There's really no easy way for ZM to discover this re-numbering, so zoneminder would still send "go to preset 3" thinking -# it's telling the camera to go to point D. In actuality it's telling the camera to go to a preset that no longer exists. -# -# As long as you define your presets in order (i.e. define preset 1, then preset 2, then preset 3, etc.) everything will work just -# fine in ZoneMinder. -# -# (Home preset needs to be set via the camera's web gui, and is unaffected by any of this.) -# -# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only through!) ZM AND DON'T SKIP ANY. -# - - -sub presetClear -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - my $cmd = "presetName=$preset&command=del"; - my $url = "/eng/admin/cam_control.cgi"; - Debug ("presetClear: " . $preset . " cmd: " . $cmd); - $self->sendCmdPost($url,$cmd); -} - - -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - my $cmd = "presetName=$preset&command=add"; - my $url = "/eng/admin/cam_control.cgi"; - Debug ("presetSet " . $preset . " cmd: " . $cmd); - $self->sendCmdPost ($url,$cmd); -} - -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - $preset = $preset - 1; - Debug( "Goto Preset $preset" ); - my $cmd = "goto_preset_position&index=$preset"; - $self->sendCmd( $cmd ); -} - -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - my $cmd = "go_home"; - $self->sendCmd( $cmd ); -} - - -# -# **** IR CONTROLS **** -# -# -# Wake: Force IR on, always. (always night mode) -# -# Sleep: Force IR off, always. (always day mode) -# -# Reset: Automatic IR mode. (day/night mode determined by camera) -# - - -sub wake -{ - # force IR on ("always night mode") - - my $self = shift; - my $url = "/eng/admin/adv_audiovideo.cgi"; - my $cmd = "irMode=3"; - - Debug("Wake -- IR on"); - - $self->sendCmdPost ($url,$cmd); -} - -sub sleep -{ - # force IR off ("always day mode") - - my $self=shift; - my $url = "/eng/admin/adv_audiovideo.cgi"; - my $cmd = "irMode=2"; - - Debug("Sleep -- IR off"); - - $self->sendCmdPost ($url,$cmd); -} - -sub reset -{ - # IR auto - - my $self=shift; - my $url = "/eng/admin/adv_audiovideo.cgi"; - my $cmd = "irMode=0"; - - Debug("Reset -- IR auto"); - - $self->sendCmdPost ($url,$cmd); -} - - -1; -__END__ -# Below is stub documentation for your module. You'd better edit it! - -=head1 NAME - -ZoneMinder::Database - Perl extension for Trendnet TVIP672 - -=head1 SYNOPSIS - - use ZoneMinder::Database; - stuff this in /usr/share/perl5/ZoneMinder/Control , then eat a sandwich - -=head1 DESCRIPTION - -Stub documentation for Trendnet TVIP672, created by Vince. - -=head2 EXPORT - -None by default. - - - -=head1 SEE ALSO - -Read the comments at the beginning of this file to see the usage for zoneminder 1.25.0 - - -=head1 AUTHOR - -Vincent Giovannone, I'd rather you not email me. - -=head1 COPYRIGHT AND LICENSE - -Copyright (C) 2014 by Vincent Giovannone - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.8.3 or, -at your option, any later version of Perl 5 you may have available. - - -=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm new file mode 100644 index 000000000..30534c516 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm @@ -0,0 +1,431 @@ +package ZoneMinder::Control::Trendnet; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +# You do not need to change the REALM, but you can get slightly faster response +# by setting so that the first auth request succeeds. +# +# The username and password should be passed in the ControlAddress field but you +# can set them here if you want. +# + +our $REALM = ''; +our $PROTOCOL = 'http://'; +our $USERNAME = 'admin'; +our $PASSWORD = ''; +our $ADDRESS = ''; + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +sub open { + my $self = shift; + $self->loadMonitor(); + + if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?https?:\/\/)?(?[^:@]+)?:?(?[^\/@]+)?@?(?
.*)$/ ) ) { + $PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL}; + $USERNAME = $+{USERNAME} if $+{USERNAME}; + $PASSWORD = $+{PASSWORD} if $+{PASSWORD}; + $ADDRESS = $+{ADDRESS} if $+{ADDRESS}; + } else { + Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress}); + $ADDRESS = $self->{Monitor}->{ControlAddress}; + } + if ( !($ADDRESS =~ /:/) ) { + Error('You generally need to also specify the port. I will append :80'); + $ADDRESS .= ':80'; + } + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/'.$ZoneMinder::Base::ZM_VERSION); + $self->{state} = 'closed'; + # credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string) + Debug ( "sendCmd credentials control address:'".$ADDRESS + ."' realm:'" . $REALM + . "' username:'" . $USERNAME + . "' password:'".$PASSWORD + ."'" + ); + $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); + + # Detect REALM + my $res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi'); + + if ( $res->is_success ) { + $self->{state} = 'open'; + return; + } + + if ( $res->status_line() eq '401 Unauthorized' ) { + + my $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); + } + + if ( $$headers{'www-authenticate'} ) { + my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; + if ( $tokens =~ /\w+="([^"]+)"/i ) { + if ( $REALM ne $1 ) { + $REALM = $1; + Debug("Changing REALM to $REALM"); + $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); + $res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi'); + if ( $res->is_success() ) { + $self->{state} = 'open'; + return; + } + Error('Authentication still failed after updating REALM' . $res->status_line); + $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); + } # end foreach + } else { + Error('Authentication failed, not a REALM problem'); + } + } else { + Error('Failed to match realm in tokens'); + } # end if + } else { + Debug('No headers line'); + } # end if headers + } # end if $res->status_line() eq '401 Unauthorized' +} # end sub open + +sub printMsg { + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug($msg.'['.$msg_len.']'); +} + +sub sendCmd { + + # This routine is used for all moving, which are all GET commands... + + my $self = shift; + my $cmd = shift; + + my $url = $PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi?command='.$cmd; + my $res = $self->{ua}->get($url); + + Debug('sendCmd command: ' . $url); + if ( $res->is_success ) { + return !undef; + } + Error("Error check failed: '".$res->status_line()."' cmd:'".$cmd."'"); + return; +} + +sub sendCmdPost { + + # + # This routine is used for setting/clearing presets and IR commands, which are POST commands... + # + + my $self = shift; + my $url = shift; + my $form = shift; + + my $result = undef; + + if ( $url eq undef ) { + Error('url passed to sendCmdPost is undefined.'); + return -1; + } + + #Debug('sendCmdPost url: ' . $url . ' cmd: ' . $cmd); + + my $res; + $res = $self->{ua}->post( + $PROTOCOL.$ADDRESS.$url, + Referer=>$PROTOCOL.$ADDRESS.$url, + Content=>$form + ); + + Debug("sendCmdPost credentials control to: $PROTOCOL$ADDRESS$url realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'"); + + if ( $res->is_success ) { + return !undef; + } + Error("sendCmdPost Error check failed: '".$res->status_line()."' cmd:"); + + return $result; +} # end sub sendCmdPost + +sub move { + my $self = shift; + my $panSteps = shift; + my $tiltSteps = shift; + + my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps"; + $self->sendCmd($cmd); +} + +sub moveRelUpLeft { + my $self = shift; + Debug('Move Up Left'); + $self->move(-3, 3); +} + +sub moveRelUp { + my $self = shift; + Debug('Move Up'); + $self->move(0, 3); +} + +sub moveRelUpRight { + my $self = shift; + Debug('Move Up Right'); + $self->move(3, 3); +} + +sub moveRelLeft { + my $self = shift; + Debug('Move Left'); + $self->move(-3, 0); +} + +sub moveRelRight { + my $self = shift; + Debug('Move Right'); + $self->move(3, 0); +} + +sub moveRelDownLeft { + my $self = shift; + Debug('Move Down Left'); + $self->move(-3, -3); +} + +sub moveRelDown { + my $self = shift; + Debug('Move Down'); + $self->move(0, -3); +} + +sub moveRelDownRight { + my $self = shift; + Debug('Move Down Right'); + $self->move(3, -3); +} + +# moves the camera to center on the point that the user clicked on in the video image. +# This isn't mega accurate but good enough for most purposes + +sub moveMap { + + # If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!) + # They scale the movement and are here to compensate for manufacturing variation. + # It's never going to be perfect, so just get somewhere in the ballpark and call it a day. + # (Don't forget to kill the zmcontrol process while tweaking!) + + # 1280x800 + my $hscale = 31; + my $vscale = 25; + + # 1280x800 with fisheye + #my $hscale = 15; + #my $vscale = 15; + + # 640x400 + #my $hscale = 14; + #my $vscale = 12; + + + my $self = shift; + my $params = shift; + my $xcoord = $self->getParam( $params, 'xcoord' ); + my $ycoord = $self->getParam( $params, 'ycoord' ); + + my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale; + my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale; + + $hor = int($hor); + $ver = -1 * int($ver); + + Debug("Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver"); + $self->move($hor, $ver); +} + + +# **** PRESETS **** +# +# OK, presets work a little funky but they DO work, provided you define them +# in order and don't skip any. +# +# The problem is that when you load the web page for this camera, it gives a +# list of preset names tied to index numbers. +# So let's say you have four presets... A, B, C, and D, and defined them in +# that order. +# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell +# the camera to go to a preset, you actually tell it by number, not by name. +# (So "Go to D" is really "go to index 3".) +# +# Now let's say somebody deletes C via the camera's web GUI. The camera +# re-numbers the existing presets A=0, B=1, D=2. +# There's really no easy way for ZM to discover this re-numbering, so +# zoneminder would still send "go to preset 3" thinking +# it's telling the camera to go to point D. In actuality it's telling the +# camera to go to a preset that no longer exists. +# +# As long as you define your presets in order (i.e. define preset 1, then +# preset 2, then preset 3, etc.) everything will work just +# fine in ZoneMinder. +# +# (Home preset needs to be set via the camera's web gui, and is unaffected by +# any of this.) +# +# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only +# through!) ZM AND DON'T SKIP ANY. +# + +sub presetClear { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + my $cmd = "presetName=$preset&command=del"; + my $url = '/eng/admin/cam_control.cgi'; + Debug('presetClear: ' . $preset . ' cmd: ' . $cmd); + $self->sendCmdPost($url,{presetName=>$preset, command=>'del'}); +} + +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + my $cmd = "presetName=$preset&command=add"; + my $url = '/eng/admin/cam_control.cgi'; + Debug('presetSet ' . $preset . ' cmd: ' . $cmd); + $self->sendCmdPost($url,{presetName=>$preset, command=>'add', Submit=>'Add'}); +} + +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + $preset = $preset - 1; + Debug("Goto Preset $preset"); + my $cmd = "goto_preset_position&index=$preset"; + $self->sendCmd($cmd); +} + +sub presetHome { + my $self = shift; + Debug('Home Preset'); + my $cmd = 'go_home'; + $self->sendCmd($cmd); +} + +# +# **** IR CONTROLS **** +# +# +# Wake: Force IR on, always. (always night mode) +# +# Sleep: Force IR off, always. (always day mode) +# +# Reset: Automatic IR mode. (day/night mode determined by camera) +# + +sub wake { + # force IR on ("always night mode") + + my $self = shift; + my $url = '/eng/admin/adv_audiovideo.cgi'; + my $cmd = 'irMode=3'; + + Debug('Wake -- IR on'); + + $self->sendCmdPost($url,$cmd); +} + +sub sleep { + # force IR off ("always day mode") + + my $self = shift; + my $url = '/eng/admin/adv_audiovideo.cgi'; + my $cmd = 'irMode=2'; + + Debug('Sleep -- IR off'); + + $self->sendCmdPost($url,$cmd); +} + +sub reset { + # IR auto + + my $self=shift; + my $url = '/eng/admin/adv_audiovideo.cgi'; + my $cmd = 'irMode=0'; + + Debug('Reset -- IR auto'); + + $self->sendCmdPost($url,$cmd); +} + +1; +__END__ + +=head1 NAME + +ZoneMinder::Control::Trendnet - Perl module for Trendnet cameras + +=head1 SYNOPSIS + +use ZoneMinder::Control::Trendnet; +place this in /usr/share/perl5/ZoneMinder/Control + +=head1 DESCRIPTION + +This module contains the implementation of the Trendnet # IP camera control +protocol. Has been tested with TV-IP862IC + +Under control capability: + +* Main: Can wake, can sleep, can reset +* Move: Can move, can move diagonally, can move mapped, can move relative +* Pan: Can pan +* Tilt: Can tilt +* Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.) + +Under control tab in the monitor itself: + +Controllable +Control type is the name you gave it in control capability above +Control address is the camera's ip address AND web port. example: 192.168.1.1:80 +You can also put the authentication information here and change the +protocol to https using something like https://admin:password@192.168.1.1:80 + +=head2 EXPORT + +None by default. + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2018 ZoneMinder LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index fcc1392a2..12e3c7065 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -184,16 +184,19 @@ sub zmDbGetMonitor { my $id = shift; - return( undef ) if ( !defined($id) ); + if ( !defined($id) ) { + croak("Undefined id in zmDbgetMonitor"); + return undef ; + } - my $sql = "select * from Monitors where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) - or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $id ) - or croak( "Can't execute '$sql': ".$sth->errstr() ); + my $sql = 'SELECT * FROM Monitors WHERE Id = ?'; + my $sth = $dbh->prepare_cached($sql) + or croak("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($id) + or croak("Can't execute '$sql': ".$sth->errstr()); my $monitor = $sth->fetchrow_hashref(); - return( $monitor ); + return $monitor; } sub zmDbGetMonitorAndControl { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index b58b98003..bac118a13 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -527,6 +527,7 @@ sub logPrint { my $this = shift; my $level = shift; my $string = shift; + my ($caller, undef, $line) = @_ ? @_ : caller; if ( $level <= $this->{effectiveLevel} ) { $string =~ s/[\r\n]+$//g; @@ -537,12 +538,14 @@ sub logPrint { my ($seconds, $microseconds) = gettimeofday(); if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) { my $message = sprintf( - '%s.%06d %s[%d].%s [%s]' + '%s.%06d %s[%d].%s [%s:%d] [%s]' , strftime('%x %H:%M:%S', localtime($seconds)) , $microseconds , $this->{id} , $$ , $codes{$level} + , $caller + , $line , $string ); if ( $this->{trace} ) { @@ -660,39 +663,39 @@ sub Dump { sub debug { my $log = shift; - $log->logPrint(DEBUG, @_); - } + $log->logPrint(DEBUG, @_, caller); +} sub Debug( @ ) { - fetch()->logPrint(DEBUG, @_); + fetch()->logPrint(DEBUG, @_, caller); } sub Info( @ ) { - fetch()->logPrint(INFO, @_); + fetch()->logPrint(INFO, @_, caller); } sub info { my $log = shift; - $log->logPrint(INFO, @_); + $log->logPrint(INFO, @_, caller); } sub Warning( @ ) { - fetch()->logPrint(WARNING, @_); + fetch()->logPrint(WARNING, @_, caller); } sub warn { my $log = shift; - $log->logPrint(WARNING, @_); + $log->logPrint(WARNING, @_, caller); } sub Error( @ ) { - fetch()->logPrint(ERROR, @_); + fetch()->logPrint(ERROR, @_, caller); } sub error { my $log = shift; - $log->logPrint(ERROR, @_); + $log->logPrint(ERROR, @_, caller); } sub Fatal( @ ) { - fetch()->logPrint(FATAL, @_); + fetch()->logPrint(FATAL, @_, caller); if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) { $SIG{TERM}(); } @@ -700,7 +703,7 @@ sub Fatal( @ ) { } sub Panic( @ ) { - fetch()->logPrint(PANIC, @_); + fetch()->logPrint(PANIC, @_, caller); confess($_[0]); } diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index 79aeccb10..7d7053335 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -21,6 +21,195 @@ # # ========================================================================== +use strict; + +@EXTRA_PERL_LIB@ +use ZoneMinder; +use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); +use POSIX qw/strftime EPIPE/; +use Socket; +#use Data::Dumper; +use Module::Load::Conditional qw{can_load};; + +use constant MAX_CONNECT_DELAY => 15; +use constant MAX_COMMAND_WAIT => 1800; + +$| = 1; + +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +logInit(); + +my $arg_string = join( " ", @ARGV ); + +my $id; +my %options; + +GetOptions( + 'id=i' =>\$id, + 'command=s' =>\$options{command}, + 'xcoord=i' =>\$options{xcoord}, + 'ycoord=i' =>\$options{ycoord}, + 'speed=i' =>\$options{speed}, + 'step=i' =>\$options{step}, + 'panspeed=i' =>\$options{panspeed}, + 'tiltspeed=i' =>\$options{tiltspeed}, + 'panstep=i' =>\$options{panstep}, + 'tiltstep=i' =>\$options{tiltstep}, + 'preset=i' =>\$options{preset}, + 'autostop' =>\$options{autostop}, +) or pod2usage(-exitstatus => -1); + +if ( !$id || !$options{command} ) { + print( STDERR "Please give a valid monitor id and command\n" ); + pod2usage(-exitstatus => -1); +} + +( $id ) = $id =~ /^(\w+)$/; + +Debug("zmcontrol: arg string: $arg_string"); + +my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock'; + +socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) + or Fatal("Can't open socket: $!"); + +my $saddr = sockaddr_un($sock_file); +my $server_up = connect(CLIENT, $saddr); +if ( !$server_up ) { + # The server isn't there + my $monitor = zmDbGetMonitorAndControl($id); + if ( !$monitor ) { + Fatal("Unable to load control data for monitor $id"); + } + my $protocol = $monitor->{Protocol}; + + if ( -x $protocol ) { + # Protocol is actually a script! + # Holdover from previous versions + my $command .= $protocol.' '.$arg_string; + Debug($command); + + my $output = qx($command); + my $status = $? >> 8; + if ( $status || logDebugging() ) { + chomp($output); + Debug("Output: $output"); + } + if ( $status ) { + Error("Command '$command' exited with status: $status"); + exit($status); + } + exit(0); + } + + Info("Starting control server $id/$protocol"); + close(CLIENT); + + if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) { + Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR"); + } + + if ( my $cpid = fork() ) { + logReinit(); + + # Parent process just sleep and fall through + socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) + or die("Can't open socket: $!"); + my $attempts = 0; + while ( !connect(CLIENT, $saddr) ) { + $attempts++; + Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY; + sleep(1); + } + } elsif ( defined($cpid) ) { + close(STDOUT); + close(STDERR); + + setpgrp(); + + logReinit(); + + Info("Control server $id/$protocol starting at " + .strftime('%y/%m/%d %H:%M:%S', localtime()) + ); + + $0 = $0." --id $id"; + + my $control = "ZoneMinder::Control::$protocol"->new($id); + my $control_key = $control->getKey(); + $control->loadMonitor(); + + $control->open(); + + socket(SERVER, PF_UNIX, SOCK_STREAM, 0) + or Fatal("Can't open socket: $!"); + unlink($sock_file); + bind(SERVER, $saddr) or Fatal("Can't bind: $!"); + listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!"); + + my $rin = ''; + vec( $rin, fileno(SERVER), 1 ) = 1; + my $win = $rin; + my $ein = $win; + my $timeout = MAX_COMMAND_WAIT; + while( 1 ) { + my $nfound = select(my $rout = $rin, undef, undef, $timeout); + if ( $nfound > 0 ) { + if ( vec( $rout, fileno(SERVER), 1 ) ) { + my $paddr = accept(CLIENT, SERVER); + my $message = ; + + next if !$message; + + my $params = jsonDecode($message); + #Debug( Dumper( $params ) ); + + my $command = $params->{command}; + close( CLIENT ); + if ( $command eq 'quit' ) { + last; + } + $control->$command($params); + } else { + Fatal('Bogus descriptor'); + } + } elsif ( $nfound < 0 ) { + if ( $! == EPIPE ) { + Error("Can't select: $!"); + } else { + Fatal("Can't select: $!"); + } + } else { + #print( "Select timed out\n" ); + last; + } + } # end while forever + Info("Control server $id/$protocol exiting"); + unlink($sock_file); + $control->close(); + exit(0); + } else { + Fatal("Can't fork: $!"); + } +} # end if !server up + +# The server is there, connect to it +#print( "Writing commands\n" ); +CLIENT->autoflush(); + +my $message = jsonEncode(\%options); +print(CLIENT $message); +shutdown(CLIENT, 1); + +exit(0); + +1; +__END__ + =head1 NAME zmcontrol.pl - ZoneMinder control script @@ -47,214 +236,3 @@ FIXME FIXME --preset [ arg ] - =cut -use strict; - -@EXTRA_PERL_LIB@ -use ZoneMinder; -use Getopt::Long; -use autouse 'Pod::Usage'=>qw(pod2usage); -use POSIX qw/strftime EPIPE/; -use Socket; -#use Data::Dumper; -use Module::Load::Conditional qw{can_load};; - -use constant MAX_CONNECT_DELAY => 10; -use constant MAX_COMMAND_WAIT => 1800; - -$| = 1; - -$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; -$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; -delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; - -logInit(); - -my $arg_string = join( " ", @ARGV ); - -my $id; -my %options; - -GetOptions( - 'id=i' =>\$id, - 'command=s' =>\$options{command}, - 'xcoord=i' =>\$options{xcoord}, - 'ycoord=i' =>\$options{ycoord}, - 'speed=i' =>\$options{speed}, - 'step=i' =>\$options{step}, - 'panspeed=i' =>\$options{panspeed}, - 'tiltspeed=i' =>\$options{tiltspeed}, - 'panstep=i' =>\$options{panstep}, - 'tiltstep=i' =>\$options{tiltstep}, - 'preset=i' =>\$options{preset}, - 'autostop' =>\$options{autostop}, -) or pod2usage(-exitstatus => -1); - -if ( !$id || !$options{command} ) -{ - print( STDERR "Please give a valid monitor id and command\n" ); - pod2usage(-exitstatus => -1); -} - -( $id ) = $id =~ /^(\w+)$/; - -Debug( $arg_string ); - -my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock'; - -socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) - or Fatal( "Can't open socket: $!" ); - -my $saddr = sockaddr_un( $sock_file ); -my $server_up = connect( CLIENT, $saddr ); -if ( !$server_up ) -{ - # The server isn't there - my $monitor = zmDbGetMonitorAndControl( $id ); - if ( !$monitor ) - { - Fatal( "Unable to load control data for monitor $id" ); - } - my $protocol = $monitor->{Protocol}; - - if ( -x $protocol ) - { - # Protocol is actually a script! - # Holdover from previous versions - my $command .= $protocol.' '.$arg_string; - Debug( $command."\n" ); - - my $output = qx($command); - my $status = $? >> 8; - if ( $status || logDebugging() ) - { - chomp( $output ); - Debug( "Output: $output\n" ); - } - if ( $status ) - { - Error( "Command '$command' exited with status: $status\n" ); - exit( $status ); - } - exit( 0 ); - } - - Info( "Starting control server $id/$protocol" ); - close( CLIENT ); - - if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) { - Fatal("Can't load ZoneMinder::Control::$protocol"); - } - - if ( my $cpid = fork() ) - { - logReinit(); - - # Parent process just sleep and fall through - socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) - or die( "Can't open socket: $!" ); - my $attempts = 0; - while (!connect( CLIENT, $saddr )) - { - $attempts++; - Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY); - sleep(1); - } - } - elsif ( defined($cpid) ) - { - close( STDOUT ); - close( STDERR ); - - setpgrp(); - - logReinit(); - - Info( "Control server $id/$protocol starting at " - .strftime( '%y/%m/%d %H:%M:%S', localtime() ) - ); - - $0 = $0." --id $id"; - - my $control = "ZoneMinder::Control::$protocol"->new( $id ); - my $control_key = $control->getKey(); - $control->loadMonitor(); - - $control->open(); - - socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) - or Fatal( "Can't open socket: $!" ); - unlink( $sock_file ); - bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); - listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); - - my $rin = ''; - vec( $rin, fileno(SERVER), 1 ) = 1; - my $win = $rin; - my $ein = $win; - my $timeout = MAX_COMMAND_WAIT; - while( 1 ) - { - my $nfound = select( my $rout = $rin, undef, undef, $timeout ); - if ( $nfound > 0 ) - { - if ( vec( $rout, fileno(SERVER), 1 ) ) - { - my $paddr = accept( CLIENT, SERVER ); - my $message = ; - - next if ( !$message ); - - my $params = jsonDecode( $message ); - #Debug( Dumper( $params ) ); - - my $command = $params->{command}; - close( CLIENT ); - if ( $command eq 'quit' ) { - last; - } - $control->$command( $params ); - } - else - { - Fatal( "Bogus descriptor" ); - } - } - elsif ( $nfound < 0 ) - { - if ( $! == EPIPE ) - { - Error( "Can't select: $!" ); - } - else - { - Fatal( "Can't select: $!" ); - } - } - else - { - #print( "Select timed out\n" ); - last; - } - } - Info( "Control server $id/$protocol exiting at " - .strftime( '%y/%m/%d %H:%M:%S', localtime() ) - ); - unlink( $sock_file ); - $control->close(); - exit( 0 ); - } - else - { - Fatal( "Can't fork: $!" ); - } -} - -# The server is there, connect to it -#print( "Writing commands\n" ); -CLIENT->autoflush(); - -my $message = jsonEncode( \%options ); -print( CLIENT $message ); -shutdown( CLIENT, 1 ); - -exit( 0 ); diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index b7a6dae05..e23413ca5 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -274,6 +274,8 @@ sub run { ."\n" ); + # We don't want to leave killall zombies, so ignore SIGCHLD + $SIG{CHLD} = 'IGNORE'; # Tell any existing processes to die, wait 1 second between TERM and KILL killAll(1); diff --git a/src/zm_camera.h b/src/zm_camera.h index 83b5f922b..cd7a024f4 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -33,8 +33,7 @@ class Camera; // Abstract base class for cameras. This is intended just to express // common attributes // -class Camera -{ +class Camera { protected: typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; @@ -53,49 +52,48 @@ protected: int contrast; bool capture; bool record_audio; - unsigned int bytes; - + unsigned int bytes; public: Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); virtual ~Camera(); - unsigned int getId() const { return( monitor_id ); } + unsigned int getId() const { return monitor_id; } Monitor *getMonitor(); void setMonitor( Monitor *p_monitor ); - SourceType Type() const { return( type ); } - bool IsLocal() const { return( type == LOCAL_SRC ); } - bool IsRemote() const { return( type == REMOTE_SRC ); } - bool IsFile() const { return( type == FILE_SRC ); } - bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } - bool IsLibvlc() const { return( type == LIBVLC_SRC ); } - bool IscURL() const { return( type == CURL_SRC ); } - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } - unsigned int Colours() const { return( colours ); } - unsigned int SubpixelOrder() const { return( subpixelorder ); } - unsigned int Pixels() const { return( pixels ); } - unsigned int ImageSize() const { return( imagesize ); } + SourceType Type() const { return type; } + bool IsLocal() const { return type == LOCAL_SRC; } + bool IsRemote() const { return type == REMOTE_SRC; } + bool IsFile() const { return type == FILE_SRC; } + bool IsFfmpeg() const { return type == FFMPEG_SRC; } + bool IsLibvlc() const { return type == LIBVLC_SRC; } + bool IscURL() const { return type == CURL_SRC; } + unsigned int Width() const { return width; } + unsigned int Height() const { return height; } + unsigned int Colours() const { return colours; } + unsigned int SubpixelOrder() const { return subpixelorder; } + unsigned int Pixels() const { return pixels; } + unsigned int ImageSize() const { return imagesize; } unsigned int Bytes() const { return bytes; }; - virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } - virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } - virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } - virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } + virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; } + virtual int Hue( int/*p_hue*/=-1 ) { return -1; } + virtual int Colour( int/*p_colour*/=-1 ) { return -1; } + virtual int Contrast( int/*p_contrast*/=-1 ) { return -1; } - bool CanCapture() const { return( capture ); } + bool CanCapture() const { return capture; } bool SupportsNativeVideo() const { return (type == FFMPEG_SRC); //return (type == FFMPEG_SRC )||(type == REMOTE_SRC); } - virtual int PrimeCapture() { return( 0 ); } - virtual int PreCapture()=0; - virtual int Capture( Image &image )=0; - virtual int PostCapture()=0; - virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0; - virtual int Close()=0; + virtual int PrimeCapture() { return 0; } + virtual int PreCapture() = 0; + virtual int Capture(Image &image) = 0; + virtual int PostCapture() = 0; + virtual int CaptureAndRecord(Image &image, timeval recording, char* event_directory) = 0; + virtual int Close() = 0; }; #endif // ZM_CAMERA_H diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 0865bc6e4..6d63be98b 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -123,9 +123,9 @@ void process_configfile( char* configFile) { if ( *line_ptr == '\0' || *line_ptr == '#' ) continue; - // Remove trailing white space + // Remove trailing white space and trailing quotes char *temp_ptr = line_ptr+strlen(line_ptr)-1; - while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) { + while ( *temp_ptr == ' ' || *temp_ptr == '\t' || *temp_ptr == '\'' || *temp_ptr == '\"') { *temp_ptr-- = '\0'; temp_ptr--; } @@ -147,8 +147,9 @@ void process_configfile( char* configFile) { temp_ptr--; } while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); - // Remove leading white space from the value part + // Remove leading white space and leading quotes from the value part white_len = strspn( val_ptr, " \t" ); + white_len += strspn( val_ptr, "\'\"" ); val_ptr += white_len; if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 713b9728d..f25f9e7f9 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -141,9 +141,8 @@ Event::Event( errno = 0; if ( mkdir(path, 0755) ) { // FIXME This should not be fatal. Should probably move to a different storage area. - if ( errno != EEXIST ) { + if ( errno != EEXIST ) Error("Can't mkdir %s: %s", path, strerror(errno)); - } } if ( i == 2 ) strncpy(date_path, path, sizeof(date_path)); diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index d41518bb7..21eeb328f 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -59,7 +59,7 @@ class FfmpegCamera : public Camera { bool hwaccel; #if HAVE_AVUTIL_HWCONTEXT_H AVFrame *hwFrame; - DecodeContext decode; + DecodeContext decode; #endif // Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero. diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 2851742ca..e42a82f2b 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -503,17 +503,17 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co ); char *syslogStart = logPtr; - va_start( argPtr, fstring ); + va_start(argPtr, fstring); if ( hex ) { - unsigned char *data = va_arg( argPtr, unsigned char * ); - int len = va_arg( argPtr, int ); + unsigned char *data = va_arg(argPtr, unsigned char *); + int len = va_arg(argPtr, int); int i; - logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len ); + logPtr += snprintf(logPtr, sizeof(logString)-(logPtr-logString), "%d:", len); for ( i = 0; i < len; i++ ) { - logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] ); + logPtr += snprintf(logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i]); } } else { - logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr ); + logPtr += vsnprintf(logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr); } va_end(argPtr); char *syslogEnd = logPtr; @@ -540,7 +540,13 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co if ( ! db_mutex.trylock() ) { mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) ); - snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line ); + snprintf(sql, sizeof(sql), + "INSERT INTO Logs " + "( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line )" + " VALUES " + "( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", + timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line + ); if ( mysql_query(&dbconn, sql) ) { Level tempDatabaseLevel = mDatabaseLevel; databaseLevel(NOLOG); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index ba1e6b21e..6a5cf208e 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -423,15 +423,9 @@ Monitor::Monitor( snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); if ( purpose == CAPTURE ) { - struct stat statbuf; - - if ( stat(monitor_dir, &statbuf) ) { - if ( errno == ENOENT || errno == ENOTDIR ) { - if ( mkdir(monitor_dir, 0755) ) { - Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); - } - } else { - Warning("Error stat'ing %s, may be fatal. error is %s", monitor_dir, strerror(errno)); + if ( mkdir(monitor_dir, 0755) ) { + if ( errno != EEXIST ) { + Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); } } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 2d7678466..b98f5953f 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -254,39 +254,39 @@ protected: VideoWriter videowriter; std::string encoderparams; std::vector encoderparamsvec; - bool record_audio; // Whether to store the audio that we receive + bool record_audio; // Whether to store the audio that we receive - int brightness; // The statically saved brightness of the camera - int contrast; // The statically saved contrast of the camera - int hue; // The statically saved hue of the camera - int colour; // The statically saved colour of the camera - char event_prefix[64]; // The prefix applied to event names as they are created - char label_format[64]; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images - int label_size; // Size of the timestamp on the images - int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count - int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, + int brightness; // The statically saved brightness of the camera + int contrast; // The statically saved contrast of the camera + int hue; // The statically saved hue of the camera + int colour; // The statically saved colour of the camera + char event_prefix[64]; // The prefix applied to event names as they are created + char label_format[64]; // The format of the timestamp on the images + Coord label_coord; // The coordinates of the timestamp on the images + int label_size; // Size of the timestamp on the images + int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count + int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, // value is pre_event_count + alarm_frame_count - 1 - int warmup_count; // How many images to process before looking for events - int pre_event_count; // How many images to hold and prepend to an alarm event - int post_event_count; // How many unalarmed images must occur before the alarm state is reset - int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now - int section_length; // How long events should last in continuous modes - bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor - int frame_skip; // How many frames to skip in continuous modes - int motion_frame_skip; // How many frames to skip in motion detection - double analysis_fps; // Target framerate for video analysis + int warmup_count; // How many images to process before looking for events + int pre_event_count; // How many images to hold and prepend to an alarm event + int post_event_count; // How many unalarmed images must occur before the alarm state is reset + int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now + int section_length; // How long events should last in continuous modes + bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor + int frame_skip; // How many frames to skip in continuous modes + int motion_frame_skip; // How many frames to skip in motion detection + double analysis_fps; // Target framerate for video analysis unsigned int analysis_update_delay; // How long we wait before updating analysis parameters - int capture_delay; // How long we wait between capture frames - int alarm_capture_delay; // How long we wait between capture frames when in alarm state - int alarm_frame_count; // How many alarm frames are required before an event is triggered - int fps_report_interval; // How many images should be captured/processed between reporting the current FPS - int ref_blend_perc; // Percentage of new image going into reference image. - int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. - bool track_motion; // Whether this monitor tries to track detected motion - int signal_check_points; // Number of points in the image to check for signal - Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected - bool embed_exif; // Whether to embed Exif data into each image frame or not + int capture_delay; // How long we wait between capture frames + int alarm_capture_delay; // How long we wait between capture frames when in alarm state + int alarm_frame_count; // How many alarm frames are required before an event is triggered + int fps_report_interval; // How many images should be captured/processed between reporting the current FPS + int ref_blend_perc; // Percentage of new image going into reference image. + int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. + bool track_motion; // Whether this monitor tries to track detected motion + int signal_check_points; // Number of points in the image to check for signal + Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected + bool embed_exif; // Whether to embed Exif data into each image frame or not bool last_signal; diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index f911f2c78..c9f74622a 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -267,7 +267,20 @@ bool StreamBase::sendTextFrame( const char *frame_text ) { void StreamBase::openComms() { if ( connkey > 0 ) { - unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey); + // Have to mkdir because systemd is now chrooting and the dir may not exist + if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) { + if ( errno != EEXIST ) { + Error("Can't mkdir ZM_PATH_SOCKS %s: %s.", staticConfig.PATH_SOCKS.c_str(), strerror(errno)); + } + } + + unsigned int length = snprintf( + sock_path_lock, + sizeof(sock_path_lock), + "%s/zms-%06d.lock", + staticConfig.PATH_SOCKS.c_str(), + connkey + ); if ( length >= sizeof(sock_path_lock) ) { Warning("Socket lock path was truncated."); } @@ -275,14 +288,14 @@ void StreamBase::openComms() { lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR); if ( lock_fd <= 0 ) { - Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) ); + Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno)); lock_fd = 0; } else if ( flock(lock_fd, LOCK_EX) != 0 ) { - Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) ); + Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno)); close(lock_fd); lock_fd = 0; } else { - Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); + Debug(1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); } sd = socket(AF_UNIX, SOCK_DGRAM, 0); @@ -292,7 +305,13 @@ void StreamBase::openComms() { Debug(1, "Have socket %d", sd); } - length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey); + length = snprintf( + loc_sock_path, + sizeof(loc_sock_path), + "%s/zms-%06ds.sock", + staticConfig.PATH_SOCKS.c_str(), + connkey + ); if ( length >= sizeof(loc_sock_path) ) { Warning("Socket path was truncated."); length = sizeof(loc_sock_path)-1; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index b62ad41fe..6d4ae15e3 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -236,7 +236,9 @@ bool Zone::CheckAlarms(const Image *delta_image) { if ( pixel_diff_count && alarm_pixels ) pixel_diff = pixel_diff_count/alarm_pixels; - Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff); + + Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", + alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff); if ( alarm_pixels ) { if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { @@ -252,7 +254,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - score = (100*alarm_pixels)/polygon.Area(); + if (max_alarm_pixels != 0) + score = (100*alarm_pixels)/max_alarm_pixels; + else + score = (100*alarm_pixels)/polygon.Area(); + if ( score < 1 ) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", score); @@ -312,7 +318,8 @@ bool Zone::CheckAlarms(const Image *delta_image) { if ( config.record_diag_images ) diff_image->WriteJpeg(diag_path); - Debug(5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels); + Debug(5, "Got %d filtered pixels, need %d -> %d", + alarm_filter_pixels, min_filter_pixels, max_filter_pixels); if ( alarm_filter_pixels ) { if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { @@ -328,7 +335,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - score = (100*alarm_filter_pixels)/(polygon.Area()); + if (max_filter_pixels != 0) + score = (100*alarm_filter_pixels)/max_filter_pixels; + else + score = (100*alarm_filter_pixels)/polygon.Area(); + if ( score < 1 ) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", score); @@ -438,7 +449,8 @@ bool Zone::CheckAlarms(const Image *delta_image) { alarm_blobs--; - Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs); + Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs", + bss->tag, bsm->tag, x, y, alarm_blobs); // Clear out the old blob bss->tag = 0; @@ -476,7 +488,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { BlobStats *bs = &blob_stats[i]; // See if we can recycle one first, only if it's at least two rows up if ( bs->count && bs->hi_y < (int)(y-1) ) { - if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) { + if ( + (min_blob_pixels && bs->count < min_blob_pixels) + || + (max_blob_pixels && bs->count > max_blob_pixels) + ) { if ( config.create_analysis_images || config.record_diag_images ) { for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) { spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); @@ -589,8 +605,12 @@ bool Zone::CheckAlarms(const Image *delta_image) { /* No blobs */ return false; } - - score = (100*alarm_blob_pixels)/(polygon.Area()); + + if (max_blob_pixels != 0) + score = (100*alarm_blob_pixels)/(max_blob_pixels); + else + score = (100*alarm_blob_pixels)/polygon.Area(); + if ( score < 1 ) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", score); diff --git a/version b/version index 6701dcb1c..d56f906c5 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.45 +1.31.47 diff --git a/web/ajax/log.php b/web/ajax/log.php index 14fc1d772..375b53a05 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -23,7 +23,7 @@ switch ( $_REQUEST['task'] ) { if ( !isset($levels[$_POST['level']]) ) Panic("Unexpected logger level '".$_POST['level']."'"); $level = $levels[$_POST['level']]; - Logger::fetch()->logPrint( $level, $string, $file, $line ); + Logger::fetch()->logPrint($level, $string, $file, $line); } ajaxResponse(); break; @@ -45,22 +45,22 @@ switch ( $_REQUEST['task'] ) { $limit = 100; if ( isset($_REQUEST['limit']) ) { - if ( ( !is_integer( $_REQUEST['limit'] ) and !ctype_digit($_REQUEST['limit']) ) ) { - Error('Invalid value for limit ' . $_REQUEST['limit'] ); + if ( ( !is_integer($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { + Error('Invalid value for limit ' . $_REQUEST['limit']); } else { $limit = $_REQUEST['limit']; } } $sortField = 'TimeKey'; if ( isset($_REQUEST['sortField']) ) { - if ( ! in_array( $_REQUEST['sortField'], $filterFields ) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) { - Error("Invalid sort field " . $_REQUEST['sortField'] ); + if ( !in_array($_REQUEST['sortField'], $filterFields) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) { + Error("Invalid sort field " . $_REQUEST['sortField']); } else { $sortField = $_REQUEST['sortField']; } } - $sortOrder = (isset($_REQUEST['sortOrder']) and $_REQUEST['sortOrder']) == 'asc' ? 'asc':'desc'; - $filter = isset($_REQUEST['filter'])?$_REQUEST['filter']:array(); + $sortOrder = (isset($_REQUEST['sortOrder']) and ($_REQUEST['sortOrder'] == 'asc')) ? 'asc' : 'desc'; + $filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array(); $total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total'); $sql = 'SELECT * FROM Logs'; @@ -89,15 +89,18 @@ switch ( $_REQUEST['task'] ) { } $options = array(); if ( count($where) ) - $sql.= ' WHERE '.join( ' AND ', $where ); + $sql.= ' WHERE '.join(' AND ', $where); $sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit; $logs = array(); foreach ( dbFetchAll($sql, NULL, $values) as $log ) { - $log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); + + $log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])); + #Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']); + #$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; - $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message'] ); + $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']); foreach( $filterFields as $field ) { - if ( ! isset( $options[$field] ) ) + if ( !isset($options[$field]) ) $options[$field] = array(); $value = $log[$field]; @@ -119,7 +122,7 @@ switch ( $_REQUEST['task'] ) { ajaxResponse( array( 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG), 'total' => $total, - 'available' => isset($available)?$available:$total, + 'available' => isset($available) ? $available : $total, 'logs' => $logs, 'state' => logState(), 'options' => $options @@ -210,8 +213,8 @@ switch ( $_REQUEST['task'] ) { if ( !($exportFP = fopen( $exportPath, "w" )) ) Fatal("Unable to open log export file $exportPath"); $logs = array(); - foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) { - $log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] ); + foreach ( dbFetchAll($sql, NULL, $values) as $log ) { + $log['DateTime'] = preg_replace('/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } @@ -347,7 +350,7 @@ switch ( $_REQUEST['task'] ) { ' ); break; } - $exportExt = "xml"; + $exportExt = 'xml'; break; } fclose( $exportFP ); @@ -363,10 +366,10 @@ switch ( $_REQUEST['task'] ) { ajaxError('Insufficient permissions to download logs'); if ( empty($_REQUEST['key']) ) - Fatal( "No log export key given" ); + Fatal('No log export key given'); $exportKey = $_REQUEST['key']; if ( empty($_REQUEST['format']) ) - Fatal( "No log export format given" ); + Fatal('No log export format given'); $format = $_REQUEST['format']; switch( $format ) { @@ -389,17 +392,17 @@ switch ( $_REQUEST['task'] ) { $exportFile = "zm-log.$exportExt"; $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; - header( "Pragma: public" ); - header( "Expires: 0" ); - header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" ); - header( "Cache-Control: private", false ); // required by certain browsers - header( "Content-Description: File Transfer" ); - header( 'Content-Disposition: attachment; filename="'.$exportFile.'"' ); - header( "Content-Transfer-Encoding: binary" ); - header( "Content-Type: application/force-download" ); - header( "Content-Length: ".filesize($exportPath) ); - readfile( $exportPath ); - exit( 0 ); + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: private', false ); // required by certain browsers + header('Content-Description: File Transfer'); + header('Content-Disposition: attachment; filename="'.$exportFile.'"' ); + header('Content-Transfer-Encoding: binary'); + header('Content-Type: application/force-download'); + header('Content-Length: '.filesize($exportPath)); + readfile($exportPath); + exit(0); break; } } diff --git a/web/includes/config.php.in b/web/includes/config.php.in index c6aa93c63..a2a89b224 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -212,7 +212,7 @@ function process_configfile($configFile) { continue; elseif ( preg_match( '/^\s*#/', $str )) continue; - elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) + elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches )) $configvals[$matches[1]] = $matches[2]; } fclose( $cfg ); diff --git a/web/includes/logger.php b/web/includes/logger.php index 0d7334ac7..8bc81fb97 100644 --- a/web/includes/logger.php +++ b/web/includes/logger.php @@ -406,7 +406,7 @@ class Logger { $result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) ); } catch(PDOException $ex) { $this->databaseLevel = self::NOLOG; - Fatal( "Can't write log entry '$sql': ". $ex->getMessage() ); + Error("Can't write log entry '$sql': ". $ex->getMessage()); } } // This has to be last as trigger_error can be fatal diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 78abe3a75..9e82a9c8a 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -199,7 +199,7 @@ if ( currentView != 'none' && currentView != 'login' ) { console.log( "Request Failed: " + err ); // The idea is that this should only fail due to auth, so reload the page // which should go to login if it can't stay logged in. - window.location.href = thisUrl; + window.location.href = thisUrl+'?view='+currentView; }); }