diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index b1cd6922d..207f244f9 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -315,7 +315,7 @@ EOF sudo apt-get install devscripts equivs sudo mk-build-deps -ir $DIRECTORY.orig/debian/control echo "Status: $?" - DEBUILD=debuild + DEBUILD=debuild -b -uc -us else if [ $TYPE == "local" ]; then # Auto-install all ZoneMinder's dependencies using the Debian control file diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index ec5d01085..815183dff 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -30,6 +30,7 @@ function MonitorStream(monitorData) { this.wsMSE = null; this.streamStartTime = 0; // Initial point of flow start time. Used for flow lag time analysis. this.waitingStart; + this.handlerEventListener = {}; this.mseListenerSourceopenBind = null; this.streamListenerBind = null; this.mseSourceBufferListenerUpdateendBind = null; @@ -441,7 +442,7 @@ function MonitorStream(monitorData) { clearInterval(this.statusCmdTimer); // Fix for issues in Chromium when quickly hiding/showing a page. Doesn't clear statusCmdTimer when minimizing a page https://stackoverflow.com/questions/9501813/clearinterval-not-working this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout); this.started = true; - this.streamListenerBind(); + this.handlerEventListener['killStream'] = this.streamListenerBind(); if (typeof observerMontage !== 'undefined') observerMontage.observe(stream); this.activePlayer = 'go2rtc'; @@ -482,7 +483,7 @@ function MonitorStream(monitorData) { attachVideo(this); this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout); this.started = true; - this.streamListenerBind(); + this.handlerEventListener['killStream'] = this.streamListenerBind(); this.activePlayer = 'janus'; this.updateStreamInfo('Janus', 'loading'); return; @@ -554,7 +555,7 @@ function MonitorStream(monitorData) { clearInterval(this.statusCmdTimer); // Fix for issues in Chromium when quickly hiding/showing a page. Doesn't clear statusCmdTimer when minimizing a page https://stackoverflow.com/questions/9501813/clearinterval-not-working this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout); this.started = true; - this.streamListenerBind(); + this.handlerEventListener['killStream'] = this.streamListenerBind(); this.updateStreamInfo((typeof players !== "undefined" && players) ? players[this.activePlayer] : 'RTSP2Web ' + this.RTSP2WebType, 'loading'); return; } else { @@ -618,12 +619,14 @@ function MonitorStream(monitorData) { } } // end if paused or not this.started = true; - this.streamListenerBind(); + this.handlerEventListener['killStream'] = this.streamListenerBind(); this.activePlayer = 'zms'; this.updateStreamInfo('ZMS MJPEG'); }; // this.start this.stop = function() { + manageEventListener.removeEventListener(this.handlerEventListener['killStream']); + /* Stop should stop the stream (killing zms) but NOT set src=''; This leaves the last jpeg up on screen instead of a broken image */ const stream = this.getElement(); if (!stream) { @@ -1976,10 +1979,10 @@ function startRTSP2WebPlay(videoEl, url, stream) { } function streamListener(stream) { - window.addEventListener('beforeunload', function(event) { + return manageEventListener.addEventListener(window, 'beforeunload', function() { console.log('streamListener'); stream.kill(); - }); + }, {capture: false}); } function mseListenerSourceopen(context, videoEl, url) { diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index aa6875e87..75a26deb4 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -898,6 +898,11 @@ function exportEvents( return false; } + // Sanitize user-supplied values used in file paths and shell commands + $export_root = preg_replace('/[^\w\-.]/', '', $export_root); + if (empty($export_root)) $export_root = 'zmExport'; + $connkey = preg_replace('/[^\w\-.]/', '', $connkey); + if (!($exportFormat == 'tar' or $exportFormat == 'zip')) { ZM\Error("None or invalid exportFormat specified $exportFormat."); return false; @@ -1027,7 +1032,7 @@ function exportEvents( } // if $exportFormat @unlink($archive_path); - $command .= ' '.$export_root.($connkey?'_'.$connkey:'').'/'; + $command .= ' '.escapeshellarg($export_root.($connkey?'_'.$connkey:'').'/'); ZM\Debug($command); exec($command, $output, $status); if ($status) { diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 1ae8e3b0e..29408e35e 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -2867,4 +2867,45 @@ const waitUntil = (condition, timeout = 0) => { }); }; +// https://stackoverflow.com/a/69273090 +class ManageEventListener { + #listeners = {}; // # in a JS class signifies private + #idx = 1; + + // add event listener, returns integer ID of new listener + addEventListener(element, type, listener, options = {}) { + this.#privateAddEventListener(element, this.#idx, type, listener, options); + return this.#idx++; + } + + // add event listener with custom ID (avoids need to retrieve return ID since you are providing it yourself) + addEventListenerById(element, id, type, listener, options = {}) { + this.#privateAddEventListener(element, id, type, listener, options); + return id; + } + + #privateAddEventListener(element, id, type, listener, options) { + if (this.#listeners[id]) throw Error(`A listener with id ${id} already exists`); + element.addEventListener(type, listener, options); + this.#listeners[id] = {element, type, listener, options}; + } + + // remove event listener with given ID, returns ID of removed listener or null (if listener with given ID does not exist) + removeEventListener(id) { + const listen = this.#listeners[id]; + if (listen) { + listen.element.removeEventListener(listen.type, listen.listener, listen.options); + delete this.#listeners[id]; + } + return !!listen ? id : null; + } + + // returns number of events listeners + length() { + return Object.keys(this.#listeners).length; + } +} +const manageEventListener = new ManageEventListener(); +window.manageEventListener = manageEventListener; + $j( window ).on("load", initPageGeneral);