Commit Graph
548 Commits
Author SHA1 Message Date
Isaac Connor 9f3f6c6770 fix: only trust X-Forwarded-For from configured proxies for auth hash IPs
With ZM_AUTH_HASH_IPS on, the client address bound into the auth hash was
taken from the left-most X-Forwarded-For value whenever the header was
present, both when PHP generated the hash (getRemoteAddr()) and when PHP or
zms validated it (getAuthUser(), zmLoadAuthUser()). The header is client
controlled, so anyone holding a leaked hash could replay it from anywhere by
sending the address it was bound to.

Add ZM_AUTH_TRUSTED_PROXIES, a list of exact reverse proxy addresses.
X-Forwarded-For is now used only when REMOTE_ADDR is one of them, and is read
from the right, skipping hops that are themselves listed proxies, so values a
client prepends are never chosen. With the option empty, the default, the
header is ignored and REMOTE_ADDR is used.

PHP (web/includes/Network.php getRemoteAddr()) and C++ (ClientAddress() in
zm_utils, used by zmLoadAuthUser()) implement the same rule so generation and
validation continue to agree. Every PHP caller already routes through
getRemoteAddr(), so session.php and auth.php need no change.

Reverse proxy users who enable ZM_AUTH_HASH_IPS must list their proxy in the
new option; until they do, hashes bind to the proxy address, which still
validates but no longer distinguishes clients. This is the behaviour change
that the #4921 work avoided by trusting the header.

The option is added through ConfigData only, like other recent options;
zmupdate.pl --freshen inserts it, zms falls back to the compiled-in default and
PHP treats an undefined constant as empty, so no schema migration is needed.

Tests: ClientAddress Catch2 case; tests/php/test_remote_addr.php updated for
the trusted-proxy rule.

refs GHSA-72rf-54rm-798c

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit acea5889dec0826f595cb736147a5fdc9c94a2a9)
2026-09-24 19:51:27 -04:00
Steve GilvarryandClaude Fable 5.1 3d98b216ca fix: frame the stream socket snapshot when a consumer connects
The cached snapshot was framed when the status last changed, so after a
generation bump or further events a new consumer received it stamped
with a stale generation and an old event-sequence baseline. Keep only
the body and frame it in AcceptClient, so the header carries the
generation and sequence in effect at the moment of connection.

Tests: a snapshot cached at generation 0 with no events is delivered to
a later consumer with the current generation and sequence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 15:07:15 +10:00
Steve GilvarryandClaude Fable 5.1 7d9d97f387 fix: apply both stream parameter sets to the stream socket in one generation
Two problems with announcing streams one at a time. SetAudioParams only
bumped the generation when audio had been announced before, so audio
joining a video-only stream was appended to the current generation after
its video HELLO, which the protocol says is the last HELLO of a
generation. And a re-prime that changed both streams bumped twice:
consumers saw, and a connecting consumer was handed, an intermediate
generation pairing the new audio with the old video, which
zm_rtsp_server built a session for and tore down again.

Add StreamSocket::SetStreams(video, audio), which applies the whole set
under one lock: unchanged is a no-op, the first announcement stays in
generation 0, and any change once a video HELLO has gone out - new
parameters, a stream appearing, a stream disappearing - is exactly one
bump with every remaining stream re-announced, audio first. The single
stream setters and the new ClearVideoParams are wrappers over the same
logic, and a null or codec-less parameter set means "no such stream".
PrimeCapture passes both streams in one call.

Tests: audio joining an announced video stream bumps the generation;
SetStreams keeps the initial announcement in generation 0, changes both
streams in one generation with consistent pairing, and drops a video
stream the source no longer has.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 15:07:14 +10:00
Claude 21042ba2f7 fix: send audio HELLO first and keep rtsp sessions across a zmc restart
Correct three problems in the stream socket generation tracking added by
the previous review fix-up:

- ClearAudioParams bumped the generation without restarting the video
  sequence or dropping the cached keyframe, so a late joiner received a
  HELLO at generation N+1 followed by a KEYFRAME still stamped N, and
  sequences did not restart as documented. It now does the same full bump
  as SetVideoParams/SetAudioParams.
- zm_rtsp_server rebuilt the xop session whenever the generation changed,
  even with identical codec parameters. Generations restart at 0 when zmc
  restarts, so every zmc restart dropped the RTSP clients; the original
  code kept the session in that case. Unchanged parameters now just adopt
  the new generation without a teardown.
- Deciding whether audio belongs to the current generation by comparing
  per-stream generation numbers raced the two HELLOs of a generation
  (double rebuild when Update() ran between them) and cannot tell a
  producer restart apart. The producer now sends the audio HELLO before
  the video HELLO within a generation (on connect and on every bump, and
  PrimeCapture announces audio before video), so the video HELLO always
  completes a generation's parameter set. The consumer forgets the
  previous generation's HELLOs when a new generation starts and on
  disconnect, and builds only once the video HELLO of the latest
  generation is in. It also records whether audio was announced at build
  time rather than whether a packer was created, so an unsupported audio
  codec no longer triggers a rebuild on every pass.

The ordering guarantee is documented in the protocol header and the
stream socket docs. Tests pin the audio-first order on connect and on a
video reconfigure, and the keyframe drop and sequence restart on audio
removal.

refs #5143

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4UcdJLt1bxwdpcigGxZRD
2026-09-20 00:36:35 +00:00
Claude 2cb01222b0 docs: update stream socket protocol notes from PR review
- Describe pts_us as signed microseconds with AV_NOPTS_VALUE for unknown.
- Document that the socket path is published in the shared-memory
  stream_socket_path field, not only derivable from the convention.
- Clarify the snapshot event's sequence: it equals the next EVENT's
  sequence, and the snapshot is a state message a consumer must not treat
  as a duplicate of that following EVENT.

refs #5143

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4UcdJLt1bxwdpcigGxZRD
2026-09-19 23:00:30 +00:00
Steve GilvarryandClaude Fable 5.1 922f3aeb39 docs: describe stream socket NAL framing, keyframe cache and consumer events
The MEDIA description promised Annex B for H.264/H.265, but zmc forwards
packets as the camera's demuxer produced them: RTSP sources give Annex B,
MP4/MKV files give AVCC length-prefixed NALs. Say so, explain how a
consumer tells them apart from the HELLO extradata, and note that
zm_rtsp_server handles Annex B only. Also document that image-only
cameras announce no media stream, that the keyframe cache is cleared
when the camera closes, that the uid check uses getpeereid off Linux,
and that the C++ consumer now surfaces EVENT frames.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 05:12:43 +10:00
SteveGilvarryandClaude Fable 5.1 0a392802f8 feat: emit capture-fault and state-change events on the monitor stream socket
Wire the monitor lifecycle events into zmc. Monitor::SetState() replaces
the scattered shared_data->state assignments in Analyse() and connect(),
publishing a state_changed event (with previous and new state id and name)
whenever the analysis state actually changes. Monitor::SendStreamHealthEvent()
emits the capture-fault edges and tracks the active fault for the snapshot;
the snapshot is refreshed on every state or health change and after a
successful prime, so a consumer connecting mid-fault learns current status.

zmc's capture loop emits the six health transitions once per edge:
connection failed/restored around connect(), prime_capture failed/restored
around PrimeCapture(), capture_failed on pre/capture/post failure, and a
single capture_resumed once the pipeline recovers. Because the stream
socket survives camera reconnects, these are observable exactly when media
has stopped. A small mutex guards the health fields shared between the
capture thread (health events) and analysis thread (state events).

Documents the EVENT frame in docs/stream_socket.rst and decodes it in
tools/zm_stream_socket_dump.py. Full build and ctest pass (121 tests).

refs #2875

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 05:02:24 +10:00
SteveGilvarryandClaude Fable 5 8b6d8c4179 docs: document the monitor stream socket and its wire protocol
Adds docs/stream_socket.rst covering the socket path convention, access
control and queue tuning settings, and the version 1 wire protocol
(header layout, HELLO TLVs, MEDIA/KEYFRAME/STATS/BYE semantics,
sequence/generation loss and discontinuity signalling), with pointers
to the reference encoder/decoder, the C++ consumer class and the python
dump tool. Linked from the documentation index.

This also serves as the migration reference for external consumers of
the removed media FIFOs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 05:02:24 +10:00
Steve GilvarryandClaude Opus 5 be9a98d856 docs: correct the macOS guide against a real install refs #4998
The guide was written from a verified build and install, but the steps
past that point were derived from the Debian packaging rather than run.
Working through them on Apple Silicon to a running system turned up
three things that were wrong or missing.

mysql -u root does not work. A Homebrew MariaDB authenticates root with
the unix_socket plugin, so only the operating system's root matches it;
your own account gets the administrative account, which is what plain
mariadb uses.

Nothing said ZoneMinder has to be started as ZM_WEB_USER. zmpkg.pl
compares the current user against it and tries sudo -u and two forms of
su to become it, all of which fail for an ordinary user, and stops with
"Unable to find valid su syntax". systemd hides this on Linux by
starting the unit as the web user. Now says so, and gives the
workstation alternative of running everything as one account.

The web server section did not mention that zms is a CGI binary. Apache
runs those with mod_cgi; nginx needs a wrapper such as fcgiwrap, which
Homebrew does not package, so with nginx alone the console works and
live streams do not. Also noted that the generated samples reference
/etc/pki, /etc/nginx and /run/fcgiwrap.sock, none of which exist here.

Added the hardcoded taint-safe PATH to the known gaps, since it is what
breaks zmupdate.pl on Apple Silicon: seventeen scripts pin
/bin:/usr/bin:/usr/local/bin, so the database client in the Homebrew
prefix cannot be found. Worth making configurable - it would equally
affect a --prefix=/opt install on Linux. Sys::CPU is listed too; it has
been removed from CPAN and only zmtelemetry.pl uses it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5KL9Xbi7K5aGsauLtd8tG
2026-09-13 12:50:36 +10:00
Steve GilvarryandClaude Opus 5 9ef8c2a17c docs: describe the launchd job in the macOS guide refs #4998
Replaces the "there is no launchd job yet" note with how to install and
load the generated one, and why unloading it does not stop ZoneMinder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5KL9Xbi7K5aGsauLtd8tG
2026-09-12 22:19:09 +10:00
Steve GilvarryandClaude Opus 5 da331916c8 docs: add a macOS installation guide refs #4998
ZoneMinder now builds and installs on macOS, but the steps that make it
work existed nowhere. The installation guide has pages for Debian,
Ubuntu, Redhat and WSL, and nothing for macOS.

Covers the parts that are not guessable, each one walked through on a
real machine rather than reasoned about:

- Sys::Mmap, Date::Manip and a DBD driver are missing from the Perl Apple
  ships, and configure only warns, so it is easy to get a clean build and
  a system whose Perl daemons cannot read monitor state at all. A
  Homebrew Perl additionally needs DBI and LWP::UserAgent, since it has
  none of the extras Apple bundles.
- DBD::MariaDB does not install without help. mysql_config hands its
  configure step -lzstd -lssl -lcrypto, but a Homebrew Perl's ldflags
  carry no -L for the Homebrew prefix, so it fails with "Can't
  link/include C library 'zstd', 'ssl', 'crypto', aborting". The guide
  gives the --libs and --cflags that get it through.
- ZoneMinder's Perl modules land in vendorlib and survive Perl upgrades;
  the CPAN modules land in sitelib, which resolves into the Cellar and
  does not. Worth knowing before an upgrade quietly breaks the daemons.
- ZM_NO_MMAP is not a way around Sys::Mmap. macOS caps SysV shared memory
  at 4 MiB per segment across 8 segments, and a 1080p RGB frame does not
  fit in one.
- mysql-client is keg-only, so the build needs an explicit -I or it stops
  at "'mysql/mysql.h' file not found".
- Nothing creates the runtime directories. On Linux the package does it.
- The test binary has to run from build/tests, because zm_font.cpp loads
  its fixtures by relative path.
- zmpkg.pl falls back to zmdc.pl without systemd, so there is a way to
  start ZoneMinder before a launchd plist exists.

Honest about what is missing rather than papering over it: no plist, no
Homebrew formula, no log rotation, no local cameras, and the arp-scan and
ip warnings configure emits. Also separates Homebrew's prefix from
ZoneMinder's install prefix, which differ on Apple Silicon and are easy
to conflate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5KL9Xbi7K5aGsauLtd8tG
2026-09-12 22:11:44 +10:00
singhharsh1708 57a5da2f20 docs: drop the Frame Skip setting, it no longer exists 2026-09-06 11:37:37 +05:30
Isaac Connor bd74472fa3 Merge pull request #5079 from AJ0070/docs/4486-zone-min-alarm-area
docs: minimum alarmed area cannot be 0
2026-08-30 22:35:57 -04:00
Jash 5f8e99f05f docs: note the render group for vaapi and qsv decoding fixes #4205 2026-08-30 01:53:35 +05:30
Jash 3082cdd9f9 docs: minimum alarmed area cannot be 0, the zone form rejects it fixes #4486 2026-08-30 01:39:13 +05:30
АнтонandClaude Opus 4.8 d01fc48848 docs: explain how Event Start/End Command are actually executed
The Recording tab docs said only that 'the parameters to the command
will be the event id and the monitor id', which is true only when the
command contains no '%' - and hides several surprises discovered by
reading zm_monitor.cpp:

- without '%' the whole string is exec'd as ONE executable path with
  event id / monitor id appended; a command with inline arguments
  ('/path/script.sh start') is treated as a single file name and fails;
- with '%' the string runs via /bin/sh -c after %EID%/%MID%/%EC%
  substitution, and nothing is appended automatically (%EC% exists for
  the start command only);
- the child runs in the background as the capture-process user with all
  file descriptors closed, so any output silently disappears;
- the end command fires after the event is finalized, so the video file
  is complete by then.

Document all of that where the two options are described.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 18:29:04 +03:00
alex-s-iv b6197c02b7 Update packpack.rst
Added -- recursive to git clone operation.
Specified correct command cd ./zoneminder
2026-06-21 22:39:08 +03:00
Isaac Connor 55bbec4c55 Merge pull request #4924 from SteveGilvarry/3816-db-ssl-verify-server-cert
feat: add ZM_DB_SSL_VERIFY_SERVER_CERT option (portable across MySQL/MariaDB)
2026-06-14 09:39:36 -04:00
SteveGilvarry e60bdc67b2 feat: add ZM_DB_SSL_VERIFY_SERVER_CERT option (portable across MySQL/MariaDB)
Add a ZM_DB_SSL_VERIFY_SERVER_CERT setting so a database connection that uses
ZM_DB_SSL_CA_CERT can talk to a server with a self-signed or otherwise
non-matching certificate. When enabled, verification is by identity (the cert
must chain to the CA and its CN/SAN must match ZM_DB_HOST), consistent across
the C++ daemons, the PHP web interface, the CakePHP API and the Perl scripts.

This re-does the reverted #3817. That PR broke the build because it called
mysql_options(MYSQL_OPT_SSL_VERIFY_SERVER_CERT, ...), and that enum was removed
from the MySQL 8.0 C client in favour of MYSQL_OPT_SSL_MODE; it also passed a
c_str() where a my_bool* was expected, and referenced the PHP constant
unconditionally (fatal on PHP 8 for an upgraded install whose zm.conf predates
the option).

The option that controls server-cert verification differs by client library and
the symbols are enum values, not macros, so CMake feature-detects them by
compiling:
  - HAVE_MYSQL_OPT_SSL_MODE  (MySQL 5.7.11+/8.0, MariaDB Connector/C 3.1+)
  - HAVE_MYSQL_OPT_SSL_VERIFY_SERVER_CERT  (older MariaDB/MySQL)
zm_db.cpp uses SSL_MODE_VERIFY_IDENTITY / SSL_MODE_REQUIRED when the former is
available, else falls back to the latter with a proper my_bool.

Value handling is three-way in every layer: a truthy value verifies, a false-y
value (0/false/no/off) skips verification, and an empty/unset value leaves the
client default in place so existing installs are unchanged on upgrade. PHP, the
API datasource (via PDO flags) and the Perl DSN are all guarded with defined()
checks. Fresh installs default to 1.

Documents the full ZM_DB_* connection and SSL settings, including the hostname
verification gotcha when connecting by IP, in docs/userguide/configfiles.rst.

refs #3816
2026-06-14 13:20:00 +10:00
Isaac Connor e71878564a docs: add per-column event sort design and plan 2026-06-09 13:10:13 -04:00
Isaac ConnorandClaude Opus 4.7 c4073c964c feat: encoder parameter templates with editor + REST API closes #4778 closes #4802
Adds a curated, per-encoder parameter-template library to ZoneMinder:

- Monitor edit page: a new Template row above the EncoderParameters
  textarea offers per-encoder templates (Balanced / Archival / Low
  Power / Low CPU). Apply merges the template's params into the
  textarea, preserving user-only keys. Advisory lint flags option
  keys that aren't recognised for the selected encoder. Switching
  encoders offers a same-name template on the new encoder via a
  native confirm.

- Options page: a new Encoder Templates tab with full CRUD —
  list / edit / copy / delete — backed by a new CakePHP REST API
  at /api/encoder_templates.

- Storage: a new EncoderTemplates DB table seeded with 14 shipped
  defaults across libx264 / libx265 / h264_nvenc / hevc_nvenc /
  h264_vaapi / hevc_vaapi. The table is mutable; ZM upgrades do not
  re-seed user-edited rows.

- valid_keys (the lint allow-list) stays in PHP code as ffmpeg
  vocabulary, not user data.

- Default params explicitly include pix_fmt to avoid the yuvj420p
  HEVC HW-decode rejection issue we hit earlier.

No C++ change. The textarea content is parsed by the existing
av_dict_parse_string call in src/zm_videostore.cpp.

version.txt -> 1.39.6.

Specs: docs/superpowers/specs/2026-05-0{1,2}-*.md
Plans: docs/superpowers/plans/2026-05-0{1,2}-*.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:24:17 -04:00
copilot-swe-agent[bot]andconnortechnology 04aa0a7781 docs: document all ONVIF_Options key=value pairs
Replaces the uninformative "Any ONVIF options required. This is an
optional field." with a full reference covering:
- key=value,key=value format with an example
- pull_timeout, subscription_timeout, max_retries,
  timestamp_validity, soap_log, renewal_enabled, expire_alarms,
  closes_event — including defaults, valid ranges, and when to use each

Also fixes pre-existing typo: "alam" → "alarm".

Agent-Logs-Url: https://github.com/ZoneMinder/zoneminder/sessions/149f7873-ca12-424f-85d4-4bdc179d2488

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-05-03 20:21:49 +00:00
Isaac ConnorandClaude Opus 4.6 863fac4d72 docs: add zma event re-analysis utility documentation
Add dedicated documentation page for the zma binary covering synopsis,
all command-line options, operating modes (update-existing vs create-events),
save-analysis functionality, and usage examples. Add zma entry to the
components page and user guide toctree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:47:26 -05:00
Isaac ConnorandClaude Opus 4.6 9f702cb315 docs: add video group setup to Debian/Ubuntu install guides refs #4642
Document the need to add www-data to the video group for local camera
access. The package postinst now does this automatically, but source
installs still require the manual step.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -05:00
Steve Gilvarry 823504d1d1 Add section on debug logs that we can reference when asking for them. 2026-01-02 14:04:22 +11:00
Isaac Connor 0234a43ff0 easoydocker is deprecated 2025-11-06 18:40:13 -05:00
Isaac Connor f826f606bc Need a blank line to make toctree valid. 2025-11-06 18:40:13 -05:00
Isaac Connor 9c96ba3d28 Fix references to StartTime => StartDateTime and EndTime 2025-09-05 11:45:49 -04:00
Wyn Williams e81cca5c37 Update easydocker.rst
Edited to remove info and links to easy docker, file left in place so as to not break any guides or links to it.
2025-08-06 21:39:27 +03:00
Wyn Williams 4c1df8a80e Update packpack.rst
Removed link to now depreciated easy docker images
2025-08-06 21:32:37 +03:00
Wyn Williams d407f3dbcf Update easydocker.rst
added a warning to not use these docker images in production
2025-08-06 21:31:13 +03:00
Wyn Williams 51145d3560 Update index.rst
removed easy docker reference
2025-08-06 21:28:10 +03:00
Isaac Connor 9cf68eb83d Fix typo. Fixes #3457 2025-07-14 12:03:12 -07:00
Isaac Connor f1cab94091 Add mysql install and db creation to Jammy instructions. Add a couple hints that it also applies to newer. Fixes #4185 2024-11-12 06:56:16 -05:00
Isaac Connor b236b48f1b Updates removing zma and fixing some of the perl module names 2024-10-16 11:42:55 -04:00
unbroken75 ea1fd08780 Update debian.rst 2024-09-22 16:15:27 +02:00
robertsilen 1b526e1053 add mariadb 2024-08-19 12:57:40 +03:00
Isaac Connor 9442259d8d Merge pull request #4113 from beltphed/patch-1
Update debian.rst -- fix redirect of stdout in sudo
2024-08-18 12:50:07 -04:00
beltphed aa892e69d0 Update debian.rst
Failing to install apache2 results in subsequent calls to a2enconf and a2enmod failing because they might not have been installed. (in my case, I was using a minimal Debian 12 install which doesn't install apache2)
2024-08-16 19:55:49 -07:00
beltphed f94fc05966 Update debian.rst
As written, you can't redirect sudo output as desired. Wrapping the "echo deb..." in quotes will permit a regular user to redirect stdout to /etc/apt/sources.list
2024-08-16 16:47:54 -07:00
Aktarus fbd7ad533f Update debian.rst, enable autostart of ZM at boot
added one line to enable autostart of ZM at boot
2024-08-04 15:23:38 +02:00
Isaac Connor 84e1869fb8 Add ELOC substitution to get Latitude and Longitude of event 2024-07-05 17:28:59 -04:00
Isaac Connor 481210feaf Rough in web ui api documentation. Add download event video documentation. 2024-07-05 15:37:12 -04:00
Isaac Connor 8618900339 If we are not doing an html email, then don't replace with img tags referencing the attachment. Just do the attachment. Introduce %FID% to sub with the id of the filter. 2024-01-15 16:58:54 -05:00
efranzwa 0026e3446b Add images for Viewing Monitors Doc Section 2023-12-20 06:24:37 -08:00
efranzwa bd863d026b Update Viewing Monitors Doc Section 2023-12-20 06:22:43 -08:00
Isaac Connor 112eb6b9ad Add support for EISNAP and EPISNAP for snapshot image 2023-12-19 12:27:02 -05:00
efranzwa b033134596 Add images for Defining Zones Doc Section 2023-12-09 09:49:15 -08:00
efranzwa 6298d48db0 Update Defining Zones Doc Section 2023-12-09 09:47:12 -08:00
efranzwa fec884493c Added images for Defining Monitors section 2023-11-10 12:59:38 -08:00