From 315b1bc11e79cc888b6d92d26bd83969a87557a2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 10 Jul 2018 11:48:08 -0400 Subject: [PATCH 001/330] Implement a GetData function and use it to simplify the code everywhere we want to get more data. --- src/zm_remote_camera_http.cpp | 51 ++++++++++++++++++----------------- src/zm_remote_camera_http.h | 3 ++- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 9c91d235b..a3b31029c 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -220,7 +220,7 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) { } else { if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) { Error( "Can't ioctl(): %s", strerror(errno) ); - return( -1 ); + return -1; } if ( total_bytes_to_read == 0 ) { @@ -228,20 +228,20 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) { int error = 0; socklen_t len = sizeof (error); int retval = getsockopt( sd, SOL_SOCKET, SO_ERROR, &error, &len ); - if(retval != 0 ) { + if ( retval != 0 ) { Debug( 1, "error getting socket error code %s", strerror(retval) ); } - if (error != 0) { + if ( error != 0 ) { return -1; } // Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close. - return( 0 ); + return 0; } // If socket is closed locally, then select will fail, but if it is closed remotely // then we have an exception on our socket.. but no data. - Debug( 3, "Socket closed remotely" ); + Debug(3, "Socket closed remotely"); //Disconnect(); // Disconnect is done outside of ReadData now. - return( -1 ); + return -1; } // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily. @@ -278,6 +278,18 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) { return( total_bytes_read ); } +int RemoteCameraHttp::GetData() { + time_t start_time = time(NULL); + int buffer_len = 0; + while ( !( buffer_len = ReadData(buffer) ) ) { + if ( zm_terminate || ( start_time - time(NULL) < ZM_WATCH_MAX_DELAY )) + return -1; + Debug(4, "Timeout waiting for REGEXP HEADER"); + usleep(100000); + } + return buffer_len; +} + int RemoteCameraHttp::GetResponse() { int buffer_len; @@ -310,9 +322,7 @@ int RemoteCameraHttp::GetResponse() static RegExpr *content_length_expr = 0; static RegExpr *content_type_expr = 0; - while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) { - Debug(4, "Timeout waiting for REGEXP HEADER"); - } + buffer_len = GetData(); if ( buffer_len < 0 ) { Error( "Unable to read header data" ); return( -1 ); @@ -485,9 +495,7 @@ int RemoteCameraHttp::GetResponse() else { Debug( 3, "Unable to extract subheader from stream, retrying" ); - while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) { - Debug(4, "Timeout waiting to extract subheader"); - } + buffer_len = GetData(); if ( buffer_len < 0 ) { Error( "Unable to extract subheader data" ); return( -1 ); @@ -528,7 +536,7 @@ int RemoteCameraHttp::GetResponse() while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) { Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); - int bytes_read = ReadData( buffer ); + int bytes_read = GetData(); if ( bytes_read < 0 ) { Error( "Unable to read content" ); @@ -542,9 +550,7 @@ int RemoteCameraHttp::GetResponse() { while ( !content_length ) { - while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) { - Debug(4, "Timeout waiting for content"); - } + buffer_len = GetData(); if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); @@ -663,9 +669,7 @@ int RemoteCameraHttp::GetResponse() } case HEADERCONT : { - while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) { - Debug(4, "Timeout waiting for HEADERCONT"); - } + buffer_len = GetData(); if ( buffer_len < 0 ) { Error( "Unable to read header" ); return( -1 ); @@ -949,9 +953,7 @@ int RemoteCameraHttp::GetResponse() state = CONTENT; } else { Debug( 3, "Unable to extract subheader from stream, retrying" ); - while ( !( buffer_len = ReadData(buffer) ) &&!zm_terminate ) { - Debug(1, "Timeout waiting to extra subheader non regexp"); - } + buffer_len = GetData(); if ( buffer_len < 0 ) { Error( "Unable to read subheader" ); return( -1 ); @@ -991,7 +993,7 @@ int RemoteCameraHttp::GetResponse() if ( content_length ) { while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) { Debug(4, "getting more data"); - int bytes_read = ReadData(buffer); + int bytes_read = GetData(); if ( bytes_read < 0 ) { Error("Unable to read content"); return -1; @@ -1004,8 +1006,7 @@ int RemoteCameraHttp::GetResponse() while ( !content_length && !zm_terminate ) { Debug(4, "!content_length, ReadData"); buffer_len = ReadData( buffer ); - if ( buffer_len < 0 ) - { + if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } diff --git a/src/zm_remote_camera_http.h b/src/zm_remote_camera_http.h index a1ed4a267..d2bc96848 100644 --- a/src/zm_remote_camera_http.h +++ b/src/zm_remote_camera_http.h @@ -53,12 +53,13 @@ public: int Disconnect(); int SendRequest(); int ReadData( Buffer &buffer, unsigned int bytes_expected=0 ); + int GetData(); int GetResponse(); int PreCapture(); int Capture( Image &image ); int PostCapture(); int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return 0;}; - int Close() { return 0; }; + int Close() { Disconnect(); return 0; }; }; #endif // ZM_REMOTE_CAMERA_HTTP_H From 312dab2c2050101fc02be886bc750a3e1aeafc1b Mon Sep 17 00:00:00 2001 From: Javier Gonzalez Date: Sat, 20 Apr 2019 17:14:43 +0200 Subject: [PATCH 002/330] Fixing video export view --- web/skins/classic/views/video.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/video.php b/web/skins/classic/views/video.php index aab9278d6..ca4c46f4b 100644 --- a/web/skins/classic/views/video.php +++ b/web/skins/classic/views/video.php @@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) ) else $scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE); -$Event = new Event($event['Id']); +$Event = new ZM\Event($event['Id']); $eventPath = $Event->Path(); $videoFormats = array(); From 2e7fad68d12e4a5da1b3403227795617e1902402 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Fri, 1 Nov 2019 17:29:55 -0400 Subject: [PATCH 003/330] Properly compute video buffer length when analysis fps is set Currently the number of video frames kept in the buffer is set as the pre_event_count. This falls short when the capture fps is larger than the analysis fps. Fix by computing the retained video duration as pre_event_count/analysis_fps No change if analysis fps is not set. Signed-off-by: Selva Nair --- src/zm_ffmpeg_camera.cpp | 10 +++++- src/zm_monitor.cpp | 7 +++++ src/zm_monitor.h | 4 ++- src/zm_packetqueue.cpp | 66 ++++++++++++++++++++++++++++++++++++++++ src/zm_packetqueue.h | 1 + 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 3ae65d97f..acc3a32c4 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -744,6 +744,8 @@ int FfmpegCamera::CaptureAndRecord( } int ret; + struct timeval video_buffer_duration = monitor->GetVideoBufferDuration(); + int frameComplete = false; while ( !frameComplete ) { av_init_packet(&packet); @@ -920,6 +922,13 @@ int FfmpegCamera::CaptureAndRecord( if ( packet.stream_index == mVideoStreamId ) { if ( keyframe ) { Debug(3, "Clearing queue"); + if (video_buffer_duration.tv_sec > 0 || video_buffer_duration.tv_usec > 0) { + packetqueue->clearQueue(&video_buffer_duration, mVideoStreamId); + } + else { + packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId); + } + if ( packetqueue->packet_count(mVideoStreamId) >= @@ -933,7 +942,6 @@ int FfmpegCamera::CaptureAndRecord( packetqueue->packet_count(mVideoStreamId)+1); } - packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId); packetqueue->queuePacket(&packet); } else if ( packetqueue->size() ) { // it's a keyframe or we already have something in the queue diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d8d06016a..2f0e4826b 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -338,6 +338,7 @@ Monitor::Monitor( warmup_count( p_warmup_count ), pre_event_count( p_pre_event_count ), post_event_count( p_post_event_count ), + video_buffer_duration({0}), stream_replay_buffer( p_stream_replay_buffer ), section_length( p_section_length ), min_section_length( p_min_section_length ), @@ -368,6 +369,12 @@ Monitor::Monitor( privacy_bitmask( NULL ), event_delete_thread(NULL) { + if (analysis_fps > 0.0) { + uint64_t usec = round(1000000*pre_event_count/analysis_fps); + video_buffer_duration.tv_sec = usec/1000000; + video_buffer_duration.tv_usec = usec % 1000000; + } + strncpy(name, p_name, sizeof(name)-1); strncpy(event_prefix, p_event_prefix, sizeof(event_prefix)-1); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 046f20c5a..97f173845 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -273,6 +273,7 @@ protected: int warmup_count; // How many images to process before looking for events int pre_event_count; // How many images to hold and prepend to an alarm event int post_event_count; // How many unalarmed images must occur before the alarm state is reset + struct timeval video_buffer_duration; // How long a video segment to keep in buffer (set only if analysis fps != 0 ) int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now int section_length; // How long events should last in continuous modes int min_section_length; // Minimum event length when using event_close_mode == ALARM @@ -467,7 +468,8 @@ public: void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; } unsigned int GetPreEventCount() const { return pre_event_count; }; - int GetImageBufferCount() const { return image_buffer_count; }; + struct timeval GetVideoBufferDuration() const { return video_buffer_duration; }; + int GetImageBufferCount() const { return image_buffer_count; }; State GetState() const; int GetImage( int index=-1, int scale=100 ); Snapshot *getSnapshot() const; diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 45be587c8..c48857be4 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -204,6 +204,72 @@ void zm_packetqueue::clearQueue() { Debug(3, "Deleted (%d) packets", delete_count ); } +// clear queue keeping only specified duration of video -- return number of pkts removed +unsigned int zm_packetqueue::clearQueue(struct timeval *duration, int streamId) { + + if (pktQueue.empty()) { + return 0; + } + struct timeval keep_from; + std::list::reverse_iterator it; + it = pktQueue.rbegin(); + + timersub(&(*it)->timestamp, duration, &keep_from); + ++it; + + Debug(3, "Looking for frame before queue keep time with stream id (%d), queue has %d packets", + streamId, pktQueue.size()); + for ( ; it != pktQueue.rend(); ++it) { + ZMPacket *zm_packet = *it; + AVPacket *av_packet = &(zm_packet->packet); + if (av_packet->stream_index == streamId + && timercmp( &zm_packet->timestamp, &keep_from, <= )) { + Debug(3, "Found frame before keep time with stream index %d at %d.%d", + av_packet->stream_index, + zm_packet->timestamp.tv_sec, + zm_packet->timestamp.tv_usec); + break; + } + } + + if (it == pktQueue.rend()) { + Debug(1, "Didn't find a frame before queue preserve time. keeping all"); + return 0; + } + + Debug(3, "Looking for keyframe"); + for ( ; it != pktQueue.rend(); ++it) { + ZMPacket *zm_packet = *it; + AVPacket *av_packet = &(zm_packet->packet); + if (av_packet->flags & AV_PKT_FLAG_KEY + && av_packet->stream_index == streamId) { + Debug(3, "Found keyframe before start with stream index %d at %d.%d", + av_packet->stream_index, + zm_packet->timestamp.tv_sec, + zm_packet->timestamp.tv_usec ); + break; + } + } + if ( it == pktQueue.rend() ) { + Debug(1, "Didn't find a keyframe before event starttime. keeping all" ); + return 0; + } + + unsigned int deleted_frames = 0; + ZMPacket *zm_packet = NULL; + while (distance(it, pktQueue.rend()) > 1) { + zm_packet = pktQueue.front(); + pktQueue.pop_front(); + packet_counts[zm_packet->packet.stream_index] -= 1; + delete zm_packet; + deleted_frames += 1; + } + zm_packet = NULL; + Debug(3, "Deleted %d frames", deleted_frames); + + return deleted_frames; +} + unsigned int zm_packetqueue::size() { return pktQueue.size(); } diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index 0faa281d2..31fe321cb 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -40,6 +40,7 @@ public: bool popVideoPacket(ZMPacket* packet); bool popAudioPacket(ZMPacket* packet); unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id); + unsigned int clearQueue(struct timeval *duration, int streamid); void clearQueue(); void dumpQueue(); unsigned int size(); From dd31279ca368f201a10eb90b22b0007976d6f7de Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Sun, 3 Nov 2019 18:11:55 -0500 Subject: [PATCH 004/330] Fix the recorded video start time The event start time already includes the duration of the pre_event frames. Do not back track the video by a further pre_event_count. Signed-off-by: Selva Nair --- src/zm_ffmpeg_camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index acc3a32c4..029a2de0d 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -875,7 +875,7 @@ int FfmpegCamera::CaptureAndRecord( // Clear all packets that predate the moment when the recording began packetqueue->clear_unwanted_packets( - &recording, monitor->GetPreEventCount(), mVideoStreamId); + &recording, 0, mVideoStreamId); while ( (queued_packet = packetqueue->popPacket()) ) { AVPacket *avp = queued_packet->av_packet(); From 5260563ec47dea417b4f69dca50e9eeeeebf43be Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Fri, 1 Nov 2019 17:30:57 -0400 Subject: [PATCH 005/330] Update frame deltas in the database to be relative to the video start time Currenltly the frames written to database have delta time measured from the event start time. As these deltas are also used to generate any frame on the fly from the recorded video, these should be relative to the video start time. Adjust by subtracting the offset of the video start from event start. Done when the event is closed. If video is not stored, this has no effect. Signed-off-by: Selva Nair --- src/zm_event.cpp | 35 +++++++++++++++++++++++++++++++++++ src/zm_event.h | 1 + src/zm_ffmpeg_camera.cpp | 9 +++++++++ src/zm_monitor.h | 2 ++ 4 files changed, 47 insertions(+) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 109df5dd3..e564479f3 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -256,6 +256,20 @@ Event::~Event() { if ( frame_data.size() ) WriteDbFrames(); + // update frame deltas to refer to video start time which may be a few frames before event start + struct timeval video_offset = {0}; + struct timeval video_start_time = monitor->GetVideoWriterStartTime(); + if (video_start_time.tv_sec > 0) { + timersub(&video_start_time, &start_time, &video_offset); + Debug(1, "Updating frames delta by %d sec %d usec", + video_offset.tv_sec, video_offset.tv_usec); + UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6); + } + else { + Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated", + video_start_time.tv_sec, video_start_time.tv_usec); + } + // Should not be static because we might be multi-threaded char sql[ZM_SQL_LGE_BUFSIZ]; snprintf(sql, sizeof(sql), @@ -554,6 +568,27 @@ void Event::WriteDbFrames() { db_mutex.unlock(); } // end void Event::WriteDbFrames() +// Subtract an offset time from frames deltas to match with video start time +void Event::UpdateFramesDelta(double offset) { + char sql[ZM_SQL_MED_BUFSIZ]; + + if (offset == 0.0) return; + // the table is set to auto update timestamp so we force it to keep current value + snprintf(sql, sizeof(sql), + "UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64, + offset, id); + + db_mutex.lock(); + if (mysql_query(&dbconn, sql)) { + db_mutex.unlock(); + Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql); + return; + } + db_mutex.unlock(); + Info("Updating frames delta by %0.2f sec to match video file", offset); +} + + void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) { if ( !timestamp.tv_sec ) { Debug(1, "Not adding new frame, zero timestamp"); diff --git a/src/zm_event.h b/src/zm_event.h index 2035b6af6..77199509c 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -132,6 +132,7 @@ class Event { private: void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); void WriteDbFrames(); + void UpdateFramesDelta(double offset); public: static const char *getSubPath( struct tm *time ) { diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 029a2de0d..ef4dfc057 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -872,6 +872,7 @@ int FfmpegCamera::CaptureAndRecord( // since the last keyframe. unsigned int packet_count = 0; ZMPacket *queued_packet; + struct timeval video_offset = {0}; // Clear all packets that predate the moment when the recording began packetqueue->clear_unwanted_packets( @@ -880,6 +881,14 @@ int FfmpegCamera::CaptureAndRecord( while ( (queued_packet = packetqueue->popPacket()) ) { AVPacket *avp = queued_packet->av_packet(); + // compute time offset between event start and first frame in video + if (packet_count == 0){ + monitor->SetVideoWriterStartTime(queued_packet->timestamp); + timersub(&queued_packet->timestamp, &recording, &video_offset); + Info("Event video offset is %.3f sec (<0 means video starts early)", + video_offset.tv_sec + video_offset.tv_usec*1e-6); + } + packet_count += 1; // Write the packet to our video store Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 97f173845..9109c9c72 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -466,6 +466,8 @@ public: const std::vector* GetOptEncoderParams() const { return &encoderparamsvec; } uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; } void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; } + struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; } + void SetVideoWriterStartTime(struct timeval &t) { video_store_data->recording = t; } unsigned int GetPreEventCount() const { return pre_event_count; }; struct timeval GetVideoBufferDuration() const { return video_buffer_duration; }; From e096e0a7fe5e20ba02d8ffdf594cd9e611428a1a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 30 Nov 2019 15:35:09 -0500 Subject: [PATCH 006/330] improve error logging when running as root as can't chown log file --- scripts/ZoneMinder/lib/ZoneMinder/Logger.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index dc843449a..5d05ae7c5 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -503,11 +503,14 @@ sub openFile { $LOGFILE->autoflush() if $this->{autoFlush}; my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2]; + Error("Can't get uid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webUid; my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2]; + Error("Can't get gid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webGid; if ( $> == 0 ) { - chown( $webUid, $webGid, $this->{logFile} ) - or Fatal("Can't change permissions on log file $$this{logFile}: $!"); - } + # If we are root, we want to make sure that www-data or whatever owns the file + chown($webUid, $webGid, $this->{logFile} ) or + Error("Can't change permissions on log file $$this{logFile}: $!"); + } # end if are root } else { $this->fileLevel(NOLOG); $this->termLevel(INFO); From 927165a934cff0437f595cac804f6fbea86bb4e9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 30 Nov 2019 15:37:05 -0500 Subject: [PATCH 007/330] debug the recording_started timestamp and warning when we can't fine a frame before the requested time --- src/zm_packetqueue.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 45be587c8..bf487d020 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -229,8 +229,8 @@ void zm_packetqueue::clear_unwanted_packets( std::list::reverse_iterator it; // Step 1 - find frame <= recording_started. - Debug(3, "Looking for frame before start recording stream id (%d), queue has %d packets", - mVideoStreamId, pktQueue.size()); + Debug(3, "Looking for frame before start (%d.%d) recording stream id (%d), queue has %d packets", + recording_started->tv_sec, recording_started->tv_usec, mVideoStreamId, pktQueue.size()); for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) { ZMPacket *zm_packet = *it; AVPacket *av_packet = &(zm_packet->packet); @@ -252,7 +252,7 @@ void zm_packetqueue::clear_unwanted_packets( } if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a frame before event starttime. keeping all"); + Info("Didn't find a frame before event starttime. keeping all"); return; } From 0b2853095e1c07c6efaddf9e639a21dc8c4fe6db Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 1 Dec 2019 12:30:27 -0500 Subject: [PATCH 008/330] cleanup auth in monitors alarm method --- web/api/app/Controller/MonitorsController.php | 60 +++++++++---------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index ea7a666a1..3be9166b8 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -249,16 +249,11 @@ class MonitorsController extends AppController { // where C=on|off|status public function alarm() { $id = $this->request->params['named']['id']; - $cmd = strtolower($this->request->params['named']['command']); if ( !$this->Monitor->exists($id) ) { throw new NotFoundException(__('Invalid monitor')); } - if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status' ) { - throw new BadRequestException(__('Invalid command')); - } - $zm_path_bin = Configure::read('ZM_PATH_BIN'); - $mToken = $this->request->query('token') ? $this->request->query('token') : null; + $cmd = strtolower($this->request->params['named']['command']); switch ($cmd) { case 'on': $q = '-a'; @@ -272,42 +267,43 @@ class MonitorsController extends AppController { $verbose = ''; // zmu has a bug - gives incorrect verbose output in this case $q = '-s'; break; + default : + throw new BadRequestException(__('Invalid command')); } // form auth key based on auth credentials - $this->loadModel('Config'); - $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); - $config = $this->Config->find('first', $options); - $zmOptAuth = $config['Config']['Value']; - - $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')); - $config = $this->Config->find('first', $options); - $zmAuthRelay = $config['Config']['Value']; - $auth = ''; - if ( $zmOptAuth ) { - if ($mToken) { + if ( ZM_OPT_USE_AUTH ) { + global $user; + $mToken = $this->request->query('token') ? $this->request->query('token') : $this->request->data('token');; + if ( $mToken ) { $auth = ' -T '.$mToken; - } - elseif ( $zmAuthRelay == 'hashed' ) { - $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET')); - $config = $this->Config->find('first', $options); - $zmAuthHashSecret = $config['Config']['Value']; + } else if ( ZM_AUTH_RELAY == 'hashed' ) { + $auth = ' -A '.generateAuthHash(ZM_AUTH_HASH_IPS); + } else if ( ZM_AUTH_RELAY == 'plain' ) { + # Plain requires the plain text password which must either be in request or stored in session + $password = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');; + if ( !$password ) + $password = $this->request->query('password') ? $this->request->query('password') : $this->request->data('password'); - $time = localtime(); - $ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5]; - $ak = md5($ak); - $auth = ' -A '.$ak; - } else if ( $zmAuthRelay == 'plain' ) { - $auth = ' -U ' .$this->Session->Read('username').' -P '.$this->Session->Read('password'); - - } else if ( $zmAuthRelay == 'none' ) { - $auth = ' -U ' .$this->Session->Read('username'); + if ( ! $password ) { + # during auth the session will have been populated with the plaintext password + $stateful = $this->request->query('stateful') ? $this->request->query('stateful') : $this->request->data('stateful'); + if ( $stateful ) { + $password = $_SESSION['password']; + } + } else if ( $_COOKIE['ZMSESSID'] ) { + $password = $_SESSION['password']; + } + + $auth = ' -U ' .$user['Username'].' -P '.$password; + } else if ( ZM_AUTH_RELAY == 'none' ) { + $auth = ' -U ' .$user['Username']; } } - $shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth"); + $shellcmd = escapeshellcmd(ZM_PATH_BIN."/zmu $verbose -m$id $q $auth"); $status = exec ($shellcmd); $this->set(array( From 76d795f413f89069a7c73ae6c0a329f015c74c10 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 1 Dec 2019 12:32:14 -0500 Subject: [PATCH 009/330] add rendering of enums for Events Controller --- web/api/app/Controller/EventsController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 2eb5f7280..d9ad29b0d 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -15,6 +15,10 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); + public function beforeRender() { + $this->set($this->Event->enumValues()); + } + public function beforeFilter() { parent::beforeFilter(); global $user; From 03d7bcdd11c50d7432a179fea9fc3c983f98def7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 12:17:18 -0500 Subject: [PATCH 010/330] better debug logging when on-click-this function isn't defined --- web/skins/classic/js/skin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 0a446e255..5ccddf96b 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -168,7 +168,7 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { document.querySelectorAll("a[data-on-click-this], button[data-on-click-this], input[data-on-click-this]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click-this"); if ( !window[fnName] ) { - console.error("Nothing found to bind to " + fnName); + console.error("Nothing found to bind to " + fnName + " on element " + el.name); return; } el.onclick = window[fnName].bind(el, el); @@ -178,7 +178,7 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { document.querySelectorAll("a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click"); if ( !window[fnName] ) { - console.error("Nothing found to bind to " + fnName); + console.error("Nothing found to bind to " + fnName + " on element " + el.name); return; } el.onclick = function() { From caefcc2f7a1325da4c639319c7cee8e2d107ac0b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 12:17:33 -0500 Subject: [PATCH 011/330] increase size of export popup --- web/skins/classic/js/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 20ca27002..24f5d9a22 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -38,7 +38,7 @@ var popupSizes = { 'event': {'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540}, 'eventdetail': {'width': 600, 'height': 420}, 'events': {'width': 1220, 'height': 780}, - 'export': {'width': 400, 'height': 340}, + 'export': {'width': 500, 'height': 640}, 'filter': {'width': 900, 'height': 700}, 'frame': {'addWidth': 32, 'minWidth': 384, 'addHeight': 200}, 'frames': {'addWidth': 600, 'addHeight': 600}, From 355fceccb417bd72e814546097d6752317ce1ac0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 12:30:03 -0500 Subject: [PATCH 012/330] test for existence of default --- web/includes/Object.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index fec0a3ff5..d51cf082d 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -192,7 +192,7 @@ class ZM_Object { continue; } - if ( $this->defaults[$field] ) { + if ( isset($this->defaults[$field]) ) { if ( is_array($this->defaults[$field]) ) { $new_values[$field] = $this->defaults[$field]['default']; } else { From 576dd239076861d75f4f1dda5c79ad0a2adf3e35 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 12:30:15 -0500 Subject: [PATCH 013/330] test for existence of filter in query --- web/includes/functions.php | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index eaa8f8601..1710b0ab4 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1449,7 +1449,7 @@ function sortHeader($field, $querySep='&') { global $view; return implode($querySep, array( '?view='.$view, - 'page=1'.$_REQUEST['filter']['query'], + 'page=1'.(isset($_REQUEST['filter'])?$_REQUEST['filter']['query']:''), 'sort_field='.$field, 'sort_asc='.($_REQUEST['sort_field'] == $field ? !$_REQUEST['sort_asc'] : 0), 'limit='.validInt($_REQUEST['limit']), @@ -2569,4 +2569,37 @@ function array_recursive_diff($aArray1, $aArray2) { return $aReturn; } +function html_radio($name, $values, $selected=null, $options=array(), $attrs=array()) { + + $html = ''; + if ( isset($options['default']) and ( $selected == null ) ) { + $selected = $options['default']; + } # end if + + foreach ( $values as $value => $label ) { + if ( isset($options['container']) ) { + $html .= $options['container'][0]; + } + $html .= sprintf(' +
+
+ ', $name, $value, $label, ($value==$selected?' checked="checked"':''), + implode(' ', array_map( + function($attr, $value){return $attr.'="'.$value.'"';}, + array_keys($attrs), + array_values($attrs) + ), + ), + ( isset($options['id']) ? $options['id'] : ''), + ( ( (!isset($options['inline'])) or $options['inline'] ) ? '-inline' : ''), + ); + if ( isset($options['container']) ) { + $html .= $options['container'][1]; + } + } # end foreach value + return $html; +} # end sub html_radio + ?> From d39193cb6b9a52dcf77adbd5b4099351ce15f9b9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 12:33:13 -0500 Subject: [PATCH 014/330] convert flip to material icons --- web/skins/classic/includes/functions.php | 107 +++++++++++++---------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 06603c7b9..5abd519cf 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -133,8 +133,14 @@ if ( $css != 'base' ) jQuery(document).ready(function() { jQuery("#flip").click(function() { jQuery("#panel").slideToggle("slow"); - jQuery("#flip").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up'); - Cookie.write( 'zmHeaderFlip', jQuery('#flip').hasClass('glyphicon-menu-up') ? 'up' : 'down', {duration: 10*365} ); + var flip = jQuery("#flip"); + if ( flip.html() == 'keyboard_arrow_up' ) { + flip.html('keyboard_arrow_down'); + Cookie.write('zmHeaderFlip', 'down', {duration: 10*365} ); + } else { + flip.html('keyboard_arrow_up'); + Cookie.write('zmHeaderFlip', 'up', {duration: 10*365} ); + } }); }); var $j = jQuery.noConflict(); @@ -255,10 +261,10 @@ function getNavBarHTML($reload = null) {