Commit Graph

1510 Commits

Author SHA1 Message Date
Isaac Connor
ecae51ed25 fix: log bind params correctly when SQL contains literal % characters
zmDbDo built log messages by s/\?/'%s'/g on the SQL and then passing
the result to sprintf with the bind values. Any literal % in the SQL
(LIKE '%foo%' patterns, or the disk-percent substitution used by
dynamic filters) was interpreted as a sprintf format spec, producing
garbage output or an uncaught sprintf error.

Replace the two-step approach with a single regex that substitutes
bind values directly, so literal % in the SQL is preserved verbatim.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 14:42:23 -04:00
Daniel Caujolle-Bert
85cb74c0c7 Use qx() lc() and chomp() for command execution and result.
Use eq in the if() statement, instead of '=='.
2026-05-24 14:20:35 -04:00
Daniel Caujolle-Bert
7ccb22ad05 Change ::Config{uname} to ::Config{ZM_PATH_UNAME} 2026-05-24 14:20:35 -04:00
Daniel Caujolle-Bert
7fd11532e9 Use ZoneMinder::Config. 2026-05-24 14:20:35 -04:00
Isaac Connor
f256e585e9 fix: use prepare instead of prepare_cached in Filter::Execute
The SQL changes every cycle due to zmDiskPercent/zmDiskBlocks/zmSystemLoad
string substitution with live values, so prepare_cached never actually
returns a cached handle. Instead it leaks one cache entry per distinct
substituted value over the process lifetime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 12:54:42 -04:00
Isaac Connor
6819e73a4b fix: clear accumulated state in Filter::Sql() before rebuilding
PostSQLConditions, HasDiskPercent, HasDiskBlocks, and HasSystemLoad were
set during Sql() term processing but never cleared between rebuilds.
Each Execute() cycle pushed duplicate ExistsInFileSystem terms onto the
PostSQLConditions array, causing it to grow unboundedly over the process
lifetime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 12:48:55 -04:00
Daniel Caujolle-Bert
96c84e8847 Fix "top" command parsing on FreeBSD (tested in FreeBSD 13.5 jail, running 1.38.1). 2026-05-24 12:39:32 -04:00
Isaac Connor
292d317d26 fix: log Error in Event::delete when deadlock retries are exhausted
zmDbDo suppresses its Error log on 1213 inside a caller-managed TX (the
caller owns the retry), and the previous fallthrough at the end of the
retry loop just `return`ed silently. After 5 failed attempts on persistent
contention the event was effectively un-deleted with no record of the
failure. Capture errstr before rollback (some drivers clear it) and emit
an Error on the bail path.
2026-05-24 12:22:46 -04:00
Isaac Connor
ad31c4a9a5 fix: rollback and retry Event::delete under deadlock at READ COMMITTED
Three issues in the existing Stats/Event_Data/Frames/Events delete
sequence:

  - On any zmDbDo error inside the transaction the code called
    dbh->commit() instead of dbh->rollback(). The server-side
    transaction was already rolled back when InnoDB picked us as the
    deadlock victim, so the commit() was effectively against a fresh
    auto-started TX, but the bug pattern leaked through as confusing
    state and prevented any retry.

  - There was no retry. errno 1213 is expected under contention with
    zmstats and zma touching the same Event_Summaries[MonitorId] row,
    and the loser is supposed to re-run.

  - At REPEATABLE READ, two concurrent filter workers deleting events
    with adjacent EventIds take next-key/gap locks on each other's
    rows in the bucket tables.

Rewrite the delete block as a retry loop: SET TRANSACTION ISOLATION
LEVEL READ COMMITTED, begin_work, run the four DELETEs, commit on
success. On any error rollback (was: commit). On errno 1213 retry up
to 5 times with backoff. Skip both the isolation switch and the
rollback-then-retry when the caller is managing their own transaction
(in_transaction); they would be the wrong scope to act in.

Falls through to the storage DiskSpace adjustment only on commit, so
a deadlocked delete leaves the event for the next filter pass instead
of orphaning the row with stale storage accounting.

Note: do NOT pre-lock Event_Summaries[MonitorId] FOR UPDATE here, even
though the trigger touches it last. Pre-locking puts ES before
buckets[Id] in the lock acquisition order, which inverts against zma's
event_update_trigger path (Events[A] -> buckets[A] -> ES[N]) and
re-introduces the cycle the rest of this work is removing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:57:48 -04:00
Isaac Connor
40b6fa4fcd fix: handle HTTP-to-HTTPS redirect in Reolink_HTTP login
Reolink cameras may return a 302 redirect from HTTP to HTTPS.
LWP::UserAgent does not follow redirects for POST requests, so the
login would fail. Detect the redirect, update protocol/host/port from
the Location header, disable SSL verification for self-signed camera
certs, and retry the POST to the HTTPS endpoint. Subsequent API calls
use the updated protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 08:00:36 -04:00
Isaac Connor
c93602d4cc fix: add credential fallback chain in ONVIF control module
When ControlAddress does not contain authentication info, fall back
to Monitor->ONVIF_Username/ONVIF_Password, then Monitor->User/Pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-24 08:00:00 -04:00
copilot-swe-agent[bot]
5603d025be fix: ensure trigger_state written last in zmTriggerEventOn to fix 1/3 event trigger rate
Agent-Logs-Url: https://github.com/ZoneMinder/zoneminder/sessions/68794b2b-7137-4888-b66e-20c629112a57

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-05-24 07:58:04 -04:00
Isaac Connor
65bb4ad6dc fix: sanitize monitor Device path to prevent command injection (GHSA-g66m-77fq-79v9)
The Device field from the Monitors table was interpolated directly into
shell commands (qx(), backticks, exec()) without sanitization, allowing
authenticated users with monitor-edit permissions to execute arbitrary
commands as www-data via the Device Path field.

Defense in depth:
- Input validation: reject Device values not matching /^\/dev\/[\w\/.\-]+$/
  at save time in both web UI and REST API
- Output sanitization: use escapeshellarg() in PHP and quote validated
  values in Perl at every shell execution point

Affected locations:
- scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm (control, zmcControl)
- scripts/zmpkg.pl.in (system startup)
- web/includes/Monitor.php (zmcControl)
- web/includes/functions.php (zmcStatus, zmcCheck, validDevicePath)
- web/includes/actions/monitor.php (save action)
- web/api/app/Model/Monitor.php (daemonControl, validation rules)
- web/api/app/Controller/MonitorsController.php (daemonStatus)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-23 19:34:05 -04:00
Isaac Connor
5ff253b7b1 Don't use Fatal when there is an error preparing the sql. handle PostSQLConditions being an empty array 2026-04-18 12:28:59 -04:00
Isaac Connor
fd6e5d63de fix: add SSL certificate verification fallback to base Control class
When cameras use HTTPS with self-signed or invalid certificates, LWP's
default SSL verification causes connection failures. Add automatic
fallback to all three HTTP methods (get, put, post) in the base Control
class: if a request fails with an SSL/certificate error, disable
verification and retry. Once disabled, the relaxed setting persists for
the rest of the control session via the ssl_verify_disabled flag.

This follows the pattern already used in Dahua.pm and
TapoC520WS_ONVIF.pm but applies it universally so all control modules
benefit without needing individual SSL handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:23:57 -05:00
Isaac Connor
1ab1528757 fix: zmDbDo error logging now substitutes all SQL placeholders with values
The error path was missing the /g flag on the regex replacing ? with %s,
so only the first placeholder was replaced. It also dumped bind values as
a space-separated string instead of using sprintf to substitute them into
the query. Now matches the existing debug path pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:23:39 -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
409a9391b7 feat: add get_config/set_config to ONVIF control module
Add configuration read/write support following the Uniview.pm pattern.
get_config() queries five ONVIF categories (DeviceInformation, DateTime,
ImagingSettings, VideoEncoderConfiguration, Profiles) via SOAP and returns
parsed XML as hashes. set_config() supports writing ImagingSettings
(Brightness/Contrast/Saturation/Sharpness/ColorSaturation) and DateTime,
treating other categories as read-only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:10:10 -05:00
Isaac Connor
a329b34486 fix: repair imaging bugs in onvif, Reolink, Netcat, and TapoC520WS control modules
- onvif.pm: irisAbsClose checked lowercase 'brightness' so getCamParams
  was never called on decrease, sending stale/zero values
- Reolink.pm: same brightness case bug, plus whiteAbsIn and whiteAbsOut
  built the SOAP message but never called sendCmd — contrast changes
  were silently discarded
- Netcat.pm: same brightness case bug, plus debug() call should be Debug()
- TapoC520WS_ONVIF.pm: same brightness case bug

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:48:45 -05:00
Isaac Connor
57054cdd5b feat: add unified ONVIF.pm control module replacing four per-vendor copies
Single ONVIF SOAP/PTZ implementation that replaces onvif.pm, Reolink.pm,
Netcat.pm, and TapoC520WS_ONVIF.pm. All state is kept in instance variables
(no package globals), SSL verification falls back automatically, and
sendCmd routes to the correct ONVIF service endpoint per command type.

Bug fixes from the originals:
- Brightness decrease checked lowercase 'brightness' (never matched)
- whiteAbsIn/whiteAbsOut in Reolink/Netcat/TapoC520WS never called sendCmd
- Imaging commands sent to PTZ endpoint instead of /onvif/imaging
- Reboot sent to PTZ endpoint instead of /onvif/device_service
- Package globals leaked state between camera instances

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:46:34 -05:00
Isaac Connor
d74913e3bf fix: resolve multiple bugs in AxisV2 control module and add config/probe support
Fix critical crash in open() where guess_credentials() boolean return was
treated as URI object. Rewrite open() to use base class get_realm() for
auth discovery, matching the Uniview/HikVision pattern.

Bug fixes:
- focusRelNear: remove double-$self in recursive call, use instance var
- focusRelFar: fix copy-paste error calling focusRelNear instead of self,
  remove double-$self, use instance var
- focusAbs: fix .cgi.cgi typo in opticssetup URL
- Replace package-global $use_optics with $$self{use_optics} to prevent
  state leaking between camera instances
- Remove unused our $uri, use ZoneMinder::Config, use URI imports
- Move use LWP::UserAgent to module-level imports

New features:
- get_config/set_config via Axis param.cgi API
- probe() for network camera discovery
- rtsp_url() returning standard Axis RTSP path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 10:10:49 -05:00
Isaac Connor
37e377acf4 feat: implement LAPI config get/set and fix PTZ in Uniview control module
Replace empty get_config/set_config stubs with working implementations
using Uniview's native LAPI JSON REST API. Modernize open() to use base
class guess_credentials() and get_realm() instead of duplicated auth
logic. Rename put() to PutCmd() to fix broken PTZ methods. Remove
package-level globals in favor of instance variables. Add lapi_get/
lapi_put helpers, probe via LAPI with ISAPI fallback, and rtsp_url().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 09:58:00 -05:00
Isaac Connor
c250eadcea fix: use ${PERL_EXECUTABLE} instead of hardcoded perl in CMake builds
Replace 4 hardcoded `perl` invocations with `${PERL_EXECUTABLE}` from
find_package(Perl) so builds work when perl isn't in PATH or the wrong
perl would be found (NixOS, custom installs, Homebrew vs system).

Also: fix ZMCONFGEN_RESULT variable case mismatch in error message,
remove deprecated IMMEDIATE keyword from configure_file(), bump
cmake_minimum_required from 3.5 to 3.12 (C++17 requires 3.9+), and
remove dead C++11 fallback for CMake < 3.8 in ConfigureBaseTargets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:25:37 -05:00
Isaac Connor
fcfc7db0c7 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-09 09:29:41 -05:00
Isaac Connor
f7858f3770 feat: add events command to zmonvif-probe.pl
Add 'events' command to check if an ONVIF camera supports the events
service. Returns "Events: yes" or "Events: no" for easy parsing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 09:29:36 -05:00
Nic Boet
d66a3525bd feat: detect MariaDB-native command names with fallback to MySQL legacy names
MariaDB is deprecating mysql-prefixed utility names (mysqldump,
mysql) in favour of its own (mariadb-dump, mariadb). Add
findDbCommand() to ZoneMinder::General that probes for the
MariaDB-native binary first and falls back to the legacy name.
Results are cached per-process. Update the three call sites in
zmupdate.pl.in and zmcamtool.pl.in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:19:32 -06:00
Isaac Connor
4ebbc540ca Merge pull request #4601 from pliablepixels/fix-tag-filtering
fix: correct tag filter operator handling for No Tag and Any Tag
2026-02-07 15:38:11 -05:00
Pliable Pixels
79253dc374 fix: address review feedback for tag filter handling
- Add IS NOT operator check alongside != in PHP FilterTerm.php
  (was already handled in Perl but missing from PHP)
- Add defined() guard on $term->{val} in Perl Filter.pm to avoid
  uninitialized value warnings with malformed/legacy saved filters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 13:11:13 -05:00
Pliable Pixels
1188635e9a fix: correct tag filter operator handling for "No Tag" and "Any Tag"
The filter system ignored the operator (= vs !=) when generating SQL
for the special tag values "No Tag" (0) and "Any Tag" (-1).

In PHP (FilterTerm.php), "Tag != Any Tag" produced EXISTS instead of
NOT EXISTS, returning events WITH tags instead of events WITHOUT tags.

In Perl (Filter.pm), != was not handled as a special case and fell
through to generic SQL (T.Id != -1), which excluded events with no
tags because LEFT JOIN produces NULL and NULL != -1 evaluates to
UNKNOWN in SQL. Additionally, T.Id was unconditionally prepended for
all tag values, producing invalid SQL (T.IdEXISTS) for the special
cases that use EXISTS/NOT EXISTS subqueries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 13:01:39 -05:00
Isaac Connor
eb7ad81e40 fix: correct syntax error and missing parameter in Database.pm
- zmDbDo: fix mismatched parentheses in Error() call
- zmDbGetMonitorAndControl: pass $id parameter to zmDbFetchOne

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:44:49 -05:00
copilot-swe-agent[bot]
27793980ef fix: Address code review feedback
- Add SSL verification state tracking to Dahua.pm to ensure retry logic works for all requests
- Add SSL retry logic to Dahua.pm sendCmd method
- Remove redundant 'use IO::Socket::SSL' statements from TapoC520WS_ONVIF.pm retry blocks

refs #TBD

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-02-02 14:37:57 +00:00
copilot-swe-agent[bot]
bce080c735 feat: Enable SSL certificate verification with fallback for all components
Enable TLS peer certificate verification by default in all components that communicate over HTTPS. If SSL verification fails, log a warning and retry without verification to maintain backward compatibility with cameras using self-signed certificates.

Changes:
- C++ (zm_monitor_go2rtc.cpp): Enable SSL verification for all curl operations (3 locations)
- C++ (zm_monitor_rtsp2web.cpp): Enable SSL verification for all curl operations (3 locations)
- PHP (monitor_probe.php): Enable SSL verification with fallback logic
- Perl (Dahua.pm): Enable SSL verification with LWP::UserAgent
- Perl (TapoC520WS_ONVIF.pm): Enable SSL verification with retry logic in request methods

refs #TBD

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-02-02 14:34:19 +00:00
Isaac Connor
397d2a357e Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-01 10:17:16 -05:00
Isaac Connor
05db51a3ff Fix package name being Frame instead of User 2026-02-01 09:06:14 -05:00
Pliable Pixels
ee99bca21c fix: correct package name in User.pm (ZoneMinder::Frame → ZoneMinder::User)
User.pm declares `package ZoneMinder::Frame` instead of
`package ZoneMinder::User`, clobbering the Frame package namespace
and making the User ORM class unusable via `ZoneMinder::User->find_one()`.

Fixes ZoneMinder/zoneminder#4581

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 09:04:40 -05:00
Isaac Connor
d89f2e59db refactor: rename Janus_Use_RTSP_Restream to Restream
Rename Janus-specific restream fields to be more generic since they are
now used by Go2RTC and RTSP2Web as well:
- Janus_Use_RTSP_Restream → Restream
- Janus_RTSP_User → RTSP_User

Update visibility logic so the Restream checkbox appears when RTSPServer
is enabled AND any streaming service (Janus, Go2RTC, or RTSP2Web) is
selected, rather than only when Janus is enabled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 18:47:37 -05:00
Isaac Connor
872cdb1b44 Add Group.pm which is not actually used anywhere 2026-01-31 16:22:24 -05:00
Isaac Connor
479d3a9b05 Split parsing Path and Control address into their own functions, adding back a hack to handle unescaped passwords on ControlAddress. In Amcrest_HTTP if using ControlAddress fails, fall back to using Path 2026-01-22 11:54:58 -05:00
Isaac Connor
b6ee583454 Merge updates from another branch, cleaning up and improve address/auth parsing and improving put() 2026-01-22 10:03:01 -05:00
Isaac Connor
f702c6c190 Add last_analysis_view_time to perl shm structure 2026-01-16 15:28:43 -05:00
Isaac Connor
0ee1da73cf Remove primark_key 2026-01-14 09:46:31 -05:00
Isaac Connor
376e6b8899 Only set timezone if it is set in config. 2026-01-14 09:33:50 -05:00
Isaac Connor
32ec9dc801 Add defaults for Type and Scheme to prevent warnings in logs due to undefined values 2026-01-14 09:32:08 -05:00
Isaac Connor
201b2bbd06 Set tz in Date::Manip so that returned unixdate from '-1 hour' is in the current timezone, not UTC 2026-01-14 09:31:55 -05:00
Isaac Connor
8864d5759d Merge pull request #4510 from SteveGilvarry/videojs_update
Videojs update
2026-01-10 10:21:50 -05:00
copilot-swe-agent[bot]
b44aafb0ca Add "Any Tag" filter option for events
- Added "Any Tag" entry with value -1 to availableTags in Filter.php (widget and simple_widget methods)
- Added "Any Tag" entry to filter.php view
- Added translation for "Any Tag" in en_gb.php language file
- Implemented SQL logic in FilterTerm.php to handle "Any Tag" (value -1) using EXISTS query
- Implemented SQL logic in Filter.pm (Perl) to handle "Any Tag" (value -1) using EXISTS query

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-01-10 14:13:37 +00:00
Isaac Connor
6cfe79e259 Add AI generated Reolink http api. Tested for camera rebooting. 2026-01-08 17:19:07 -05:00
Isaac Connor
6b30d733c5 Update to use shared code from Control.pm 2026-01-06 14:58:40 -05:00
Isaac Connor
cb11f1f264 Clear query as well as path. Use canonical instead of creating our own BaseURL, set uri userinfo 2026-01-06 14:58:13 -05:00
Isaac Connor
b323c48b0f address and port are now self{address} and self{port} 2026-01-06 12:36:21 -05:00