mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-07 13:13:05 -05:00
dbConnect() turns emulated prepares off, so every query of the web UI, the ajax calls and the includes the API runs (config, servers, users, permissions) is a server-side prepare: prepare, execute and close, three round trips to the database where one would do. From PHP 8.1, pdo_mysql returns integers and floats as PHP ints and floats with emulated prepares too, so values keep the types they have now. Emulate prepares there and keep server-side prepares on older PHP, where every value would become a string. Emulated prepares quote parameters bound as strings, which breaks LIMIT ?. dbQuery() now binds integer values as integers (everything else as strings, as execute() did), and the log and watch ajax calls cast their validated offset and limit to int. With emulation, PDO would also run several statements given in one string; MYSQL_ATTR_MULTI_STATEMENTS is turned off so that is refused as before. CakePHP's own connection is not changed: it already uses PDO's default, emulated prepares. tests/php/test_database_emulated_prepares.php compares every column of every table read through both kinds of prepare (types and values), checks aggregates, LIMIT with positional and named integers, string and null parameters, and that two statements in one string are refused.
110 lines
3.7 KiB
PHP
110 lines
3.7 KiB
PHP
<?php
|
|
//
|
|
// This is simplified version of the logic from ajax/events.php
|
|
// For efficiency, sorting and and pagination functionality have been excluded
|
|
//
|
|
|
|
$message = '';
|
|
$data = array();
|
|
|
|
//
|
|
// INITIALIZE AND CHECK SANITY
|
|
//
|
|
|
|
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user->Username();
|
|
|
|
// Mid specifies the monitor id we are searching on
|
|
if ( empty($_REQUEST['mid']) ) {
|
|
$message = 'Must specify a monitor Id';
|
|
} else {
|
|
$mid = validCardinal($_REQUEST['mid']);
|
|
}
|
|
|
|
// Limit specifies the number of rows to return
|
|
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
|
$message = 'Invalid value for limit: '.$_REQUEST['limit'];
|
|
} else {
|
|
$limit = (int)$_REQUEST['limit'];
|
|
}
|
|
|
|
if ($message) {
|
|
ZM\Error($message);
|
|
ajaxError($message);
|
|
return;
|
|
}
|
|
|
|
// Sort specifies the name of the column to sort on
|
|
$sort = 'Id';
|
|
if (isset($_REQUEST['sort'])) {
|
|
$sort = $_REQUEST['sort'];
|
|
}
|
|
|
|
// Order specifies the sort direction, either asc or desc
|
|
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
|
|
|
//
|
|
// MAIN LOOP
|
|
//
|
|
|
|
// The names of the dB columns in the events table we are interested in
|
|
$columns = array('Id', 'MonitorId', 'StorageId', 'Name', 'Cause', 'StartDateTime', 'EndDateTime', 'Length', 'Frames', 'AlarmFrames', 'TotScore', 'AvgScore', 'MaxScore', 'Archived', 'Emailed', 'Notes', 'DiskSpace');
|
|
|
|
if ( $sort != 'Id' ) {
|
|
if (!in_array($sort, $columns)) {
|
|
ZM\Error('Invalid sort field: ' . $sort);
|
|
$sort = 'Id';
|
|
} else if ($sort == 'EndDateTime') {
|
|
if ($order == 'ASC') {
|
|
$sort = 'E.EndDateTime IS NULL, E.EndDateTime';
|
|
} else {
|
|
$sort = 'E.EndDateTime IS NOT NULL, E.EndDateTime';
|
|
}
|
|
} else {
|
|
$sort = 'E.'.$sort;
|
|
}
|
|
}
|
|
$where = 'WHERE MonitorId = '.$mid;
|
|
|
|
$col_str = ' E.*, GROUP_CONCAT(T.Name SEPARATOR ", ") AS Tags ';
|
|
|
|
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E
|
|
LEFT JOIN Events_Tags AS ET ON E.Id = ET.EventId
|
|
LEFT JOIN Tags AS T ON T.Id = ET.TagId
|
|
'.$where.' GROUP BY E.Id ORDER BY '.$sort.' '.$order.' LIMIT ?';
|
|
|
|
$rows = dbQuery($sql, array($limit));
|
|
$returned_rows = array();
|
|
|
|
if ($rows) {
|
|
foreach ( $rows as $row ) {
|
|
$event = new ZM\Event($row);
|
|
$event->remove_from_cache();
|
|
if (!$event->canView()) continue;
|
|
if ($event->Monitor()->Deleted()) continue;
|
|
|
|
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
|
|
$imgSrc = $event->getThumbnailSrc(array(), '&');
|
|
$streamSrc = $event->getStreamSrc(array(
|
|
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&');
|
|
|
|
$videoAttr = '';
|
|
if ($event->DefaultVideo()) {
|
|
$videoSrc = $event->getStreamSrc(array('mode'=>'mp4'), '&');
|
|
$videoHlsSrc = $event->getStreamSrc(array('mode'=>'mp4hls'), '&');
|
|
$videoDuration = htmlspecialchars((string)$event->Length());
|
|
$videoAttr = ' video_src="' .$videoSrc. '" data-video-hls-src="' .$videoHlsSrc. '" data-video-duration-secs="' .$videoDuration. '" data-event-start="'.htmlspecialchars($event->StartDateTime()).'"';
|
|
}
|
|
|
|
// Modify the row data as needed
|
|
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="Event '.$event->Id().'" width="' .validInt($event->ThumbnailWidth()). '" height="' .validInt($event->ThumbnailHeight()).'" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"' .$videoAttr. ' data-monitor-width="'.$event->Width().'" data-monitor-height="'.$event->Height().'" loading="lazy" />';
|
|
$row['Name'] = validHtmlStr($row['Name']);
|
|
$row['Length'] = gmdate('H:i:s', intval($row['Length']));
|
|
|
|
$returned_rows[] = $row;
|
|
} # end foreach row matching search
|
|
}
|
|
|
|
$data['rows'] = &$returned_rows;
|
|
ajaxResponse($data);
|
|
?>
|