27198 Commits

Author SHA1 Message Date
Isaac Connor
80c93084af feat: add AUDIT logging level for tracking administrative changes
Add a new AUDIT logging level (-5) between PANIC (-4) and NOLOG (shifted
to -6) across C++, PHP, and Perl loggers. AUDIT entries use code 'AUD'
and syslog priority LOG_NOTICE. They record who changed what, from where,
for monitors, filters, users, config, roles, groups, zones, states,
servers, storage, events, snapshots, control caps, and login/logout.

AUDIT entries have their own retention period (ZM_LOG_AUDIT_DATABASE_LIMIT,
default 1 year) separate from regular log pruning. The log pruning in
zmstats.pl and zmaudit.pl now excludes AUDIT rows from regular pruning
and prunes them independently.

Critical safety: the C++ termination logic is changed from
'if (level <= FATAL)' to 'if (level == FATAL || level == PANIC)' to
prevent AUDIT-level log calls from killing the process.

Includes db migration zm_update-1.39.1.sql to shift any stored NOLOG
config values from -5 to -6.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 18:17:48 -05:00
Isaac Connor
cbda01f0cb Merge branch 'master' into edge 2026-02-17 13:28:36 -05:00
Isaac Connor
00779c6b67 fix: guard against null evtStream element in restartZmsStream
document.getElementById('evtStream') can return null if the element
was removed from the DOM during event navigation or deletion, causing
a TypeError when accessing .src on the result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 11:38:54 -05:00
Isaac Connor
5434518046 fix: guard against null monitor div in video-stream.js divMode and divError setters
this.closest() returns null when the <video-stream> element isn't inside
a monitor div, causing TypeError on the subsequent querySelector call.
Add null checks in both divMode and divError setters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 10:52:40 -05:00
Isaac Connor
6913b0615a Release 1.38.1 1.38.1 2026-02-16 18:32:05 -05:00
Isaac Connor
af73704bcb fix: normalize date/time filter values to ISO format in montagereview API requests
In loadEventData, date/time filter values from form inputs were passed
directly into the CakePHP REST URL path. Locale-formatted dates using /
as separator (e.g. 01/15/2024) break URL routing because Apache decodes
%2F back to / in path segments.

Use moment() to normalize date/time values to YYYY-MM-DD HH:mm:ss before
encoding them into the URL. Also replace hardcoded fallback dates in
clicknav that used MM/DD/YYYY format with ISO format equivalents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 17:59:02 -05:00
Isaac Connor
b1ff22e58d fix: fall back to pixel parsing when zone Units=Percent but coords exceed 100
When zones have Units='Percent' in the database but their Coords contain
pixel values (>100), ParsePercentagePolygon treats them as percentages,
causing wild scaling (e.g., 639% * 1920 / 100 = 12269) followed by
clamping to monitor bounds, producing degenerate full-frame zones.

Add a pre-check in Zone::Load that scans coordinate values before calling
ParsePercentagePolygon. If any value exceeds 100, log a warning and use
ParsePolygonString (pixel path) instead. Also add unit tests for both
ParsePolygonString and ParsePercentagePolygon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 17:26:16 -05:00
Isaac Connor
ad2373af67 FIx missing / on bottomBlock end div 2026-02-16 15:56:43 -05:00
Isaac Connor
091ce8bb00 fix: default analysis overlay to off in zms streams
MonitorStream.js initialized analyse_frames = true, causing zone
overlays to always appear on stream start regardless of view settings.
Change default to false so watch view starts clean. The zone editor
view already explicitly enables analysis after monitor creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 15:30:36 -05:00
Isaac Connor
5136052a0e fix: use int instead of unsigned int for zone pixel coordinates in zm_zone.cpp
The lo_x/lo_y/hi_x/hi_y variables were unsigned int despite being assigned
from signed int32 Vector2 members and compared against signed int dimensions.
This caused scattered (int) and (unsigned int) casts throughout CheckAlarms,
std_alarmedpixels, and Setup. Switching to int eliminates the casts, fixes
the signed/unsigned comparison warnings, and makes the -1 sentinel check in
std_alarmedpixels explicit rather than relying on unsigned wraparound.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 12:14:09 -05:00
Isaac Connor
ffc5a8f4c1 feat: expand ONVIF config_types with full device/media/imaging/PTZ queries
Add config type entries for Hostname, DNS, NTP, NetworkInterfaces,
Capabilities, Scopes, Services, VideoSources, VideoSourceConfigurations,
AudioSources, AudioSourceConfigurations, AudioEncoderConfigurations,
StreamUri, SnapshotUri, ImagingOptions, PTZConfigurations, PTZNodes,
PTZPresets, and PTZStatus.

- Support __PROFILE_TOKEN__ substitution in request bodies
- Add writable flag to config types and enforce it in set_config
- Add NTP set handler for SetNTP ONVIF command
- Organize config_types by ONVIF service (device, media, imaging, PTZ)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:08:45 -05:00
Isaac Connor
1e5500b0fd fix: clamp zone polygon extents to actual frame dimensions in CheckAlarms
When a camera reconnects at a different resolution than the zone polygons
were configured for, the polygon bounding box can exceed the frame
dimensions. This caused a heap buffer overflow in the memset that zeros
the bbox rows of the diff image, confirmed by Valgrind (invalid write
at 0 bytes past a 921600-byte / 1280x720 block).

- Validate delta_image buffer and dimensions at entry
- Clamp hi_y/hi_x to image height/width before memset and pixel loops
- Guard filter, blob, and HighlightEdges loops against empty rows where
  ranges[y].lo_x is -1 (would wrap to UINT_MAX as unsigned)
- Clamp extents in std_alarmedpixels independently for defense in depth

Warning() logs identify which zones need polygon reconfiguration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:01:32 -05:00
Isaac Connor
8bc20a2d95 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-15 15:59:33 -05:00
Isaac Connor
113f1435f6 fix: restore filter Name and UserId after save/reload
The post-save redirect included filter query params in the URL, which
caused the view to populate from request data instead of loading from
the database. Since the querystring omitted Name and UserId, both
fields appeared empty/wrong after reload. Remove the querystring from
the redirect so the view loads the saved filter from the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:39:17 -05:00
Isaac Connor
58aa6c9a44 feat: update Controls entries to use unified ONVIF.pm module
Add new 'ONVIF' Controls entry with corrected brightness/contrast
ranges (0-100). Update legacy entries (ONVIF Camera, Netcat ONVIF,
Reolink RLC-423/411/420) to use Protocol 'ONVIF' instead of the
old per-vendor module names (onvif, Netcat, Reolink).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:39:17 -05:00
Isaac Connor
bddb8811db Merge pull request #4625 from IgorA100/patch-987944
Feat: Merging and moving two identical functions monitorsSetScale from montage.js and watch.js to skin.js
2026-02-15 12:52:12 -05:00
Isaac Connor
cb1ff156ed Merge branch 'master' into patch-987944 2026-02-15 12:52:04 -05:00
Isaac Connor
e8f3be2e66 Merge pull request #4626 from IgorA100/patch-274642
Removed styles for GridStack, since in version 12 styles are now generated dynamically (montage.css)
2026-02-15 12:48:24 -05:00
Isaac Connor
aba8af91a3 Merge pull request #4627 from IgorA100/patch-418578
Created some slightly rounded corners for the streams, tables, and navigation block: .imageFeed, .nav, .fixed-table-body, id='videoFeedStream*' (skin.css)
2026-02-15 12:47:58 -05:00
Isaac Connor
a2949b7f2b fix: update heartbeat during camera close and reconnect
avformat_close_input() can block for 75-90s on TCP retransmit timeout
when an RTSP camera becomes unresponsive, and the connect() retry loop
also lacks heartbeat updates. This causes zmwatch to kill zmc with a
stale heartbeat even though the process is actively reconnecting.

Add SetHeartbeatTime() calls before/after Close() and in the connect()
retry loop so zmwatch knows zmc is still alive during reconnection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:47:33 -05:00
Isaac Connor
ce9f6f4a85 Add 4px border-radius to thumbnail img 2026-02-15 12:47:33 -05:00
Isaac Connor
55d209285c Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-15 12:39:25 -05:00
Isaac Connor
8ac681d79d Merge pull request #4628 from nabbi/fix-cameramodel-app-uses
fix: correct App::uses package path in CameraModel
2026-02-15 12:36:07 -05:00
Isaac Connor
0f375d2941 Merge pull request #4629 from IgorA100/patch-650947
Fix: Stop executing "streamQuery" if "zmsBroke" (event.js)
2026-02-15 12:35:15 -05:00
Isaac Connor
ab409c4211 Merge pull request #4630 from IgorA100/patch-232910
Fix: Color of some buttons & input[type='checkbox'], input[type='radio'] on a dark skin (skin.css)
2026-02-15 12:34:54 -05:00
Isaac Connor
c09b3186c8 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-15 12:33:55 -05:00
Isaac Connor
283eb3df9f fix: warn when zone coordinates extend beyond image dimensions
Before limitPoints() silently clamps coordinates, check if any zone
point exceeds the monitor's image dimensions and show a warning icon
with tooltip in the zones table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:25:09 -05:00
Isaac Connor
1e3f58f847 fix: skip V4L2 AutoSelectFormat in non-capture mode
AutoSelectFormat() opens the video device to enumerate formats, but
non-capture processes like zms (QUERY mode) should never need to access
the device. Guard the call with the existing capture flag so zms no
longer fails with "No such file or directory" errors when running under
Apache with systemd PrivateDevices=yes.

Also downgrade the fallback message from Error to Warning since YUYV
fallback is a recoverable condition, and add a hint about PrivateDevices
to the ENOENT error in AutoSelectFormat for cases where capture mode
legitimately can't open the device.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:12:06 -05:00
Isaac Connor
8600393bfb fix: remove file_exists() gate on /dev/video in settings modal
Debian Forky's stock apache2 systemd unit sets PrivateDevices=yes,
giving Apache its own /dev namespace without video devices. This
caused file_exists('/dev/video0') to return false even though the
device exists and www-data is in the video group.

Remove the file_exists() pre-check and just attempt v4l2-ctl directly.
When it fails, provide targeted diagnostics: missing v4l2-ctl, systemd
PrivateDevices detected, or generic permission error with the running
username.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 11:13:20 -05:00
IgorA100
c0bd9b703d Color input[type='checkbox'], input[type='radio'] on dark skin (skin.css) 2026-02-15 18:53:08 +03:00
IgorA100
25d207a3f8 Fix: Color of some buttons on a dark skin (skin.css) 2026-02-15 18:43:49 +03:00
Isaac Connor
0f7aceb19e perf: replace sleep polling with condition wait in Event::Run()
The event thread was sleeping 33ms (ZM_SAMPLE_RATE) between checks for
packet decoded/analyzed status. Replace with a condition variable wait
on the packet itself, using a 2ms timeout as safety net for the race
between flag check and wait entry.

Add packet->notify_all() at every site where decoded or analyzed is set
to true, so the event thread wakes up near-instantly. Add wait_for()
to ZMPacketLock to support timed waits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 09:44:42 -05:00
IgorA100
551065c70d Fix: Stop executing "streamQuery" if "zmsBroke" (event.js)
For example, we viewed an event, but it was then deleted or destroyed.
Currently, queries will continue to run indefinitely.
2026-02-15 17:32:20 +03:00
Isaac Connor
edf4d24aa3 perf: reduce lock contention in PacketQueue
Remove per-packet ZMPacketLock trylock() from clearPackets() scan loops
and queuePacket() GOP deletion — the iterator check is sufficient since
threads only access packets through their own shared_ptr.

Add a monotonic uint64_t queue_index to ZMPacket, assigned on enqueue.
clearPackets() now finds the earliest iterator position with a single
min() over the 2-3 iterators, then uses one integer comparison per
scanned packet instead of walking all iterators per packet.

Defer packet destruction outside the mutex in both clearPackets() and
queuePacket() by collecting removed shared_ptrs into a local vector
and releasing the lock before they are destroyed.

Raise per-packet deletion Debug(1) to Debug(4) in both paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:22:19 -05:00
Nic Boet
c54899a8d4 fix: correct App::uses package path in CameraModel
App::uses('AppModel', 'CameraModel') tells CakePHP to look for AppModel
in a non-existent 'CameraModel' package. The correct second argument is
'Model', which points to app/Model/AppModel.php where the base class
actually lives.

This was likely a copy-paste error — every other model in the codebase
correctly uses App::uses('AppModel', 'Model'). The bug may go unnoticed
when another model loads AppModel first via CakePHP's autoloader, but
causes a fatal error if CameraModel is the first model resolved in a
request (e.g. hitting the camera models API endpoint directly).
2026-02-13 19:53:13 -06:00
IgorA100
931ac2da08 I created some slightly rounded corners for the streams, tables, and navigation block: .imageFeed, .nav, .fixed-table-body, id='videoFeedStream*' (skin.css) 2026-02-14 00:03:57 +03:00
IgorA100
3f78562de8 Fix: Eslint (skin.js) 2026-02-13 23:27:42 +03:00
IgorA100
a754967c28 Removed styles for GridStack, since in version 12 styles are now generated dynamically (montage.css) 2026-02-13 23:12:10 +03:00
IgorA100
327dd9999d Changed the style for ".monitor" a bit (watch.css) 2026-02-13 23:07:03 +03:00
IgorA100
0d712deb4c Added styles for id=liveStream* (skin.css) 2026-02-13 23:00:58 +03:00
Isaac Connor
08144613fc Merge pull request #4618 from pliablepixels/fix_event_start_stop_commands
feat: support token substitution in EventStartCommand/EventEndCommand
2026-02-13 14:40:18 -05:00
Isaac Connor
e8de2d8a43 feat: add --protocol mode to zmcontrol.pl for direct module testing
Add --protocol, --address, and --device CLI options that bypass the
database and socket machinery to load a control module directly and
execute a single command. This enables standalone testing of control
modules (e.g. ONVIF.pm get_config) without a running ZoneMinder
instance. Also update POD with full documentation and examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:37:03 -05:00
Pliable Pixels
c4fa045d1e fix: preserve legacy execlp() behavior for commands without % tokens
If EventStartCommand/EventEndCommand contains a % character, use the
new token substitution (%EID%, %MID%, %EC%) with sh -c execution.
Otherwise, fall back to the original execlp() behavior that passes
event_id and monitor_id as $1 and $2, so existing installs are not
broken.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:35:17 -05:00
IgorA100
97929d1c9a Merging and moving two identical functions monitorsSetScale from montage.js and watch.js to skin.js
And take into account "scrollTop" when performing scaleToFit
2026-02-13 22:27:52 +03:00
IgorA100
6d5ebc131b Moving "monitorsSetScale" function from to skin.js (watch.js)
And Remove Event listener when restarting the stream
2026-02-13 22:20:06 +03:00
IgorA100
a5828debb9 Wrap "#dvrControls" and "#extButton" in a single DIV "#bottomBlock" for correct scaling in scale=Auto mode (watch.php) 2026-02-13 22:12:39 +03:00
IgorA100
b7c01c9f0f Moving "monitorsSetScale" function from montage.js to skin.js (montage.js) 2026-02-13 21:49:11 +03:00
Isaac Connor
632adaec62 Merge pull request #4623 from IgorA100/patch-164992
Fix: Clear "srcObject" for "Stream" and adjust Janus connection timeout (MonitorStream.js)
2026-02-13 13:25:44 -05:00
Isaac Connor
e1effdb1c7 fix: restore filter Name and UserId after save/reload
The post-save redirect included filter query params in the URL, which
caused the view to populate from request data instead of loading from
the database. Since the querystring omitted Name and UserId, both
fields appeared empty/wrong after reload. Remove the querystring from
the redirect so the view loads the saved filter from the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:33:46 -05:00
Isaac Connor
d8f9e13804 feat: update Controls entries to use unified ONVIF.pm module
Add new 'ONVIF' Controls entry with corrected brightness/contrast
ranges (0-100). Update legacy entries (ONVIF Camera, Netcat ONVIF,
Reolink RLC-423/411/420) to use Protocol 'ONVIF' instead of the
old per-vendor module names (onvif, Netcat, Reolink).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:17:39 -05:00