mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-07 13:13:05 -05:00
In a multi-server setup a PTZ move, preset or light command sent to one server for a monitor that another server records never reached the camera. Monitor::sendControlCommand() passed the zmcontrol option string to the other server's monitors/daemonControl API, which only starts and stops zmcontrol.pl, so the request built a URL like daemonControl/4/ --panspeed=51 ... --command=moveConLeft/zmcontrol.pl.json and answered "Failed". The web UI's own PTZ buttons send to the monitor's server and were not affected; clients that send every request to the server they log in to (zm-py, pyzmNg, zmNinjaNg, aiozoneminder) and monitor actions whose target is on another server were. ajax/control.php now hands such a request, as received, to the index.php of the server that records the monitor (or the action's target), with the auth relay in place of the caller's credentials, and answers with that server's reply. That server checks the permissions and builds the command itself. A request is forwarded at most once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
94 lines
2.8 KiB
PHP
94 lines
2.8 KiB
PHP
<?php
|
|
require_once('includes/control_functions.php');
|
|
require_once('includes/Monitor.php');
|
|
|
|
// Monitor control actions, require a monitor id and control view permissions for that monitor
|
|
if ( empty($_REQUEST['id']) )
|
|
ajaxError('No monitor id supplied');
|
|
|
|
if ( isset($_REQUEST['action']) and ($_REQUEST['action'] == 'monitorAction') ) {
|
|
// Fire one of this monitor's manually-triggered actions. Only the action id
|
|
// arrives from the browser; the command and its target are rebuilt from the
|
|
// stored row, so a request cannot name an arbitrary control method or drive
|
|
// a device the operator has no rights on.
|
|
require_once('includes/MonitorAction.php');
|
|
|
|
if ( !canView('Control', $_REQUEST['id']) )
|
|
ajaxError('Insufficient permissions');
|
|
|
|
if ( empty($_REQUEST['aid']) )
|
|
ajaxError('No action id supplied');
|
|
|
|
$monitorAction = ZM\MonitorAction::find_one(array(
|
|
'Id' => validInt($_REQUEST['aid']),
|
|
'MonitorId' => validInt($_REQUEST['id']),
|
|
'TriggerOn' => 'Manual',
|
|
'Enabled' => 1,
|
|
));
|
|
if ( !$monitorAction )
|
|
ajaxError('No such enabled manual action for this monitor');
|
|
|
|
$target = $monitorAction->TargetMonitor();
|
|
if ( !$target )
|
|
ajaxError('Action has no target monitor');
|
|
|
|
// Rights are needed on the device being driven, not just on the monitor the
|
|
// action hangs off.
|
|
if ( !canView('Control', $target->Id()) )
|
|
ajaxError('Insufficient permissions on the target monitor');
|
|
|
|
if ( $url = remoteControlUrl($target, $_REQUEST) )
|
|
relayControlReply($url);
|
|
|
|
$ctrlCommand = $monitorAction->controlCommand();
|
|
if ( !$ctrlCommand )
|
|
ajaxError('Action has no usable command');
|
|
|
|
if ( $target->sendControlCommand($ctrlCommand) ) {
|
|
ajaxResponse('Success');
|
|
} else {
|
|
ajaxError('Failed');
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( canView('Control', $_REQUEST['id']) ) {
|
|
$monitor = new ZM\Monitor(validCardinal($_REQUEST['id']));
|
|
|
|
if ( $url = remoteControlUrl($monitor, $_REQUEST) )
|
|
relayControlReply($url);
|
|
|
|
$ctrlCommand = buildControlCommand($monitor);
|
|
|
|
if ( !$ctrlCommand ) {
|
|
ajaxError('No command received');
|
|
return;
|
|
}
|
|
|
|
// Opt-in query: return the daemon's reply (e.g. light status) to the browser.
|
|
if ( !empty($_REQUEST['response']) ) {
|
|
ajaxResponse(array('status' => $monitor->sendControlCommandWithResponse($ctrlCommand)));
|
|
return;
|
|
}
|
|
|
|
if ( $monitor->sendControlCommand($ctrlCommand) ) {
|
|
ajaxResponse('Success');
|
|
} else {
|
|
ajaxError('Failed');
|
|
}
|
|
}
|
|
|
|
ajaxError('Unrecognised action or insufficient permissions');
|
|
|
|
// Hands the request to the server that records the monitor and answers with its reply.
|
|
function relayControlReply($url) {
|
|
$reply = forwardControlRequest($url);
|
|
if ( $reply === false )
|
|
ajaxError('Failed');
|
|
if ( function_exists('ajaxCleanup') )
|
|
ajaxCleanup();
|
|
header('Content-type: application/json');
|
|
exit($reply);
|
|
}
|
|
?>
|