From 7b8ee6af9f5342752809064a5fd12026d12dd9d8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Nov 2020 12:20:49 -0500 Subject: [PATCH 001/138] Rework code to use Filter methods to generate advanced search functionality. Reduces sql queries to 1 or 2, using count() to populate [total] and [totalNotFiltered]. Does pagination using array_splice instead of SQL queries. Will use more ram, but reduces db load. --- web/ajax/events.php | 115 +++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/web/ajax/events.php b/web/ajax/events.php index 5d58a5a41..3a2e6ad53 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -1,5 +1,4 @@ addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds'])); @@ -115,11 +115,12 @@ function deleteRequest($eid) { } function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) { + $data = array( 'total' => 0, 'totalNotFiltered' => 0, 'rows' => array(), - 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) ); $failed = !$filter->test_pre_sql_conditions(); @@ -143,50 +144,15 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $sort = 'Id'; } - $data = array(); - $query = array(); - $query['values'] = array(); + $values = array(); $likes = array(); - $where = ($filter->sql()?'('.$filter->sql().')' : ''); - // There are two search bars in the log view, normal and advanced - // Making an exuctive decision to ignore the normal search, when advanced search is in use - // Alternatively we could try to do both - if ( count($advsearch) ) { - - foreach ( $advsearch as $col=>$text ) { - if ( in_array($col, $columns) ) { - array_push($likes, 'E.'.$col.' LIKE ?'); - array_push($query['values'], $text); - } else if ( in_array($col, $col_alt) ) { - array_push($likes, 'M.'.$col.' LIKE ?'); - array_push($query['values'], $text); - } else { - ZM\Error("'$col' is not a sortable column name"); - continue; - } - } # end foreach col in advsearch - $wherevalues = $query['values']; - $where .= ($where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes); - - } else if ( $search != '' ) { - - $search = '%' .$search. '%'; - foreach ( $columns as $col ) { - array_push($likes, 'E.'.$col.' LIKE ?'); - array_push($query['values'], $search); - } - $wherevalues = $query['values']; - $where .= ( $where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes); - } - if ( $where ) - $where = ' WHERE '.$where; + $where = $filter->sql()?' WHERE ('.$filter->sql().')' : ''; $sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort; $col_str = 'E.*, M.Name AS Monitor'; - $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; - array_push($query['values'], $offset, $limit); + $sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY '.$sort.' '.$order; - //ZM\Debug('Calling the following sql query: ' .$query['sql']); + //ZM\Debug('Calling the following sql query: ' .$sql); $storage_areas = ZM\Storage::find(); $StorageById = array(); @@ -194,13 +160,61 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $StorageById[$S->Id()] = $S; } - $rows = array(); - foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) { + $unfiltered_rows = array(); + $event_ids = array(); + + foreach ( dbFetchAll($sql, NULL, $values) as $row ) { $event = new ZM\Event($row); if ( !$filter->test_post_sql_conditions($event) ) { $event->remove_from_cache(); continue; } + $event_ids[] = $event->Id(); + $unfiltered_rows[] = $row; + } + + ZM\Debug('Have ' . count($event_ids) . ' events matching base filter.'); + + $filtered_rows = null; + + if ( count($advsearch) or $search != '' ) { + $search_filter = new ZM\Filter(); + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids)); + + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + if ( count($advsearch) ) { + $terms = array(); + foreach ( $advsearch as $col=>$text ) { + $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); + } # end foreach col in advsearch + $terms[0]['obr'] = 1; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter->addTerms($terms); + } else if ( $search != '' ) { + $search = '%' .$search. '%'; + $terms = array(); + foreach ( $columns as $col ) { + $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + } + $terms[0]['obr'] = 1; + $terms[0]['cnj'] = 'and'; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); + } # end if search + + $sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; + $filtered_rows = dbFetchAll($sql); + ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter.'); + } else { + $filtered_rows = $unfiltered_rows; + } # end if search_filter->terms() > 1 + + $returned_rows = array(); + foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { + $event = new ZM\Event($row); + $scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width()); $imgSrc = $event->getThumbnailSrc(array(),'&'); $streamSrc = $event->getStreamSrc(array( @@ -218,16 +232,17 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default'; $row['Notes'] = nl2br(htmlspecialchars($row['Notes'])); $row['DiskSpace'] = human_filesize($event->DiskSpace()); - $rows[] = $row; - } - $data['rows'] = $rows; + $returned_rows[] = $row; + } # end foreach row matching search + + $data['rows'] = $returned_rows; # totalNotFiltered must equal total, except when either search bar has been used - $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'. ($filter->sql() ? ' WHERE '.$filter->sql():''), 'Total'); + $data['totalNotFiltered'] = count($event_ids); if ( $search != '' || count($advsearch) ) { - $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where , 'Total', $wherevalues); + $data['total'] = count($filtered_rows); } else { - $data['total'] = $data['totalNotFiltered']; + $data['total'] = count($unfiltered_rows); } return $data; From 2c5b13d001c3ba1ebe0e6cf88860170e9f7c9578 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Nov 2020 12:04:55 -0500 Subject: [PATCH 002/138] Revert to FriendsOfCake instead of our fork, as they have merged our patch. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index eb0e282a2..9f21bb62b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "web/api/app/Plugin/Crud"] path = web/api/app/Plugin/Crud - url = https://github.com/ZoneMinder/crud.git + url = https://github.com/FriendsOfCake/crud.git branch = 3.0 [submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"] path = web/api/app/Plugin/CakePHP-Enum-Behavior From 02f2c86d430d95b275a6e60498e28316c5b7ad4c Mon Sep 17 00:00:00 2001 From: Bluemax <1403092+BlueMax@users.noreply.github.com> Date: Thu, 12 Nov 2020 01:32:15 +0100 Subject: [PATCH 003/138] Honor conf.d (zmlinkcontent.sh) Signed-off-by: Bluemax <1403092+BlueMax@users.noreply.github.com> --- zmlinkcontent.sh.in | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/zmlinkcontent.sh.in b/zmlinkcontent.sh.in index 409202cb1..f6e170ffa 100755 --- a/zmlinkcontent.sh.in +++ b/zmlinkcontent.sh.in @@ -88,16 +88,19 @@ if [[ -n "$ZM_CONFIG" && ! -f "$ZM_CONFIG" ]]; then fi # Load zm.conf -if [ -n "$ZM_CONFIG" ]; then - echo "Using custom zm.conf $ZM_CONFIG" - source "$ZM_CONFIG" -elif [ -f "zm.conf" ]; then - echo "Using local zm.conf" - source "zm.conf" -elif [ -f "/etc/zm.conf" ]; then - echo "Using system zm.conf" - source "/etc/zm.conf" -else +for zmconf in "$ZM_CONFIG" ./zm.conf /etc/zm.conf /etc/zoneminder/zm.conf; do + if [[ -f "$zmconf" ]]; then + echo "Using $zmconf" + source "$zmconf" + # remove filename from path + zmconf2="${zmconf%/*}" + # source conf.d + for i in $(find "${zmconf2}/conf.d" -name \*.conf |sort); do . "$i"; done; + break + fi +done + +if [[ -z "$zmconf2" ]]; then echo -e "Failed locating zoneminder configuration file (zm.conf)\nUse the -z option to specify the full path to the zoneminder configuration file" exit 45 fi From de74a15ab1df18c84647123eee552c5cfb775d82 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Nov 2020 11:53:51 -0500 Subject: [PATCH 004/138] Move diag_fifo pipes in SOCKS_DIR instead of assigned Storage area. Storage areas could be a fs that cannot handle sockets or fifos like NFS. --- src/zm_fifo.cpp | 2 +- src/zm_monitor.cpp | 15 +++++++++------ src/zm_monitor.h | 4 ++-- src/zm_zone.cpp | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 53025f597..8743fa844 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -221,7 +221,7 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) { } snprintf(diag_path, sizeof(diag_path), "%s/%d/%s", - monitor->getStorage()->Path(), monitor->Id(), filename); + staticConfig.PATH_SOCKS.c_str(), monitor->Id(), filename); setStreamStart(diag_path); } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d8e327eac..741903a0c 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -540,11 +540,14 @@ Monitor::Monitor( ReloadLinkedMonitors(p_linked_monitors); if ( config.record_diag_images ) { - diag_path_r = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-r.jpg" : "%s/%d/diag-r.jpg", storage->Path(), id); - diag_path_d = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-d.jpg" : "%s/%d/diag-d.jpg", storage->Path(), id); if ( config.record_diag_images_fifo ) { - FifoStream::fifo_create_if_missing(diag_path_r.c_str()); - FifoStream::fifo_create_if_missing(diag_path_d.c_str()); + diag_path_ref = stringtf("%s/%d/diagpipe-r.jpg", staticConfig.PATH_SOCKS.c_str(), id); + diag_path_delta = stringtf("%s/%d/diagpipe-d.jpg", staticConfig.PATH_SOCKS.c_str(), id); + FifoStream::fifo_create_if_missing(diag_path_ref.c_str()); + FifoStream::fifo_create_if_missing(diag_path_delta.c_str()); + } else { + diag_path_ref = stringtf("%s/%d/diag-r.jpg", storage->Path(), id); + diag_path_delta = stringtf("%s/%d/diag-d.jpg", storage->Path(), id); } } } // end if purpose == ANALYSIS @@ -2683,8 +2686,8 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo ref_image.Delta(comp_image, &delta_image); if ( config.record_diag_images ) { - ref_image.WriteJpeg(diag_path_r.c_str(), config.record_diag_images_fifo); - delta_image.WriteJpeg(diag_path_d.c_str(), config.record_diag_images_fifo); + ref_image.WriteJpeg(diag_path_ref.c_str(), config.record_diag_images_fifo); + delta_image.WriteJpeg(diag_path_delta.c_str(), config.record_diag_images_fifo); } // Blank out all exclusion zones diff --git a/src/zm_monitor.h b/src/zm_monitor.h index a6d5084bb..858ea5d72 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -313,8 +313,8 @@ protected: Image ref_image; Image alarm_image; // Used in creating analysis images, will be initialized in Analysis Image write_image; // Used when creating snapshot images - std::string diag_path_r; - std::string diag_path_d; + std::string diag_path_ref; + std::string diag_path_delta; Purpose purpose; // What this monitor has been created to do int event_count; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 8fc7f88c9..8ce486821 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -113,11 +113,16 @@ void Zone::Setup( } if ( config.record_diag_images ) { - snprintf(diag_path, sizeof(diag_path), - config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg", - monitor->getStorage()->Path(), id); - if ( config.record_diag_images_fifo ) + if ( config.record_diag_images_fifo ) { + snprintf(diag_path, sizeof(diag_path), + "%s/diagpipe-%d-poly.jpg", + staticConfig.PATH_SOCKS.c_str(), id); + FifoStream::fifo_create_if_missing(diag_path); + } else { + snprintf(diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", + monitor->getStorage()->Path(), id); + } pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo); } else { diag_path[0] = 0; @@ -139,10 +144,11 @@ void Zone::RecordStats(const Event *event) { "INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); - if ( mysql_query(&dbconn, sql) ) { + int rc = mysql_query(&dbconn, sql); + db_mutex.unlock(); + if ( rc ) { Error("Can't insert event stats: %s", mysql_error(&dbconn)); } - db_mutex.unlock(); } // end void Zone::RecordStats( const Event *event ) bool Zone::CheckOverloadCount() { From c8efddddf5ffae8835ec6fb4ea9195113107d474 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 12 Nov 2020 12:27:06 -0600 Subject: [PATCH 005/138] fix typo --- distros/redhat/zoneminder.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 16904e725..c1d6283f2 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -44,7 +44,7 @@ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz -Source1: https://github.com/FriendOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz +Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz %{?rhel:BuildRequires: epel-rpm-macros} From bbc2c2a607c49f70847b5d1611d297013d5f8e30 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Nov 2020 13:27:10 -0500 Subject: [PATCH 006/138] Crud has more than 1 friend --- distros/redhat/zoneminder.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 16904e725..c1d6283f2 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -44,7 +44,7 @@ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz -Source1: https://github.com/FriendOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz +Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz %{?rhel:BuildRequires: epel-rpm-macros} From ad4d0efba6166873058d6331a95504d34484327c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 12 Nov 2020 17:00:32 -0500 Subject: [PATCH 007/138] 121 frames is too many, use >= instead --- src/zm_event.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index c858b1877..e2b697e48 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -276,7 +276,7 @@ Event::~Event() { // 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(); + 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", @@ -700,7 +700,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score)); if ( write_to_db or - (frame_data.size() > MAX_DB_FRAMES) + (frame_data.size() >= MAX_DB_FRAMES) or (frame_type==BULK) or From c0225a35aade6977f1d3649225f856f4708e892a Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 13 Nov 2020 07:24:17 -0600 Subject: [PATCH 008/138] use different variable assignment for clarity --- web/ajax/events.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/ajax/events.php b/web/ajax/events.php index 3a2e6ad53..cde2acc08 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -173,7 +173,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $unfiltered_rows[] = $row; } - ZM\Debug('Have ' . count($event_ids) . ' events matching base filter.'); + ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.'); $filtered_rows = null; @@ -238,11 +238,11 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $data['rows'] = $returned_rows; # totalNotFiltered must equal total, except when either search bar has been used - $data['totalNotFiltered'] = count($event_ids); + $data['totalNotFiltered'] = count($unfiltered_rows); if ( $search != '' || count($advsearch) ) { $data['total'] = count($filtered_rows); } else { - $data['total'] = count($unfiltered_rows); + $data['total'] = $data['totalNotFiltered']; } return $data; From 3cf31435a784a2a0f5f4dcb2a3ddc356c0edd451 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 09:51:54 -0500 Subject: [PATCH 009/138] fix next/prev buttons by correcting the sort_field when it is StartTime or EndTime --- web/includes/functions.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/includes/functions.php b/web/includes/functions.php index 9ac46cd3c..d5478b74f 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1003,10 +1003,18 @@ function parseSort($saveToSession=false, $querySep='&') { $sortColumn = 'E.DiskSpace'; break; case 'StartTime' : + # legacy + $_REQUEST['sort_field'] = 'StartDateTime'; + $sortColumn = 'E.StartDateTime'; + break; case 'StartDateTime' : $sortColumn = 'E.StartDateTime'; break; case 'EndTime' : + #legacy + $_REQUEST['sort_field'] = 'EndDateTime'; + $sortColumn = 'E.EndDateTime'; + break; case 'EndDateTime' : $sortColumn = 'E.EndDateTime'; break; From a581cb9de1e082cbb330a0a892cbf7a1a677a65f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 10:18:35 -0500 Subject: [PATCH 010/138] Make various text input options be 90% --- web/skins/classic/css/base/views/options.css | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/css/base/views/options.css b/web/skins/classic/css/base/views/options.css index e13409b35..8d7b9bc70 100644 --- a/web/skins/classic/css/base/views/options.css +++ b/web/skins/classic/css/base/views/options.css @@ -26,8 +26,25 @@ input.large { } input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SITEKEY]"], -input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"] { - width: 100%; +input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"], +input[name="newConfig[ZM_OPT_GEOLOCATION_TILE_PROVIDER]"], +input[name="newConfig[ZM_OPT_GEOLOCATION_ACCESS_TOKEN]"], +input[name="newConfig[ZM_AUTH_HASH_SECRET]"], +input[name="newConfig[ZM_UPDATE_CHECK_PROXY]"], +input[name="newConfig[ZM_WEB_TITLE]"], +input[name="newConfig[ZM_WEB_TITLE_PREFIX]"], +input[name="newConfig[ZM_HOME_URL]"], +input[name="newConfig[ZM_HOME_CONTENT]"], +input[name="newConfig[ZM_WEB_CONSOLE_BANNER]"], +input[name="newConfig[ZM_LOG_DEBUG_TARGET]"], +input[name="newConfig[ZM_LOG_DEBUG_FILE]"], +input[name="newConfig[ZM_MESSAGE_ADDRESS]"], +input[name="newConfig[ZM_MESSAGE_SUBJECT]"], +input[name="newConfig[ZM_FROM_EMAIL]"], +input[name="newConfig[ZM_URL]"], +input[name="newConfig[ZM_SSMTP_PATH]"], +input[name="newConfig[ZM_CSP_REPORT_URI]"] { + width: 90%; } #options label { From da99426535cf3d426f77165e11287a9a6847b4a0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 10:40:55 -0500 Subject: [PATCH 011/138] if monitor is not found call loadMonitors to make sure it isn't new --- scripts/zmtrigger.pl.in | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index acd4ce255..5fc632165 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -350,7 +350,7 @@ sub loadMonitors { $new_monitors{$monitor->{Id}} = $monitor; } # end while fetchrow %monitors = %new_monitors; -} +} # end sub loadMonitors sub handleMessage { my $connection = shift; @@ -369,8 +369,12 @@ sub handleMessage { my $monitor = $monitors{$id}; if ( !$monitor ) { - Warning("Can't find monitor '$id' for message '$message'"); - return; + loadMonitors(); + $monitor = $monitors{$id}; + if ( !$monitor ) { + Warning("Can't find monitor '$id' for message '$message'"); + return; + } } if ( !zmMemVerify($monitor) ) { Warning("Can't verify monitor '$id' for message '$message'"); From 08a8155b478af3e96f728df8fe757978616cc3f5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 12:27:51 -0500 Subject: [PATCH 012/138] Implement a Monitor::disconnect function. Fix Fatals during connect and cleanup Analysis Monitor code to wait around for zmc. Handle zmc going away gracefully. Fixes a slow zma startup due to zmc not being setup yet. --- src/zm_monitor.cpp | 173 +++++++++++++++++++++++++-------------------- src/zm_monitor.h | 1 + src/zma.cpp | 15 ++-- 3 files changed, 108 insertions(+), 81 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 741903a0c..8fed25acc 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -448,6 +448,8 @@ Monitor::Monitor( char monitor_dir[PATH_MAX]; snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); + shared_data = nullptr; + if ( purpose == CAPTURE ) { if ( mkdir(monitor_dir, 0755) && ( errno != EEXIST ) ) { Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); @@ -489,9 +491,39 @@ Monitor::Monitor( video_store_data->size = sizeof(VideoStoreData); //video_store_data->frameNumber = 0; } else if ( purpose == ANALYSIS ) { - if ( ! (this->connect() && mem_ptr && shared_data->valid) ) { - Error("Shared data not initialised by capture daemon for monitor %s", name); - exit(-1); + while ( + (!zm_terminate) + and + ( !(this->connect() and shared_data->valid) ) + and + ( shared_data->last_write_index == (unsigned int)image_buffer_count ) + and + ( shared_data->last_write_time == 0 ) + ) { + Debug(1, "blah"); + Debug(1, "Waiting for capture daemon shared_data(%d) last_write_index(%d), last_write_time(%d)", + (shared_data ? 1:0), + (shared_data ? shared_data->last_write_index : 0), + (shared_data ? shared_data->last_write_time : 0)); + usleep(100000); + } + + ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), + image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); + adaptive_skip = true; + + ReloadLinkedMonitors(p_linked_monitors); + + if ( config.record_diag_images ) { + if ( config.record_diag_images_fifo ) { + diag_path_ref = stringtf("%s/%d/diagpipe-r.jpg", staticConfig.PATH_SOCKS.c_str(), id); + diag_path_delta = stringtf("%s/%d/diagpipe-d.jpg", staticConfig.PATH_SOCKS.c_str(), id); + FifoStream::fifo_create_if_missing(diag_path_ref.c_str()); + FifoStream::fifo_create_if_missing(diag_path_delta.c_str()); + } else { + diag_path_ref = stringtf("%s/%d/diag-r.jpg", storage->Path(), id); + diag_path_delta = stringtf("%s/%d/diag-d.jpg", storage->Path(), id); + } } shared_data->state = IDLE; shared_data->last_read_time = 0; @@ -520,37 +552,6 @@ Monitor::Monitor( n_linked_monitors = 0; linked_monitors = nullptr; - if ( purpose == ANALYSIS ) { - while( - ( shared_data->last_write_index == (unsigned int)image_buffer_count ) - && - ( shared_data->last_write_time == 0 ) - && - ( !zm_terminate ) - ) { - Debug(1, "Waiting for capture daemon last_write_index(%d), last_write_time(%d)", - shared_data->last_write_index, shared_data->last_write_time ); - sleep(1); - } - - ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), - image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); - adaptive_skip = true; - - ReloadLinkedMonitors(p_linked_monitors); - - if ( config.record_diag_images ) { - if ( config.record_diag_images_fifo ) { - diag_path_ref = stringtf("%s/%d/diagpipe-r.jpg", staticConfig.PATH_SOCKS.c_str(), id); - diag_path_delta = stringtf("%s/%d/diagpipe-d.jpg", staticConfig.PATH_SOCKS.c_str(), id); - FifoStream::fifo_create_if_missing(diag_path_ref.c_str()); - FifoStream::fifo_create_if_missing(diag_path_delta.c_str()); - } else { - diag_path_ref = stringtf("%s/%d/diag-r.jpg", storage->Path(), id); - diag_path_delta = stringtf("%s/%d/diag-d.jpg", storage->Path(), id); - } - } - } // end if purpose == ANALYSIS } // Monitor::Monitor bool Monitor::connect() { @@ -606,12 +607,12 @@ bool Monitor::connect() { } } #endif - if ( mem_ptr == MAP_FAILED ) - Fatal("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno); - if ( mem_ptr == nullptr ) { - Error("mmap gave a NULL address:"); - } else { - Debug(3, "mmapped to %p", mem_ptr); + if ( (mem_ptr == MAP_FAILED) or (mem_ptr == nullptr) ) { + Error("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno); + close(map_fd); + map_fd = -1; + mem_ptr = nullptr; + return false; } #else // ZM_MEM_MAPPED shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700); @@ -669,6 +670,51 @@ Debug(3, "Success connecting"); return true; } // end Monitor::connect +bool Monitor::disconnect() { + if ( !mem_ptr ) + return true; + +#if ZM_MEM_MAPPED + if ( mem_ptr > (void *)0 ) { + msync(mem_ptr, mem_size, MS_ASYNC); + munmap(mem_ptr, mem_size); + } + if ( map_fd >= 0 ) + close(map_fd); + + map_fd = -1; + + if ( purpose == CAPTURE ) { + if ( unlink(mem_file) < 0 ) { + Warning("Can't unlink '%s': %s", mem_file, strerror(errno)); + } + } +#else // ZM_MEM_MAPPED + struct shmid_ds shm_data; + if ( shmctl(shm_id, IPC_STAT, &shm_data) < 0 ) { + Debug(3, "Can't shmctl: %s", strerror(errno)); + return false; + } + + shm_id = 0; + + if ( shm_data.shm_nattch <= 1 ) { + if ( shmctl(shm_id, IPC_RMID, 0) < 0 ) { + Debug(3, "Can't shmctl: %s", strerror(errno)); + return false; + } + } + + if ( shmdt(mem_ptr) < 0 ) { + Debug(3, "Can't shmdt: %s", strerror(errno)); + return false; + } +#endif // ZM_MEM_MAPPED + mem_ptr = nullptr; + shared_data = nullptr; + return true; +} // end bool Monitor::disconnect() + Monitor::~Monitor() { if ( n_linked_monitors ) { for( int i = 0; i < n_linked_monitors; i++ ) { @@ -712,14 +758,6 @@ Monitor::~Monitor() { delete[] image_buffer; } // end if mem_ptr - for ( int i = 0; i < n_zones; i++ ) { - delete zones[i]; - } - delete[] zones; - - delete camera; - delete storage; - if ( mem_ptr ) { if ( purpose == ANALYSIS ) { shared_data->state = state = IDLE; @@ -739,36 +777,17 @@ Monitor::~Monitor() { shared_data->valid = false; memset(mem_ptr, 0, mem_size); } - -#if ZM_MEM_MAPPED - if ( msync(mem_ptr, mem_size, MS_SYNC) < 0 ) - Error("Can't msync: %s", strerror(errno)); - if ( munmap(mem_ptr, mem_size) < 0 ) - Fatal("Can't munmap: %s", strerror(errno)); - close( map_fd ); - - if ( purpose == CAPTURE ) { - // How about we store this in the object on instantiation so that we don't have to do this again. - char mmap_path[PATH_MAX] = ""; - snprintf(mmap_path, sizeof(mmap_path), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id); - - if ( unlink(mmap_path) < 0 ) { - Warning("Can't unlink '%s': %s", mmap_path, strerror(errno)); - } - } -#else // ZM_MEM_MAPPED - struct shmid_ds shm_data; - if ( shmctl(shm_id, IPC_STAT, &shm_data) < 0 ) { - Fatal("Can't shmctl: %s", strerror(errno)); - } - if ( shm_data.shm_nattch <= 1 ) { - if ( shmctl(shm_id, IPC_RMID, 0) < 0 ) { - Fatal("Can't shmctl: %s", strerror(errno)); - } - } -#endif // ZM_MEM_MAPPED + disconnect(); } // end if mem_ptr -} + + for ( int i = 0; i < n_zones; i++ ) { + delete zones[i]; + } + delete[] zones; + + delete camera; + delete storage; +} // end Monitor::~Monitor() void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) { for ( int i = 0; i < n_zones; i++ ) diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 858ea5d72..9a023d53a 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -424,6 +424,7 @@ public: void AddPrivacyBitmask( Zone *p_zones[] ); bool connect(); + bool disconnect(); inline int ShmValid() const { return shared_data && shared_data->valid; diff --git a/src/zma.cpp b/src/zma.cpp index edaf9c722..5e71abcdc 100644 --- a/src/zma.cpp +++ b/src/zma.cpp @@ -106,9 +106,9 @@ int main( int argc, char *argv[] ) { } } - if (optind < argc) { + if ( optind < argc ) { fprintf(stderr, "Extraneous options, "); - while (optind < argc) + while ( optind < argc ) printf("%s ", argv[optind++]); printf("\n"); Usage(); @@ -130,7 +130,7 @@ int main( int argc, char *argv[] ) { hwcaps_detect(); Monitor *monitor = Monitor::Load(id, true, Monitor::ANALYSIS); - zmFifoDbgInit( monitor ); + zmFifoDbgInit(monitor); if ( monitor ) { Info("In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled()); @@ -148,7 +148,14 @@ int main( int argc, char *argv[] ) { monitor->UpdateAdaptiveSkip(); last_analysis_update_time = time(nullptr); - while( (!zm_terminate) && monitor->ShmValid() ) { + while ( !zm_terminate ) { + if ( !monitor->ShmValid() ) { + monitor->disconnect(); + Info("Waiting for shm to become valid"); + usleep(100000); + monitor->connect(); + continue; + } // Process the next image sigprocmask(SIG_BLOCK, &block_set, nullptr); From 23f27d5a112ec90f7c501bad77804587f8a678b3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 12:43:31 -0500 Subject: [PATCH 013/138] include frame_type in debug message --- src/zm_event.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e2b697e48..efd7d8bc6 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -706,8 +706,8 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a or ( fps and (frame_data.size() > fps) ) ) { - Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK", - frame_data.size(), write_to_db, fps); + Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)", + frame_data.size(), write_to_db, fps, (frame_type==BULK)); WriteDbFrames(); last_db_frame = frames; From 52e747791d64f6050ed20499b52c87ebe7f3b234 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 13 Nov 2020 12:34:01 -0600 Subject: [PATCH 014/138] rough in frames server pagination backend --- web/ajax/frames.php | 165 +++++++++++++++++++++++ web/skins/classic/views/js/frames.js | 22 +++ web/skins/classic/views/js/frames.js.php | 1 + 3 files changed, 188 insertions(+) create mode 100644 web/ajax/frames.php create mode 100644 web/skins/classic/views/js/frames.js.php diff --git a/web/ajax/frames.php b/web/ajax/frames.php new file mode 100644 index 000000000..fb23980a8 --- /dev/null +++ b/web/ajax/frames.php @@ -0,0 +1,165 @@ + "search text" pairs +// Bootstrap table sends json_ecoded array, which we must decode +$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); + +// Sort specifies the name of the column to sort on +$sort = 'FrameId'; +if ( isset($_REQUEST['sort']) ) { + $sort = $_REQUEST['sort']; +} + +// Offset specifies the starting row to return, used for pagination +$offset = 0; +if ( isset($_REQUEST['offset']) ) { + if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { + ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); + } else { + $offset = $_REQUEST['offset']; + } +} + +// Order specifies the sort direction, either asc or desc +$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; + +// Limit specifies the number of rows to return +$limit = 100; +if ( isset($_REQUEST['limit']) ) { + if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { + ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); + } else { + $limit = $_REQUEST['limit']; + } +} + +// +// MAIN LOOP +// + +// Only one supported task at the moment +switch ( $task ) { + case 'query' : + $data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit); + break; + default : + ZM\Fatal("Unrecognised task '$task'"); +} // end switch task + +ajaxResponse($data); + +// +// FUNCTION DEFINITIONS +// + +function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { + + // The table we want our data from + $table = 'Frames'; + + // The names of the dB columns in the events table we are interested in + $columns = array('EventId', 'FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); + + if ( !in_array($sort, $columns) ) { + ZM\Error('Invalid sort field: ' . $sort); + $sort = 'FrameId'; + } + + $Event = new ZM\Event($eid); + $Monitor = $Event->Monitor(); + $values = array(); + $likes = array(); + $where = 'EventId ='.$eid; + + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + if ( count($advsearch) ) { + + foreach ( $advsearch as $col=>$text ) { + if ( !in_array($col, array_merge($columns, $col_alt)) ) { + ZM\Error("'$col' is not a searchable column name"); + continue; + } + // Don't use wildcards on advanced search + //$text = '%' .$text. '%'; + array_push($likes, $col.' LIKE ?'); + array_push($query['values'], $text); + } + $wherevalues = $query['values']; + $where = ' WHERE (' .implode(' OR ', $likes). ')'; + + } else if ( $search != '' ) { + + $search = '%' .$search. '%'; + foreach ( $columns as $col ) { + array_push($likes, $col.' LIKE ?'); + array_push($query['values'], $search); + } + $wherevalues = $query['values']; + $where = ' WHERE (' .implode(' OR ', $likes). ')'; + } + + $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; + array_push($query['values'], $offset, $limit); + + $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total'); + if ( $search != '' || count($advsearch) ) { + $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues); + } else { + $data['total'] = $data['totalNotFiltered']; + } + + $returned_rows = array(); + $results = dbFetchAll($query['sql'], NULL, $query['values']); + if ( !$results ) { + return $data; + } + + foreach ( $results as $row ) { + $base_img_src = '?view=image&fid=' .$row['FrameId']; + $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); + $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; + $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; + $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; + $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); + $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); + $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; + + $row['imgHtml'] = ''.PHP_EOL; + $returned_rows[] = $row; + } + $data['rows'] = $returned_rows; + + return $data; +} diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js index 57bf8e4b0..fcdb27303 100644 --- a/web/skins/classic/views/js/frames.js +++ b/web/skins/classic/views/js/frames.js @@ -1,3 +1,25 @@ +// Called by bootstrap-table to retrieve zm frame data +function ajaxRequest(params) { + if ( params.data && params.data.filter ) { + params.data.advsearch = params.data.filter; + delete params.data.filter; + } + $j.getJSON(thisUrl + '?view=request&request=frames&task=query&eid='+eid, params.data) + .done(function(data) { + var rows = processRows(data.rows); + // rearrange the result into what bootstrap-table expects + params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows}); + }) + .fail(logAjaxFail); +} + +function processRows(rows) { + $j.each(rows, function(ndx, row) { + // WIP: process each row here + }); + return rows; +} + function thumbnail_onmouseover(event) { var img = event.target; img.src = ''; diff --git a/web/skins/classic/views/js/frames.js.php b/web/skins/classic/views/js/frames.js.php new file mode 100644 index 000000000..48b33b5e3 --- /dev/null +++ b/web/skins/classic/views/js/frames.js.php @@ -0,0 +1 @@ +var eid = ; From f1b8266e26fed5c842b227944dfc7ba355a7ccb8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 17:03:51 -0500 Subject: [PATCH 015/138] Only join storage and Monitors if necessary --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index eb1b36077..0c78c33ca 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -148,15 +148,8 @@ sub Sql { } my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query_json}); - my $sql = 'SELECT E.*, - unix_timestamp(E.StartDateTime) as Time, - M.Name as MonitorName, - M.DefaultRate, - M.DefaultScale - FROM Events as E - INNER JOIN Monitors as M on M.Id = E.MonitorId - LEFT JOIN Storage as S on S.Id = E.StorageId - '; + my $sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time + FROM Events as E'; if ( $filter_expr->{terms} ) { foreach my $term ( @{$filter_expr->{terms}} ) { @@ -174,12 +167,16 @@ sub Sql { if ( $term->{attr} eq 'AlarmedZoneId' ) { $term->{op} = 'EXISTS'; } elsif ( $term->{attr} =~ /^Monitor/ ) { + $sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName + FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId'; my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/; $self->{Sql} .= 'M.'.$temp_attr_name; } elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) { + $sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName + FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId'; $self->{Sql} .= 'M.ServerId'; } elsif ( $term->{attr} eq 'StorageServerId' ) { - $self->{Sql} .= 'S.ServerId'; + $self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)'; } elsif ( $term->{attr} eq 'FilterServerId' ) { $self->{Sql} .= $Config{ZM_SERVER_ID}; # StartTime options @@ -308,7 +305,7 @@ sub Sql { } elsif ( $value eq 'Even' ) { $self->{Sql} .= ' % 2 = 0'; } else { - $self->{Sql} .= " IS $value"; + $self->{Sql} .= ' IS '.$value; } } elsif ( $term->{op} eq 'EXISTS' ) { $self->{Sql} .= ' EXISTS '.$value; @@ -373,6 +370,8 @@ sub Sql { if ( $filter_expr->{sort_field} eq 'Id' ) { $sort_column = 'E.Id'; } elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) { + $sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName + FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId'; $sort_column = 'M.Name'; } elsif ( $filter_expr->{sort_field} eq 'Name' ) { $sort_column = 'E.Name'; From a0dcdd135ad8c797e29175d293d0f692b0cf548d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 17:04:22 -0500 Subject: [PATCH 016/138] DefaultRate and DefaultScale are Monitor properties, so just load them in a Monitor object so we don't have to JOIN the Monitors table --- scripts/zmfilter.pl.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 834da99f8..b2dd7a2b4 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -405,8 +405,9 @@ sub generateVideo { my $Event = shift; my $phone = shift; - my $rate = $Event->{DefaultRate}/100; - my $scale = $Event->{DefaultScale}/100; + my $Monitor = $Event->Monitor(); + my $rate = $$Monitor{DefaultRate}/100; + my $scale = $$Monitor{DefaultScale}/100; my $format; my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS}); From d2a203014f03cd88ea15e327a8719b20b20f8bcc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 17:05:03 -0500 Subject: [PATCH 017/138] rename diagpipe-r so that we don't have to create directories in SOCKS_DIR --- src/zm_fifo.cpp | 15 ++++++++------- src/zm_monitor.cpp | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 8743fa844..d3952a169 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -57,8 +57,8 @@ static bool zmFifoDbgOpen() { int zmFifoDbgInit(Monitor *monitor) { zm_fifodbg_inited = true; - snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log", - monitor->getStorage()->Path(), monitor->Id()); + snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/dbgpipe-%d.log", + staticConfig.PATH_SOCKS.c_str(), monitor->Id()); zmFifoDbgOpen(); return 1; } @@ -211,17 +211,18 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) { if ( !strcmp(format, "reference") ) { stream_type = MJPEG; - filename = "diagpipe-r.jpg"; + snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%d.jpg", + staticConfig.PATH_SOCKS.c_str(), monitor->Id()); } else if ( !strcmp(format, "delta") ) { - filename = "diagpipe-d.jpg"; + snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%d.jpg", + staticConfig.PATH_SOCKS.c_str(), monitor->Id()); stream_type = MJPEG; } else { + snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%d.log", + staticConfig.PATH_SOCKS.c_str(), monitor->Id()); stream_type = RAW; - filename = "dbgpipe.log"; } - snprintf(diag_path, sizeof(diag_path), "%s/%d/%s", - staticConfig.PATH_SOCKS.c_str(), monitor->Id(), filename); setStreamStart(diag_path); } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 8fed25acc..01779eed7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -516,8 +516,8 @@ Monitor::Monitor( if ( config.record_diag_images ) { if ( config.record_diag_images_fifo ) { - diag_path_ref = stringtf("%s/%d/diagpipe-r.jpg", staticConfig.PATH_SOCKS.c_str(), id); - diag_path_delta = stringtf("%s/%d/diagpipe-d.jpg", staticConfig.PATH_SOCKS.c_str(), id); + diag_path_ref = stringtf("%s/diagpipe-r-%d.jpg", staticConfig.PATH_SOCKS.c_str(), id); + diag_path_delta = stringtf("%s/diagpipe-d-%d.jpg", staticConfig.PATH_SOCKS.c_str(), id); FifoStream::fifo_create_if_missing(diag_path_ref.c_str()); FifoStream::fifo_create_if_missing(diag_path_delta.c_str()); } else { From 66f7cc55dc670c0a5458b827b0d4f8345f7b4028 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 13 Nov 2020 17:05:23 -0500 Subject: [PATCH 018/138] Spacing code comments, quotes --- web/ajax/status.php | 74 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/web/ajax/status.php b/web/ajax/status.php index 795a4b581..994644afc 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -267,7 +267,6 @@ function collectData() { if ( count($fieldSql) ) { $sql = 'SELECT '.join(', ', $fieldSql).' FROM '.$entitySpec['table']; - #$sql = 'SELECT '.join(', ', array_map($fieldSql, function($f){return '`'.$f.'`';})).' FROM '.$entitySpec['table']; if ( $joinSql ) $sql .= ' '.join(' ', array_unique($joinSql)); if ( $id && !empty($entitySpec['selector']) ) { @@ -314,27 +313,27 @@ function collectData() { $limit = $entitySpec['limit']; elseif ( !empty($_REQUEST['count']) ) $limit = validInt($_REQUEST['count']); - $limit_offset=''; + $limit_offset = ''; if ( !empty($_REQUEST['offset']) ) $limit_offset = validInt($_REQUEST['offset']) . ', '; - if ( !empty( $limit ) ) + if ( !empty($limit) ) $sql .= ' limit '.$limit_offset.$limit; if ( isset($limit) && $limit == 1 ) { if ( $sqlData = dbFetchOne($sql, NULL, $values) ) { foreach ( $postFuncs as $element=>$func ) $sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' ); - $data = array_merge( $data, $sqlData ); + $data = array_merge($data, $sqlData); } } else { $count = 0; - foreach( dbFetchAll( $sql, NULL, $values ) as $sqlData ) { + foreach ( dbFetchAll($sql, NULL, $values) as $sqlData ) { foreach ( $postFuncs as $element=>$func ) - $sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' ); + $sqlData[$element] = eval('return( '.$func.'( $sqlData ) );'); $data[] = $sqlData; if ( isset($limi) && ++$count >= $limit ) break; - } - } + } # end foreach + } # end if have limit == 1 } } #ZM\Debug(print_r($data, true)); @@ -347,19 +346,19 @@ if ( !isset($_REQUEST['layout']) ) { $_REQUEST['layout'] = 'json'; } -switch( $_REQUEST['layout'] ) { +switch ( $_REQUEST['layout'] ) { case 'xml NOT CURRENTLY SUPPORTED' : - header('Content-type: application/xml'); - echo(' -'); - echo '<'.strtolower($_REQUEST['entity']).'> + header('Content-type: application/xml'); + echo(' + '); + echo '<'.strtolower($_REQUEST['entity']).'> '; - foreach ( $data as $key=>$value ) { - $key = strtolower($key); - echo "<$key>".htmlentities($value)."\n"; - } - echo '\n"; - break; + foreach ( $data as $key=>$value ) { + $key = strtolower($key); + echo "<$key>".htmlentities($value)."\n"; + } + echo '\n"; + break; case 'json' : { $response = array( strtolower(validJsStr($_REQUEST['entity'])) => $data ); @@ -369,11 +368,11 @@ switch( $_REQUEST['layout'] ) { break; } case 'text' : - header('Content-type: text/plain' ); - echo join( ' ', array_values( $data ) ); - break; + header('Content-type: text/plain'); + echo join(' ', array_values($data)); + break; default: - ZM\Error('Unsupported layout: '. $_REQUEST['layout']); + ZM\Error('Unsupported layout: '.$_REQUEST['layout']); } function getFrameImage() { @@ -381,38 +380,38 @@ function getFrameImage() { $frameId = $_REQUEST['id'][1]; $sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?'; - if ( !($frame = dbFetchOne( $sql, NULL, array($eventId, $frameId ) )) ) { + if ( !($frame = dbFetchOne($sql, NULL, array($eventId, $frameId))) ) { $frame = array(); $frame['EventId'] = $eventId; $frame['FrameId'] = $frameId; $frame['Type'] = 'Virtual'; } - $event = dbFetchOne( 'select * from Events where Id = ?', NULL, array( $frame['EventId'] ) ); - $frame['Image'] = getImageSrc( $event, $frame, SCALE_BASE ); - return( $frame ); + $event = dbFetchOne('SELECT * FROM Events WHERE Id = ?', NULL, array($frame['EventId'])); + $frame['Image'] = getImageSrc($event, $frame, SCALE_BASE); + return $frame; } function getNearFrame() { $eventId = $_REQUEST['id'][0]; $frameId = $_REQUEST['id'][1]; - $sql = 'select FrameId from Frames where EventId = ? and FrameId <= ? order by FrameId desc limit 1'; - if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId', array( $eventId, $frameId ) ) ) { - $sql = 'select * from Frames where EventId = ? and FrameId > ? order by FrameId asc limit 1'; - if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId', array( $eventId, $frameId ) ) ) { + $sql = 'SELECT FrameId FROM Frames WHERE EventId = ? AND FrameId <= ? ORDER BY FrameId DESC LIMIT 1'; + if ( !$nearFrameId = dbFetchOne($sql, 'FrameId', array($eventId, $frameId)) ) { + $sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId > ? ORDER BY FrameId ASC LIMIT 1'; + if ( !$nearFrameId = dbFetchOne($sql, 'FrameId', array($eventId, $frameId)) ) { return( array() ); } } $_REQUEST['entity'] = 'frame'; $_REQUEST['id'][1] = $nearFrameId; - return( collectData() ); + return collectData(); } function getNearEvents() { global $user, $sortColumn, $sortOrder; $eventId = $_REQUEST['id']; - $NearEvents = array( 'EventId'=>$eventId ); + $NearEvents = array('EventId'=>$eventId); $event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, array($eventId)); if ( !$event ) return $NearEvents; @@ -423,7 +422,8 @@ function getNearEvents() { $filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds'])); } - # When listing, it may make sense to list them in descending order. But when viewing Prev should timewise earlier and Next should be after. + # When listing, it may make sense to list them in descending order. + # But when viewing Prev should timewise earlier and Next should be after. if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartDateTime' ) { $sortOrder = 'ASC'; } @@ -436,7 +436,7 @@ function getNearEvents() { $sql .= ' LIMIT 1'; $result = dbQuery($sql); if ( !$result ) { - ZM\Error("Failed to load previous event using $sql"); + ZM\Error('Failed to load previous event using '.$sql); return $NearEvents; } @@ -450,7 +450,7 @@ function getNearEvents() { $sql .= ' LIMIT 1'; $result = dbQuery($sql); if ( !$result ) { - ZM\Error("Failed to load next event using $sql"); + ZM\Error('Failed to load next event using '.$sql); return $NearEvents; } $nextEvent = dbFetchNext($result); @@ -470,6 +470,6 @@ function getNearEvents() { $NearEvents['NextEventId'] = $NearEvents['NextEventStartTime'] = $NearEvents['NextEventDefVideoPath'] = 0; } return $NearEvents; -} +} # end function getNearEvents() ?> From f88d721ae3675b8f631f8bab0979c2aa4ce4c7f2 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 10:27:33 -0600 Subject: [PATCH 019/138] fix issue with events view and ALL pagination --- web/ajax/events.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/ajax/events.php b/web/ajax/events.php index cde2acc08..37e225527 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -58,7 +58,8 @@ if ( isset($_REQUEST['offset']) ) { $order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; // Limit specifies the number of rows to return -$limit = 100; +// Set the default to 0 for events view, to prevent an issue with ALL pagination +$limit = 0; if ( isset($_REQUEST['limit']) ) { if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); From 9aa6fea6a3456411530f97924853dc7e559dce44 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 14:19:28 -0600 Subject: [PATCH 020/138] frames view server pagination WIP --- web/ajax/frames.php | 118 ++++++++++++++--------- web/skins/classic/views/js/frames.js | 29 +++++- web/skins/classic/views/js/frames.js.php | 1 + 3 files changed, 99 insertions(+), 49 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index fb23980a8..664f14bf7 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -14,6 +14,8 @@ if ( empty($_REQUEST['task']) ) { // query is the only supported task at the moment } else if ( $_REQUEST['task'] != 'query' ) { $message = 'Unrecognised task '.$_REQUEST['task']; + } else { + $task = $_REQUEST['task']; } if ( empty($_REQUEST['eid']) ) { @@ -54,7 +56,7 @@ if ( isset($_REQUEST['offset']) ) { $order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; // Limit specifies the number of rows to return -$limit = 100; +$limit = 0; if ( isset($_REQUEST['limit']) ) { if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); @@ -84,8 +86,12 @@ ajaxResponse($data); function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { - // The table we want our data from - $table = 'Frames'; + $data = array( + 'total' => 0, + 'totalNotFiltered' => 0, + 'rows' => array(), + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) + ); // The names of the dB columns in the events table we are interested in $columns = array('EventId', 'FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); @@ -99,56 +105,64 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) $Monitor = $Event->Monitor(); $values = array(); $likes = array(); - $where = 'EventId ='.$eid; + $where = 'WHERE EventId = '.$eid; - // There are two search bars in the log view, normal and advanced - // Making an exuctive decision to ignore the normal search, when advanced search is in use - // Alternatively we could try to do both - if ( count($advsearch) ) { + $sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order; - foreach ( $advsearch as $col=>$text ) { - if ( !in_array($col, array_merge($columns, $col_alt)) ) { - ZM\Error("'$col' is not a searchable column name"); - continue; + //ZM\Debug('Calling the following sql query: ' .$sql); + + $unfiltered_rows = array(); + $frame_ids = array(); + require_once('includes/Frame.php'); + foreach ( dbFetchAll($sql, NULL, $values) as $row ) { + $frame = new ZM\Frame($row); + $frame_ids[] = $frame->Id(); + $unfiltered_rows[] = $row; + } + + ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.'); + + $filtered_rows = null; + require_once('includes/Filter.php'); + if ( count($advsearch) or $search != '' ) { + $search_filter = new ZM\Filter(); + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$frame_ids)); + + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + if ( count($advsearch) ) { + $terms = array(); + foreach ( $advsearch as $col=>$text ) { + $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); + } # end foreach col in advsearch + $terms[0]['obr'] = 1; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter->addTerms($terms); + } else if ( $search != '' ) { + $search = '%' .$search. '%'; + $terms = array(); + foreach ( $columns as $col ) { + $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); } - // Don't use wildcards on advanced search - //$text = '%' .$text. '%'; - array_push($likes, $col.' LIKE ?'); - array_push($query['values'], $text); - } - $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; + $terms[0]['obr'] = 1; + $terms[0]['cnj'] = 'and'; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); + } # end if search - } else if ( $search != '' ) { - - $search = '%' .$search. '%'; - foreach ( $columns as $col ) { - array_push($likes, $col.' LIKE ?'); - array_push($query['values'], $search); - } - $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; - } - - $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; - array_push($query['values'], $offset, $limit); - - $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total'); - if ( $search != '' || count($advsearch) ) { - $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues); + $sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; + $filtered_rows = dbFetchAll($sql); + ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.'); } else { - $data['total'] = $data['totalNotFiltered']; - } + $filtered_rows = $unfiltered_rows; + } # end if search_filter->terms() > 1 $returned_rows = array(); - $results = dbFetchAll($query['sql'], NULL, $query['values']); - if ( !$results ) { - return $data; - } - - foreach ( $results as $row ) { + foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { + if ( ZM_WEB_LIST_THUMBS ) { $base_img_src = '?view=image&fid=' .$row['FrameId']; - $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); + $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; @@ -156,10 +170,20 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; - $row['imgHtml'] = ''.PHP_EOL; + $row['Thumbnail'] = ''; + } $returned_rows[] = $row; - } + } # end foreach row matching search + $data['rows'] = $returned_rows; + # totalNotFiltered must equal total, except when either search bar has been used + $data['totalNotFiltered'] = count($unfiltered_rows); + if ( $search != '' || count($advsearch) ) { + $data['total'] = count($filtered_rows); + } else { + $data['total'] = $data['totalNotFiltered']; + } + return $data; } diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js index fcdb27303..ff407589b 100644 --- a/web/skins/classic/views/js/frames.js +++ b/web/skins/classic/views/js/frames.js @@ -1,3 +1,6 @@ +var backBtn = $j('#backBtn'); +var table = $j('#framesTable'); + // Called by bootstrap-table to retrieve zm frame data function ajaxRequest(params) { if ( params.data && params.data.filter ) { @@ -8,6 +11,8 @@ function ajaxRequest(params) { .done(function(data) { var rows = processRows(data.rows); // rearrange the result into what bootstrap-table expects + console.log('Total: '+data.total); + console.log('TotalnotFiltered: '+data.totalNotFiltered); params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows}); }) .fail(logAjaxFail); @@ -16,6 +21,7 @@ function ajaxRequest(params) { function processRows(rows) { $j.each(rows, function(ndx, row) { // WIP: process each row here + // VERIFY: Might not need to do anything here for the frames table }); return rows; } @@ -56,9 +62,10 @@ function detailFormatter(index, row, $detail) { }) .fail(logAjaxFail); } + function initPage() { - var backBtn = $j('#backBtn'); - var table = $j('#framesTable'); + // Remove the thumbnail column from the DOM if thumbnails are off globally + if ( !WEB_LIST_THUMBS ) $j('th[data-field="Thumbnail"]').remove(); // Init the bootstrap-table table.bootstrapTable({icons: icons}); @@ -93,6 +100,24 @@ function initPage() { evt.preventDefault(); window.location.reload(true); }); + + // Update table links each time after new data is loaded + table.on('post-body.bs.table', function(data) { + var type_ndx = $j('#framesTable tr th').filter(function() { + return $j(this).text().trim() == 'Type'; + }).index(); + + $j('#framesTable tr').each(function(ndx, row) { + var row = $j(row); + var type = row.find('td').eq(type_ndx).text().trim(); + row.addClass(type.toLowerCase()); + }); + + var thumb_ndx = $j('#framesTable tr th').filter(function() { + return $j(this).text().trim() == 'Thumbnail'; + }).index(); + table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom'); + }); } $j(document).ready(function() { diff --git a/web/skins/classic/views/js/frames.js.php b/web/skins/classic/views/js/frames.js.php index 48b33b5e3..e8aa37a4d 100644 --- a/web/skins/classic/views/js/frames.js.php +++ b/web/skins/classic/views/js/frames.js.php @@ -1 +1,2 @@ var eid = ; +var WEB_LIST_THUMBS = ; From f3756def08f49e08ffb47a9abaa5084ab331eb7a Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 14:45:22 -0600 Subject: [PATCH 021/138] deploy server side pagination for frames view --- web/ajax/frames.php | 2 +- web/skins/classic/views/frames.php | 138 ++------------------------- web/skins/classic/views/js/frames.js | 2 +- 3 files changed, 11 insertions(+), 131 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 664f14bf7..440c9769e 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -161,7 +161,7 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) $returned_rows = array(); foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { if ( ZM_WEB_LIST_THUMBS ) { - $base_img_src = '?view=image&fid=' .$row['FrameId']; + $base_img_src = '?view=image&fid=' .$row['Id']; $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; diff --git a/web/skins/classic/views/frames.php b/web/skins/classic/views/frames.php index da2f4c2ae..fce04cc60 100644 --- a/web/skins/classic/views/frames.php +++ b/web/skins/classic/views/frames.php @@ -28,87 +28,6 @@ require_once('includes/Filter.php'); $eid = validInt($_REQUEST['eid']); $Event = new ZM\Event($eid); -$Monitor = $Event->Monitor(); - -$countSql = 'SELECT COUNT(*) AS FrameCount FROM Frames AS F WHERE 1 '; -$frameSql = 'SELECT *, unix_timestamp(TimeStamp) AS UnixTimeStamp FROM Frames AS F WHERE 1 '; - -// override the sort_field handling in parseSort for frames -if ( empty($_REQUEST['sort_field']) ) - $_REQUEST['sort_field'] = 'FramesTimeStamp'; - -if ( !isset($_REQUEST['sort_asc']) ) - $_REQUEST['sort_asc'] = true; - -if ( !isset($_REQUEST['filter']) ) { - // generate a dummy filter from the eid for pagination - $_REQUEST['filter'] = array('Query' => array('terms' => array())); - $_REQUEST['filter'] = addFilterTerm( - $_REQUEST['filter'], - 0, - array( 'cnj' => 'and', 'attr' => 'FramesEventId', 'op' => '=', 'val' => $eid ) - ); -} - -parseSort(); -$filter = ZM\Filter::parse($_REQUEST['filter']); -$filterQuery = $filter->querystring(); - -if ( $filter->sql() ) { - $countSql .= ' AND ('.$filter->sql().')'; - $frameSql .= ' AND ('.$filter->sql().')'; -} - -$frameSql .= " ORDER BY $sortColumn $sortOrder"; -if ( $sortColumn != 'Id' ) - $frameSql .= ',Id '.$sortOrder; - -if ( isset($_REQUEST['scale']) ) { - $scale = validNum($_REQUEST['scale']); -} else if ( isset($_COOKIE['zmWatchScale'.$Monitor->Id()]) ) { - $scale = validNum($_COOKIE['zmWatchScale'.$Monitor->Id()]); -} else if ( isset($_COOKIE['zmWatchScale']) ) { - $scale = validNum($_COOKIE['zmWatchScale']); -} else { - $scale = max(reScale(SCALE_BASE, $Monitor->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); -} - -$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 1; -$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0; - -$nFrames = dbFetchOne($countSql, 'FrameCount'); - -if ( !empty($limit) && ($nFrames > $limit) ) { - $nFrames = $limit; -} - -$pages = (int)ceil($nFrames/ZM_WEB_EVENTS_PER_PAGE); - -if ( !empty($page) ) { - if ( $page <= 0 ) - $page = 1; - else if ( $pages and ( $page > $pages ) ) - $page = $pages; - - $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); - if ( empty($limit) ) { - $limitAmount = ZM_WEB_EVENTS_PER_PAGE; - } else { - $limitLeft = $limit - $limitStart; - $limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft; - } - $frameSql .= " LIMIT $limitStart, $limitAmount"; -} else if ( !empty($limit) ) { - $frameSql .= ' LIMIT 0, '.$limit; -} - -$maxShortcuts = 5; -$totalQuery = $sortQuery.'&eid='.$eid.$limitQuery.$filterQuery; -$pagination = getPagination($pages, $page, $maxShortcuts, $totalQuery); - -$frames = dbFetchAll($frameSql); - -$focusWindow = true; xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id()); ?> @@ -126,6 +45,8 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id()); Id()); data-detail-formatter="detailFormatter" data-show-toggle="true" data-show-jump-to="true" + data-show-refresh="true" class="table-sm table-borderless"> @@ -152,58 +74,16 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id()); - - - - - + + + + - - - - - - - - - -Id(); - $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); - $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; - $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; - $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$frame['EventId']. '_' .$frame['FrameId']. '.jpg'; - $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); - $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); - $frame_src = '?view=frame&eid=' .$Event->Id(). '&fid=' .$frame['FrameId']; - - echo ''.PHP_EOL; - } -?> - - - - - - - + +
diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js index ff407589b..c569d8447 100644 --- a/web/skins/classic/views/js/frames.js +++ b/web/skins/classic/views/js/frames.js @@ -46,7 +46,7 @@ function initThumbAnimation() { } function processClicks(event, field, value, row, $element) { - if ( field == 'FrameScore' ) { + if ( field == 'Score' ) { window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FrameId); } else { window.location.assign('?view=frame&eid='+row.EventId+'&fid='+row.FrameId); From 45fbdfa0502768cea450db6901e330dbd0f5ca96 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 16:01:34 -0600 Subject: [PATCH 022/138] rough in support for frames view search function --- web/includes/FilterTerm.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 6b41a2e1d..3675a502f 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -237,6 +237,9 @@ class FilterTerm { case 'StartDateTime': $sql .= 'E.StartDateTime'; break; + case 'FramesId': + $sql .= 'Id'; + break; case 'FramesEventId': $sql .= 'F.EventId'; break; @@ -419,6 +422,12 @@ class FilterTerm { public static function is_valid_attr($attr) { $attrs = array( + 'Score', + 'Delta', + 'TimeStamp', + 'Type', + 'FrameId', + 'EventId', 'ExistsInFileSystem', 'Emailed', 'DiskSpace', @@ -434,6 +443,7 @@ class FilterTerm { 'Time', 'Weekday', 'StartDateTime', + 'FramesId', 'FramesEventId', 'StartDate', 'StartTime', From 416d68a9718764dc09d8e4fa5f546632bf94df9b Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 16:49:22 -0600 Subject: [PATCH 023/138] rough in support for frames view search function --- web/ajax/frames.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 440c9769e..6438b2b92 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -126,7 +126,7 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) require_once('includes/Filter.php'); if ( count($advsearch) or $search != '' ) { $search_filter = new ZM\Filter(); - $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$frame_ids)); + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'FramesId', 'op'=>'IN', 'val'=>$frame_ids)); // There are two search bars in the log view, normal and advanced // Making an exuctive decision to ignore the normal search, when advanced search is in use From 0ffb5e153c23642282d8aae0772d01b8fab8477c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 17:33:18 -0600 Subject: [PATCH 024/138] modify FilterTerm to support Frames view searches --- web/ajax/frames.php | 638 ++++++++++++++++++++++++++---------- web/includes/FilterTerm.php | 16 + 2 files changed, 481 insertions(+), 173 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 440c9769e..42f939128 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -1,189 +1,481 @@ "search text" pairs -// Bootstrap table sends json_ecoded array, which we must decode -$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); - -// Sort specifies the name of the column to sort on -$sort = 'FrameId'; -if ( isset($_REQUEST['sort']) ) { - $sort = $_REQUEST['sort']; -} - -// Offset specifies the starting row to return, used for pagination -$offset = 0; -if ( isset($_REQUEST['offset']) ) { - if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { - ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); - } else { - $offset = $_REQUEST['offset']; +function getFilterQueryConjunctionTypes() { + if ( !isset($validConjunctionTypes ) ) { + $validConjunctionTypes = array( + 'and' => translate('ConjAnd'), + 'or' => translate('ConjOr') + ); } + return $validConjunctionTypes; } -// Order specifies the sort direction, either asc or desc -$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; -// Limit specifies the number of rows to return -$limit = 0; -if ( isset($_REQUEST['limit']) ) { - if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { - ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); - } else { - $limit = $_REQUEST['limit']; - } -} +class FilterTerm { + public $filter; + public $index; + public $attr; + public $op; + public $val; + public $values; + public $cnj; + public $obr; + public $cbr; -// -// MAIN LOOP -// -// Only one supported task at the moment -switch ( $task ) { - case 'query' : - $data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit); - break; - default : - ZM\Fatal("Unrecognised task '$task'"); -} // end switch task + public function __construct($filter = null, $term = NULL, $index=0) { + $this->filter = $filter; + $validConjunctionTypes = getFilterQueryConjunctionTypes(); -ajaxResponse($data); - -// -// FUNCTION DEFINITIONS -// - -function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { - - $data = array( - 'total' => 0, - 'totalNotFiltered' => 0, - 'rows' => array(), - 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) - ); - - // The names of the dB columns in the events table we are interested in - $columns = array('EventId', 'FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); - - if ( !in_array($sort, $columns) ) { - ZM\Error('Invalid sort field: ' . $sort); - $sort = 'FrameId'; - } - - $Event = new ZM\Event($eid); - $Monitor = $Event->Monitor(); - $values = array(); - $likes = array(); - $where = 'WHERE EventId = '.$eid; - - $sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order; - - //ZM\Debug('Calling the following sql query: ' .$sql); - - $unfiltered_rows = array(); - $frame_ids = array(); - require_once('includes/Frame.php'); - foreach ( dbFetchAll($sql, NULL, $values) as $row ) { - $frame = new ZM\Frame($row); - $frame_ids[] = $frame->Id(); - $unfiltered_rows[] = $row; - } - - ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.'); - - $filtered_rows = null; - require_once('includes/Filter.php'); - if ( count($advsearch) or $search != '' ) { - $search_filter = new ZM\Filter(); - $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$frame_ids)); - - // There are two search bars in the log view, normal and advanced - // Making an exuctive decision to ignore the normal search, when advanced search is in use - // Alternatively we could try to do both - if ( count($advsearch) ) { - $terms = array(); - foreach ( $advsearch as $col=>$text ) { - $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); - } # end foreach col in advsearch - $terms[0]['obr'] = 1; - $terms[count($terms)-1]['cbr'] = 1; - $search_filter->addTerms($terms); - } else if ( $search != '' ) { - $search = '%' .$search. '%'; - $terms = array(); - foreach ( $columns as $col ) { - $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + $this->index = $index; + $this->attr = $term['attr']; + $this->op = $term['op']; + $this->val = $term['val']; + if ( isset($term['cnj']) ) { + if ( array_key_exists($term['cnj'], $validConjunctionTypes) ) { + $this->cnj = $term['cnj']; + } else { + Warning('Invalid cnj ' . $term['cnj'].' in '.print_r($term, true)); } - $terms[0]['obr'] = 1; - $terms[0]['cnj'] = 'and'; - $terms[count($terms)-1]['cbr'] = 1; - $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); - } # end if search - - $sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; - $filtered_rows = dbFetchAll($sql); - ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.'); - } else { - $filtered_rows = $unfiltered_rows; - } # end if search_filter->terms() > 1 - - $returned_rows = array(); - foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { - if ( ZM_WEB_LIST_THUMBS ) { - $base_img_src = '?view=image&fid=' .$row['Id']; - $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); - $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; - $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; - $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; - $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); - $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); - $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; - - $row['Thumbnail'] = ''; } - $returned_rows[] = $row; - } # end foreach row matching search - $data['rows'] = $returned_rows; + if ( isset($term['obr']) ) { + if ( (string)(int)$term['obr'] == $term['obr'] ) { + $this->obr = $term['obr']; + } else { + Warning('Invalid obr ' . $term['obr'] . ' in ' . print_r($term, true)); + } + } + if ( isset($term['cbr']) ) { + if ( (string)(int)$term['cbr'] == $term['cbr'] ) { + $this->cbr = $term['cbr']; + } else { + Warning('Invalid cbr ' . $term['cbr'] . ' in ' . print_r($term, true)); + } + } + } # end function __construct - # totalNotFiltered must equal total, except when either search bar has been used - $data['totalNotFiltered'] = count($unfiltered_rows); - if ( $search != '' || count($advsearch) ) { - $data['total'] = count($filtered_rows); - } else { - $data['total'] = $data['totalNotFiltered']; + # Returns an array of values. AS term->value can be a list, we will break it apart, remove quotes etc + public function sql_values() { + $values = array(); + if ( !isset($this->val) ) { + Logger::Warning('No value in term'.$this->attr); + return $values; + } + + $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); + foreach ( $vals as $value ) { + + switch ( $this->attr ) { + + case 'AlarmedZoneId': + $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; + break; + case 'ExistsInFileSystem': + $value = ''; + break; + case 'DiskPercent': + $value = ''; + break; + case 'MonitorName': + case 'MonitorName': + case 'Name': + case 'Cause': + case 'Notes': + if ( strstr($this->op, 'LIKE') and ! strstr($this->val, '%' ) ) { + $value = '%'.$value.'%'; + } + $value = dbEscape($value); + break; + case 'MonitorServerId': + case 'FilterServerId': + case 'StorageServerId': + case 'ServerId': + if ( $value == 'ZM_SERVER_ID' ) { + $value = ZM_SERVER_ID; + } else if ( $value == 'NULL' ) { + + } else { + $value = dbEscape($value); + } + break; + case 'StorageId': + if ( $value != 'NULL' ) { + $value = dbEscape($value); + } + break; + case 'DateTime': + case 'StartDateTime': + case 'EndDateTime': + if ( $value != 'NULL' ) + $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; + break; + case 'Date': + case 'StartDate': + case 'EndDate': + if ( $value == 'CURDATE()' or $value == 'NOW()' ) { + $value = 'to_days('.$value.')'; + } else if ( $value != 'NULL' ) { + $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; + } + break; + case 'Time': + case 'StartTime': + case 'EndTime': + if ( $value != 'NULL' ) + $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; + break; + default : + if ( $value == 'Odd' ) { + $value = 1; + } else if ( $value == 'Even' ) { + $value = 0; + } else if ( $value != 'NULL' ) + $value = dbEscape($value); + break; + } + $values[] = $value; + } // end foreach value + return $values; + } # end function sql_values + + public function sql_operator() { + switch ( $this->attr ) { + case 'AlarmZoneId': + return ' EXISTS '; + case 'ExistsInFileSystem': + case 'DiskPercent': + return ''; + } + + + switch ( $this->op ) { + case '=' : + case '!=' : + case '>=' : + case '>' : + case '<' : + case '<=' : + case 'LIKE' : + case 'NOT LIKE': + return ' '.$this->op.' '; + case '=~' : + return ' regexp '; + case '!~' : + return ' not regexp '; + case '=[]' : + case 'IN' : + return ' IN '; + case '![]' : + return ' NOT IN '; + case 'EXISTS' : + return ' EXISTS '; + case 'IS' : + # Odd will be replaced with 1 + # Even will be replaced with 0 + if ( $this->val == 'Odd' or $this->val == 'Even' ) { + return ' % 2 = '; + } else { + return ' IS '; + } + case 'IS NOT' : + if ( $this->val == 'Odd' or $this->val == 'Even' ) { + return ' % 2 = '; + } + return ' IS NOT '; + default: + Warning('Invalid operator in filter: ' . print_r($this->op, true)); + } // end switch op + } # end public function sql_operator + + /* Some terms don't have related SQL */ + public function sql() { + + $sql = ''; + if ( isset($this->cnj) ) { + $sql .= ' '.$this->cnj.' '; + } + if ( isset($this->obr) ) { + $sql .= ' '.str_repeat('(', $this->obr).' '; + } + + switch ( $this->attr ) { + case 'ExistsInFileSystem': + case 'DiskPercent': + $sql .= 'TRUE /*'.$this->attr.'*/'; + break; + case 'MonitorName': + $sql .= 'M.Name'; + break; + case 'ServerId': + case 'MonitorServerId': + $sql .= 'M.ServerId'; + break; + case 'StorageServerId': + $sql .= 'S.ServerId'; + break; + case 'FilterServerId': + $sql .= ZM_SERVER_ID; + break; + # Unspecified start or end, so assume start, this is to support legacy filters + case 'DateTime': + $sql .= 'E.StartDateTime'; + break; + case 'Date': + $sql .= 'to_days(E.StartDateTime)'; + break; + case 'Time': + $sql .= 'extract(hour_second FROM E.StartDateTime)'; + break; + case 'Weekday': + $sql .= 'weekday(E.StartDateTime)'; + break; + # Starting Time + case 'StartDateTime': + $sql .= 'E.StartDateTime'; + break; + case 'FrameId': + $sql .= 'Id'; + break; + case 'Type': + case 'TimeStamp': + case 'Delta': + case 'Score': + $sql .= $this->attr; + break; + case 'FramesEventId': + $sql .= 'F.EventId'; + break; + case 'StartDate': + $sql .= 'to_days(E.StartDateTime)'; + break; + case 'StartTime': + $sql .= 'extract(hour_second FROM E.StartDateTime)'; + break; + case 'StartWeekday': + $sql .= 'weekday(E.StartDateTime)'; + break; + # Ending Time + case 'EndDateTime': + $sql .= 'E.EndDateTime'; + break; + case 'EndDate': + $sql .= 'to_days(E.EndDateTime)'; + break; + case 'EndTime': + $sql .= 'extract(hour_second FROM E.EndDateTime)'; + break; + case 'EndWeekday': + $sql .= 'weekday(E.EndDateTime)'; + break; + case 'Emailed': + case 'Id': + case 'Name': + case 'DiskSpace': + case 'MonitorId': + case 'StorageId': + case 'SecondaryStorageId': + case 'Length': + case 'Frames': + case 'AlarmFrames': + case 'TotScore': + case 'AvgScore': + case 'MaxScore': + case 'Cause': + case 'Notes': + case 'StateId': + case 'Archived': + $sql .= 'E.'.$this->attr; + } + $sql .= $this->sql_operator(); + $values = $this->sql_values(); + if ( (count($values) > 1) or ($this->op == 'IN') or ($this->op == 'NOT IN') ) { + $sql .= '('.join(',', $values).')'; + } else { + $sql .= $values[0]; + } + + if ( isset($this->cbr) ) { + $sql .= ' '.str_repeat(')', $this->cbr).' '; + } + return $sql; + } # end public function sql + + public function querystring($objectname='filter', $querySep='&') { + # We don't validate the term parameters here + $query = ''; + if ( $this->cnj ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; + if ( $this->obr ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][obr]').'='.$this->obr; + + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); + if ( $this->cbr ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; + return $query; + } # end public function querystring + + public function hidden_fields() { + $html =''; + if ( $this->cnj ) + $html .= ''.PHP_EOL; + + if ( $this->obr ) + $html .= ''.PHP_EOL; + + # attr should have been already validation, so shouldn't need htmlspecialchars + $html .= ''.PHP_EOL; + $html .= ''.PHP_EOL; + $html .= ''.PHP_EOL; + if ( $this->cbr ) + $html .= ''.PHP_EOL; + + return $html; + } # end public function hiddens_fields + + public function test($event=null) { + if ( !isset($event) ) { + # Is a Pre Condition + Debug("Testing " . $this->attr); + if ( $this->attr == 'DiskPercent' ) { + # The logic on this is really ugly. We are going to treat it as an OR + foreach ( $this->filter->get_StorageAreas() as $storage ) { + $string_to_eval = 'return $storage->disk_usage_percent() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } # end foreach Storage Area + } else if ( $this->attr == 'SystemLoad' ) { + $string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evaled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else { + Error('testing unsupported pre term ' . $this->attr); + } + } else { + # Is a Post Condition + if ( $this->attr == 'ExistsInFileSystem' ) { + if ( + ($this->op == 'IS' and $this->val == 'True') + or + ($this->op == 'IS NOT' and $this->val == 'False') + ) { + return file_exists($event->Path()); + } else { + return !file_exists($event->Path()); + } + } else if ( $this->attr == 'DiskPercent' ) { + $string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else if ( $this->attr == 'DiskBlocks' ) { + $string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else { + Error('testing unsupported post term ' . $this->attr); + } + } + return false; + } + + public function is_pre_sql() { + if ( $this->attr == 'DiskPercent' ) + return true; + if ( $this->attr == 'DiskBlocks' ) + return true; + return false; } - return $data; -} + public function is_post_sql() { + if ( $this->attr == 'ExistsInFileSystem' ) { + return true; + } + return false; + } + + public static function is_valid_attr($attr) { + $attrs = array( + 'Score', + 'Delta', + 'TimeStamp', + 'Type', + 'FrameId', + 'EventId', + 'ExistsInFileSystem', + 'Emailed', + 'DiskSpace', + 'DiskPercent', + 'DiskBlocks', + 'MonitorName', + 'ServerId', + 'MonitorServerId', + 'StorageServerId', + 'FilterServerId', + 'DateTime', + 'Date', + 'Time', + 'Weekday', + 'StartDateTime', + 'FramesId', + 'FramesEventId', + 'StartDate', + 'StartTime', + 'StartWeekday', + 'EndDateTime', + 'EndDate', + 'EndTime', + 'EndWeekday', + 'Id', + 'Name', + 'MonitorId', + 'StorageId', + 'SecondaryStorageId', + 'Length', + 'Frames', + 'AlarmFrames', + 'TotScore', + 'AvgScore', + 'MaxScore', + 'Cause', + 'Notes', + 'StateId', + 'Archived' + ); + return in_array($attr, $attrs); + } +} # end class FilterTerm + +?> diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 6b41a2e1d..42f939128 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -237,6 +237,15 @@ class FilterTerm { case 'StartDateTime': $sql .= 'E.StartDateTime'; break; + case 'FrameId': + $sql .= 'Id'; + break; + case 'Type': + case 'TimeStamp': + case 'Delta': + case 'Score': + $sql .= $this->attr; + break; case 'FramesEventId': $sql .= 'F.EventId'; break; @@ -419,6 +428,12 @@ class FilterTerm { public static function is_valid_attr($attr) { $attrs = array( + 'Score', + 'Delta', + 'TimeStamp', + 'Type', + 'FrameId', + 'EventId', 'ExistsInFileSystem', 'Emailed', 'DiskSpace', @@ -434,6 +449,7 @@ class FilterTerm { 'Time', 'Weekday', 'StartDateTime', + 'FramesId', 'FramesEventId', 'StartDate', 'StartTime', From a2b5271835654c559850214b12ac0c9f6892de6f Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 17:34:59 -0600 Subject: [PATCH 025/138] fix copy/paste typo --- web/ajax/frames.php | 638 ++++++++++++-------------------------------- 1 file changed, 173 insertions(+), 465 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 42f939128..e85988dbc 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -1,481 +1,189 @@ translate('ConjAnd'), - 'or' => translate('ConjOr') - ); - } - return $validConjunctionTypes; +if ( !canView('Events') ) $message = 'Insufficient permissions to view frames for user '.$user['Username']; + +// task must be set +if ( empty($_REQUEST['task']) ) { + $message = 'This request requires a task to be set'; +// query is the only supported task at the moment +} else if ( $_REQUEST['task'] != 'query' ) { + $message = 'Unrecognised task '.$_REQUEST['task']; + } else { + $task = $_REQUEST['task']; } +if ( empty($_REQUEST['eid']) ) { + $message = 'No event id supplied'; +} else { + $eid = validInt($_REQUEST['eid']); +} -class FilterTerm { - public $filter; - public $index; - public $attr; - public $op; - public $val; - public $values; - public $cnj; - public $obr; - public $cbr; +if ( $message ) { + ajaxError($message); + return; +} +// Search contains a user entered string to search on +$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : ''; - public function __construct($filter = null, $term = NULL, $index=0) { - $this->filter = $filter; - $validConjunctionTypes = getFilterQueryConjunctionTypes(); +// Advanced search contains an array of "column name" => "search text" pairs +// Bootstrap table sends json_ecoded array, which we must decode +$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); - $this->index = $index; - $this->attr = $term['attr']; - $this->op = $term['op']; - $this->val = $term['val']; - if ( isset($term['cnj']) ) { - if ( array_key_exists($term['cnj'], $validConjunctionTypes) ) { - $this->cnj = $term['cnj']; - } else { - Warning('Invalid cnj ' . $term['cnj'].' in '.print_r($term, true)); - } - } +// Sort specifies the name of the column to sort on +$sort = 'FrameId'; +if ( isset($_REQUEST['sort']) ) { + $sort = $_REQUEST['sort']; +} - if ( isset($term['obr']) ) { - if ( (string)(int)$term['obr'] == $term['obr'] ) { - $this->obr = $term['obr']; - } else { - Warning('Invalid obr ' . $term['obr'] . ' in ' . print_r($term, true)); - } - } - if ( isset($term['cbr']) ) { - if ( (string)(int)$term['cbr'] == $term['cbr'] ) { - $this->cbr = $term['cbr']; - } else { - Warning('Invalid cbr ' . $term['cbr'] . ' in ' . print_r($term, true)); - } - } - } # end function __construct - - # Returns an array of values. AS term->value can be a list, we will break it apart, remove quotes etc - public function sql_values() { - $values = array(); - if ( !isset($this->val) ) { - Logger::Warning('No value in term'.$this->attr); - return $values; - } - - $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); - foreach ( $vals as $value ) { - - switch ( $this->attr ) { - - case 'AlarmedZoneId': - $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; - break; - case 'ExistsInFileSystem': - $value = ''; - break; - case 'DiskPercent': - $value = ''; - break; - case 'MonitorName': - case 'MonitorName': - case 'Name': - case 'Cause': - case 'Notes': - if ( strstr($this->op, 'LIKE') and ! strstr($this->val, '%' ) ) { - $value = '%'.$value.'%'; - } - $value = dbEscape($value); - break; - case 'MonitorServerId': - case 'FilterServerId': - case 'StorageServerId': - case 'ServerId': - if ( $value == 'ZM_SERVER_ID' ) { - $value = ZM_SERVER_ID; - } else if ( $value == 'NULL' ) { - - } else { - $value = dbEscape($value); - } - break; - case 'StorageId': - if ( $value != 'NULL' ) { - $value = dbEscape($value); - } - break; - case 'DateTime': - case 'StartDateTime': - case 'EndDateTime': - if ( $value != 'NULL' ) - $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; - break; - case 'Date': - case 'StartDate': - case 'EndDate': - if ( $value == 'CURDATE()' or $value == 'NOW()' ) { - $value = 'to_days('.$value.')'; - } else if ( $value != 'NULL' ) { - $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; - } - break; - case 'Time': - case 'StartTime': - case 'EndTime': - if ( $value != 'NULL' ) - $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; - break; - default : - if ( $value == 'Odd' ) { - $value = 1; - } else if ( $value == 'Even' ) { - $value = 0; - } else if ( $value != 'NULL' ) - $value = dbEscape($value); - break; - } - $values[] = $value; - } // end foreach value - return $values; - } # end function sql_values - - public function sql_operator() { - switch ( $this->attr ) { - case 'AlarmZoneId': - return ' EXISTS '; - case 'ExistsInFileSystem': - case 'DiskPercent': - return ''; - } - - - switch ( $this->op ) { - case '=' : - case '!=' : - case '>=' : - case '>' : - case '<' : - case '<=' : - case 'LIKE' : - case 'NOT LIKE': - return ' '.$this->op.' '; - case '=~' : - return ' regexp '; - case '!~' : - return ' not regexp '; - case '=[]' : - case 'IN' : - return ' IN '; - case '![]' : - return ' NOT IN '; - case 'EXISTS' : - return ' EXISTS '; - case 'IS' : - # Odd will be replaced with 1 - # Even will be replaced with 0 - if ( $this->val == 'Odd' or $this->val == 'Even' ) { - return ' % 2 = '; - } else { - return ' IS '; - } - case 'IS NOT' : - if ( $this->val == 'Odd' or $this->val == 'Even' ) { - return ' % 2 = '; - } - return ' IS NOT '; - default: - Warning('Invalid operator in filter: ' . print_r($this->op, true)); - } // end switch op - } # end public function sql_operator - - /* Some terms don't have related SQL */ - public function sql() { - - $sql = ''; - if ( isset($this->cnj) ) { - $sql .= ' '.$this->cnj.' '; - } - if ( isset($this->obr) ) { - $sql .= ' '.str_repeat('(', $this->obr).' '; - } - - switch ( $this->attr ) { - case 'ExistsInFileSystem': - case 'DiskPercent': - $sql .= 'TRUE /*'.$this->attr.'*/'; - break; - case 'MonitorName': - $sql .= 'M.Name'; - break; - case 'ServerId': - case 'MonitorServerId': - $sql .= 'M.ServerId'; - break; - case 'StorageServerId': - $sql .= 'S.ServerId'; - break; - case 'FilterServerId': - $sql .= ZM_SERVER_ID; - break; - # Unspecified start or end, so assume start, this is to support legacy filters - case 'DateTime': - $sql .= 'E.StartDateTime'; - break; - case 'Date': - $sql .= 'to_days(E.StartDateTime)'; - break; - case 'Time': - $sql .= 'extract(hour_second FROM E.StartDateTime)'; - break; - case 'Weekday': - $sql .= 'weekday(E.StartDateTime)'; - break; - # Starting Time - case 'StartDateTime': - $sql .= 'E.StartDateTime'; - break; - case 'FrameId': - $sql .= 'Id'; - break; - case 'Type': - case 'TimeStamp': - case 'Delta': - case 'Score': - $sql .= $this->attr; - break; - case 'FramesEventId': - $sql .= 'F.EventId'; - break; - case 'StartDate': - $sql .= 'to_days(E.StartDateTime)'; - break; - case 'StartTime': - $sql .= 'extract(hour_second FROM E.StartDateTime)'; - break; - case 'StartWeekday': - $sql .= 'weekday(E.StartDateTime)'; - break; - # Ending Time - case 'EndDateTime': - $sql .= 'E.EndDateTime'; - break; - case 'EndDate': - $sql .= 'to_days(E.EndDateTime)'; - break; - case 'EndTime': - $sql .= 'extract(hour_second FROM E.EndDateTime)'; - break; - case 'EndWeekday': - $sql .= 'weekday(E.EndDateTime)'; - break; - case 'Emailed': - case 'Id': - case 'Name': - case 'DiskSpace': - case 'MonitorId': - case 'StorageId': - case 'SecondaryStorageId': - case 'Length': - case 'Frames': - case 'AlarmFrames': - case 'TotScore': - case 'AvgScore': - case 'MaxScore': - case 'Cause': - case 'Notes': - case 'StateId': - case 'Archived': - $sql .= 'E.'.$this->attr; - } - $sql .= $this->sql_operator(); - $values = $this->sql_values(); - if ( (count($values) > 1) or ($this->op == 'IN') or ($this->op == 'NOT IN') ) { - $sql .= '('.join(',', $values).')'; - } else { - $sql .= $values[0]; - } - - if ( isset($this->cbr) ) { - $sql .= ' '.str_repeat(')', $this->cbr).' '; - } - return $sql; - } # end public function sql - - public function querystring($objectname='filter', $querySep='&') { - # We don't validate the term parameters here - $query = ''; - if ( $this->cnj ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; - if ( $this->obr ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][obr]').'='.$this->obr; - - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); - if ( $this->cbr ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; - return $query; - } # end public function querystring - - public function hidden_fields() { - $html =''; - if ( $this->cnj ) - $html .= ''.PHP_EOL; - - if ( $this->obr ) - $html .= ''.PHP_EOL; - - # attr should have been already validation, so shouldn't need htmlspecialchars - $html .= ''.PHP_EOL; - $html .= ''.PHP_EOL; - $html .= ''.PHP_EOL; - if ( $this->cbr ) - $html .= ''.PHP_EOL; - - return $html; - } # end public function hiddens_fields - - public function test($event=null) { - if ( !isset($event) ) { - # Is a Pre Condition - Debug("Testing " . $this->attr); - if ( $this->attr == 'DiskPercent' ) { - # The logic on this is really ugly. We are going to treat it as an OR - foreach ( $this->filter->get_StorageAreas() as $storage ) { - $string_to_eval = 'return $storage->disk_usage_percent() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } # end foreach Storage Area - } else if ( $this->attr == 'SystemLoad' ) { - $string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evaled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else { - Error('testing unsupported pre term ' . $this->attr); - } - } else { - # Is a Post Condition - if ( $this->attr == 'ExistsInFileSystem' ) { - if ( - ($this->op == 'IS' and $this->val == 'True') - or - ($this->op == 'IS NOT' and $this->val == 'False') - ) { - return file_exists($event->Path()); - } else { - return !file_exists($event->Path()); - } - } else if ( $this->attr == 'DiskPercent' ) { - $string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else if ( $this->attr == 'DiskBlocks' ) { - $string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else { - Error('testing unsupported post term ' . $this->attr); - } - } - return false; +// Offset specifies the starting row to return, used for pagination +$offset = 0; +if ( isset($_REQUEST['offset']) ) { + if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { + ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); + } else { + $offset = $_REQUEST['offset']; } - - public function is_pre_sql() { - if ( $this->attr == 'DiskPercent' ) - return true; - if ( $this->attr == 'DiskBlocks' ) - return true; - return false; +} + +// Order specifies the sort direction, either asc or desc +$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; + +// Limit specifies the number of rows to return +$limit = 0; +if ( isset($_REQUEST['limit']) ) { + if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { + ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); + } else { + $limit = $_REQUEST['limit']; + } +} + +// +// MAIN LOOP +// + +// Only one supported task at the moment +switch ( $task ) { + case 'query' : + $data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit); + break; + default : + ZM\Fatal("Unrecognised task '$task'"); +} // end switch task + +ajaxResponse($data); + +// +// FUNCTION DEFINITIONS +// + +function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { + + $data = array( + 'total' => 0, + 'totalNotFiltered' => 0, + 'rows' => array(), + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) + ); + + // The names of the dB columns in the events table we are interested in + $columns = array('FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); + + if ( !in_array($sort, $columns) ) { + ZM\Error('Invalid sort field: ' . $sort); + $sort = 'FrameId'; } - public function is_post_sql() { - if ( $this->attr == 'ExistsInFileSystem' ) { - return true; + $Event = new ZM\Event($eid); + $Monitor = $Event->Monitor(); + $values = array(); + $likes = array(); + $where = 'WHERE EventId = '.$eid; + + $sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order; + + //ZM\Debug('Calling the following sql query: ' .$sql); + + $unfiltered_rows = array(); + $frame_ids = array(); + require_once('includes/Frame.php'); + foreach ( dbFetchAll($sql, NULL, $values) as $row ) { + $frame = new ZM\Frame($row); + $frame_ids[] = $frame->Id(); + $unfiltered_rows[] = $row; + } + + ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.'); + + $filtered_rows = null; + require_once('includes/Filter.php'); + if ( count($advsearch) or $search != '' ) { + $search_filter = new ZM\Filter(); + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'FrameId', 'op'=>'IN', 'val'=>$frame_ids)); + + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + if ( count($advsearch) ) { + $terms = array(); + foreach ( $advsearch as $col=>$text ) { + $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); + } # end foreach col in advsearch + $terms[0]['obr'] = 1; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter->addTerms($terms); + } else if ( $search != '' ) { + $search = '%' .$search. '%'; + $terms = array(); + foreach ( $columns as $col ) { + $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + } + $terms[0]['obr'] = 1; + $terms[0]['cnj'] = 'and'; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); + } # end if search + + $sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; + $filtered_rows = dbFetchAll($sql); + ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.'); + } else { + $filtered_rows = $unfiltered_rows; + } # end if search_filter->terms() > 1 + + $returned_rows = array(); + foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { + if ( ZM_WEB_LIST_THUMBS ) { + $base_img_src = '?view=image&fid=' .$row['Id']; + $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); + $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; + $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; + $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; + $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); + $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); + $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; + + $row['Thumbnail'] = ''; } - return false; + $returned_rows[] = $row; + } # end foreach row matching search + + $data['rows'] = $returned_rows; + + # totalNotFiltered must equal total, except when either search bar has been used + $data['totalNotFiltered'] = count($unfiltered_rows); + if ( $search != '' || count($advsearch) ) { + $data['total'] = count($filtered_rows); + } else { + $data['total'] = $data['totalNotFiltered']; } - public static function is_valid_attr($attr) { - $attrs = array( - 'Score', - 'Delta', - 'TimeStamp', - 'Type', - 'FrameId', - 'EventId', - 'ExistsInFileSystem', - 'Emailed', - 'DiskSpace', - 'DiskPercent', - 'DiskBlocks', - 'MonitorName', - 'ServerId', - 'MonitorServerId', - 'StorageServerId', - 'FilterServerId', - 'DateTime', - 'Date', - 'Time', - 'Weekday', - 'StartDateTime', - 'FramesId', - 'FramesEventId', - 'StartDate', - 'StartTime', - 'StartWeekday', - 'EndDateTime', - 'EndDate', - 'EndTime', - 'EndWeekday', - 'Id', - 'Name', - 'MonitorId', - 'StorageId', - 'SecondaryStorageId', - 'Length', - 'Frames', - 'AlarmFrames', - 'TotScore', - 'AvgScore', - 'MaxScore', - 'Cause', - 'Notes', - 'StateId', - 'Archived' - ); - return in_array($attr, $attrs); - } -} # end class FilterTerm - -?> + return $data; +} From a47b72af49a2a1d8299b5f9f5b082c4a96b7de40 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 16 Nov 2020 11:31:36 -0500 Subject: [PATCH 026/138] Fixes recovering frames from jpegs. Use Time::HiRes stat to get microseconds. --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 32 +++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 2664830dd..49c2e47f3 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -41,7 +41,7 @@ require Number::Bytes::Human; require Date::Parse; require POSIX; use Date::Format qw(time2str); -use Time::HiRes qw(gettimeofday tv_interval); +use Time::HiRes qw(gettimeofday tv_interval stat); #our @ISA = qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object); @@ -791,44 +791,50 @@ sub recover_timestamps { return; } my @contents = readdir(DIR); - Debug('Have ' . @contents . " files in $path"); + Debug('Have ' . @contents . ' files in '.$path); closedir(DIR); - my @mp4_files = grep( /^\d+\-video\.mp4$/, @contents); + my @mp4_files = grep(/^\d+\-video\.mp4$/, @contents); if ( @mp4_files ) { $$Event{DefaultVideo} = $mp4_files[0]; } - my @analyse_jpgs = grep( /^\d+\-analyse\.jpg$/, @contents); + my @analyse_jpgs = grep(/^\d+\-analyse\.jpg$/, @contents); if ( @analyse_jpgs ) { - $$Event{Save_JPEGs} |= 2; + $$Event{SaveJPEGs} |= 2; } - my @capture_jpgs = grep( /^\d+\-capture\.jpg$/, @contents); + my @capture_jpgs = grep(/^\d+\-capture\.jpg$/, @contents); if ( @capture_jpgs ) { $$Event{Frames} = scalar @capture_jpgs; - $$Event{Save_JPEGs} |= 1; + $$Event{SaveJPEGs} |= 1; # can get start and end times from stat'ing first and last jpg @capture_jpgs = sort { $a cmp $b } @capture_jpgs; my $first_file = "$path/$capture_jpgs[0]"; ( $first_file ) = $first_file =~ /^(.*)$/; my $first_timestamp = (stat($first_file))[9]; - my $last_file = "$path/$capture_jpgs[@capture_jpgs-1]"; + my $last_file = $path.'/'.$capture_jpgs[@capture_jpgs-1]; ( $last_file ) = $last_file =~ /^(.*)$/; my $last_timestamp = (stat($last_file))[9]; my $duration = $last_timestamp - $first_timestamp; $Event->Length($duration); $Event->StartDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); + if ( $Event->Scheme() eq 'Deep' and $Event->RelativePath(undef) and ($path ne $Event->Path(undef)) ) { + my ( $year, $month, $day, $hour, $minute, $second ) = + ($path =~ /(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})$/); + Error("Updating starttime to $path $year/$month/$day $hour:$minute:$second"); + $Event->StartDateTime(sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', 2000+$year, $month, $day, $hour, $minute, $second)); + } $Event->EndDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) ); Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartDateTime} to $$Event{EndDateTime}"); $ZoneMinder::Database::dbh->begin_work(); foreach my $jpg ( @capture_jpgs ) { my ( $id ) = $jpg =~ /^(\d+)\-capture\.jpg$/; - if ( ! ZoneMinder::Frame->find_one( EventId=>$$Event{Id}, FrameId=>$id ) ) { - my $file = "$path/$jpg"; + if ( ! ZoneMinder::Frame->find_one(EventId=>$$Event{Id}, FrameId=>$id) ) { + my $file = $path.'/'.$jpg; ( $file ) = $file =~ /^(.*)$/; my $timestamp = (stat($file))[9]; my $Frame = new ZoneMinder::Frame(); @@ -839,11 +845,11 @@ sub recover_timestamps { Type=>'Normal', Score=>0, }); - } - } + } # end if Frame not found + } # end foreach capture jpg $ZoneMinder::Database::dbh->commit(); } elsif ( @mp4_files ) { - my $file = "$path/$mp4_files[0]"; + my $file = $path.'/'.$mp4_files[0]; ( $file ) = $file =~ /^(.*)$/; my $first_timestamp = (stat($file))[9]; From 5bf5d58ac125f14f28f22725bbfd951229b91806 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 16 Nov 2020 11:31:36 -0500 Subject: [PATCH 027/138] Fixes recovering frames from jpegs. Use Time::HiRes stat to get microseconds. --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 32 +++++++++++++--------- web/api/app/Plugin/Crud | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 589e4bbba..f2c7ea210 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -41,7 +41,7 @@ require Number::Bytes::Human; require Date::Parse; require POSIX; use Date::Format qw(time2str); -use Time::HiRes qw(gettimeofday tv_interval); +use Time::HiRes qw(gettimeofday tv_interval stat); #our @ISA = qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object); @@ -765,44 +765,50 @@ sub recover_timestamps { return; } my @contents = readdir(DIR); - Debug('Have ' . @contents . " files in $path"); + Debug('Have ' . @contents . ' files in '.$path); closedir(DIR); - my @mp4_files = grep( /^\d+\-video\.mp4$/, @contents); + my @mp4_files = grep(/^\d+\-video\.mp4$/, @contents); if ( @mp4_files ) { $$Event{DefaultVideo} = $mp4_files[0]; } - my @analyse_jpgs = grep( /^\d+\-analyse\.jpg$/, @contents); + my @analyse_jpgs = grep(/^\d+\-analyse\.jpg$/, @contents); if ( @analyse_jpgs ) { - $$Event{Save_JPEGs} |= 2; + $$Event{SaveJPEGs} |= 2; } - my @capture_jpgs = grep( /^\d+\-capture\.jpg$/, @contents); + my @capture_jpgs = grep(/^\d+\-capture\.jpg$/, @contents); if ( @capture_jpgs ) { $$Event{Frames} = scalar @capture_jpgs; - $$Event{Save_JPEGs} |= 1; + $$Event{SaveJPEGs} |= 1; # can get start and end times from stat'ing first and last jpg @capture_jpgs = sort { $a cmp $b } @capture_jpgs; my $first_file = "$path/$capture_jpgs[0]"; ( $first_file ) = $first_file =~ /^(.*)$/; my $first_timestamp = (stat($first_file))[9]; - my $last_file = "$path/$capture_jpgs[@capture_jpgs-1]"; + my $last_file = $path.'/'.$capture_jpgs[@capture_jpgs-1]; ( $last_file ) = $last_file =~ /^(.*)$/; my $last_timestamp = (stat($last_file))[9]; my $duration = $last_timestamp - $first_timestamp; $Event->Length($duration); $Event->StartTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); + if ( $Event->Scheme() eq 'Deep' and $Event->RelativePath(undef) and ($path ne $Event->Path(undef)) ) { + my ( $year, $month, $day, $hour, $minute, $second ) = + ($path =~ /(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})$/); + Error("Updating starttime to $path $year/$month/$day $hour:$minute:$second"); + $Event->StartTime(sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', 2000+$year, $month, $day, $hour, $minute, $second)); + } $Event->EndTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) ); Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartTime} to $$Event{EndTime}"); $ZoneMinder::Database::dbh->begin_work(); foreach my $jpg ( @capture_jpgs ) { my ( $id ) = $jpg =~ /^(\d+)\-capture\.jpg$/; - if ( ! ZoneMinder::Frame->find_one( EventId=>$$Event{Id}, FrameId=>$id ) ) { - my $file = "$path/$jpg"; + if ( ! ZoneMinder::Frame->find_one(EventId=>$$Event{Id}, FrameId=>$id) ) { + my $file = $path.'/'.$jpg; ( $file ) = $file =~ /^(.*)$/; my $timestamp = (stat($file))[9]; my $Frame = new ZoneMinder::Frame(); @@ -813,11 +819,11 @@ sub recover_timestamps { Type=>'Normal', Score=>0, }); - } - } + } # end if Frame not found + } # end foreach capture jpg $ZoneMinder::Database::dbh->commit(); } elsif ( @mp4_files ) { - my $file = "$path/$mp4_files[0]"; + my $file = $path.'/'.$mp4_files[0]; ( $file ) = $file =~ /^(.*)$/; my $first_timestamp = (stat($file))[9]; diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 0bd63fb46..2ab7e23fb 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef +Subproject commit 2ab7e23fb4f5b31a2f043ba9b9f607a518007f75 From d5295a4464df788f95521bd44876067682e962f3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 17 Nov 2020 11:08:11 -0500 Subject: [PATCH 028/138] Fix segfaults and valgrind complaints. Initialize vncClientData, handle failure to connect freeing our client. --- src/zm_libvnc_camera.cpp | 90 ++++++++++++++++++++++++---------------- src/zm_libvnc_camera.h | 7 +--- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index 315cc19dd..f7a3c05ec 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -40,10 +40,14 @@ void bind_libvnc_symbols() { static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, int h){ VncPrivateData *data = (VncPrivateData *)(*rfbClientGetClientData_f)(rfb, &TAG_0); data->buffer = rfb->frameBuffer; + Debug(1, "GotFrameBufferUpdateallback x:%d y:%d w%d h:%d width: %d, height: %d", + x,y,w,h, rfb->width, rfb->height); } static char* GetPasswordCallback(rfbClient* cl){ - return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); + Debug(1, "Getcredentials: %s", (*rfbClientGetClientData_f)(cl, &TAG_1)); + return strdup( + (const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); } static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ @@ -53,6 +57,7 @@ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ return nullptr; } + Debug(1, "Getcredentials: %s:%s", (*rfbClientGetClientData_f)(cl, &TAG_1), (*rfbClientGetClientData_f)(cl, &TAG_2)); c->userCredential.password = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); c->userCredential.username = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_2)); return c; @@ -87,6 +92,7 @@ VncCamera::VncCamera( p_capture, p_record_audio ), + mRfb(nullptr), mHost(host), mPort(port), mUser(user), @@ -110,52 +116,61 @@ VncCamera::VncCamera( Panic("Unexpected colours: %d", colours); } - if ( capture ) - Initialise(); -} + if ( capture ) { + Debug(3, "Initializing Client"); + bind_libvnc_symbols(); + scale.init(); + } +} VncCamera::~VncCamera() { - if ( capture ) - Terminate(); -} - -void VncCamera::Initialise() { - Debug(2, "Initializing Client"); - bind_libvnc_symbols(); - mRfb = (*rfbGetClient_f)(8, 3, 4); - - (*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData); - (*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str()); - (*rfbClientSetClientData_f)(mRfb, &TAG_2, (void *)mUser.c_str()); - - mRfb->GotFrameBufferUpdate = GotFrameBufferUpdateCallback; - mRfb->GetPassword = GetPasswordCallback; - mRfb->GetCredential = GetCredentialsCallback; - - mRfb->programName = "Zoneminder VNC Monitor"; - mRfb->serverHost = strdup(mHost.c_str()); - mRfb->serverPort = atoi(mPort.c_str()); - scale.init(); -} - -void VncCamera::Terminate() { - if ( mRfb->frameBuffer ) - free(mRfb->frameBuffer); - (*rfbClientCleanup_f)(mRfb); - return; + if ( capture ) { + if ( mRfb->frameBuffer ) + free(mRfb->frameBuffer); + (*rfbClientCleanup_f)(mRfb); + } } int VncCamera::PrimeCapture() { Debug(1, "Priming capture from %s", mHost.c_str()); + + if ( ! mRfb ) { + mVncData.buffer = nullptr; + mVncData.width = 0; + mVncData.height = 0; + + mRfb = (*rfbGetClient_f)(8 /* bits per sample */, 3 /* samples per pixel */, 4 /* bytes Per Pixel */); + mRfb->frameBuffer = (uint8_t *)av_malloc(8*4*width*height); + mRfb->canHandleNewFBSize = false; + + (*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData); + (*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str()); + (*rfbClientSetClientData_f)(mRfb, &TAG_2, (void *)mUser.c_str()); + + mRfb->GotFrameBufferUpdate = GotFrameBufferUpdateCallback; + mRfb->GetPassword = GetPasswordCallback; + mRfb->GetCredential = GetCredentialsCallback; + + mRfb->programName = "Zoneminder VNC Monitor"; + mRfb->serverHost = strdup(mHost.c_str()); + mRfb->serverPort = atoi(mPort.c_str()); + } if ( ! (*rfbInitClient_f)(mRfb, 0, nullptr) ) { + /* IF rfbInitClient fails, it calls rdbClientCleanup which will free mRfb */ + Warning("Failed to Priming capture from %s", mHost.c_str()); + mRfb = nullptr; return -1; } + if ( (mRfb->width != width) or (mRfb->height != height) ) { + Warning("Specified dimensions do not match screen size monitor: (%dx%d) != vnc: (%dx%d)", + width, height, mRfb->width, mRfb->height); + } + return 0; } int VncCamera::PreCapture() { - Debug(2, "PreCapture"); int rc = (*WaitForMessage_f)(mRfb, 500); if ( rc < 0 ) { return -1; @@ -163,13 +178,16 @@ int VncCamera::PreCapture() { return rc; } rfbBool res = (*HandleRFBServerMessage_f)(mRfb); + Debug(3, "PreCapture rc from HandleMessage %d", res == TRUE ? 1 : -1); return res == TRUE ? 1 : -1; } int VncCamera::Capture(Image &image) { - Debug(2, "Capturing"); + if ( ! mVncData.buffer ) { + return 0; + } uint8_t *directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - scale.Convert( + int rc = scale.Convert( mVncData.buffer, mRfb->si.framebufferHeight * mRfb->si.framebufferWidth * 4, directbuffer, @@ -180,7 +198,7 @@ int VncCamera::Capture(Image &image) { mRfb->si.framebufferHeight, width, height); - return 1; + return rc == 0 ? 1 : rc; } int VncCamera::PostCapture() { diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 250cfaa2e..8ffa0d9eb 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -9,9 +9,9 @@ #if HAVE_LIBVNC #include + // Used by vnc callbacks -struct VncPrivateData -{ +struct VncPrivateData { uint8_t *buffer; uint8_t width; uint8_t height; @@ -47,9 +47,6 @@ public: ~VncCamera(); - void Initialise(); - void Terminate(); - int PreCapture(); int PrimeCapture(); int Capture( Image &image ); From 65420723349d08e4ada31968b5f53b4c354039df Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 18 Nov 2020 09:56:13 -0500 Subject: [PATCH 029/138] fix logic causing segfault instead of waiting for zmc. Remove trailing whitespaces --- src/zm_monitor.cpp | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 01779eed7..e29036e4c 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -425,8 +425,6 @@ Monitor::Monitor( else event_close_mode = CLOSE_IDLE; - Debug(1, "monitor purpose=%d", purpose); - mem_size = sizeof(SharedData) + sizeof(TriggerData) + sizeof(VideoStoreData) //Information to pass back to the capture process @@ -491,21 +489,19 @@ Monitor::Monitor( video_store_data->size = sizeof(VideoStoreData); //video_store_data->frameNumber = 0; } else if ( purpose == ANALYSIS ) { - while ( - (!zm_terminate) - and + while ( ( !(this->connect() and shared_data->valid) ) - and + or ( shared_data->last_write_index == (unsigned int)image_buffer_count ) - and + or ( shared_data->last_write_time == 0 ) ) { - Debug(1, "blah"); Debug(1, "Waiting for capture daemon shared_data(%d) last_write_index(%d), last_write_time(%d)", (shared_data ? 1:0), (shared_data ? shared_data->last_write_index : 0), (shared_data ? shared_data->last_write_time : 0)); - usleep(100000); + sleep(1); + if ( zm_terminate ) break; } ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), @@ -666,7 +662,7 @@ bool Monitor::connect() { images = new Image *[pre_event_count]; last_signal = shared_data->signal; } // end if purpose == ANALYSIS -Debug(3, "Success connecting"); + Debug(3, "Success connecting"); return true; } // end Monitor::connect @@ -1480,7 +1476,7 @@ bool Monitor::Analyse() { } if ( last_motion_score ) { score += last_motion_score; - // cause is calculated every frame, + // cause is calculated every frame, //if ( !event ) { if ( cause.length() ) cause += ", "; @@ -1518,7 +1514,7 @@ bool Monitor::Analyse() { } // end foreach linked_monit if ( noteSet.size() > 0 ) noteSetMap[LINKED_CAUSE] = noteSet; - } // end if linked_monitors + } // end if linked_monitors //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? if ( function == RECORD || function == MOCORD ) { @@ -1527,7 +1523,7 @@ bool Monitor::Analyse() { if ( section_length && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) - && ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) ) + && ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) ) ) { Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d", name, image_count, event->Id(), @@ -1572,7 +1568,7 @@ bool Monitor::Analyse() { } else if ( event ) { // This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d min", - Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(), + Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(), ( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length ); } @@ -1600,7 +1596,7 @@ bool Monitor::Analyse() { // compute the index for pre event images in the dedicated buffer pre_index = pre_event_buffer_count ? image_count % pre_event_buffer_count : 0; Debug(3, "pre-index %d = image_count(%d) %% pre_event_buffer_count(%d)", - pre_index, image_count, pre_event_buffer_count); + pre_index, image_count, pre_event_buffer_count); // Seek forward the next filled slot in to the buffer (oldest data) // from the current position @@ -1610,7 +1606,7 @@ bool Monitor::Analyse() { pre_event_images--; } Debug(3, "pre-index %d, pre-event_images %d", - pre_index, pre_event_images); + pre_index, pre_event_images); event = new Event(this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap); } else { @@ -1685,7 +1681,7 @@ bool Monitor::Analyse() { Info("%s: %03d - Gone into alert state", name, image_count); shared_data->state = state = ALERT; } else if ( state == ALERT ) { - if ( + if ( ( image_count-last_alarm_count > post_event_count ) && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length ) ) { @@ -1751,7 +1747,7 @@ bool Monitor::Analyse() { } } // end if config.record_event_stats } - } // end if savejpegs > 1 + } // end if savejpegs > 1 if ( event ) { if ( noteSetMap.size() > 0 ) @@ -1842,7 +1838,7 @@ void Monitor::Reload() { static char sql[ZM_SQL_MED_BUFSIZ]; // This seems to have fallen out of date. - snprintf(sql, sizeof(sql), + snprintf(sql, sizeof(sql), "SELECT `Function`+0, `Enabled`, `LinkedMonitors`, `EventPrefix`, `LabelFormat`, " "`LabelX`, `LabelY`, `LabelSize`, `WarmupCount`, `PreEventCount`, `PostEventCount`, " "`AlarmFrameCount`, `SectionLength`, `MinSectionLength`, `FrameSkip`, " @@ -2369,7 +2365,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { width, height, colours, - brightness, + brightness, contrast, hue, colour, From cf08010ebe520565bb0672b55146f176104974e3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 18 Nov 2020 13:04:56 -0500 Subject: [PATCH 030/138] Fix redirect on zone editing --- web/includes/actions/zone.php | 4 +++- web/skins/classic/views/zone.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/web/includes/actions/zone.php b/web/includes/actions/zone.php index 0b2809fcb..9515fc626 100644 --- a/web/includes/actions/zone.php +++ b/web/includes/actions/zone.php @@ -71,7 +71,9 @@ if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) { $monitor->sendControlCommand('quit'); } } // end if changes - $redirect = $_SERVER['HTTP_REFERER']; + # HTTP_REFERER will typically be ?view=zone so no good. + # if a referer is passed in $_REQUEST then use it otherwise go to ?view=zones + $redirect = isset($_REQUEST['REFERER']) ? $_REQUEST['REFERER'] : '?view=zones'; } // end if action } // end if $mid and canEdit($mid) ?> diff --git a/web/skins/classic/views/zone.php b/web/skins/classic/views/zone.php index cf03104c7..2c41a885c 100644 --- a/web/skins/classic/views/zone.php +++ b/web/skins/classic/views/zone.php @@ -131,6 +131,7 @@ xhtmlHeaders(__FILE__, translate('Zone'));
+ From d4253be69f620a44ab4f13583eb83b343c44d00c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 18 Nov 2020 13:15:07 -0500 Subject: [PATCH 031/138] Use proper C++ initializer for Config. Don't copy the sql string, just use it from whereever in ram it is. --- src/zm_config.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 015095472..9c91d7d96 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -324,10 +324,7 @@ const char *ConfigItem::StringValue() const { return cfg_value.string_value; } -Config::Config() { - n_items = 0; - items = 0; -} +Config::Config() : n_items(0), items(nullptr) { } Config::~Config() { if ( items ) { @@ -341,10 +338,7 @@ Config::~Config() { } void Config::Load() { - static char sql[ZM_SQL_SML_BUFSIZ]; - - strncpy(sql, "SELECT `Name`, `Value`, `Type` FROM `Config` ORDER BY `Id`", sizeof(sql) ); - if ( mysql_query(&dbconn, sql) ) { + if ( mysql_query(&dbconn, "SELECT `Name`, `Value`, `Type` FROM `Config` ORDER BY `Id`") ) { Error("Can't run query: %s", mysql_error(&dbconn)); exit(mysql_errno(&dbconn)); } @@ -362,10 +356,11 @@ void Config::Load() { } items = new ConfigItem *[n_items]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { + for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { items[i] = new ConfigItem(dbrow[0], dbrow[1], dbrow[2]); } mysql_free_result(result); + result = nullptr; } void Config::Assign() { From 74972be9b5b505b2bfd35e2d9e5ad3d286e73c0d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 18 Nov 2020 13:15:52 -0500 Subject: [PATCH 032/138] spacing, code style. Set row=nullptr to quiet valgrind --- src/zm_db.cpp | 44 +++++++++++++++++++++++++++++++++----------- src/zm_db.h | 4 ++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 37c3cd0f7..1f40b9790 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -40,38 +40,59 @@ bool zmDbConnect() { Error("Can't initialise database connection: %s", mysql_error(&dbconn)); return false; } + bool reconnect = 1; if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) ) Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn)); - if ( !staticConfig.DB_SSL_CA_CERT.empty() ) + + if ( !staticConfig.DB_SSL_CA_CERT.empty() ) { mysql_ssl_set(&dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), nullptr, nullptr); + } + std::string::size_type colonIndex = staticConfig.DB_HOST.find(":"); if ( colonIndex == std::string::npos ) { - if ( !mysql_real_connect(&dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, 0, nullptr, 0) ) { - Error( "Can't connect to server: %s", mysql_error(&dbconn)); + if ( !mysql_real_connect( + &dbconn, + staticConfig.DB_HOST.c_str(), + staticConfig.DB_USER.c_str(), + staticConfig.DB_PASS.c_str(), + nullptr, 0, nullptr, 0) ) { + Error("Can't connect to server: %s", mysql_error(&dbconn)); return false; } } else { - std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); - std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 ); + std::string dbHost = staticConfig.DB_HOST.substr(0, colonIndex); + std::string dbPortOrSocket = staticConfig.DB_HOST.substr(colonIndex+1); if ( dbPortOrSocket[0] == '/' ) { - if ( !mysql_real_connect(&dbconn, nullptr, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, 0, dbPortOrSocket.c_str(), 0) ) { + if ( !mysql_real_connect( + &dbconn, + nullptr, + staticConfig.DB_USER.c_str(), + staticConfig.DB_PASS.c_str(), + nullptr, 0, dbPortOrSocket.c_str(), 0) ) { Error("Can't connect to server: %s", mysql_error(&dbconn)); return false; } } else { - if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), nullptr, atoi(dbPortOrSocket.c_str()), nullptr, 0 ) ) { - Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); + if ( !mysql_real_connect( + &dbconn, + dbHost.c_str(), + staticConfig.DB_USER.c_str(), + staticConfig.DB_PASS.c_str(), + nullptr, + atoi(dbPortOrSocket.c_str()), + nullptr, 0) ) { + Error("Can't connect to server: %s", mysql_error(&dbconn)); return false; } } } - if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) { - Error( "Can't select database: %s", mysql_error( &dbconn ) ); + if ( mysql_select_db(&dbconn, staticConfig.DB_NAME.c_str()) ) { + Error("Can't select database: %s", mysql_error(&dbconn)); return false; } zmDbConnected = true; @@ -140,7 +161,7 @@ MYSQL_RES *zmDbRow::fetch(const char *query) { } row = mysql_fetch_row(result_set); - if ( ! row ) { + if ( !row ) { mysql_free_result(result_set); result_set = nullptr; Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn)); @@ -155,4 +176,5 @@ zmDbRow::~zmDbRow() { mysql_free_result(result_set); result_set = nullptr; } + row = nullptr; } diff --git a/src/zm_db.h b/src/zm_db.h index f9f168cd0..8c641b0c2 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -29,8 +29,8 @@ class zmDbRow { MYSQL_ROW row; public: zmDbRow() : result_set(nullptr), row(nullptr) { }; - MYSQL_RES *fetch( const char *query ); - zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); + MYSQL_RES *fetch(const char *query); + zmDbRow(MYSQL_RES *, MYSQL_ROW *row); ~zmDbRow(); MYSQL_ROW mysql_row() const { return row; }; From ae4e2a1d5ef04541afb147726f8793bc7f8b76c5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 10:42:17 -0500 Subject: [PATCH 033/138] We were overwriting the privacy/inactive zone with a more general one. Slight memleak --- src/zm_zone.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 8ce486821..deb0b6ed1 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -911,14 +911,15 @@ int Zone::Load(Monitor *monitor, Zone **&zones) { zones[i] = new Zone(monitor, Id, Name, polygon); } else if ( atoi(dbrow[2]) == Zone::PRIVACY ) { zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon); + } else { + zones[i] = new Zone( + monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, + (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, + MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), + MinFilterPixels, MaxFilterPixels, + MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, + OverloadFrames, ExtendAlarmFrames); } - zones[i] = new Zone( - monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, - (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, - MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), - MinFilterPixels, MaxFilterPixels, - MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, - OverloadFrames, ExtendAlarmFrames); } // end foreach row mysql_free_result(result); return n_zones; From 634a3dbe81bb06453c13bb476c918cfb2f6459e6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 10:43:02 -0500 Subject: [PATCH 034/138] fix crashes/memleaks when waiting for a zmc process to start. Fix other memleaks --- src/zm_monitor.cpp | 49 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e29036e4c..1636ff5e3 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -373,10 +373,10 @@ Monitor::Monitor( privacy_bitmask( nullptr ), event_delete_thread(nullptr) { - 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; + 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); @@ -500,6 +500,7 @@ Monitor::Monitor( (shared_data ? 1:0), (shared_data ? shared_data->last_write_index : 0), (shared_data ? shared_data->last_write_time : 0)); + this->disconnect(); sleep(1); if ( zm_terminate ) break; } @@ -706,6 +707,35 @@ bool Monitor::disconnect() { return false; } #endif // ZM_MEM_MAPPED + if ( image_buffer ) { + for ( int i = 0; i < image_buffer_count; i++ ) { + delete image_buffer[i].image; + image_buffer[i].image = nullptr; + } + delete[] image_buffer; + image_buffer = nullptr; + } + if ( purpose == ANALYSIS ) { + if ( analysis_fps ) { + // Size of pre event buffer must be greater than pre_event_count + // if alarm_frame_count > 1, because in this case the buffer contains + // alarmed images that must be discarded when event is created + for ( int i = 0; i < pre_event_buffer_count; i++ ) { + delete pre_event_buffer[i].timestamp; + pre_event_buffer[i].timestamp = nullptr; + delete pre_event_buffer[i].image; + pre_event_buffer[i].image = nullptr; + } + delete[] pre_event_buffer; + pre_event_buffer = nullptr; + } // end if max_analysis_fps + + delete[] timestamps; + timestamps = nullptr; + delete[] images; + images = nullptr; + } // end if purpose == ANALYSIS + mem_ptr = nullptr; shared_data = nullptr; return true; @@ -1849,7 +1879,9 @@ void Monitor::Reload() { zmDbRow *row = zmDbFetchOne(sql); if ( !row ) { Error("Can't run query: %s", mysql_error(&dbconn)); - } else if ( MYSQL_ROW dbrow = row->mysql_row() ) { + return; + } + if ( MYSQL_ROW dbrow = row->mysql_row() ) { int index = 0; function = (Function)atoi(dbrow[index++]); enabled = atoi(dbrow[index++]); @@ -1900,8 +1932,8 @@ void Monitor::Reload() { ready_count = image_count+warmup_count; ReloadLinkedMonitors(p_linked_monitors); - delete row; } // end if row + delete row; ReloadZones(); } // end void Monitor::Reload() @@ -2030,6 +2062,7 @@ int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose) } if ( mysql_errno(&dbconn) ) { Error("Can't fetch row: %s", mysql_error(&dbconn)); + mysql_free_result(result); return 0; } mysql_free_result(result); @@ -2124,7 +2157,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { int format = atoi(dbrow[col]); col++; bool v4l_multi_buffer = config.v4l_multi_buffer; if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { + if ( *dbrow[col] == '0' ) { v4l_multi_buffer = false; } else if ( *dbrow[col] == '1' ) { v4l_multi_buffer = true; @@ -2442,7 +2475,7 @@ Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose) { std::string sql = load_monitor_sql + stringtf(" WHERE `Id`=%d", p_id); zmDbRow dbrow; - if ( ! dbrow.fetch(sql.c_str()) ) { + if ( !dbrow.fetch(sql.c_str()) ) { Error("Can't use query result: %s", mysql_error(&dbconn)); return nullptr; } From 58ccea6aa80862ac32ef2f0f73e4d943dc11260d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 16:37:28 -0500 Subject: [PATCH 035/138] use better c++ class initialisation. Use ZM_BUFTYPE_DONTFREE instead of 0 for init buffertype. --- src/zm_image.cpp | 50 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index b2707dd52..6848633ca 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -116,7 +116,7 @@ Image::Image() { size = 0; allocation = 0; buffer = 0; - buffertype = 0; + buffertype = ZM_BUFTYPE_DONTFREE; holdbuffer = 0; text[0] = '\0'; blend = fptr_blend; @@ -134,24 +134,26 @@ Image::Image(const char *filename) { size = 0; allocation = 0; buffer = 0; - buffertype = 0; + buffertype = ZM_BUFTYPE_DONTFREE; holdbuffer = 0; ReadJpeg(filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); text[0] = '\0'; update_function_pointers(); } -Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) { +Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) : + width(p_width), + height(p_height), + colours(p_colours), + padding(p_padding), + subpixelorder(p_subpixelorder), + buffer(p_buffer) { + if ( !initialised ) Initialise(); - width = p_width; - height = p_height; - pixels = width*height; - colours = p_colours; + pixels = width * height; linesize = p_width * p_colours; - padding = p_padding; - subpixelorder = p_subpixelorder; - size = linesize*height + padding; + size = linesize * height + padding; buffer = nullptr; holdbuffer = 0; if ( p_buffer ) { @@ -166,16 +168,18 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint update_function_pointers(); } -Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) { +Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) : + width(p_width), + linesize(p_linesize), + height(p_height), + colours(p_colours), + padding(p_padding), + subpixelorder(p_subpixelorder), + buffer(p_buffer) +{ if ( !initialised ) Initialise(); - width = p_width; - linesize = p_linesize; - height = p_height; pixels = width*height; - colours = p_colours; - padding = p_padding; - subpixelorder = p_subpixelorder; size = linesize*height + padding; buffer = nullptr; holdbuffer = 0; @@ -651,7 +655,7 @@ void Image::Assign( return; } } else { - if ( new_size > allocation || !buffer ) { + if ( (new_size > allocation) || !buffer ) { DumpImgBuffer(); AllocImgBuffer(new_size); } @@ -886,8 +890,8 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int jpeg_read_header(cinfo, TRUE); - if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) { - Error( "Unexpected colours when reading jpeg image: %d", colours ); + if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) { + Error("Unexpected colours when reading jpeg image: %d", colours); jpeg_abort_decompress(cinfo); fclose(infile); return false; @@ -902,7 +906,7 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int new_width = cinfo->image_width; new_height = cinfo->image_height; - if ( width != new_width || height != new_height ) { + if ( (width != new_width) || (height != new_height) ) { Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height); } @@ -1106,7 +1110,7 @@ cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif - */ +*/ cinfo->in_color_space = JCS_RGB; } break; @@ -1197,7 +1201,7 @@ bool Image::DecodeJpeg( new_width = cinfo->image_width; new_height = cinfo->image_height; - if ( width != new_width || height != new_height ) { + if ( (width != new_width) || (height != new_height) ) { Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height); } From 3f1e7e793fe5b37b8e38d379f54c4638c9b3acc7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 16:38:49 -0500 Subject: [PATCH 036/138] update buffertype after DumpBUffer --- src/zm_image.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zm_image.h b/src/zm_image.h index 3e07e41eb..a444f5e53 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -64,7 +64,7 @@ inline static uint8_t* AllocBuffer(size_t p_bufsize) { } inline static void DumpBuffer(uint8_t* buffer, int buffertype) { - if ( buffer && buffertype != ZM_BUFTYPE_DONTFREE ) { + if ( buffer && (buffertype != ZM_BUFTYPE_DONTFREE) ) { if ( buffertype == ZM_BUFTYPE_ZM ) { zm_freealigned(buffer); } else if ( buffertype == ZM_BUFTYPE_MALLOC ) { @@ -122,6 +122,7 @@ protected: inline void DumpImgBuffer() { DumpBuffer(buffer, buffertype); + buffertype = ZM_BUFTYPE_DONTFREE; buffer = nullptr; allocation = 0; } From 03f033cf367e93b5232b5a67f63b1adce411a60a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 16:39:14 -0500 Subject: [PATCH 037/138] cleanup MOnitor destructor. Most of the freeing is done in disconnect now. Fixes zms crash --- src/zm_monitor.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 1636ff5e3..4ea7ffdfc 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -774,34 +774,20 @@ Monitor::~Monitor() { } } - if ( (deinterlacing & 0xff) == 4) { - delete next_buffer.image; - delete next_buffer.timestamp; - } - for ( int i = 0; i < image_buffer_count; i++ ) { - delete image_buffer[i].image; - } - delete[] image_buffer; - } // end if mem_ptr - - if ( mem_ptr ) { if ( purpose == ANALYSIS ) { shared_data->state = state = IDLE; shared_data->last_read_index = image_buffer_count; shared_data->last_read_time = 0; - if ( analysis_fps ) { - for ( int i = 0; i < pre_event_buffer_count; i++ ) { - delete pre_event_buffer[i].image; - delete pre_event_buffer[i].timestamp; - } - delete[] pre_event_buffer; - } if ( Event::PreAlarmCount() ) Event::EmptyPreAlarmFrames(); } else if ( purpose == CAPTURE ) { shared_data->valid = false; memset(mem_ptr, 0, mem_size); + if ( (deinterlacing & 0xff) == 4 ) { + delete next_buffer.image; + delete next_buffer.timestamp; + } } disconnect(); } // end if mem_ptr From 4f178e47bcde3e05b7eb646c0a27d13744c84214 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Nov 2020 16:39:53 -0500 Subject: [PATCH 038/138] Break early after setting zm_terminate so that zms quits faster. Use a basic assignment instead of memcpy for last_frame_timestamp --- src/zm_monitorstream.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 58039b2bf..ea34f4d89 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -691,6 +691,7 @@ void MonitorStream::runStream() { if ( !sendFrame(snap->image, snap->timestamp) ) { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; + break; } if ( frame_count == 0 ) { // Chrome will not display the first frame until it receives another. @@ -698,14 +699,18 @@ void MonitorStream::runStream() { if ( !sendFrame(snap->image, snap->timestamp) ) { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; + break; } } // Perhaps we should use NOW instead. + last_frame_timestamp = *snap->timestamp; + /* memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); + */ // frame_sent = true; temp_read_index = temp_write_index; From 65d4c43cc27a03c13e0be8a14e930a624789a493 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Nov 2020 16:29:10 -0500 Subject: [PATCH 039/138] Use BOUNDARY instead of ZoneMinderFrame. If we get EAGAIN when flocking, try again. --- src/zm_fifo.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index d3952a169..577281890 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -181,7 +181,7 @@ bool FifoStream::sendMJEGFrames() { return true; if ( fprintf(stdout, - "--ZoneMinderFrame\r\n" + "--" BOUNDARY "\r\n" "Content-Type: image/jpeg\r\n" "Content-Length: %d\r\n\r\n", total_read) < 0 ) { @@ -210,9 +210,9 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) { Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); if ( !strcmp(format, "reference") ) { - stream_type = MJPEG; snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%d.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id()); + stream_type = MJPEG; } else if ( !strcmp(format, "delta") ) { snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%d.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id()); @@ -228,14 +228,16 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) { void FifoStream::runStream() { if ( stream_type == MJPEG ) { - fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n"); + fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n"); } else { fprintf(stdout, "Content-Type: text/html\r\n\r\n"); } + /* only 1 person can read from a fifo at a time, so use a lock */ char lock_file[PATH_MAX]; snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path); file_create_if_missing(lock_file, false); + Debug(1, "Locking %s", lock_file); int fd_lock = open(lock_file, O_RDONLY); if ( fd_lock < 0 ) { @@ -243,6 +245,11 @@ void FifoStream::runStream() { return; } int res = flock(fd_lock, LOCK_EX | LOCK_NB); + while ( (res == EAGAIN) and (! zm_terminate) ) { + Warning("Flocking problem on %s: - %s", lock_file, strerror(errno)); + sleep(1); + res = flock(fd_lock, LOCK_EX | LOCK_NB); + } if ( res < 0 ) { Error("Flocking problem on %s: - %s", lock_file, strerror(errno)); close(fd_lock); From e1a0c4f9735590299744a5e1c94d417ee8aaf8ea Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Nov 2020 16:31:12 -0500 Subject: [PATCH 040/138] Don't log a warning of comms not open if no connkey --- src/zm_stream.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index e73813b4b..613e6c97e 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -124,11 +124,14 @@ bool StreamBase::checkCommandQueue() { processCommand(&msg); return true; } - } else { + } else if ( connkey ) { Warning("No sd in checkCommandQueue, comms not open?"); + } else { + // Perfectly valid if only getting a snapshot + Debug(1, "No sd in checkCommandQueue, comms not open?"); } return false; -} +} // end bool StreamBase::checkCommandQueue() Image *StreamBase::prepareImage(Image *image) { From 7653a058a372abef93d15c656a5070bb83f24dba Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Nov 2020 16:31:40 -0500 Subject: [PATCH 041/138] More correct code for setting source --- src/zms.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/zms.cpp b/src/zms.cpp index 4e77ea4c1..38917a388 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -118,9 +118,13 @@ int main(int argc, const char *argv[], char **envp) { if ( !value ) value = ""; if ( !strcmp(name, "source") ) { - source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR; - if ( !strcmp(value, "fifo") ) + if ( !strcmp(value, "event") ) { + source = ZMS_EVENT; + } else if ( !strcmp(value, "fifo") ) { source = ZMS_FIFO; + } else { + source = ZMS_MONITOR; + } } else if ( !strcmp(name, "mode") ) { mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG; mode = !strcmp(value, "raw")?ZMS_RAW:mode; @@ -329,6 +333,7 @@ int main(int argc, const char *argv[], char **envp) { Error("Neither a monitor or event was specified."); } // end if monitor or event + Debug(1, "Terminating"); logTerm(); zmDbClose(); From 1b5448ede4b0a0ed70d10dee025b20d318a007b0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Nov 2020 16:35:48 -0500 Subject: [PATCH 042/138] Handle there being no services in the response --- onvif/modules/lib/ONVIF/Client.pm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/onvif/modules/lib/ONVIF/Client.pm b/onvif/modules/lib/ONVIF/Client.pm index 9676dff85..945e96075 100644 --- a/onvif/modules/lib/ONVIF/Client.pm +++ b/onvif/modules/lib/ONVIF/Client.pm @@ -117,12 +117,15 @@ sub get_service_urls { } ); if ( $result ) { - foreach my $svc ( @{ $result->get_Service() } ) { - my $short_name = $namespace_map{$svc->get_Namespace()}; - my $url_svc = $svc->get_XAddr()->get_value(); - if ( defined $short_name && defined $url_svc ) { - #print "Got $short_name service\n"; - $self->set_service($short_name, 'url', $url_svc); + my $services = $result->get_Service(); + if ( $services ) { + foreach my $svc ( @{ $services } ) { + my $short_name = $namespace_map{$svc->get_Namespace()}; + my $url_svc = $svc->get_XAddr()->get_value(); + if ( defined $short_name && defined $url_svc ) { + #print "Got $short_name service\n"; + $self->set_service($short_name, 'url', $url_svc); + } } } #} else { From 271dcbc4e8506c809cba48e9d4c8a8125aaf6f11 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 09:12:41 -0500 Subject: [PATCH 043/138] select returns EINTR when HUP'd. This is not fatal. Handling this gracefully reduces log spam --- scripts/zmcontrol.pl.in | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index c9a548eef..32c1132d6 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -27,7 +27,7 @@ use strict; use ZoneMinder; use Getopt::Long; use autouse 'Pod::Usage'=>qw(pod2usage); -use POSIX qw/strftime EPIPE/; +use POSIX qw/strftime EPIPE EINTR/; use Socket; use Data::Dumper; use Module::Load::Conditional qw{can_load}; @@ -191,7 +191,10 @@ if ( $options{command} ) { Fatal('Bogus descriptor'); } } elsif ( $nfound < 0 ) { - if ( $! == EPIPE ) { + if ( $! == EINTR ) { + # Likely just SIGHUP + Debug("Can't select: $!"); + } elsif ( $! == EPIPE ) { Error("Can't select: $!"); } else { Fatal("Can't select: $!"); From 2cdf0da3c05820a6e971d96ebe04702aa9c76d08 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 09:15:36 -0500 Subject: [PATCH 044/138] Remove unused includes and apply google code style --- src/zm_buffer.cpp | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/zm_buffer.cpp b/src/zm_buffer.cpp index 73c9603e1..be20c008e 100644 --- a/src/zm_buffer.cpp +++ b/src/zm_buffer.cpp @@ -17,62 +17,51 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include -#include - #include "zm.h" #include "zm_buffer.h" -unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize ) -{ - if ( mAllocation < pSize ) - { +unsigned int Buffer::assign(const unsigned char *pStorage, unsigned int pSize) { + if ( mAllocation < pSize ) { delete[] mStorage; mAllocation = pSize; mHead = mStorage = new unsigned char[pSize]; } mSize = pSize; - memcpy( mStorage, pStorage, mSize ); + memcpy(mStorage, pStorage, mSize); mHead = mStorage; mTail = mHead + mSize; - return( mSize ); + return mSize; } -unsigned int Buffer::expand( unsigned int count ) -{ +unsigned int Buffer::expand(unsigned int count) { int spare = mAllocation - mSize; int headSpace = mHead - mStorage; int tailSpace = spare - headSpace; int width = mTail - mHead; - if ( spare > (int)count ) - { - if ( tailSpace < (int)count ) - { - memmove( mStorage, mHead, mSize ); + if ( spare > static_castcount ) { + if ( tailSpace < static_castcount ) { + memmove(mStorage, mHead, mSize); mHead = mStorage; mTail = mHead + width; } - } - else - { + } else { mAllocation += count; unsigned char *newStorage = new unsigned char[mAllocation]; - if ( mStorage ) - { - memcpy( newStorage, mHead, mSize ); + if ( mStorage ) { + memcpy(newStorage, mHead, mSize); delete[] mStorage; } mStorage = newStorage; mHead = mStorage; mTail = mHead + width; } - return( mSize ); + return mSize; } -int Buffer::read_into( int sd, unsigned int bytes ) { +int Buffer::read_into(int sd, unsigned int bytes) { // Make sure there is enough space this->expand(bytes); - int bytes_read = read( sd, mTail, bytes ); + int bytes_read = read(sd, mTail, bytes); if ( bytes_read > 0 ) { mTail += bytes_read; mSize += bytes_read; From 5f4fb4200f80a2c497b6fbf715ec23641a495a92 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 09:25:10 -0500 Subject: [PATCH 045/138] googe code style --- src/zm_comms.cpp | 890 ++++++++++++++++++++--------------------------- 1 file changed, 383 insertions(+), 507 deletions(-) diff --git a/src/zm_comms.cpp b/src/zm_comms.cpp index b7231d4fd..ccf84ba91 100644 --- a/src/zm_comms.cpp +++ b/src/zm_comms.cpp @@ -18,20 +18,19 @@ // #include "zm_comms.h" - #include "zm_logger.h" #include #include #include -//#include +#include + #if defined(BSD) #include #else #include #endif -//#include #include #include #include @@ -42,793 +41,677 @@ #include // define FIONREAD #endif -int CommsBase::readV( int iovcnt, /* const void *, int, */ ... ) { +int CommsBase::readV(int iovcnt, /* const void *, int, */ ...) { va_list arg_ptr; struct iovec iov[iovcnt]; - //struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); - va_start( arg_ptr, iovcnt ); - for ( int i = 0; i < iovcnt; i++ ) - { - iov[i].iov_base = va_arg( arg_ptr, void * ); - iov[i].iov_len = va_arg( arg_ptr, int ); + va_start(arg_ptr, iovcnt); + for ( int i = 0; i < iovcnt; i++ ) { + iov[i].iov_base = va_arg(arg_ptr, void *); + iov[i].iov_len = va_arg(arg_ptr, int); } - va_end( arg_ptr ); + va_end(arg_ptr); - int nBytes = ::readv( mRd, iov, iovcnt ); + int nBytes = ::readv(mRd, iov, iovcnt); if ( nBytes < 0 ) - Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); - return( nBytes ); + Debug(1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno)); + return nBytes; } -int CommsBase::writeV( int iovcnt, /* const void *, int, */ ... ) { +int CommsBase::writeV(int iovcnt, /* const void *, int, */ ...) { va_list arg_ptr; struct iovec iov[iovcnt]; - //struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); - va_start( arg_ptr, iovcnt ); - for ( int i = 0; i < iovcnt; i++ ) - { - iov[i].iov_base = va_arg( arg_ptr, void * ); - iov[i].iov_len = va_arg( arg_ptr, int ); + va_start(arg_ptr, iovcnt); + for ( int i = 0; i < iovcnt; i++ ) { + iov[i].iov_base = va_arg(arg_ptr, void *); + iov[i].iov_len = va_arg(arg_ptr, int); } - va_end( arg_ptr ); + va_end(arg_ptr); - ssize_t nBytes = ::writev( mWd, iov, iovcnt ); + ssize_t nBytes = ::writev(mWd, iov, iovcnt); if ( nBytes < 0 ) - Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); - return( nBytes ); + Debug(1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno)); + return nBytes; } -bool Pipe::open() -{ - if ( ::pipe( mFd ) < 0 ) - { - Error( "pipe(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); +bool Pipe::open() { + if ( ::pipe(mFd) < 0 ) { + Error("pipe(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool Pipe::close() -{ +bool Pipe::close() { if ( mFd[0] > -1 ) ::close( mFd[0] ); mFd[0] = -1; if ( mFd[1] > -1 ) ::close( mFd[1] ); mFd[1] = -1; - return( true ); + return true; } -bool Pipe::setBlocking( bool blocking ) -{ +bool Pipe::setBlocking(bool blocking) { int flags; /* Now set it for non-blocking I/O */ - if ( (flags = fcntl( mFd[1], F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( (flags = fcntl(mFd[1], F_GETFL)) < 0 ) { + Error("fcntl(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - if ( blocking ) - { + if ( blocking ) { flags &= ~O_NONBLOCK; - } - else - { + } else { flags |= O_NONBLOCK; } - if ( fcntl( mFd[1], F_SETFL, flags ) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( fcntl(mFd[1], F_SETFL, flags) < 0 ) { + Error("fcntl(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -SockAddr::SockAddr( const struct sockaddr *addr ) : mAddr( addr ) -{ +SockAddr::SockAddr(const struct sockaddr *addr) : mAddr(addr) { } -SockAddr *SockAddr::newSockAddr( const struct sockaddr &addr, socklen_t len ) -{ - if ( addr.sa_family == AF_INET && len == SockAddrInet::addrSize() ) - { - return( new SockAddrInet( (const struct sockaddr_in *)&addr ) ); +SockAddr *SockAddr::newSockAddr(const struct sockaddr &addr, socklen_t len) { + if ( (addr.sa_family == AF_INET) && (len == SockAddrInet::addrSize()) ) { + return new SockAddrInet((const struct sockaddr_in *)&addr); + } else if ( (addr.sa_family == AF_UNIX) && (len == SockAddrUnix::addrSize()) ) { + return new SockAddrUnix((const struct sockaddr_un *)&addr); } - else if ( addr.sa_family == AF_UNIX && len == SockAddrUnix::addrSize() ) - { - return( new SockAddrUnix( (const struct sockaddr_un *)&addr ) ); - } - Error( "Unable to create new SockAddr from addr family %d with size %d", addr.sa_family, len ); + Error("Unable to create new SockAddr from addr family %d with size %d", addr.sa_family, len); return nullptr; } -SockAddr *SockAddr::newSockAddr( const SockAddr *addr ) -{ +SockAddr *SockAddr::newSockAddr(const SockAddr *addr) { if ( !addr ) return nullptr; - if ( addr->getDomain() == AF_INET ) - { - return( new SockAddrInet( *(SockAddrInet *)addr ) ); + if ( addr->getDomain() == AF_INET ) { + return new SockAddrInet(*(SockAddrInet *)addr); + } else if ( addr->getDomain() == AF_UNIX ) { + return new SockAddrUnix(*(SockAddrUnix *)addr); } - else if ( addr->getDomain() == AF_UNIX ) - { - return( new SockAddrUnix( *(SockAddrUnix *)addr ) ); - } - Error( "Unable to create new SockAddr from addr family %d", addr->getDomain() ); + Error("Unable to create new SockAddr from addr family %d", addr->getDomain()); return nullptr; } -SockAddrInet::SockAddrInet() : SockAddr( (struct sockaddr *)&mAddrIn ) -{ +SockAddrInet::SockAddrInet() : SockAddr( (struct sockaddr *)&mAddrIn ) { } -bool SockAddrInet::resolve( const char *host, const char *serv, const char *proto ) -{ - memset( &mAddrIn, 0, sizeof(mAddrIn) ); +bool SockAddrInet::resolve(const char *host, const char *serv, const char *proto) { + memset(&mAddrIn, 0, sizeof(mAddrIn)); - struct hostent *hostent=nullptr; - if ( !(hostent = ::gethostbyname( host ) ) ) - { - Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); - return( false ); + struct hostent *hostent = nullptr; + if ( !(hostent = ::gethostbyname(host) ) ) { + Error("gethostbyname(%s), h_errno = %d", host, h_errno); + return false; } - struct servent *servent=nullptr; - if ( !(servent = ::getservbyname( serv, proto ) ) ) - { - Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); - return( false ); + struct servent *servent = nullptr; + if ( !(servent = ::getservbyname(serv, proto) ) ) { + Error("getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno)); + return false; } mAddrIn.sin_port = servent->s_port; mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; - return( true ); + return true; } -bool SockAddrInet::resolve( const char *host, int port, const char *proto ) -{ - memset( &mAddrIn, 0, sizeof(mAddrIn) ); +bool SockAddrInet::resolve(const char *host, int port, const char *proto) { + memset(&mAddrIn, 0, sizeof(mAddrIn)); - struct hostent *hostent=nullptr; - if ( !(hostent = ::gethostbyname( host ) ) ) - { - Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); - return( false ); + struct hostent *hostent = nullptr; + if ( !(hostent = ::gethostbyname(host)) ) { + Error("gethostbyname(%s), h_errno = %d", host, h_errno); + return false; } mAddrIn.sin_port = htons(port); mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; - return( true ); + return true; } -bool SockAddrInet::resolve( const char *serv, const char *proto ) -{ - memset( &mAddrIn, 0, sizeof(mAddrIn) ); +bool SockAddrInet::resolve(const char *serv, const char *proto) { + memset(&mAddrIn, 0, sizeof(mAddrIn)); - struct servent *servent=nullptr; - if ( !(servent = ::getservbyname( serv, proto ) ) ) - { - Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); - return( false ); + struct servent *servent = nullptr; + if ( !(servent = ::getservbyname(serv, proto)) ) { + Error("getservbyname(%s), errno = %d, error = %s", serv, errno, strerror(errno)); + return false; } mAddrIn.sin_port = servent->s_port; mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = INADDR_ANY; - return( true ); + return true; } -bool SockAddrInet::resolve( int port, const char *proto ) -{ - memset( &mAddrIn, 0, sizeof(mAddrIn) ); +bool SockAddrInet::resolve(int port, const char *proto) { + memset(&mAddrIn, 0, sizeof(mAddrIn)); mAddrIn.sin_port = htons(port); mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = INADDR_ANY; - return( true ); + return true; } -SockAddrUnix::SockAddrUnix() : SockAddr( (struct sockaddr *)&mAddrUn ) -{ +SockAddrUnix::SockAddrUnix() : SockAddr((struct sockaddr *)&mAddrUn ) { } -bool SockAddrUnix::resolve( const char *path, const char *proto ) -{ - memset( &mAddrUn, 0, sizeof(mAddrUn) ); +bool SockAddrUnix::resolve(const char *path, const char *proto) { + memset(&mAddrUn, 0, sizeof(mAddrUn)); - strncpy( mAddrUn.sun_path, path, sizeof(mAddrUn.sun_path) ); + strncpy(mAddrUn.sun_path, path, sizeof(mAddrUn.sun_path)); mAddrUn.sun_family = AF_UNIX; - return( true ); + return true; } -bool Socket::socket() -{ +bool Socket::socket() { if ( mSd >= 0 ) - return( true ); + return true; - if ( (mSd = ::socket( getDomain(), getType(), 0 ) ) < 0 ) - { - Error( "socket(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( (mSd = ::socket(getDomain(), getType(), 0) ) < 0 ) { + Error("socket(), errno = %d, error = %s", errno, strerror(errno)); + return false; } int val = 1; - (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); - (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); + (void)::setsockopt(mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + (void)::setsockopt(mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); mState = DISCONNECTED; - return( true ); + return true; } -bool Socket::connect() -{ - if ( !socket() ) - return( false ); +bool Socket::connect() { + if ( !socket() ) + return false; - if ( ::connect( mSd, mRemoteAddr->getAddr(), getAddrSize() ) == -1 ) - { - Error( "connect(), errno = %d, error = %s", errno, strerror(errno) ); + if ( ::connect(mSd, mRemoteAddr->getAddr(), getAddrSize()) == -1 ) { + Error("connect(), errno = %d, error = %s", errno, strerror(errno)); close(); - return( false ); + return false; } mState = CONNECTED; - return( true ); + return true; } -bool Socket::bind() -{ +bool Socket::bind() { if ( !socket() ) - return( false ); + return false; - if ( ::bind( mSd, mLocalAddr->getAddr(), getAddrSize() ) == -1 ) - { - Error( "bind(), errno = %d, error = %s", errno, strerror(errno) ); + if ( ::bind(mSd, mLocalAddr->getAddr(), getAddrSize()) == -1 ) { + Error("bind(), errno = %d, error = %s", errno, strerror(errno)); close(); - return( false ); + return false; } - return( true ); + return true; } -bool Socket::listen() -{ - if ( ::listen( mSd, SOMAXCONN ) == -1 ) - { - Error( "listen(), errno = %d, error = %s", errno, strerror(errno) ); +bool Socket::listen() { + if ( ::listen(mSd, SOMAXCONN) == -1 ) { + Error("listen(), errno = %d, error = %s", errno, strerror(errno)); close(); - return( false ); + return false; } mState = LISTENING; - - return( true ); + return true; } -bool Socket::accept() -{ +bool Socket::accept() { struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); socklen_t rem_addr_size = getAddrSize(); int newSd = -1; - if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) - { - Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); + if ( (newSd = ::accept(mSd, rem_addr, &rem_addr_size)) == -1 ) { + Error("accept(), errno = %d, error = %s", errno, strerror(errno)); close(); - return( false ); + return false; } - ::close( mSd ); + ::close(mSd); mSd = newSd; mState = CONNECTED; - return( true ); + return true; } -bool Socket::accept( int &newSd ) -{ +bool Socket::accept(int &newSd) { struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); socklen_t rem_addr_size = getAddrSize(); newSd = -1; - if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) - { - Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); + if ( (newSd = ::accept(mSd, rem_addr, &rem_addr_size)) == -1 ) { + Error("accept(), errno = %d, error = %s", errno, strerror(errno)); close(); - return( false ); + return false; } - return( true ); + return true; } -bool Socket::close() -{ - if ( mSd > -1 ) ::close( mSd ); +bool Socket::close() { + if ( mSd > -1 ) ::close(mSd); mSd = -1; mState = CLOSED; - return( true ); + return true; } -int Socket::bytesToRead() const -{ +int Socket::bytesToRead() const { int bytes_to_read = 0; - if ( ioctl( mSd, FIONREAD, &bytes_to_read ) < 0 ) - { - Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); + if ( ioctl(mSd, FIONREAD, &bytes_to_read) < 0 ) { + Error("ioctl(), errno = %d, error = %s", errno, strerror(errno)); + return -1; } - return( bytes_to_read ); + return bytes_to_read; } -bool Socket::getBlocking( bool &blocking ) -{ +bool Socket::getBlocking(bool &blocking) { int flags; - if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( (flags = fcntl(mSd, F_GETFL)) < 0 ) { + Error("fcntl(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - blocking = (flags & O_NONBLOCK); - return( true ); + blocking = flags & O_NONBLOCK; + return true; } -bool Socket::setBlocking( bool blocking ) -{ -#if 0 - // ioctl is apparently not recommended - int ioctl_arg = !blocking; - if ( ioctl( mSd, FIONBIO, &ioctl_arg ) < 0 ) - { - Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); -#endif - +bool Socket::setBlocking(bool blocking) { int flags; /* Now set it for non-blocking I/O */ - if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( (flags = fcntl(mSd, F_GETFL)) < 0 ) { + Error("fcntl(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - if ( blocking ) - { + if ( blocking ) { flags &= ~O_NONBLOCK; - } - else - { + } else { flags |= O_NONBLOCK; } - if ( fcntl( mSd, F_SETFL, flags ) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( fcntl(mSd, F_SETFL, flags) < 0 ) { + Error("fcntl(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool Socket::getSendBufferSize( int &buffersize ) const -{ +bool Socket::getSendBufferSize(int &buffersize) const { socklen_t optlen = sizeof(buffersize); - if ( getsockopt( mSd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); + if ( getsockopt(mSd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen) < 0 ) { + Error("getsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return -1; } - return( buffersize ); + return buffersize; } -bool Socket::getRecvBufferSize( int &buffersize ) const -{ +bool Socket::getRecvBufferSize(int &buffersize) const { socklen_t optlen = sizeof(buffersize); - if ( getsockopt( mSd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); + if ( getsockopt(mSd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen) < 0 ) { + Error("getsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return -1; } - return( buffersize ); + return buffersize; } -bool Socket::setSendBufferSize( int buffersize ) -{ - if ( setsockopt( mSd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); +bool Socket::setSendBufferSize(int buffersize) { + if ( setsockopt(mSd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) { + Error("setsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool Socket::setRecvBufferSize( int buffersize ) -{ - if ( setsockopt( mSd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); +bool Socket::setRecvBufferSize(int buffersize) { + if ( setsockopt( mSd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) { + Error("setsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool Socket::getRouting( bool &route ) const -{ +bool Socket::getRouting(bool &route) const { int dontRoute; socklen_t optlen = sizeof(dontRoute); - if ( getsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, &dontRoute, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( getsockopt(mSd, SOL_SOCKET, SO_DONTROUTE, &dontRoute, &optlen) < 0 ) { + Error("getsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } route = !dontRoute; - return( true ); + return true; } -bool Socket::setRouting( bool route ) -{ +bool Socket::setRouting(bool route) { int dontRoute = !route; - if ( setsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontRoute, sizeof(dontRoute)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( setsockopt(mSd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontRoute, sizeof(dontRoute)) < 0 ) { + Error("setsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool Socket::getNoDelay( bool &nodelay ) const -{ +bool Socket::getNoDelay(bool &nodelay) const { int int_nodelay; socklen_t optlen = sizeof(int_nodelay); - if ( getsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( getsockopt(mSd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen) < 0 ) { + Error("getsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } nodelay = int_nodelay; - return( true ); + return true; } -bool Socket::setNoDelay( bool nodelay ) -{ +bool Socket::setNoDelay(bool nodelay) { int int_nodelay = nodelay; - if ( setsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); + if ( setsockopt(mSd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) { + Error("setsockopt(), errno = %d, error = %s", errno, strerror(errno)); + return false; } - return( true ); + return true; } -bool InetSocket::connect( const char *host, const char *serv ) -{ - struct addrinfo hints; - struct addrinfo *result, *rp; - int s; - char buf[255]; +bool InetSocket::connect(const char *host, const char *serv) { + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + char buf[255]; + mAddressFamily = AF_UNSPEC; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + s = getaddrinfo(host, serv, &hints, &result); + if ( s != 0 ) { + Error("connect(): getaddrinfo: %s", gai_strerror(s)); + return false; + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully connect(2). + * If socket(2) (or connect(2)) fails, we (close the socket + * and) try the next address. */ + + for ( rp = result; rp != nullptr; rp = rp->ai_next ) { + if ( mSd != -1 ) { + if ( ::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1 ) + break; /* Success */ + continue; + } + memset(&buf, 0, sizeof(buf)); + if ( rp->ai_family == AF_INET ) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } else if (rp->ai_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } else { + strncpy(buf, "n/a", sizeof(buf)-1); + } + Debug(1, "connect(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if ( mSd == -1 ) + continue; + + int val = 1; + + (void)::setsockopt(mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + (void)::setsockopt(mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + mAddressFamily = rp->ai_family; /* save AF_ for ctrl and data connections */ + + if ( ::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1 ) + break; /* Success */ + + ::close(mSd); + } // end for + + freeaddrinfo(result); /* No longer needed */ + + if ( rp == nullptr ) { /* No address succeeded */ + Error("connect(), Could not connect"); mAddressFamily = AF_UNSPEC; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = getType(); - hints.ai_flags = 0; - hints.ai_protocol = 0; /* Any protocol */ + return false; + } - s = getaddrinfo(host, serv, &hints, &result); - if (s != 0) { - Error( "connect(): getaddrinfo: %s", gai_strerror(s) ); - return( false ); - } + mState = CONNECTED; - /* getaddrinfo() returns a list of address structures. - * Try each address until we successfully connect(2). - * If socket(2) (or connect(2)) fails, we (close the socket - * and) try the next address. */ - - for (rp = result; rp != nullptr; rp = rp->ai_next) { - if (mSd != -1) { - if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) - break; /* Success */ - continue; - } - memset(&buf, 0, sizeof(buf)); - if (rp->ai_family == AF_INET) { - inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); - } - else if (rp->ai_family == AF_INET6) { - inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); - } - else { - strncpy(buf, "n/a", sizeof(buf)-1); - } - Debug( 1, "connect(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); - mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (mSd == -1) - continue; - - int val = 1; - - (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); - (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); - mAddressFamily = rp->ai_family; /* save AF_ for ctrl and data connections */ - - if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) - break; /* Success */ - - ::close(mSd); - } - - freeaddrinfo(result); /* No longer needed */ - - if (rp == nullptr) { /* No address succeeded */ - Error( "connect(), Could not connect" ); - mAddressFamily = AF_UNSPEC; - return( false ); - } - - mState = CONNECTED; - - return( true ); + return true; } -bool InetSocket::connect( const char *host, int port ) -{ - char serv[8]; - snprintf(serv, sizeof(serv), "%d", port); +bool InetSocket::connect(const char *host, int port) { + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); - return connect( host, serv ); + return connect(host, serv); } -bool InetSocket::bind( const char * host, const char * serv ) -{ - struct addrinfo hints; - struct addrinfo *result, *rp; - int s; - char buf[255]; +bool InetSocket::bind(const char * host, const char * serv) { + struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = getType(); - hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ - hints.ai_protocol = 0; /* Any protocol */ - hints.ai_canonname = nullptr; - hints.ai_addr = nullptr; - hints.ai_next = nullptr; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = nullptr; + hints.ai_addr = nullptr; + hints.ai_next = nullptr; - s = getaddrinfo(host, serv, &hints, &result); - if (s != 0) { - Error( "bind(): getaddrinfo: %s", gai_strerror(s) ); - return( false ); + struct addrinfo *result, *rp; + int s = getaddrinfo(host, serv, &hints, &result); + if ( s != 0 ) { + Error("bind(): getaddrinfo: %s", gai_strerror(s)); + return false; + } + + char buf[255]; + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully bind(2). + * If socket(2) (or bind(2)) fails, we (close the socket + * and) try the next address. */ + for ( rp = result; rp != nullptr; rp = rp->ai_next ) { + memset(&buf, 0, sizeof(buf)); + if ( rp->ai_family == AF_INET ) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } else if ( rp->ai_family == AF_INET6 ) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } else { + strncpy(buf, "n/a", sizeof(buf)-1); } - - /* getaddrinfo() returns a list of address structures. - * Try each address until we successfully bind(2). - * If socket(2) (or bind(2)) fails, we (close the socket - * and) try the next address. */ - for (rp = result; rp != nullptr; rp = rp->ai_next) { - memset(&buf, 0, sizeof(buf)); - if (rp->ai_family == AF_INET) { - inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); - } - else if (rp->ai_family == AF_INET6) { - inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); - } - else { - strncpy(buf, "n/a", sizeof(buf)-1); - } - Debug( 1, "bind(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); - mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (mSd == -1) - continue; + Debug(1, "bind(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if ( mSd == -1 ) + continue; mState = DISCONNECTED; - if (::bind(mSd, rp->ai_addr, rp->ai_addrlen) == 0) - break; /* Success */ + if ( ::bind(mSd, rp->ai_addr, rp->ai_addrlen) == 0 ) + break; /* Success */ - ::close(mSd); - mSd = -1; - } + ::close(mSd); + mSd = -1; + } // end foreach result - if (rp == nullptr) { /* No address succeeded */ - Error( "bind(), Could not bind" ); - return( false ); - } + if ( rp == nullptr ) { /* No address succeeded */ + Error("bind(), Could not bind"); + return false; + } - freeaddrinfo(result); /* No longer needed */ - - return( true ); + freeaddrinfo(result); /* No longer needed */ + return true; } -bool InetSocket::bind( const char * serv ) -{ - return bind( nullptr, serv); +bool InetSocket::bind(const char * serv) { + return bind(nullptr, serv); } -bool InetSocket::bind( const char * host, int port ) -{ - char serv[8]; - snprintf(serv, sizeof(serv), "%d", port); +bool InetSocket::bind(const char * host, int port) { + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); - return bind( host, serv ); + return bind(host, serv); } -bool InetSocket::bind( int port ) -{ - char serv[8]; - snprintf(serv, sizeof(serv), "%d", port); +bool InetSocket::bind(int port) { + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); - return bind( nullptr, serv ); + return bind(nullptr, serv); } -bool TcpInetServer::listen() -{ - return( Socket::listen() ); +bool TcpInetServer::listen() { + return Socket::listen(); } -bool TcpInetServer::accept() -{ - return( Socket::accept() ); +bool TcpInetServer::accept() { + return Socket::accept(); } -bool TcpInetServer::accept( TcpInetSocket *&newSocket ) -{ +bool TcpInetServer::accept(TcpInetSocket *&newSocket) { int newSd = -1; newSocket = nullptr; - if ( !Socket::accept( newSd ) ) - return( false ); + if ( !Socket::accept(newSd) ) + return false; - newSocket = new TcpInetSocket( *this, newSd ); + newSocket = new TcpInetSocket(*this, newSd); - return( true ); + return true; } -bool TcpUnixServer::accept( TcpUnixSocket *&newSocket ) -{ +bool TcpUnixServer::accept(TcpUnixSocket *&newSocket) { int newSd = -1; newSocket = nullptr; - if ( !Socket::accept( newSd ) ) - return( false ); + if ( !Socket::accept(newSd) ) + return false; - newSocket = new TcpUnixSocket( *this, newSd ); + newSocket = new TcpUnixSocket(*this, newSd); - return( true ); + return true; } -Select::Select() : mHasTimeout( false ), mMaxFd( -1 ) -{ +Select::Select() : mHasTimeout(false), mMaxFd(-1) { } -Select::Select( struct timeval timeout ) : mMaxFd( -1 ) -{ - setTimeout( timeout ); +Select::Select(struct timeval timeout) : mMaxFd(-1) { + setTimeout(timeout); } -Select::Select( int timeout ) : mMaxFd( -1 ) -{ - setTimeout( timeout ); +Select::Select(int timeout) : mMaxFd(-1) { + setTimeout(timeout); } -Select::Select( double timeout ) : mMaxFd( -1 ) -{ - setTimeout( timeout ); +Select::Select(double timeout) : mMaxFd(-1) { + setTimeout(timeout); } -void Select::setTimeout( int timeout ) -{ +void Select::setTimeout(int timeout) { mTimeout.tv_sec = timeout; mTimeout.tv_usec = 0; mHasTimeout = true; } -void Select::setTimeout( double timeout ) -{ +void Select::setTimeout(double timeout) { mTimeout.tv_sec = int(timeout); mTimeout.tv_usec = suseconds_t((timeout-mTimeout.tv_sec)*1000000.0); mHasTimeout = true; } -void Select::setTimeout( struct timeval timeout ) -{ +void Select::setTimeout(struct timeval timeout) { mTimeout = timeout; mHasTimeout = true; } -void Select::clearTimeout() -{ +void Select::clearTimeout() { mHasTimeout = false; } -void Select::calcMaxFd() -{ +void Select::calcMaxFd() { mMaxFd = -1; - for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); ++iter ) + for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); ++iter ) { if ( (*iter)->getMaxDesc() > mMaxFd ) mMaxFd = (*iter)->getMaxDesc(); - for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); ++iter ) + } + for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); ++iter ) { if ( (*iter)->getMaxDesc() > mMaxFd ) mMaxFd = (*iter)->getMaxDesc(); + } } -bool Select::addReader( CommsBase *comms ) -{ - if ( !comms->isOpen() ) - { - Error( "Unable to add closed reader" ); - return( false ); +bool Select::addReader(CommsBase *comms) { + if ( !comms->isOpen() ) { + Error("Unable to add closed reader"); + return false; } - std::pair result = mReaders.insert( comms ); - if ( result.second ) + std::pair result = mReaders.insert(comms); + if ( result.second ) { if ( comms->getMaxDesc() > mMaxFd ) mMaxFd = comms->getMaxDesc(); - return( result.second ); + } + return result.second; } -bool Select::deleteReader( CommsBase *comms ) -{ - if ( !comms->isOpen() ) - { - Error( "Unable to add closed reader" ); - return( false ); +bool Select::deleteReader(CommsBase *comms) { + if ( !comms->isOpen() ) { + Error("Unable to add closed reader"); + return false; } - if ( mReaders.erase( comms ) ) - { + if ( mReaders.erase(comms) ) { calcMaxFd(); - return( true ); + return true; } - return( false ); + return false; } -void Select::clearReaders() -{ +void Select::clearReaders() { mReaders.clear(); mMaxFd = -1; } -bool Select::addWriter( CommsBase *comms ) -{ - std::pair result = mWriters.insert( comms ); - if ( result.second ) +bool Select::addWriter(CommsBase *comms) { + std::pair result = mWriters.insert(comms); + if ( result.second ) { if ( comms->getMaxDesc() > mMaxFd ) mMaxFd = comms->getMaxDesc(); - return( result.second ); -} - -bool Select::deleteWriter( CommsBase *comms ) -{ - if ( mWriters.erase( comms ) ) - { - calcMaxFd(); - return( true ); } - return( false ); + return result.second; } -void Select::clearWriters() -{ +bool Select::deleteWriter(CommsBase *comms) { + if ( mWriters.erase(comms) ) { + calcMaxFd(); + return true; + } + return false; +} + +void Select::clearWriters() { mWriters.clear(); mMaxFd = -1; } -int Select::wait() -{ +int Select::wait() { struct timeval tempTimeout = mTimeout; struct timeval *selectTimeout = mHasTimeout?&tempTimeout:nullptr; @@ -838,40 +721,33 @@ int Select::wait() mReadable.clear(); FD_ZERO(&rfds); for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); ++iter ) - FD_SET((*iter)->getReadDesc(),&rfds); + FD_SET((*iter)->getReadDesc(), &rfds); mWriteable.clear(); FD_ZERO(&wfds); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); ++iter ) - FD_SET((*iter)->getWriteDesc(),&wfds); + FD_SET((*iter)->getWriteDesc(), &wfds); - int nFound = select( mMaxFd+1, &rfds, &wfds, nullptr, selectTimeout ); - if( nFound == 0 ) - { - Debug( 1, "Select timed out" ); - } - else if ( nFound < 0) - { - Error( "Select error: %s", strerror(errno) ); - } - else - { + int nFound = select(mMaxFd+1, &rfds, &wfds, nullptr, selectTimeout); + if ( nFound == 0 ) { + Debug(1, "Select timed out"); + } else if ( nFound < 0 ) { + Error("Select error: %s", strerror(errno)); + } else { for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); ++iter ) - if ( FD_ISSET((*iter)->getReadDesc(),&rfds) ) - mReadable.push_back( *iter ); + if ( FD_ISSET((*iter)->getReadDesc(), &rfds) ) + mReadable.push_back(*iter); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); ++iter ) - if ( FD_ISSET((*iter)->getWriteDesc(),&rfds) ) - mWriteable.push_back( *iter ); + if ( FD_ISSET((*iter)->getWriteDesc(), &rfds) ) + mWriteable.push_back(*iter); } - return( nFound ); + return nFound; } -const Select::CommsList &Select::getReadable() const -{ - return( mReadable ); +const Select::CommsList &Select::getReadable() const { + return mReadable; } -const Select::CommsList &Select::getWriteable() const -{ - return( mWriteable ); +const Select::CommsList &Select::getWriteable() const { + return mWriteable; } From 6da673dd698546097448b0f5af6968e825507029 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 09:25:36 -0500 Subject: [PATCH 046/138] remove unused includes --- src/zm_eventstream.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 2c8ec7c08..a60723d86 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -1,5 +1,5 @@ // -// ZoneMinder Event Class Implementation, $Date$, $Revision$ +// ZoneMinder Event Stream Class Implementation // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or @@ -16,24 +16,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // - -#include -#include +// #include -#include -#include -#include -#include -#include -#include -#include -#include #include "zm.h" #include "zm_db.h" #include "zm_time.h" #include "zm_mpeg.h" -#include "zm_signal.h" #include "zm_event.h" #include "zm_eventstream.h" #include "zm_storage.h" From b44f14691e8cfe2776a8f91e8cd345948c32430d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 15:08:09 -0500 Subject: [PATCH 047/138] Don't exit after 30 minutes. Google code style. --- scripts/zmcontrol.pl.in | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index 32c1132d6..474148df3 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -162,24 +162,24 @@ if ( $options{command} ) { listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!"); my $rin = ''; - vec( $rin, fileno(SERVER), 1 ) = 1; + vec($rin, fileno(SERVER), 1) = 1; my $win = $rin; my $ein = $win; my $timeout = MAX_COMMAND_WAIT; - while( 1 ) { + while ( 1 ) { my $nfound = select(my $rout = $rin, undef, undef, $timeout); if ( $nfound > 0 ) { - if ( vec( $rout, fileno(SERVER), 1 ) ) { + if ( vec($rout, fileno(SERVER), 1) ) { my $paddr = accept(CLIENT, SERVER); my $message = ; + close(CLIENT); next if !$message; my $params = jsonDecode($message); - Debug( Dumper( $params ) ); + Debug(Dumper($params)); my $command = $params->{command}; - close(CLIENT); if ( $command eq 'quit' ) { last; } elsif ( $command ) { @@ -192,7 +192,7 @@ if ( $options{command} ) { } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { - # Likely just SIGHUP + # Likely just SIGHUP Debug("Can't select: $!"); } elsif ( $! == EPIPE ) { Error("Can't select: $!"); @@ -200,17 +200,14 @@ if ( $options{command} ) { Fatal("Can't select: $!"); } } else { - #print( "Select timed out\n" ); - last; + Debug('Select timed out'); } } # end while forever Info("Control server $id/$protocol exiting"); unlink($sock_file); $control->close(); - exit(0); } # end if !server up - exit(0); 1; From 30363c1d4be207f95c7c6c70362c5140f4534b75 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 15:08:29 -0500 Subject: [PATCH 048/138] Add Monitor Status loading --- scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm index 80b4fb75a..bbcb5af71 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm @@ -32,6 +32,7 @@ require ZoneMinder::Base; require ZoneMinder::Object; require ZoneMinder::Storage; require ZoneMinder::Server; +require ZoneMinder::Monitor_Status; #our @ISA = qw(Exporter ZoneMinder::Base); use parent qw(ZoneMinder::Object); @@ -245,6 +246,14 @@ sub control { } } # end sub control +sub Status { + my $self = shift; + $$self{Status} = shift if @_; + if ( ! $$self{Status} ) { + $$self{Status} = ZoneMinder::Monitor_Status->find_one(MonitorId=>$$self{Id}); + } + return $$self{Status}; +} 1; __END__ From 98c2c6cccfef7d0105b97941ddbff7528b07cf39 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 15:09:43 -0500 Subject: [PATCH 049/138] Add Status,CaptureFPS,AnalysisFPS, CaptureBandWidth to MonitorStatus fields --- scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm index 059c38121..9a9077653 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm @@ -39,6 +39,10 @@ $table = 'Monitor_Status'; $serial = $primary_key = 'MonitorId'; %fields = map { $_ => $_ } qw( MonitorId + Status + CaptureFPS + AnalysisFPS + CaptureBandwidth TotalEvents TotalEventDiskSpace HourEvents @@ -54,6 +58,10 @@ $serial = $primary_key = 'MonitorId'; ); %defaults = ( + Status => 'Unknown', + CaptureFPS => undef, + AnalysisFPS => undef, + CaptureBandwidth => undef, TotalEvents => undef, TotalEventDiskSpace => undef, HourEvents => undef, From 51ebaecc44ba1d00e1499621f2cb438609fe6e5d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 15:10:23 -0500 Subject: [PATCH 050/138] Fix MN and the various Event Total substitutions --- scripts/zmfilter.pl.in | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index b2dd7a2b4..38873e348 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -656,9 +656,11 @@ sub substituteTags { # First we'd better check what we need to get # We have a filter and an event, do we need any more # monitor information? - my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; + my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/; + my $need_status = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; my $Monitor = $Event->Monitor() if $need_monitor; + my $Status = $Monitor->Status() if $need_status; # Do we need the image information too? my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD)%/; @@ -688,13 +690,13 @@ sub substituteTags { my $url = $Config{ZM_URL}; $text =~ s/%ZP%/$url/g; - $text =~ s/%MN%/$Event->{MonitorName}/g; - $text =~ s/%MET%/$Monitor->{TotalEvents}/g; - $text =~ s/%MEH%/$Monitor->{HourEvents}/g; - $text =~ s/%MED%/$Monitor->{DayEvents}/g; - $text =~ s/%MEW%/$Monitor->{WeekEvents}/g; - $text =~ s/%MEM%/$Monitor->{MonthEvents}/g; - $text =~ s/%MEA%/$Monitor->{ArchivedEvents}/g; + $text =~ s/%MN%/$Monitor->{Name}/g; + $text =~ s/%MET%/$Status->{TotalEvents}/g; + $text =~ s/%MEH%/$Status->{HourEvents}/g; + $text =~ s/%MED%/$Status->{DayEvents}/g; + $text =~ s/%MEW%/$Status->{WeekEvents}/g; + $text =~ s/%MEM%/$Status->{MonthEvents}/g; + $text =~ s/%MEA%/$Status->{ArchivedEvents}/g; $text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g; $text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g; $text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g; From 1b472edc2b7507720516121eabe58a4223a284bf Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 16:58:22 -0500 Subject: [PATCH 051/138] fix cast --- src/zm_buffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_buffer.cpp b/src/zm_buffer.cpp index be20c008e..76d11d969 100644 --- a/src/zm_buffer.cpp +++ b/src/zm_buffer.cpp @@ -38,8 +38,8 @@ unsigned int Buffer::expand(unsigned int count) { int headSpace = mHead - mStorage; int tailSpace = spare - headSpace; int width = mTail - mHead; - if ( spare > static_castcount ) { - if ( tailSpace < static_castcount ) { + if ( spare > static_cast(count) ) { + if ( tailSpace < static_cast(count) ) { memmove(mStorage, mHead, mSize); mHead = mStorage; mTail = mHead + width; From 4d8f45d284a522f54a862824c81d6597582f2f60 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Nov 2020 16:59:21 -0500 Subject: [PATCH 052/138] There is no need to copy query. We do not modify it. --- src/zms.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/zms.cpp b/src/zms.cpp index 38917a388..8ecaf4e36 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -102,9 +102,7 @@ int main(int argc, const char *argv[], char **envp) { Debug(1, "Query: %s", query); - char temp_query[1024]; - strncpy(temp_query, query, sizeof(temp_query)-1); - char *q_ptr = temp_query; + char *q_ptr = (char *)query; char *parms[16]; // Shouldn't be more than this int parm_no = 0; while ( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) { From 660636c0e2763571704486bcf9694978fd1e5d62 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 22 Nov 2020 16:54:42 -0500 Subject: [PATCH 053/138] loop if we get egain when flocking. Warn when format not specified. --- src/zm_fifo.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 577281890..bf1ac5484 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -206,7 +206,6 @@ void FifoStream::setStreamStart(const char * path) { void FifoStream::setStreamStart(int monitor_id, const char * format) { char diag_path[PATH_MAX]; - const char * filename; Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); if ( !strcmp(format, "reference") ) { @@ -218,6 +217,9 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) { staticConfig.PATH_SOCKS.c_str(), monitor->Id()); stream_type = MJPEG; } else { + if ( strcmp(format, "raw") ) { + Warning("Unknown or unspecified format. Defaulting to raw"); + } snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%d.log", staticConfig.PATH_SOCKS.c_str(), monitor->Id()); stream_type = RAW; @@ -245,13 +247,13 @@ void FifoStream::runStream() { return; } int res = flock(fd_lock, LOCK_EX | LOCK_NB); - while ( (res == EAGAIN) and (! zm_terminate) ) { + while ( (res < 0 and errno == EAGAIN) and (! zm_terminate) ) { Warning("Flocking problem on %s: - %s", lock_file, strerror(errno)); sleep(1); res = flock(fd_lock, LOCK_EX | LOCK_NB); } if ( res < 0 ) { - Error("Flocking problem on %s: - %s", lock_file, strerror(errno)); + Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file, strerror(errno)); close(fd_lock); return; } From e0a30ab5e0077fb2c4bf94fdbdb8ac1f1421ab1d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 22 Nov 2020 17:28:53 -0500 Subject: [PATCH 054/138] Only log an error if we haven't been given sigpipe. --- src/zm_fifo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index bf1ac5484..d743611b8 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -117,7 +117,8 @@ bool FifoStream::sendRAWFrames() { return false; } if ( fwrite(buffer, bytes_read, 1, stdout) != 1 ) { - Error("Problem during writing: %s", strerror(errno)); + if ( !zm_terminate ) + Error("Problem during writing: %s", strerror(errno)); close(fd); return false; } From 4deb3a8d845cd407538ed0ef4f46169493810e94 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 23 Nov 2020 19:31:21 -0500 Subject: [PATCH 055/138] escape the word Groups --- db/zm_update-1.31.5.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_update-1.31.5.sql b/db/zm_update-1.31.5.sql index 859487e88..d315c8e84 100644 --- a/db/zm_update-1.31.5.sql +++ b/db/zm_update-1.31.5.sql @@ -10,7 +10,7 @@ SET @s = (SELECT IF( AND column_name = 'ParentId' ) > 0, "SELECT 'Column GroupId exists in Groups'", -"ALTER TABLE Groups ADD `ParentId` int(10) unsigned AFTER `Name`" +"ALTER TABLE `Groups` ADD `ParentId` int(10) unsigned AFTER `Name`" )); PREPARE stmt FROM @s; From be27630a8508f6e0517fb6e3b70832cdc694c5f2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 23 Nov 2020 19:33:53 -0500 Subject: [PATCH 056/138] escape the word Groups --- db/zm_update-1.31.7.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_update-1.31.7.sql b/db/zm_update-1.31.7.sql index 1221d9adb..0afd76ce5 100644 --- a/db/zm_update-1.31.7.sql +++ b/db/zm_update-1.31.7.sql @@ -3,7 +3,7 @@ SET @s = (SELECT IF( AND table_name = 'Groups' AND column_name = 'MonitorIds' ) > 0, - "ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL", + "ALTER TABLE `Groups` MODIFY `MonitorIds` text NOT NULL", "SELECT 'Groups no longer has MonitorIds'" )); From d6345d32fdec3f5e8ccb36a79bebc1cc50d37089 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 25 Nov 2020 09:53:06 -0500 Subject: [PATCH 057/138] Reduce error level of warnings about monitors having Function==NONE --- src/zm_stream.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 613e6c97e..98c08922d 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -52,8 +52,9 @@ bool StreamBase::loadMonitor(int p_monitor_id) { Error("Unable to load monitor id %d for streaming", monitor_id); return false; } + if ( monitor->GetFunction() == Monitor::NONE ) { - Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); + Info("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); return false; } @@ -71,7 +72,7 @@ bool StreamBase::checkInitialised() { return false; } if ( monitor->GetFunction() == Monitor::NONE ) { - Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); + Info("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); return false; } if ( !monitor->ShmValid() ) { From ff4b0e6309b428ee8f5a08be595a240217b0d81b Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 25 Nov 2020 13:03:16 -0600 Subject: [PATCH 058/138] add option to disable thumb animation --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in | 13 +++++++++++++ web/skins/classic/views/js/console.js | 2 +- web/skins/classic/views/js/console.js.php | 1 + web/skins/classic/views/js/events.js | 2 +- web/skins/classic/views/js/events.js.php | 1 + web/skins/classic/views/js/frames.js | 2 +- web/skins/classic/views/js/frames.js.php | 1 + 7 files changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 98ed7fa69..bc194ebc9 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -2748,6 +2748,19 @@ our @options = ( requires => [ { name => 'ZM_WEB_LIST_THUMBS', value => 'yes' } ], category => 'web', }, + { + name => 'ZM_WEB_ANIMATE_THUMBS', + default => 'yes', + description => 'Enlarge and show the live stream when a thumbnail is hovered over', + help => q` + Enabling this option causes the static thumbnail, shown on certain + views, to enlarge and show the live stream, when the thumbnail is + hovered over by the mouse. + `, + type => $types{boolean}, + requires => [ { name => 'ZM_WEB_LIST_THUMBS', value => 'yes' } ], + category => 'web', + }, { name => 'ZM_WEB_USE_OBJECT_TAGS', default => 'yes', diff --git a/web/skins/classic/views/js/console.js b/web/skins/classic/views/js/console.js index 52b7aedc2..5ef7029f3 100644 --- a/web/skins/classic/views/js/console.js +++ b/web/skins/classic/views/js/console.js @@ -204,7 +204,7 @@ function initPage() { } ); // Setup the thumbnail video animation - initThumbAnimation(); + if ( WEB_ANIMATE_THUMBS ) initThumbAnimation(); $j('.functionLnk').click(manageFunctionModal); } // end function initPage diff --git a/web/skins/classic/views/js/console.js.php b/web/skins/classic/views/js/console.js.php index 9f5355dc6..c5a7f5e92 100644 --- a/web/skins/classic/views/js/console.js.php +++ b/web/skins/classic/views/js/console.js.php @@ -1,4 +1,5 @@ var consoleRefreshTimeout = ; +var WEB_ANIMATE_THUMBS = ; "; var yesString = ""; var noString = ""; var WEB_LIST_THUMBS = ; +var WEB_ANIMATE_THUMBS = ; diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js index c569d8447..4594ed392 100644 --- a/web/skins/classic/views/js/frames.js +++ b/web/skins/classic/views/js/frames.js @@ -82,7 +82,7 @@ function initPage() { backBtn.prop('disabled', !document.referrer.length); // Setup the thumbnail animation - initThumbAnimation(); + if ( WEB_ANIMATE_THUMBS ) initThumbAnimation(); // Some toolbar events break the thumbnail animation, so re-init eventlistener table.on('all.bs.table', initThumbAnimation); diff --git a/web/skins/classic/views/js/frames.js.php b/web/skins/classic/views/js/frames.js.php index e8aa37a4d..b27e1e5ec 100644 --- a/web/skins/classic/views/js/frames.js.php +++ b/web/skins/classic/views/js/frames.js.php @@ -1,2 +1,3 @@ var eid = ; var WEB_LIST_THUMBS = ; +var WEB_ANIMATE_THUMBS = ; From 28614ce7039f665ae2db6504589be5db385b5536 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 25 Nov 2020 13:16:11 -0600 Subject: [PATCH 059/138] change where we check for web_animate_thumbs --- web/skins/classic/views/js/console.js | 12 +++++++----- web/skins/classic/views/js/events.js | 12 +++++++----- web/skins/classic/views/js/frames.js | 12 +++++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/web/skins/classic/views/js/console.js b/web/skins/classic/views/js/console.js index 5ef7029f3..1098635b8 100644 --- a/web/skins/classic/views/js/console.js +++ b/web/skins/classic/views/js/console.js @@ -11,10 +11,12 @@ function thumbnail_onmouseout(event) { } function initThumbAnimation() { - $j('.colThumbnail img').each(function() { - this.addEventListener('mouseover', thumbnail_onmouseover, false); - this.addEventListener('mouseout', thumbnail_onmouseout, false); - }); + if ( WEB_ANIMATE_THUMBS ) { + $j('.colThumbnail img').each(function() { + this.addEventListener('mouseover', thumbnail_onmouseover, false); + this.addEventListener('mouseout', thumbnail_onmouseout, false); + }); + } } function setButtonStates( element ) { @@ -204,7 +206,7 @@ function initPage() { } ); // Setup the thumbnail video animation - if ( WEB_ANIMATE_THUMBS ) initThumbAnimation(); + initThumbAnimation(); $j('.functionLnk').click(manageFunctionModal); } // end function initPage diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index 8300b7f73..6be2747d5 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -87,10 +87,12 @@ function thumbnail_onmouseout(event) { } function initThumbAnimation() { - $j('.colThumbnail img').each(function() { - this.addEventListener('mouseover', thumbnail_onmouseover, false); - this.addEventListener('mouseout', thumbnail_onmouseout, false); - }); + if ( WEB_ANIMATE_THUMBS ) { + $j('.colThumbnail img').each(function() { + this.addEventListener('mouseover', thumbnail_onmouseover, false); + this.addEventListener('mouseout', thumbnail_onmouseout, false); + }); + } } // Returns the event id's of the selected rows @@ -195,7 +197,7 @@ function initPage() { backBtn.prop('disabled', !document.referrer.length); // Setup the thumbnail video animation - if ( WEB_ANIMATE_THUMBS ) initThumbAnimation(); + initThumbAnimation(); // Some toolbar events break the thumbnail animation, so re-init eventlistener table.on('all.bs.table', initThumbAnimation); diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js index 4594ed392..cb09d1952 100644 --- a/web/skins/classic/views/js/frames.js +++ b/web/skins/classic/views/js/frames.js @@ -39,10 +39,12 @@ function thumbnail_onmouseout(event) { } function initThumbAnimation() { - $j('.colThumbnail img').each(function() { - this.addEventListener('mouseover', thumbnail_onmouseover, false); - this.addEventListener('mouseout', thumbnail_onmouseout, false); - }); + if ( WEB_ANIMATE_THUMBS ) { + $j('.colThumbnail img').each(function() { + this.addEventListener('mouseover', thumbnail_onmouseover, false); + this.addEventListener('mouseout', thumbnail_onmouseout, false); + }); + } } function processClicks(event, field, value, row, $element) { @@ -82,7 +84,7 @@ function initPage() { backBtn.prop('disabled', !document.referrer.length); // Setup the thumbnail animation - if ( WEB_ANIMATE_THUMBS ) initThumbAnimation(); + initThumbAnimation(); // Some toolbar events break the thumbnail animation, so re-init eventlistener table.on('all.bs.table', initThumbAnimation); From 08dabf1a4f530ad32bf8ce6cb248ab73c8bd4f0f Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 25 Nov 2020 13:29:14 -0600 Subject: [PATCH 060/138] don't add "zoom" class when thumb animation is off --- web/skins/classic/views/console.php | 3 ++- web/skins/classic/views/js/events.js | 3 ++- web/skins/classic/views/js/frames.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 11c1e12ad..3a736529c 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -296,6 +296,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $imgHTML=''; if ( ZM_WEB_LIST_THUMBS && ($monitor['Status'] == 'Connected') && $running ) { $options = array(); + $thmbClass = ZM_WEB_ANIMATE_THUMBS ? 'colThumbnail zoom-right' : 'colThumbnail'; $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); $options['width'] = ZM_WEB_LIST_THUMB_WIDTH; $options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor; @@ -308,7 +309,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $thmbWidth = ( $options['width'] ) ? 'width:'.$options['width'].'px;' : ''; $thmbHeight = ( $options['height'] ) ? 'height:'.$options['height'].'px;' : ''; - $imgHTML = '
' : '>'; $imgHTML .= ' Date: Thu, 26 Nov 2020 17:00:05 -0600 Subject: [PATCH 061/138] deploy bstable to watch view --- web/skins/classic/views/js/watch.js | 348 +++++++++++----------------- web/skins/classic/views/watch.php | 43 +++- 2 files changed, 171 insertions(+), 220 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index feea5e698..337d36081 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1,7 +1,89 @@ +var streamStatus; +var auth_hash; +var alarmState = STATE_IDLE; +var lastAlarmState = STATE_IDLE; var backBtn = $j('#backBtn'); var settingsBtn = $j('#settingsBtn'); var enableAlmBtn = $j('#enableAlmBtn'); var forceAlmBtn = $j('#forceAlmBtn'); +var table = $j('#eventList'); + +if ( monitorType != 'WebSite' ) { + var streamCmdParms = 'view=request&request=stream&connkey='+connKey; + if ( auth_hash ) { + streamCmdParms += '&auth='+auth_hash; + } + var streamCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'chain', + onError: getStreamCmdError, + onSuccess: getStreamCmdResponse, + onFailure: getStreamCmdFailure + } ); + var streamCmdTimer = null; +} + +/* +This is the format of the json object sent by bootstrap-table + +var params = +{ +"type":"get", +"data": + { + "search":"some search text", + "sort":"StartDateTime", + "order":"asc", + "offset":0, + "limit":25 + "filter": + { + "Name":"some advanced search text" + "StartDateTime":"some more advanced search text" + } + }, +"cache":true, +"contentType":"application/json", +"dataType":"json" +}; +*/ + +// Called by bootstrap-table to retrieve zm event data +function ajaxRequest(params) { + // Maintain legacy behavior of sorting by Id column only + delete params.data.order; + delete params.data.limit; + params.data.sort = 'Id desc'; + params.data.count = maxDisplayEvents; + params.data.id = monitorId; + if ( auth_hash ) params.data.auth = auth_hash; + + $j.getJSON(thisUrl + '?view=request&request=status&entity=events', params.data) + .done(function(data) { + var rows = processRows(data.events); + // rearrange the result into what bootstrap-table expects + params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows}); + }) + .fail(logAjaxFail); +} + +function processRows(rows) { + $j.each(rows, function(ndx, row) { + var eid = row.Id; + var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId; + + row.Id = '' + eid + ''; + row.Name = '' + row.Name + ''; + row.Frames = '' + row.Frames + ''; + row.AlarmFrames = '' + row.AlarmFrames + ''; + row.MaxScore = '' + row.MaxScore + ''; + row.Delete = ''; + }); + + return rows; +} function showEvents() { $('ptzControls').addClass('hidden'); @@ -56,9 +138,6 @@ function changeScale() { } } -var alarmState = STATE_IDLE; -var lastAlarmState = STATE_IDLE; - function setAlarmState( currentAlarmState ) { alarmState = currentAlarmState; @@ -103,39 +182,22 @@ function setAlarmState( currentAlarmState ) { $('MediaPlayer').Stop(); } } - eventCmdQuery(); + table.bootstrapTable('refresh'); } lastAlarmState = alarmState; } // end function setAlarmState( currentAlarmState ) -if ( monitorType != 'WebSite' ) { - var streamCmdParms = 'view=request&request=stream&connkey='+connKey; - if ( auth_hash ) { - streamCmdParms += '&auth='+auth_hash; - } - var streamCmdReq = new Request.JSON( { - url: monitorUrl, - method: 'get', - timeout: AJAX_TIMEOUT, - link: 'chain', - onError: getStreamCmdError, - onSuccess: getStreamCmdResponse, - onFailure: getStreamCmdFailure - } ); - var streamCmdTimer = null; -} - -var streamStatus; - function getStreamCmdError(text, error) { console.log(error); // Error are normally due to failed auth. reload the page. window.location.reload(); } + function getStreamCmdFailure(xhr) { console.log(xhr); } + function getStreamCmdResponse(respObj, respText) { watchdogOk('stream'); if ( streamCmdTimer ) { @@ -233,7 +295,7 @@ function getStreamCmdResponse(respObj, respText) { } streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); - eventCmdParms = eventCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + table.bootstrapTable('refresh'); controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } // end if have a new auth hash } // end if respObj.status @@ -503,187 +565,6 @@ function cmdForce() { } } -function getActResponse( respObj, respText ) { - if ( respObj.result == 'Ok' ) { - if ( respObj.refreshParent && window.opener ) { - console.log('refreshing parent'); - window.opener.location.reload(); - } - } - eventCmdQuery(); -} - -function deleteEvent(event, eventId) { - var actParms = 'view=request&request=event&action=delete&id='+eventId; - if ( auth_hash ) { - actParms += '&auth='+auth_hash; - } - var actReq = new Request.JSON( { - url: thisUrl, - method: 'post', - timeout: 3000, - onSuccess: getActResponse - } ); - actReq.send(actParms); - event.stop(); -} - -if ( monitorType != 'WebSite' ) { - var eventCmdParms = "view=request&request=status&entity=events&id="+monitorId+"&count="+maxDisplayEvents+"&sort=Id%20desc"; - if ( auth_hash ) { - eventCmdParms += '&auth='+auth_hash; - } - var eventCmdReq = new Request.JSON( { - url: monitorUrl, - method: 'get', - timeout: AJAX_TIMEOUT, - link: 'cancel', - onSuccess: getEventCmdResponse, - onTimeout: eventCmdQuery - } ); - var eventCmdTimer = null; - var eventCmdFirst = true; -} - -function highlightRow( row ) { - $(row).toggleClass('highlight'); -} - -function getEventCmdResponse( respObj, respText ) { - watchdogOk('event'); - if ( eventCmdTimer ) { - eventCmdTimer = clearTimeout(eventCmdTimer); - } - - if ( respObj.result == 'Ok' ) { - var dbEvents = respObj.events.reverse(); - var eventList = $('eventList'); - var eventListBody = $(eventList).getElement('tbody'); - var eventListRows = $(eventListBody).getElements('tr'); - - eventListRows.each( function(row) { - row.removeClass('updated'); - } ); - - for ( var i = 0; i < dbEvents.length; i++ ) { - var zm_event = dbEvents[i]; - var row = $('event'+zm_event.Id); - var newEvent = (row == null ? true : false); - if ( newEvent ) { - row = new Element('tr', {'id': 'event'+zm_event.Id}); - new Element('td', {'class': 'colId'}).inject(row); - new Element('td', {'class': 'colName'}).inject(row); - new Element('td', {'class': 'colTime'}).inject(row); - new Element('td', {'class': 'colSecs'}).inject(row); - new Element('td', {'class': 'colFrames'}).inject(row); - new Element('td', {'class': 'colScore'}).inject(row); - new Element('td', {'class': 'colDelete'}).inject(row); - - var link = new Element('a', { - 'href': '#', - 'events': { - 'click': openEvent.pass( [ - zm_event.Id, - '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1' - ] ) - } - }); - link.set('text', zm_event.Id); - link.inject(row.getElement('td.colId')); - - link = new Element('a', { - 'href': '#', - 'events': { - 'click': openEvent.pass( [ - zm_event.Id, - '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1' - ] ) - } - }); - link.set('text', zm_event.Name); - link.inject(row.getElement('td.colName')); - - row.getElement('td.colTime').set('text', zm_event.StartDateTime); - row.getElement('td.colSecs').set('text', zm_event.Length); - - link = new Element('a', {'href': '#', 'events': {'click': openFrames.pass( [zm_event.Id] )}}); - link.set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); - link.inject(row.getElement('td.colFrames')); - - link = new Element('a', {'href': '#', 'events': {'click': openFrame.pass( [zm_event.Id, '0'] )}}); - link.set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); - link.inject(row.getElement('td.colScore')); - - link = new Element('button', { - 'type': 'button', - 'title': deleteString, - 'data-event-id': zm_event.Id, - 'events': { - 'click': function(e) { - var event_id = e.target.getAttribute('data-event-id'); - if ( !event_id ) { - console.log('No event id in deleteEvent'); - console.log(e); - } else { - deleteEvent(e, event_id); - } - }, - 'mouseover': highlightRow.pass(row), - 'mouseout': highlightRow.pass(row) - } - }); - link.set('text', 'X'); - link.inject(row.getElement('td.colDelete')); - - if ( i == 0 ) { - row.inject($(eventListBody)); - } else { - row.inject($(eventListBody), 'top'); - if ( !eventCmdFirst ) { - row.addClass('recent'); - } - } - } else { - row.getElement('td.colName a').set('text', zm_event.Name); - row.getElement('td.colSecs').set('text', zm_event.Length); - row.getElement('td.colFrames a').set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); - row.getElement('td.colScore a').set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); - row.removeClass('recent'); - } - row.addClass('updated'); - } // end foreach event - - var rows = $(eventListBody).getElements('tr'); - for ( var i = 0; i < rows.length; i++ ) { - if ( !rows[i].hasClass('updated') ) { - rows[i].destroy(); - rows.splice( i, 1 ); - i--; - } - } - while ( rows.length > maxDisplayEvents ) { - rows[rows.length-1].destroy(); - rows.length--; - } - } else { - checkStreamForErrors('getEventCmdResponse', respObj); - } // end if objresult == ok - - var eventCmdTimeout = eventsRefreshTimeout; - if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { - eventCmdTimeout = eventCmdTimeout/5; - } - eventCmdTimer = eventCmdQuery.delay(eventCmdTimeout); - eventCmdFirst = false; -} - -function eventCmdQuery() { - if ( eventCmdTimer ) { // avoid firing another if we are firing one - eventCmdTimer = clearTimeout(eventCmdTimer); - } - eventCmdReq.send(eventCmdParms); -} - if ( monitorType != 'WebSite' ) { var controlParms = 'view=request&request=control&id='+monitorId; if ( auth_hash ) { @@ -802,14 +683,12 @@ function appletRefresh() { var watchdogInactive = { 'stream': false, - 'status': false, - 'event': false + 'status': false }; var watchdogFunctions = { 'stream': streamCmdQuery, 'status': statusCmdQuery, - 'event': eventCmdQuery }; //Make sure the various refreshes are still taking effect @@ -872,6 +751,44 @@ function getSettingsModal() { .fail(logAjaxFail); } +function processClicks(event, field, value, row, $element) { + if ( field == 'Delete' ) { + $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') + .done(function(data) { + insertModalHtml('deleteConfirm', data.html); + manageDelConfirmModalBtns(); + $j('#deleteConfirm').data('eid', row.Id.replace(/(<([^>]+)>)/gi, '')); + $j('#deleteConfirm').modal('show'); + }) + .fail(logAjaxFail); + } +} + +// Manage the DELETE CONFIRMATION modal button +function manageDelConfirmModalBtns() { + document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) { + if ( ! canEditEvents ) { + enoperm(); + return; + } + + var eid = $j('#deleteConfirm').data('eid'); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eid) + .done( function(data) { + table.bootstrapTable('refresh'); + $j('#deleteConfirm').modal('hide'); + }) + .fail(logAjaxFail); + }); + + // Manage the CANCEL modal button + document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) { + $j('#deleteConfirm').modal('hide'); + }); +} + function initPage() { if ( canViewControl ) { // Load the PTZ Preset modal into the DOM @@ -889,9 +806,6 @@ function initPage() { watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2); } - eventCmdTimer = eventCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); - watchdogCheck.pass('event').periodical(eventsRefreshTimeout*2); - if ( canStreamNative || (streamMode == 'single') ) { var streamImg = $('imageFeed').getElement('img'); if ( !streamImg ) { @@ -924,6 +838,7 @@ function initPage() { } else if ( monitorRefresh > 0 ) { setInterval(reloadWebSite, monitorRefresh*1000); } + // Manage the BACK button document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { evt.preventDefault(); @@ -948,6 +863,17 @@ function initPage() { // Only enable the settings button for local cameras settingsBtn.prop('disabled', !canViewControl); if ( monitorType != 'Local' ) settingsBtn.hide(); + + // Init the bootstrap-table + if ( monitorType != 'WebSite' ) table.bootstrapTable({icons: icons}); + + // Update table rows each time after new data is loaded + table.on('post-body.bs.table', function(data) { + $j('#eventList tr:contains("New Event")').addClass('recent'); + }); + + // Take appropriate action when the user clicks on a cell + table.on('click-cell.bs.table', processClicks); } // initPage // Kick everything off diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index a8b9f24bd..579f8b11e 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -159,21 +159,46 @@ if ( $showPtzControls ) { } if ( canView('Events') && ($monitor->Type() != 'WebSite') ) { ?> -
- + +
+
+ - - - - - - - + + + + + + + + + + + +
 
Date: Thu, 26 Nov 2020 19:28:25 -0600 Subject: [PATCH 062/138] eslint --- web/skins/classic/views/js/watch.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 337d36081..bff6246ea 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -754,13 +754,13 @@ function getSettingsModal() { function processClicks(event, field, value, row, $element) { if ( field == 'Delete' ) { $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') - .done(function(data) { - insertModalHtml('deleteConfirm', data.html); - manageDelConfirmModalBtns(); - $j('#deleteConfirm').data('eid', row.Id.replace(/(<([^>]+)>)/gi, '')); - $j('#deleteConfirm').modal('show'); - }) - .fail(logAjaxFail); + .done(function(data) { + insertModalHtml('deleteConfirm', data.html); + manageDelConfirmModalBtns(); + $j('#deleteConfirm').data('eid', row.Id.replace(/(<([^>]+)>)/gi, '')); + $j('#deleteConfirm').modal('show'); + }) + .fail(logAjaxFail); } } From a6245d9e90be40d2d8d38f9c5351f1aaa1be5fa8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Nov 2020 13:27:51 -0500 Subject: [PATCH 063/138] Minor code style update --- src/zm_storage.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/zm_storage.cpp b/src/zm_storage.cpp index 4bba82bab..9eae26b85 100644 --- a/src/zm_storage.cpp +++ b/src/zm_storage.cpp @@ -26,25 +26,25 @@ #include #include -Storage::Storage() { +Storage::Storage() : id(0) { Warning("Instantiating default Storage Object. Should not happen."); - id = 0; strcpy(name, "Default"); if ( staticConfig.DIR_EVENTS[0] != '/' ) { // not using an absolute path. Make it one by appending ZM_PATH_WEB - snprintf( path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str( ), staticConfig.DIR_EVENTS.c_str() ); + snprintf(path, sizeof(path), "%s/%s", + staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str()); } else { - strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1 ); + strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1); } scheme = MEDIUM; scheme_str = "Medium"; } -Storage::Storage( MYSQL_ROW &dbrow ) { +Storage::Storage(MYSQL_ROW &dbrow) { unsigned int index = 0; - id = atoi( dbrow[index++] ); - strncpy( name, dbrow[index++], sizeof(name)-1 ); - strncpy( path, dbrow[index++], sizeof(path)-1 ); + id = atoi(dbrow[index++]); + strncpy(name, dbrow[index++], sizeof(name)-1); + strncpy(path, dbrow[index++], sizeof(path)-1); type_str = std::string(dbrow[index++]); scheme_str = std::string(dbrow[index++]); if ( scheme_str == "Deep" ) { @@ -57,16 +57,15 @@ Storage::Storage( MYSQL_ROW &dbrow ) { } /* If a zero or invalid p_id is passed, then the old default path will be assumed. */ -Storage::Storage( unsigned int p_id ) { - id = 0; +Storage::Storage(unsigned int p_id) : id(p_id) { - if ( p_id ) { + if ( id ) { char sql[ZM_SQL_SML_BUFSIZ]; - snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%d", p_id); - Debug(2,"Loading Storage for %d using %s", p_id, sql ); + snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id); + Debug(2, "Loading Storage for %u using %s", id, sql); zmDbRow dbrow; if ( !dbrow.fetch(sql) ) { - Error("Unable to load storage area for id %d: %s", p_id, mysql_error(&dbconn)); + Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn)); } else { unsigned int index = 0; id = atoi(dbrow[index++]); @@ -81,17 +80,18 @@ Storage::Storage( unsigned int p_id ) { } else { scheme = SHALLOW; } - Debug(1, "Loaded Storage area %d '%s'", id, this->Name()); + Debug(1, "Loaded Storage area %d '%s'", id, name); } } if ( !id ) { if ( staticConfig.DIR_EVENTS[0] != '/' ) { // not using an absolute path. Make it one by appending ZM_PATH_WEB - snprintf(path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str()); + snprintf(path, sizeof(path), "%s/%s", + staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str()); } else { strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1); } - Debug(1,"No id passed to Storage constructor. Using default path %s instead", path); + Debug(1, "No id passed to Storage constructor. Using default path %s instead", path); strcpy(name, "Default"); scheme = MEDIUM; scheme_str = "Medium"; From 95cbc053fcd60fdd202bbd09405d6019d6a73f35 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Nov 2020 13:28:38 -0500 Subject: [PATCH 064/138] Try other storage areas if we fail to create event dir on the assigned area. --- src/zm_event.cpp | 200 +++++++++++++++++++++++++++++------------------ src/zm_event.h | 1 + 2 files changed, 127 insertions(+), 74 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index efd7d8bc6..7f0ae2031 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -78,8 +78,6 @@ Event::Event( start_time = now; } - Storage * storage = monitor->getStorage(); - scheme = storage->Scheme(); unsigned int state_id = 0; zmDbRow dbrow; @@ -87,8 +85,58 @@ Event::Event( state_id = atoi(dbrow[0]); } + Storage * storage = monitor->getStorage(); + if ( !SetPath(storage) ) { + // Try another + Warning("Failed creating event dir at %s", storage->Path()); + + std::string sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id()); + if ( monitor->ServerId() ) + sql += stringtf(" AND ServerId=%u", monitor->ServerId()); + + Debug(1, "%s", sql.c_str()); + storage = nullptr; + + MYSQL_RES *result = zmDbFetch(sql.c_str()); + if ( result ) { + for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { + storage = new Storage(atoi(dbrow[0])); + if ( SetPath(storage) ) + break; + delete storage; + storage = nullptr; + } // end foreach row of Storage + mysql_free_result(result); + result = nullptr; + } + if ( !storage ) { + Info("No valid local storage area found. Trying all other areas."); + // Try remote + std::string sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL"; + if ( monitor->ServerId() ) + sql += stringtf(" OR ServerId != %u", monitor->ServerId()); + + MYSQL_RES *result = zmDbFetch(sql.c_str()); + if ( result ) { + for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { + storage = new Storage(atoi(dbrow[0])); + if ( SetPath(storage) ) + break; + delete storage; + storage = nullptr; + } // end foreach row of Storage + mysql_free_result(result); + result = nullptr; + } + } + if ( !storage ) { + storage = new Storage(); + Warning("Failed to find a storage area to save events."); + } + } + Debug(1, "Using storage area at %s", path.c_str()); + char sql[ZM_SQL_MED_BUFSIZ]; - struct tm *stime = localtime(&start_time.tv_sec); snprintf(sql, sizeof(sql), "INSERT INTO Events " "( MonitorId, StorageId, Name, StartDateTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme )" " VALUES ( %u, %u, 'New Event', from_unixtime( %ld ), %u, %u, '%s', '%s', %u, %d, %d, '%s', %d, '%s' )", @@ -116,7 +164,7 @@ Event::Event( db_mutex.unlock(); if ( untimedEvent ) { - Warning("Event %d has zero time, setting to current", id); + Warning("Event %" PRIu64 " has zero time, setting to current", id); } end_time.tv_sec = 0; frames = 0; @@ -124,77 +172,7 @@ Event::Event( tot_score = 0; max_score = 0; alarm_frame_written = false; - - std::string id_file; - - path = stringtf("%s/%d", storage->Path(), monitor->Id()); - // Try to make the Monitor Dir. Normally this would exist, but in odd cases might not. - if ( mkdir(path.c_str(), 0755) ) { - if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); - } - - if ( storage->Scheme() == Storage::DEEP ) { - - int dt_parts[6]; - dt_parts[0] = stime->tm_year-100; - dt_parts[1] = stime->tm_mon+1; - dt_parts[2] = stime->tm_mday; - dt_parts[3] = stime->tm_hour; - dt_parts[4] = stime->tm_min; - dt_parts[5] = stime->tm_sec; - - std::string date_path; - std::string time_path; - - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { - path += stringtf("/%02d", dt_parts[i]); - - if ( mkdir(path.c_str(), 0755) ) { - // FIXME This should not be fatal. Should probably move to a different storage area. - if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); - } - if ( i == 2 ) - date_path = path; - } - time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec); - - // Create event id symlink - id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id); - if ( symlink(time_path.c_str(), id_file.c_str()) < 0 ) - Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno)); - } else if ( storage->Scheme() == Storage::MEDIUM ) { - path += stringtf("/%04d-%02d-%02d", - stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday - ); - if ( mkdir(path.c_str(), 0755) ) { - if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); - } - path += stringtf("/%" PRIu64, id); - if ( mkdir(path.c_str(), 0755) ) { - if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); - } - } else { - path += stringtf("/%" PRIu64, id); - if ( mkdir(path.c_str(), 0755) ) { - if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); - } - - // Create empty id tag file - id_file = stringtf("%s/.%" PRIu64, path.c_str(), id); - if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) { - fclose(id_fp); - } else { - Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno)); - } - } // deep storage or not - last_db_frame = 0; - video_name = ""; snapshot_file = path + "/snapshot.jpg"; @@ -739,3 +717,77 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a end_time = timestamp; } // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) + +bool Event::SetPath(Storage *storage) { + scheme = storage->Scheme(); + + path = stringtf("%s/%d", storage->Path(), monitor->Id()); + // Try to make the Monitor Dir. Normally this would exist, but in odd cases might not. + if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); + return false; + } + + struct tm *stime = localtime(&start_time.tv_sec); + if ( scheme == Storage::DEEP ) { + + int dt_parts[6]; + dt_parts[0] = stime->tm_year-100; + dt_parts[1] = stime->tm_mon+1; + dt_parts[2] = stime->tm_mday; + dt_parts[3] = stime->tm_hour; + dt_parts[4] = stime->tm_min; + dt_parts[5] = stime->tm_sec; + + std::string date_path; + std::string time_path; + + for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { + path += stringtf("/%02d", dt_parts[i]); + + if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); + return false; + } + if ( i == 2 ) + date_path = path; + } + time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec); + + // Create event id symlink + std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id); + if ( symlink(time_path.c_str(), id_file.c_str()) < 0 ) { + Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno)); + return false; + } + } else if ( scheme == Storage::MEDIUM ) { + path += stringtf("/%04d-%02d-%02d", + stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday + ); + if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); + return false; + } + path += stringtf("/%" PRIu64, id); + if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); + return false; + } + } else { + path += stringtf("/%" PRIu64, id); + if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); + return false; + } + + // Create empty id tag file + std::string id_file = stringtf("%s/.%" PRIu64, path.c_str(), id); + if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) { + fclose(id_fp); + } else { + Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno)); + return false; + } + } // deep storage or not + return true; +} // end bool Event::SetPath diff --git a/src/zm_event.h b/src/zm_event.h index ab837658e..e8e1e567e 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -133,6 +133,7 @@ class Event { void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); void WriteDbFrames(); void UpdateFramesDelta(double offset); + bool SetPath(Storage *storage); public: static const char *getSubPath( struct tm *time ) { From 3a943d6f09a94e9b94754a8293217cc636bfefb1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Nov 2020 13:29:05 -0500 Subject: [PATCH 065/138] Add ServerId method, code style --- src/zm_monitor.h | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 9a023d53a..e43f1dda0 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -430,29 +430,22 @@ public: return shared_data && shared_data->valid; } - inline unsigned int Id() const { - return id; - } - inline const char *Name() const { - return name; - } + inline unsigned int Id() const { return id; } + inline const char *Name() const { return name; } + inline unsigned int ServerId() { return server_id; } inline Storage *getStorage() { if ( ! storage ) { storage = new Storage( storage_id ); } return storage; } - inline Function GetFunction() const { - return( function ); - } + inline Function GetFunction() const { return function; } inline bool Enabled() const { if ( function <= MONITOR ) return false; return enabled; } - inline const char *EventPrefix() const { - return event_prefix; - } + inline const char *EventPrefix() const { return event_prefix; } inline bool Ready() const { if ( function <= MONITOR ) return false; @@ -463,9 +456,7 @@ public: return false; return( enabled && shared_data->active ); } - inline bool Exif() const { - return embed_exif; - } + inline bool Exif() const { return embed_exif; } Orientation getOrientation() const; unsigned int Width() const { return width; } From 80c4e2fa57b88000597074d914ec497981021c82 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Nov 2020 13:29:57 -0500 Subject: [PATCH 066/138] Fix table not being 100%. Use appropriate bootstrap style labels for radios --- web/ajax/modals/storage.php | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/web/ajax/modals/storage.php b/web/ajax/modals/storage.php index 70cc956dc..c439c384c 100644 --- a/web/ajax/modals/storage.php +++ b/web/ajax/modals/storage.php @@ -31,12 +31,11 @@ 'Shallow' => translate('Shallow'), ); - $servers = ZM\Server::find( null, array('order'=>'lower(Name)') ); + global $Servers; $ServersById = array(); - foreach ( $servers as $S ) { + foreach ( $Servers as $S ) { $ServersById[$S->Id()] = $S; } - ?>