Commit Graph

2481 Commits

Author SHA1 Message Date
Isaac Connor
a90a3bccea fix: auto-detect and convert pixel zone coordinates to percentages in web layer
When zone coordinates are stored as pixel values (e.g. from a missed DB
migration), the web layer now detects values > 100 and converts them to
percentages using the monitor's dimensions, mirroring the existing C++
detection logic in zm_zone.cpp. This prevents limitPoints() from clamping
pixel values to 0-100 and zones rendering incorrectly in SVG overlays.

- Add convertPixelPointsToPercent() helper in functions.php
- Call conversion before limitPoints() in zone.php and zones.php
- Update Zone::svg_polygon() to accept monitor dimensions and convert
- Pass ViewWidth/ViewHeight to svg_polygon() from Monitor::getStreamHTML()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:49:14 -05:00
Isaac Connor
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
9ebf48da35 Add Create to canEdit 2026-02-26 07:22:31 -05:00
Isaac Connor
c2ab47ae0d Don't audit log logins 2026-02-23 22:36:03 -05:00
Isaac Connor
e6ace6fcf4 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-23 18:19:20 -05:00
Isaac Connor
c0016fa00b feat: store zone coordinates as percentages for resolution independence
Convert zone coordinates from absolute pixel values to percentages
(0.00-100.00) so zones automatically adapt when monitor resolution
changes. This eliminates the need to manually reconfigure zones after
resolution adjustments.

Changes:
- Add DB migration (zm_update-1.37.81.sql) to convert existing pixel
  coords to percentages, recalculate area, and update Units default
- Add Zone::ParsePercentagePolygon() in C++ to parse percentage coords
  and convert to pixels at runtime using monitor dimensions
- Backwards compat: C++ Zone::Load() checks Units column and uses old
  pixel parser for legacy 'Pixels' zones
- Update PHP coordsToPoints/mapCoords/getPolyArea for float coords,
  replace scanline area algorithm with shoelace formula
- Update JS zone editor to work in percentage coordinate space with
  SVG viewBox "0 0 100 100" and non-scaling-stroke for consistent
  line thickness
- Position zone SVG overlay inside imageFeed container via JS to align
  with image only (not status bar)
- Support array of zone IDs in Monitor::getStreamHTML zones option
- Update monitor resize handler: percentage coords don't need rescaling,
  only threshold pixel counts are adjusted
- Add 8 Catch2 unit tests for ParsePercentagePolygon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -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
f127359fe0 feat: add network probe function for Uniview cameras
Add probeUniview() that queries the camera's LAPI for device model,
name, resolution, and codec. Uses RTSP main stream URL format
rtsp://ip:554/media/video1 and LAPI snapshot endpoint for thumbnails.
Includes OUI alias probeZhejiangUniviewTechnologiesCoLtd for IEEE
vendor name matching. Adds all 4 registered Uniview MAC OUI prefixes
(48ea63, 6cf17e, 88263f, c47905) to MacVendors.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 09:17:58 -05:00
Isaac Connor
7df902abdc fix: resolve "no url in button" when adding detected cameras
- Provide default rtsp://<ip>/ monitor entry for cameras discovered
  via ARP that lack a vendor-specific probe function, so they always
  have a URL for the Add button
- Only render the Add button and populate ProbeResults when url is
  non-empty, preventing the "No url in button" alert
- Fix curl_getinfo() called after curl_close() which broke HTTP
  response body parsing in probe functions
- Add missing break in import switch case to prevent fall-through
  to default warning

maybe fixes #4613

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:55:32 -05: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
60fbea3880 fix: security and code quality improvements in auth.php
- Fix SQL injection vulnerability in migrateHash() by using prepared statements
- Add null/empty check in password_type() to prevent array access error
- Remove dead code branch in generateAuthHash() (unreachable $_SESSION check)
- Fix PHP version in error message (5.3 -> 5.5 for password_hash)
- Prevent username enumeration by using consistent error messages
- Fix spacing inconsistency in substr() call
- Add TODO comment about MD5 hash weakness

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:10:55 -05:00
copilot-swe-agent[bot]
3d8399bfab fix: Use numeric constant for CURLE_PEER_FAILED_VERIFICATION in PHP
The CURLE_PEER_FAILED_VERIFICATION constant may not be defined in all PHP curl versions. Use the numeric value (51) instead for better compatibility.

refs #TBD

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-02-02 14:36:00 +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
68add32f9c Merge pull request #4576 from Simpler1/patch-6
Fix(): Find server in database instead of defaulting to port 80
2026-02-01 11:27:50 -05:00
Simpler1
391e9d3097 Merge branch 'master' into patch-6 2026-01-31 21:19:30 -05:00
Simpler1
e922b9e16d Update Server.php
Don't require a database entry for Server.
Use ZM_BASE_URL.
2026-01-31 21:01:00 -05:00
Isaac Connor
eae89025ee refactor: rename RTSP2WebStream to StreamChannel
Rename applies to Go2RTC, Janus, and RTSP2Web streaming options.
Update enum values from Primary/Secondary to Restream/CameraDirectPrimary/CameraDirectSecondary.

- Add db migration zm_update-1.37.79.sql to rename column and migrate data
- Update C++ enum StreamChannelOption and member stream_channel
- Update PHP getStreamChannelOptions() method
- Update all JavaScript references
- Auto-select CameraDirectPrimary when Restream option becomes disabled

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 19:37:08 -05:00
Isaac Connor
02685bff7e Pick up missed update for Restream and RTSP_User 2026-01-31 18:54:14 -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
df33e75afd Add support for the string NULL is object lookup values 2026-01-30 08:33:49 -05:00
BHMSD-bdailey
faca0a57c5 Add Not Null to ZM_Object 2026-01-29 21:54:04 -05:00
Isaac Connor
53d3f6e32c fix: improve port detection for nginx/reverse proxy setups
The Port() method now tries to extract the port from HTTP_HOST header
before falling back to SERVER_PORT, which can be incorrect with nginx
and PHP-FPM configurations. Also adds a sensible default (80/443) when
no port can be determined.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 15:48:27 -05:00
Isaac Connor
010959c9d4 fix: only show monitor edit button if user has edit permission
The edit button overlay on the montage view stream is now conditionally
displayed based on the user's edit permission for that monitor.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:56:03 -05:00
Isaac Connor
4e60cb96a7 feat: add User Roles feature for reusable permission templates
Add a User Roles system where roles define reusable permission templates.
When a user has a role assigned, the role provides fallback permissions
(user's direct permissions take precedence; role is used when user has 'None').

Database changes:
- Add User_Roles table with same permission fields as Users
- Add Role_Groups_Permissions table for per-role group overrides
- Add Role_Monitors_Permissions table for per-role monitor overrides
- Add RoleId foreign key to Users table

Permission resolution order:
1. User's direct Monitor/Group permissions (if not 'Inherit')
2. Role's Monitor/Group permissions (if user has role)
3. Role's base permission (if user's is 'None')
4. User's base permission (fallback)

Includes:
- PHP models: User_Role, Role_Group_Permission, Role_Monitor_Permission
- Role management UI in Options > Roles tab
- Role selector in user edit form
- REST API endpoints for roles CRUD
- Translation strings for en_gb

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:34:27 -05:00
Ben Dailey
c80542343a Remove Preview Rate Control. Disable BuiltIn Video controls. 2026-01-29 11:18:30 -05:00
Ben Dailey
65373ed8d3 feat: add Preview Rate dropdown to events page filter bar
Add a Preview Rate dropdown control that allows users to adjust the
playback speed of thumbnail hover preview videos on the events page.

- Add previewRateHtml() helper method to Filter class
- Add Preview Rate to simple_widget() filter bar with matching term styling
- Add getPreviewRate() and changePreviewRate() JS functions in skin.js
- Add helper functions in _monitor_filters.php for rate options
- Store selected rate in zmPreviewRate cookie (values: 100-1600 for 1x-16x)
- Add PreviewRate translation to en_gb.php
- Add CSS styling for preview rate control

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:06:03 -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
IgorA100
da63055802 Now you can download without archiving, and archiving is performed in stages as new MP4 files are generated (download_functions.php)
This allows you to save significant disk space.
2026-01-09 16:26:33 +03:00
Claude
639bb6821e Fix ONVIF password URL encoding in camera configuration
When adding cameras via ONVIF probe, passwords containing special
characters (like parentheses, slashes, etc.) were being stored in the
database in URL-encoded form instead of plain text. This caused
authentication failures when the encoded password was used.

The issue was in extract_auth_values_from_url() which extracted
credentials from the stream URI but didn't decode them. Since the ONVIF
probe process double-encodes passwords (to survive POST encoding), and
monitor.php decodes once, the extracted password still remained
URL-encoded.

The fix adds urldecode() to both username and password after extraction,
ensuring they're stored in their original form in the database.

Example: Password "pass)word" was being stored as "pass%29word"
2026-01-09 11:00:59 +11:00
Steve Gilvarry
c4385768ad Fix default zone creation to account for monitor rotation
When creating a new monitor with Orientation set to ROTATE_90 or
ROTATE_270, the default "All" zone dimensions are now correctly swapped
to match the rotated image dimensions. This prevents zm_zone.cpp from
reporting that zones extend outside of image dimensions and having to
fix them at runtime.

Fixes issue where monitors created with Rotate Right or Rotate Left
would generate warnings like:
"Zone 1/All for monitor X extends outside of image dimensions,
(0,0), (3839,2159) != (2160,3840), fixing"

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-08 22:58:59 +11:00
copilot-swe-agent[bot]
cd1bb4bda4 Remove session usage for monitor filters, use cookies only
- Removed all $_SESSION reads/writes in _monitor_filters.php
- Removed zm_session_start() and session_write_close() calls
- Updated all filter value retrieval to use cookies only via getFilterFromCookie()
- Updated Group::get_group_dropdown() to use cookies instead of session
- Filter persistence now entirely client-side via cookies
- Simplified code by removing session management complexity

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-01-07 18:23:47 +00:00
copilot-swe-agent[bot]
62e86716af Fix monitor filter AJAX and Group dropdown onChange handler
- Updated Group::get_group_dropdown() to accept view parameter and use monitorFilterOnChange for console view
- Updated ajaxRequest in console.js to include filter form data in params.data
- Added session update logic in AJAX console handler to persist filter selections
- Added cookie storage in monitorFilterOnChange for client-side persistence

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-01-07 16:36:53 +00:00
Isaac Connor
b2861d5cf0 Remove zm_configvals. Just use zm_config. Move code into loadConfig. 2026-01-05 09:32:25 -05:00
Isaac Connor
ed91d600d1 Only show Tags filter if there are tags to filter by 2026-01-04 19:05:07 -05:00
Isaac Connor
50fbbb0154 When logging in by auth hash, clear old auth in session. 2026-01-04 18:27:14 -05:00
Steve Gilvarry
e389eed485 Final cleanup of videojs update 2025-12-31 19:33:54 +11:00
Steve Gilvarry
819da45ecc Fix double rotation 2025-12-31 19:33:54 +11:00
Steve Gilvarry
12450b390f Removing object tags as plugins are dead. I think this page still has reasons to exist but none of this code works in modern browsers. Well that is my opinion. 2025-12-31 19:33:54 +11:00
Steve Gilvarry
7abf180a27 Revert "Update getVideoStreamHtml to use videojs for mp4, I suspect this whole section including ZM_WEB_USE_OBJECT_TAGS can be deprecated. But minimal changes to upgrade videojs is all I am going for here"
This reverts commit ed64f084af.
2025-12-31 19:33:53 +11:00
Steve Gilvarry
caab1cc6ee Update getVideoStreamHtml to use videojs for mp4, I suspect this whole section including ZM_WEB_USE_OBJECT_TAGS can be deprecated. But minimal changes to upgrade videojs is all I am going for here 2025-12-31 19:33:53 +11:00
Steve Gilvarry
1d5498270a Fix nonce 2025-12-31 19:33:53 +11:00
Steve Gilvarry
4463f5dcf7 Videojs not loaded when inline script called. 2025-12-31 19:33:53 +11:00
Steve Gilvarry
277cf15518 Fix Nonce and attempt text tracks fixes 2025-12-31 19:33:53 +11:00
Steve Gilvarry
a71d5d9c3c Update Videojs to v8 2025-12-31 19:33:53 +11:00
Isaac Connor
e6c5bdb85d Use modern string interpolation style 2025-12-22 19:43:05 -05:00
Isaac Connor
3b0feba15f Add db config entries to zm_configvals 2025-12-19 16:48:44 -05:00
Isaac Connor
254fd58c40 Use css style= to set width of video tag, and if height is specified, also set it. Fixes streams not having height. 2025-12-19 12:03:29 -05:00