126 Commits
Author SHA1 Message Date
Isaac ConnorandClaude Opus 5 9c06eb6ba2 refactor: connect to the database on first use, not on include
database.php called dbConnect() at file scope, so including it opened a socket,
and on failure rendered views/no_database_connection.php and exit()ed from
inside a library include. Every model in web/includes requires this file, so
merely loading a class did both.

Connect on first use instead. $dbConn becomes tri-state - false for "not
attempted", null for "attempt failed", a PDO for connected - and two accessors
sit on top of it:

  zmDbConn()        opens if needed; on failure renders the error view and
                    stops, which is what the include used to do, just at the
                    point a query is actually attempted.
  zmDbConnOrNull()  opens if needed but returns null instead of ending the
                    request, for callers with a fallback.

dbQuery() is the funnel every fetch helper goes through, so routing it plus
dbEscape(), dbError() and dbInsertId() through the accessors covers the library.
The five callers that reached for the raw global are updated: config.php.in,
Event.php and ajax/console.php need a connection and take zmDbConn(); logger.php
takes zmDbConnOrNull() and falls through to its error_log target, so a logging
call can no longer end the request or open a connection by itself.

ZMSessionHandler captured $dbConn in its constructor. It is constructed while
session.php is being included, before anything has needed the database, so with
a lazy connection that captured false. It now resolves per call and its methods
return "no session" rather than dereferencing a bool.

Two smaller fixes fall out. The error view was included by a relative path that
only resolved when the cwd was web/, so it never worked for requests served out
of web/api/; it is now anchored with __DIR__. And dbDisconnect() set $dbConn to
null, which in the new tri-state means "connecting failed" and would send the
next query to the error page; it sets false so a later query can reconnect.
Nothing calls dbDisconnect() today.

This does NOT make database.php includable without a database. It requires
logger.php, which requires config.php, which reads ZoneMinder's configuration
out of the Config table at include time. Until that cycle is broken the
connection still happens during bootstrap, just from config.php rather than from
here.

Tests: tests/php/test_database_lazy_connect.php, 7 assertions, all pass. It
tokenises database.php and asserts nothing runs at include time, that dbQuery()
goes through the accessor, and that only the connection plumbing touches the
global. Verified it reports the pre-refactor file's `if ( !dbConnect() )` - an
earlier version of the check skipped tokens inside parentheses and so passed on
exactly the code it exists to reject.

Not covered by tests: behaviour when the database is genuinely unreachable, and
the session handler against a live database. Needs manual testing on an
installed tree, including stopping mysql to confirm the error view still renders
for both a web request and an API request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01477mR97vfnK6zczbHgzq6T
2026-08-17 20:52:10 -04:00
SteveGilvarryandClaude Opus 4.8 cd6aeb2c74 fix: correct thumbnail aspect ratio for high-resolution monitors
Event::ThumbnailWidth()/ThumbnailHeight() derived the secondary dimension
through an integer SCALE_BASE scale:

  $scale = intval((SCALE_BASE * THUMB_WIDTH) / Width);
  ThumbnailHeight = reScale(Height, $scale);

For a high-resolution monitor the scale factor is small and intval()
truncates it. e.g. a 2688x1520 monitor with WEB_LIST_THUMB_WIDTH=48 gives
scale = intval(100*48/2688) = intval(1.78) = 1, so the height becomes 15
instead of 27 — a 3.2:1 thumbnail instead of 16:9. The events-list hover
overlay sizes its container from that thumbnail and object-fit:cover-crops
the real (correct-aspect) video into the squashed box, which shows as a
"long narrow" popout.

Compute the secondary dimension directly from the aspect ratio
(round(Height * THUMB_WIDTH / Width)) so no precision is lost.

refs #3443

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:47:13 +10:00
IgorA100 f3ac936605 Deleted the unnecessary line (Event.php) 2026-06-04 14:47:51 +03:00
IgorA100 a233fc1224 - Correct HLS playback on the Watch page
- When executing Event->Length() , get the duration of the recorded event file if the duration in the database table is 0
- If native HLS playback fails in Safari, we first try playing MP4, and only if MP4 playback fails do we switch to MJPEG playback
2026-05-31 16:25:51 +03:00
IgorA100 964ea784fc - Do not check the M3u8 file if currentView === 'frames'
- When executing Length() and Duration() , do not write the result to the database.
- When assigning a value to the "data-video-duration-secs" attribute, first check the Length in $row['Length'] and, if it is 0, only then check the file's Duration.
- If the M3u8 file is missing or invalid, do not cache the server response.
2026-05-26 19:23:59 +03:00
IgorA100andCopilot Autofix powered by AI 0ff920ec07 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 16:58:19 +03:00
IgorA100 be29253b12 Added comment (Event.php) 2026-05-24 11:03:19 +03:00
IgorA100 5cdeca1529 Duration is only saved to the database if it's greater than zero. (Event.php) 2026-05-24 10:51:28 +03:00
IgorA100 3c7410bf09 Added a private Duration() method as a synonym for the Length() method. (Event.php)
Added retrieval of the Duration from a video file and saving it to the database if it wasn't already present.
2026-05-24 00:54:06 +03:00
IgorA100 16f350c4da Fixed the value assignment for "$args['view']" (Event.php) 2026-05-21 13:17:15 +03:00
IgorA100 1678c13993 Option to generate 'view=view_hls' (Event.php) 2026-05-17 01:27:19 +03:00
Isaac ConnorandClaude Opus 4.6 5561829450 fix: include username in auth relay and fix stale auth in stream restart
- Add user= parameter to get_auth_relay() so zms can use the indexed
  Username column instead of iterating all users to validate the hash
- Apply the same fix to Event.php getStreamSrc() and getThumbnailSrc()
- Tighten Monitor.php from isset() to !empty() for consistency
- In MonitorStream.js start(), check if the auth hash in the img src
  matches the current auth_hash before resuming via CMD_PLAY. If stale,
  fall through to rebuild the URL with fresh auth_relay. This prevents
  long-running montage pages from spawning zms with expired credentials.
- Downgrade zms auth failure from Error to Warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 10:01:47 -04:00
Isaac ConnorandClaude Opus 4.6 ffe6362dc3 fix: harden web interface against injection and SSRF vulnerabilities
FilterTerm.php:
- Replace eval() with safe compare() method for SystemLoad, DiskPercent,
  and DiskBlocks filter conditions (RCE via crafted op/val)
- Validate operator against allowlist in constructor
- Sanitize collate field to alphanumeric/underscore only (SQLi)

onvifprobe.php:
- Use escapeshellarg() on interface, device_ep, soapversion, username,
  and password arguments passed to execONVIF() (command injection)

Event.php:
- Use escapeshellarg() on all arguments to zmvideo.pl instead of
  escapeshellcmd() on the whole command (command injection via format)
- Anchor scale regex with ^ and $ to prevent partial matches

image.php:
- Restrict proxy URL scheme to http/https only (SSRF via file:// etc)

filterdebug.php:
- Use already-sanitized $fid instead of raw $_REQUEST['fid'] (XSS)

MonitorsController.php:
- Use escapeshellarg() on token, username, password, and monitor id
  in zmu shell command instead of escapeshellcmd() on whole command

HostController.php:
- Use escapeshellarg() on path in du command (command injection via mid)
- Remove space from daemon name allowlist (argument injection)

EventsController.php:
- Remove single quotes from interval expression regex (SQLi)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:30:49 -04:00
Isaac ConnorandClaude Opus 4.6 b036408a5b Fix RCE vulnerability via API config edit privilege escalation
Add RBAC checks to ConfigsController edit() and delete() requiring
System=Edit permission, matching the pattern used by other controllers.
Harden System/Readonly column checks with !empty() to handle missing
columns gracefully. Fix command injection in Event.php by using
ZM_PATH_FFMPEG constant with escapeshellarg() instead of hardcoded
unsanitized ffmpeg call. Add is_executable() validation at all exec()
sites using ZM_PATH_FFMPEG as defense-in-depth against poisoned config
values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:51:30 -05:00
Isaac Connor aeff647f90 Apply intval to scale calculations 2025-10-10 13:26:30 -04:00
Isaac Connor 2ba42345a7 Don't commit updated DiskSpace to the db. If we want to do that, we can call Save. This prevents UI from hanging waiting for this update to happen 2025-08-07 11:29:46 -04:00
IgorA100 f2aed5fc21 Chore: Adjust spaces, tabs, newlines 2024-04-03 22:58:21 +03:00
IgorA100 70de139e77 Feat: Ability to download videos on the "Event" page with a multiport configuration 2024-03-28 19:47:53 +03:00
Simpler1 992b729329 chore(lint): $ locations 2024-03-28 09:59:58 -04:00
Isaac Connor be61184faf Add defaults to GenerateVideo, don't chdir, fix other code 2023-12-01 13:05:36 -05:00
Isaac Connor 49af487746 port over generateVideo from perl side and stick into Event. 2023-12-01 12:46:26 -05:00
adhamiamirhossein 27977acdd4 fix: php 8.3 deprecated get_class method call without argument 2023-11-22 10:48:04 +03:30
Isaac Connor 092f8df115 Handle scale=0 in getImageSrc 2023-11-20 17:23:51 -05:00
Isaac Connor c287b7f645 Add Tags() and Event_Tags() functions to Event 2023-10-20 14:02:25 -04:00
Isaac Connor feb3d91e8b Remove Tags from Event Data defaults. I don't think it is meant to be there 2023-10-06 14:54:17 -04:00
Isaac Connor 82aeac076d Fix incorrect var name frameid=>fid in find_virtual_frame 2023-09-26 12:10:36 -04:00
Isaac Connor f8c89a0405 Merge branch 'master' into tags 2023-09-14 16:42:06 -04:00
Isaac Connor 82e972943e Add frame loading including virtual frames 2023-09-08 14:18:11 -04:00
Simpler1 18d74ed7ac (feat): Tags
fix(tag): Create tags on mobile

chore(tags): Change TagName to Name

chore(tags): eslint

chore(tags): dbFetchAll to dbQuery for removetag

chore(events): eslint (attempt 2)

feat(tags): Better handling of keyboard

fix(tags): Enter key for creating new tag

fix(tags): Don't allow space as a tag name

feat(tags): Delete tag if last assignment removed

fix(tags): Increase height of dropdown

in progress

fix(Tags): Use T.Id on the events page dropdown

fix(Tags): Remove $availableTags from events.php

chore(sql): Formatting sql statements

feat(Tags): Working OR on filters and events pages

fix(filter): Populate availableTags

chore(Tags): code formatting

fix(tag): Add tag on create tag

Fix(tags): Remove tag from available if last

feat(tags): Add zm_update.sql

fix(chosen): Undo css width

fix(chosen): tags dropdown width

fix(tags): dropdown over timeline

fix(tags): Full width input

fix(events): Refresh table on page show

chore(filter): Clean up availableTags

chore(event): Clean up available & selected Tags

fix(event): Update available tags on remove

fix(event): Remove hack for selected tags

feat(tags): Blur input after adding tag

doc(tags): Initial tags documentation

fix(tags): Dark theme dropdown

fix(tags): Dark theme for tags on input

fix(tags): Dark theme for highlight in dropdown

fix(tags): Populate filter tags droplist

chore(): Bump zm_update to 1.37.42

chore(tags): Move mobile check to skin.js

chore(tags): Comment debug statements

fix(tags): Enter key to create tag on mobile Chome

chore(tags): Space in 'All Tags' for translation

Temporary commit to handle cookie expiration times

chore(tags): Remove unnecessary Tag(s) from en_gb

chore(): Cleanup unnecessary Error and Debug

chore(): Resolve merge conflicts

chore(): Address merge conflicts with master
2023-08-31 15:50:08 -04:00
Isaac Connor 4b417b8937 Fix other cases of user as an array 2023-04-23 15:35:26 -04:00
Isaac Connor d9f5d3c357 Implement StartDateTimeSecs 2023-04-22 10:48:41 -04:00
Isaac Connor 312d0ba841 Use objdetect as thumbnail if it exists. 2023-04-22 10:47:33 -04:00
Isaac Connor 44c7582003 Don't shorten analysis 2023-02-20 17:30:48 -05:00
Martin Tiernan 68283c01eb Added length and frames getters 2022-11-21 09:06:38 -06:00
Isaac Connor a891b528d1 Use Monitor::canView in Event::canView 2022-11-02 13:25:44 -04:00
Isaac Connor 3079438038 Move createVideo from functions to Event 2022-09-06 13:46:41 -04:00
Isaac Connor b611a4fc08 Use y instead of Y for path generation when using Deep scheme. Fixes #3583 2022-09-04 13:53:19 -04:00
Isaac Connor 907cdcd952 convert from strftime() to date() when forming the event path 2022-06-01 15:46:53 -04:00
Isaac Connor 1314295020 Don't update DiskSpace with a 0 value when listing events. This generally happens with missing events and causes too much contention on the Events and summaries tables 2022-05-30 10:26:04 -04:00
Isaac Connor 7515711eb8 Implement Server function which figures out which Server likely has the video. Use it to remove duplicate logic 2022-02-03 14:45:17 -05:00
Isaac Connor 193f349e38 implement Event::canEdit 2021-11-12 13:37:01 -05:00
Isaac Connor b9efe627f3 implement Event->canView 2021-08-18 10:53:59 -04:00
Isaac Connor 973533c809 Use method to handle case where Event wasn't found 2021-01-25 18:39:57 -05:00
Isaac Connor 2d33dd5386 Rename StartTime, EndTime in Events to StartDateTime and EndDateTime 2020-11-04 13:52:32 -05:00
Isaac Connor 10c0a6617c Return Debug to a regular function to match other logging functions. Since we switched to using namespaces we no longer clash with cake_php. 2020-10-14 10:39:25 -04:00
Isaac Connor c934dee233 Do not allow the deleting of Archived Events 2020-09-03 17:02:48 -04:00
Isaac Connor d0f6f8755c Implement a remove_from_cache function so we can free mem for objects we are not interested in 2020-08-27 17:14:47 -04:00
Isaac Connor d2b7aa3e90 Populate Scheme of default Storage Area when event Storage is not valid 2020-08-17 16:55:29 -04:00
Isaac Connor da757d075b Only save the DiskSpace on completed events. Fixes #3007 2020-08-07 09:53:35 -04:00
Isaac Connor 7fd038d99b spacing and quotes 2020-07-22 17:28:12 -04:00