Files
zoneminder/tests/zm_config.cpp
Isaac Connor e792020bb4 feat: support backslash line continuation in conf.d parsers fixes #4943
The C++ and PHP parsers for zm.conf and /etc/zm/conf.d/*.conf used fgets
with a 512-byte buffer, silently truncating any line longer than that.
There was also no way to split a value across multiple lines, so editors
hit the cap with no workaround.

Accept a trailing backslash (with optional whitespace before the newline)
as a line-continuation marker. Leading whitespace on continuation lines
is stripped so users can indent for readability without it leaking into
the value. Three parsers all read the same files and must agree:

- src/zm_config.cpp: switch to std::ifstream + std::getline so a single
  physical line is no longer capped at 512 bytes, then join continuation
  lines before running the existing pointer-based parser
- scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in: accumulate a logical
  line across trailing-backslash physical lines
- web/includes/config.php.in: drop the 512-byte fgets cap and accumulate
  in the same way

tests/zm_config.cpp covers three cases against the C++ parser: a
three-segment continuation joins to "firstsecondthird" with leading
whitespace stripped, a bare backslash inside a value is preserved
(C:\Users\zm), and a 1500-byte single line survives intact.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 12:13:49 -04:00

82 lines
2.5 KiB
C++

/*
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "zm_catch2.h"
#include "zm_config.h"
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <unistd.h>
namespace {
// Writes `body` to a fresh /tmp file and returns its path; caller unlinks.
std::string WriteTempConf(const std::string &body) {
char path[] = "/tmp/zm_conf_test_XXXXXX";
int fd = mkstemp(path);
REQUIRE(fd >= 0);
ssize_t n = write(fd, body.data(), body.size());
REQUIRE(n == static_cast<ssize_t>(body.size()));
close(fd);
return std::string(path);
}
} // namespace
TEST_CASE("process_configfile: backslash continuation joins lines") {
const std::string body =
"ZM_SERVER_NAME = first\\\n"
" second\\\n"
" third\n";
std::string path = WriteTempConf(body);
staticConfig.SERVER_NAME.clear();
process_configfile(path.c_str());
unlink(path.c_str());
// Leading whitespace on continuation lines is trimmed.
REQUIRE(staticConfig.SERVER_NAME == "firstsecondthird");
}
TEST_CASE("process_configfile: bare backslash inside value is preserved") {
// No newline after the backslash, so it is not a continuation marker.
const std::string body = "ZM_DIR_EXPORTS = C:\\Users\\zm\n";
std::string path = WriteTempConf(body);
staticConfig.DIR_EXPORTS.clear();
process_configfile(path.c_str());
unlink(path.c_str());
REQUIRE(staticConfig.DIR_EXPORTS == "C:\\Users\\zm");
}
TEST_CASE("process_configfile: lines longer than the legacy 512-byte cap survive") {
// A 1500-byte value would have been truncated by the old fgets() buffer.
std::string long_value(1500, 'a');
const std::string body = "ZM_DB_HOST = " + long_value + "\n";
std::string path = WriteTempConf(body);
staticConfig.DB_HOST.clear();
process_configfile(path.c_str());
unlink(path.c_str());
REQUIRE(staticConfig.DB_HOST == long_value);
}