From 3b9b49c44d540f5b6071288b0b8a5ad23126fdb5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 15:51:03 -0500 Subject: [PATCH 001/319] wip work on fixing zms mp4 to mjpeg --- src/zm_ffmpeg_input.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index ab7cbe8d3..ea21d82be 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -102,7 +102,7 @@ int FFmpeg_Input::Open(const char *filepath) { } // end int FFmpeg_Input::Open( const char * filepath ) AVFrame *FFmpeg_Input::get_frame(int stream_id) { - Debug(1, "Getting frame from stream %d", stream_id); + Debug(4, "Getting frame from stream %d", stream_id); int frameComplete = false; AVPacket packet; @@ -138,9 +138,9 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { frame = zm_av_frame_alloc(); } ret = zm_send_packet_receive_frame(context, frame, packet); - if ( ret <= 0 ) { - Error("Unable to decode frame at frame %d: %s, continuing", - streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str()); + if ( ret < 0 ) { + Error("Unable to decode frame at frame %d: %d %s, continuing", + streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str()); zm_av_packet_unref(&packet); av_frame_free(&frame); continue; @@ -175,7 +175,9 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { } // Have to grab a frame to update our current frame to know where we are get_frame(stream_id); - } // end if ! frame + zm_dump_frame(frame, "Got first frame, returning it"); + return frame; + } // end if !frame if ( frame->pts > seek_target ) { zm_dump_frame(frame, "frame->pts > seek_target, seek backwards"); @@ -192,12 +194,15 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { } // end if frame->pts > seek_target // Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want. - if ( frame->pts <= seek_target ) { + if ( frame->pts <= seek_target ) { zm_dump_frame(frame, "pts <= seek_target"); while ( frame && (frame->pts < seek_target) ) { - if ( !get_frame(stream_id) ) + if ( !get_frame(stream_id) ) { + Warning("Got no frame. returning nothing"); return frame; + } } + zm_dump_frame(frame, "frame->pts <= seek_target, got"); return frame; } From 9972a2fd0c686529bfda11523687130c9fe312a9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Dec 2019 15:51:08 -0500 Subject: [PATCH 002/319] wip work on fixing zms mp4 to mjpeg --- src/zm_eventstream.cpp | 193 +++++++++++++++++++++++------------------ src/zm_eventstream.h | 2 +- 2 files changed, 111 insertions(+), 84 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 2ca12972d..f9a4b5d9c 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -244,7 +244,7 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->frames[i-1].timestamp = last_timestamp + ((i-last_id)*frame_delta); event_data->frames[i-1].offset = event_data->frames[i-1].timestamp - event_data->start_time; event_data->frames[i-1].in_db = false; - Debug(3,"Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", + Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", i, event_data->frames[i-1].timestamp, event_data->frames[i-1].offset, @@ -260,7 +260,7 @@ bool EventStream::loadEventData(uint64_t event_id) { last_id = id; last_delta = delta; last_timestamp = event_data->frames[id-1].timestamp; - Debug(4, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", + Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", id, event_data->frames[id-1].timestamp, event_data->frames[id-1].offset, @@ -303,7 +303,7 @@ bool EventStream::loadEventData(uint64_t event_id) { void EventStream::processCommand(const CmdMsg *msg) { Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]); // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) { + switch ( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : Debug(1, "Got PAUSE command"); @@ -498,6 +498,7 @@ void EventStream::processCommand(const CmdMsg *msg) { // Do nothing, for now break; } + struct { uint64_t event_id; int progress; @@ -535,9 +536,9 @@ void EventStream::processCommand(const CmdMsg *msg) { exit(0); updateFrameRate((double)event_data->frame_count/event_data->duration); -} // void EventStream::processCommand(const CmdMsg *msg) +} // void EventStream::processCommand(const CmdMsg *msg) -void EventStream::checkEventLoaded() { +bool EventStream::checkEventLoaded() { static char sql[ZM_SQL_SML_BUFSIZ]; if ( curr_frame_id <= 0 ) { @@ -552,7 +553,7 @@ void EventStream::checkEventLoaded() { // No event change required Debug(3, "No event change required, as curr frame %d <=> event frames %d", curr_frame_id, event_data->frame_count); - return; + return false; } // Event change required. @@ -586,6 +587,7 @@ void EventStream::checkEventLoaded() { else curr_frame_id = 1; Debug(2, "New frame id = %d", curr_frame_id); + return true; } else { Debug(2, "No next event loaded using %s. Pausing", sql); if ( curr_frame_id <= 0 ) @@ -594,7 +596,7 @@ void EventStream::checkEventLoaded() { curr_frame_id = event_data->frame_count; paused = true; sendTextFrame("No more event data found"); - } // end if found a new event or not + } // end if found a new event or not mysql_free_result(result); forceEventChange = false; } else { @@ -605,7 +607,8 @@ void EventStream::checkEventLoaded() { curr_frame_id = event_data->frame_count; paused = true; } -} // void EventStream::checkEventLoaded() + return false; +} // void EventStream::checkEventLoaded() Image * EventStream::getImage( ) { static char filepath[PATH_MAX]; @@ -650,7 +653,8 @@ bool EventStream::sendFrame(int delta_us) { Image *send_image = prepareImage(&image); if ( !vid_stream ) { - vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); + vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, + send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType()); vid_stream->OpenStream(); } @@ -805,6 +809,10 @@ void EventStream::runStream() { updateFrameRate((double)event_data->frame_count/event_data->duration); gettimeofday(&start, NULL); uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec; + uint64_t last_frame_offset = 0; + + bool in_event = true; + double time_to_event = 0; while ( !zm_terminate ) { gettimeofday(&now, NULL); @@ -829,77 +837,17 @@ void EventStream::runStream() { Debug(2, "Not checking command queue"); } - // Get current frame data FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - //Info( "cst:%.2f", curr_stream_time ); - //Info( "cfid:%d", curr_frame_id ); - //Info( "fdt:%d", frame_data->timestamp ); if ( !paused ) { - Debug(3, "Not paused at frame %d", curr_frame_id); - - // This next bit is to determine if we are in the current event time wise - // and whether to show an image saying how long until the next event. - bool in_event = true; - double time_to_event = 0; - if ( replay_rate > 0 ) { - time_to_event = event_data->frames[0].timestamp - curr_stream_time; - if ( time_to_event > 0 ) - in_event = false; - } else if ( replay_rate < 0 ) { - time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; - if ( time_to_event > 0 ) - in_event = false; - } - Debug(1, "replay rate(%d) in_event(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f", - replay_rate, in_event, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp); - if ( !in_event ) { - double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; - Debug(1, "Ctual delta time = %f = %f - %f", actual_delta_time , TV_2_FLOAT(now) , last_frame_sent); - // > 1 second - if ( actual_delta_time > 1 ) { - Debug(1, "Sending time to next event frame"); - static char frame_text[64]; - snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event); - if ( !sendTextFrame(frame_text) ) - zm_terminate = true; - } else { - Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time); - } - //else - //{ - // FIXME ICON But we are not paused. We are somehow still in the event? - double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); - //double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //// ZM_RATE_BASE == 100, and 1x replay_rate is 100 - //double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000; - if ( ! sleep_time ) { - sleep_time += STREAM_PAUSE_WAIT/1000000; - } - curr_stream_time += sleep_time; - Debug(2, "Sleeping (%dus) because we are not at the next event yet, adding %f", STREAM_PAUSE_WAIT, sleep_time); - usleep(STREAM_PAUSE_WAIT); - - //curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //} - continue; - } // end if !in_event - // Figure out if we should send this frame - Debug(3, "cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); + Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); // If we are streaming and this frame is due to be sent // frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2 // so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc. + if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { - delta_us = (unsigned int)(frame_data->delta * 1000000); - Debug(3, "frame delta %uus ", delta_us); - // if effective > base we should speed up frame delivery - delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); - Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps); - // but must not exceed maxfps - delta_us = max(delta_us, 1000000 / maxfps); - Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps); send_frame = true; } } else if ( step != 0 ) { @@ -908,16 +856,48 @@ void EventStream::runStream() { step = 0; send_frame = true; } else if ( !send_frame ) { - // We are paused, not stepping and doing nothing + // We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; if ( actual_delta_time > MAX_STREAM_DELAY ) { // Send keepalive Debug(2, "Sending keepalive frame"); send_frame = true; - //} else { - //Debug(2, "Not Sending keepalive frame"); } - } // end if streaming stepping or doing nothing + } // end if streaming stepping or doing nothing + + // time_to_event > 0 means that we are not in the event + if ( time_to_event > 0 ) { + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; + Debug(1, "Actual delta time = %f = %f - %f", actual_delta_time, TV_2_FLOAT(now), last_frame_sent); + // > 1 second + if ( actual_delta_time > 1 ) { + Debug(1, "Sending time to next event frame"); + static char frame_text[64]; + snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event); + if ( !sendTextFrame(frame_text) ) + zm_terminate = true; + } else { + Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time); + } + //else + //{ + // FIXME ICON But we are not paused. We are somehow still in the event? + double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); + //double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //// ZM_RATE_BASE == 100, and 1x replay_rate is 100 + //double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000; + if ( !sleep_time ) { + sleep_time += STREAM_PAUSE_WAIT/1000000; + } + curr_stream_time += sleep_time; + time_to_event -= sleep_time; + Debug(2, "Sleeping (%dus) because we are not at the next event yet, adding %f", STREAM_PAUSE_WAIT, sleep_time); + usleep(STREAM_PAUSE_WAIT); + + //curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //} + continue; + } // end if !in_event if ( send_frame ) { if ( !sendFrame(delta_us) ) { @@ -929,6 +909,17 @@ void EventStream::runStream() { curr_stream_time = frame_data->timestamp; if ( !paused ) { + + // delta is since the last frame + delta_us = (unsigned int)(frame_data->delta * 1000000); + Debug(3, "frame delta %uus ", delta_us); + // if effective > base we should speed up frame delivery + delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); + Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps); + // but must not exceed maxfps + delta_us = max(delta_us, 1000000/maxfps); + Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps); + // +/- 1? What if we are skipping frames? curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod; // sending the frame may have taken some time, so reload now @@ -936,7 +927,12 @@ void EventStream::runStream() { uint64_t now_usec = (now.tv_sec * 1000000 + now.tv_usec); // we incremented by replay_rate, so might have jumped past frame_count - if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id >= event_data->frame_count) ) { + if ( (mode == MODE_SINGLE) && ( + (curr_frame_id < 1 ) + || + ((unsigned int)curr_frame_id >= event_data->frame_count) + ) + ) { Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); curr_frame_id = 1; // Have to reset start_usec to now when replaying @@ -951,9 +947,21 @@ void EventStream::runStream() { // There are two ways to go about this, not sure which is correct. // you can calculate the relationship between now and the start // or calc the relationship from the last frame. I think from the start is better as it self-corrects + // + if ( last_frame_offset ) { + // We assume that we are going forward and the next frame is in the future. + delta_us = frame_data->offset * 1000000 - (now_usec-start_usec); + // - (now_usec - start_usec); + Debug(2, "New delta_us now %" PRIu64 " - start %" PRIu64 " = %d offset %" PRId64 " - elapsed = %dusec", + now_usec, start_usec, now_usec-start_usec, frame_data->offset * 1000000, delta_us); + } else { + Debug(2, "No last frame_offset, no sleep"); + delta_us = 0; + } + last_frame_offset = frame_data->offset * 1000000; - if ( send_frame && type != STREAM_MPEG ) { - if ( delta_us > 0) { + if ( send_frame && (type != STREAM_MPEG) ) { + if ( delta_us > 0 ) { if ( delta_us > MAX_SLEEP_USEC ) { Debug(1, "Limiting sleep to %d because calculated sleep is too long %d", MAX_SLEEP_USEC, delta_us); delta_us = MAX_SLEEP_USEC; @@ -978,14 +986,33 @@ void EventStream::runStream() { } usleep(delta_us); } + // We are paused, so might be stepping + //if ( step != 0 )// Adding 0 is cheaper than an if 0 + // curr_frame_id starts at 1 though, so we might skip the first frame? + curr_frame_id += step; } // end if !paused - //if ( step != 0 )// Adding 0 is cheaper than an if 0 - // curr_frame_id starts at 1 though, so we might skip the first frame? - curr_frame_id += step; - // Detects when we hit end of event and will load the next event or previous event - checkEventLoaded(); + if ( checkEventLoaded() ) { + // Have change of event + + // This next bit is to determine if we are in the current event time wise + // and whether to show an image saying how long until the next event. + if ( replay_rate > 0 ) { + // This doesn't make sense unless we have hit the end of the event. + time_to_event = event_data->frames[0].timestamp - curr_stream_time; + Debug(1, "replay rate(%d) time_to_event(%f)=frame timestamp:%f - curr_stream_time(%f)", + replay_rate, time_to_event, + event_data->frames[0].timestamp, + curr_stream_time); + + } else if ( replay_rate < 0 ) { + time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; + Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f", + replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp); + } + + } } // end while ! zm_terminate #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 5e7d91bb2..980f2f6e1 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -89,7 +89,7 @@ class EventStream : public StreamBase { bool loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( int monitor_id, time_t event_time ); - void checkEventLoaded(); + bool checkEventLoaded(); void processCommand( const CmdMsg *msg ); bool sendFrame( int delta_us ); From d87bf5a15663b7f1181ca74d103799cb0bed420f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Dec 2019 11:02:12 -0500 Subject: [PATCH 003/319] replace the rate display with a dropdown --- web/skins/classic/views/event.php | 7 ++++++- web/skins/classic/views/js/event.js | 31 ++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 947bd3972..a17e48b65 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -257,7 +257,12 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {

: Replay - : x + : +translate('Stopped'), 100 => '1x', 200=>'2x', 400=>'4x', 800=>'8x' ); +echo htmlSelect('rate', $rates, $rate, array('id'=>'rateValue')); +?> + : 0s : 1x
diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 76fc47c79..96f56dbd4 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -200,6 +200,19 @@ function changeReplayMode() { refreshWindow(); } +function changeRate() { + var rate = $j('select[name="rate"]').val(); + if ( ! rate ) { + pauseClicked(); + } else { + if ( vid ) { + vid.playbackRate(rate/100); + Cookie.write('zmEventRate', rate, {duration: 10*365}); + } + } +} + + var streamParms = "view=request&request=stream&connkey="+connKey; if ( auth_hash ) { streamParms += '&auth='+auth_hash; @@ -245,7 +258,7 @@ function getCmdResponse( respObj, respText ) { if ( streamStatus.paused == true ) { streamPause( ); } else { - $j('#rateValue').html(streamStatus.rate); + $j('select[name="rate"]').val(streamStatus.rate*100); Cookie.write('zmEventRate', streamStatus.rate*100, {duration: 10*365}); streamPlay( ); } @@ -296,7 +309,7 @@ function vjsPause() { function streamPause( ) { $j('#modeValue').html('Paused'); - $j('#rateValue').html('0'); + $j('select[name="rate"]').val('0'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); @@ -322,7 +335,7 @@ function vjsPlay() { //catches if we change mode programatically if ( intervalRewind ) { stopFastRev(); } - $j('#rateValue').html(vid.playbackRate()); + $j('select[name="rate"]').val(vid.playbackRate()); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); streamPlay(); } @@ -350,7 +363,7 @@ function streamFastFwd( action ) { if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) { setButtonState($('fastFwdBtn'), 'unavail'); } - $j('#rateValue').html(vid.playbackRate()); + $j('#rate').val(vid.playbackRate()); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); } else { streamReq.send(streamParms+"&command="+CMD_FASTFWD); @@ -397,7 +410,7 @@ function streamFastRev( action ) { setButtonState( $('fastRevBtn'), 'unavail' ); } clearInterval(intervalRewind); - $j('#rateValue').html(-revSpeed); + $j('#rate').val(-revSpeed); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); intervalRewind = setInterval(function() { if (vid.currentTime() <= 0) { @@ -588,7 +601,7 @@ function getEventResponse( respObj, respText ) { CurEventDefVideoPath = null; $j('#modeValue').html('Replay'); $j('#zoomValue').html('1'); - $j('#rateValue').html('1'); + $j('#rate').val('100'); vjsPanZoom('zoomOut'); } else { drawProgressBar(); @@ -1066,7 +1079,8 @@ function initPage() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); - if ( rate > 1 ) { + // rate is in % so 100 would be 1x + if ( rate > 0 ) { // rate should be 100 = 1x, etc. vid.playbackRate(rate/100); } @@ -1091,6 +1105,9 @@ function initPage() { nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues if (scale == "auto") changeScale(); + document.querySelectorAll('select[name="rate"]').forEach(function(el) { + el.onchange = window['changeRate']; + }); } // Kick everything off From 1bdabefb2d06ef343782cb9c723fb9b506a8bb3e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 7 Jan 2020 17:07:35 -0500 Subject: [PATCH 004/319] fix single stepping in reverse but actually modifying curr_frame_id --- src/zm_eventstream.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index e2f1862ab..2ed2dc167 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -383,6 +383,8 @@ void EventStream::processCommand(const CmdMsg *msg) { paused = true; replay_rate = ZM_RATE_BASE; step = -1; + curr_frame_id -= 1; + if ( curr_frame_id < 1 ) curr_frame_id = 1; break; case CMD_FASTREV : Debug(1, "Got FAST REV command"); @@ -904,7 +906,7 @@ void EventStream::runStream() { send_frame = true; } } else if ( step != 0 ) { - Debug(2, "Paused with step"); + Debug(2, "Paused with step %d", step); // We are paused and are just stepping forward or backward one frame step = 0; send_frame = true; From 1959b2e070e1968101677d01b0a3bb19db53455c Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 20 Feb 2020 07:45:33 -0800 Subject: [PATCH 005/319] Add optional library jwt --- src/jwt-cpp/vcpkg/fix-wolfssl.patch | 214 ++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 src/jwt-cpp/vcpkg/fix-wolfssl.patch diff --git a/src/jwt-cpp/vcpkg/fix-wolfssl.patch b/src/jwt-cpp/vcpkg/fix-wolfssl.patch new file mode 100644 index 000000000..cf535a1e2 --- /dev/null +++ b/src/jwt-cpp/vcpkg/fix-wolfssl.patch @@ -0,0 +1,214 @@ +diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h +index ed93fd5..977e6aa 100644 +--- a/include/jwt-cpp/jwt.h ++++ b/include/jwt-cpp/jwt.h +@@ -6,11 +6,13 @@ + #include + #include + #include +-#include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + + //If openssl version less than 1.1 + #if OPENSSL_VERSION_NUMBER < 269484032 +@@ -280,7 +282,7 @@ namespace jwt { + throw signature_verification_exception("failed to verify signature: VerifyInit failed"); + if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) + throw signature_verification_exception("failed to verify signature: VerifyUpdate failed"); +- auto res = EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get()); ++ auto res = EVP_VerifyFinal(ctx.get(), (unsigned char*)signature.data(), signature.size(), pkey.get()); + if (res != 1) + throw signature_verification_exception("evp verify final failed: " + std::to_string(res) + " " + ERR_error_string(ERR_get_error(), NULL)); + } +@@ -342,8 +344,8 @@ namespace jwt { + if(!pkey) + throw rsa_exception("at least one of public or private key need to be present"); + +- if(EC_KEY_check_key(pkey.get()) == 0) +- throw ecdsa_exception("failed to load key: key is invalid"); ++ //if(EC_KEY_check_key(pkey.get()) == 0) ++ // throw ecdsa_exception("failed to load key: key is invalid"); + } + /** + * Sign jwt data +@@ -355,7 +357,7 @@ namespace jwt { + const std::string hash = generate_hash(data); + + std::unique_ptr +- sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); ++ sig(wolfSSL_ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); + if(!sig) + throw signature_generation_exception(); + #ifdef OPENSSL10 +@@ -470,109 +472,6 @@ namespace jwt { + const size_t signature_length; + }; + +- /** +- * Base class for PSS-RSA family of algorithms +- */ +- struct pss { +- /** +- * Construct new pss algorithm +- * \param public_key RSA public key in PEM format +- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. +- * \param public_key_password Password to decrypt public key pem. +- * \param privat_key_password Password to decrypt private key pem. +- * \param md Pointer to hash function +- * \param name Name of the algorithm +- */ +- pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) +- : md(md), alg_name(name) +- { +- if (!private_key.empty()) { +- pkey = helper::load_private_key_from_string(private_key, private_key_password); +- } else if(!public_key.empty()) { +- pkey = helper::load_public_key_from_string(public_key, public_key_password); +- } else +- throw rsa_exception("at least one of public or private key need to be present"); +- } +- /** +- * Sign jwt data +- * \param data The data to sign +- * \return ECDSA signature for the given data +- * \throws signature_generation_exception +- */ +- std::string sign(const std::string& data) const { +- auto hash = this->generate_hash(data); +- +- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); +- const int size = RSA_size(key.get()); +- +- std::string padded(size, 0x00); +- if (!RSA_padding_add_PKCS1_PSS_mgf1(key.get(), (unsigned char*)padded.data(), (const unsigned char*)hash.data(), md(), md(), -1)) +- throw signature_generation_exception("failed to create signature: RSA_padding_add_PKCS1_PSS_mgf1 failed"); +- +- std::string res(size, 0x00); +- if (RSA_private_encrypt(size, (const unsigned char*)padded.data(), (unsigned char*)res.data(), key.get(), RSA_NO_PADDING) < 0) +- throw signature_generation_exception("failed to create signature: RSA_private_encrypt failed"); +- return res; +- } +- /** +- * Check if signature is valid +- * \param data The data to check signature against +- * \param signature Signature provided by the jwt +- * \throws signature_verification_exception If the provided signature does not match +- */ +- void verify(const std::string& data, const std::string& signature) const { +- auto hash = this->generate_hash(data); +- +- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); +- const int size = RSA_size(key.get()); +- +- std::string sig(size, 0x00); +- if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) +- throw signature_verification_exception("Invalid signature"); +- +- if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1)) +- throw signature_verification_exception("Invalid signature"); +- } +- /** +- * Returns the algorithm name provided to the constructor +- * \return Algorithmname +- */ +- std::string name() const { +- return alg_name; +- } +- private: +- /** +- * Hash the provided data using the hash function specified in constructor +- * \param data Data to hash +- * \return Hash of data +- */ +- std::string generate_hash(const std::string& data) const { +-#ifdef OPENSSL10 +- std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); +-#else +- std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); +-#endif +- if(EVP_DigestInit(ctx.get(), md()) == 0) +- throw signature_generation_exception("EVP_DigestInit failed"); +- if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) +- throw signature_generation_exception("EVP_DigestUpdate failed"); +- unsigned int len = 0; +- std::string res; +- res.resize(EVP_MD_CTX_size(ctx.get())); +- if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) +- throw signature_generation_exception("EVP_DigestFinal failed"); +- res.resize(len); +- return res; +- } +- +- /// OpenSSL structure containing keys +- std::shared_ptr pkey; +- /// Hash generator function +- const EVP_MD*(*md)(); +- /// Algorithmname +- const std::string alg_name; +- }; +- + /** + * HS256 algorithm + */ +@@ -700,51 +599,6 @@ namespace jwt { + {} + }; + +- /** +- * PS256 algorithm +- */ +- struct ps256 : public pss { +- /** +- * Construct new instance of algorithm +- * \param public_key RSA public key in PEM format +- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. +- * \param public_key_password Password to decrypt public key pem. +- * \param privat_key_password Password to decrypt private key pem. +- */ +- explicit ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") +- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") +- {} +- }; +- /** +- * PS384 algorithm +- */ +- struct ps384 : public pss { +- /** +- * Construct new instance of algorithm +- * \param public_key RSA public key in PEM format +- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. +- * \param public_key_password Password to decrypt public key pem. +- * \param privat_key_password Password to decrypt private key pem. +- */ +- explicit ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") +- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") +- {} +- }; +- /** +- * PS512 algorithm +- */ +- struct ps512 : public pss { +- /** +- * Construct new instance of algorithm +- * \param public_key RSA public key in PEM format +- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. +- * \param public_key_password Password to decrypt public key pem. +- * \param privat_key_password Password to decrypt private key pem. +- */ +- explicit ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") +- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") +- {} +- }; + } + + /** From 04b78ac7e61e0d7e278547b1b76d16646d9cc5bb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Feb 2020 17:56:56 -0500 Subject: [PATCH 006/319] typo and punctuation fixes. --- docs/userguide/options/options_system.rst | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/userguide/options/options_system.rst b/docs/userguide/options/options_system.rst index bd871a1e8..33c59cf1c 100644 --- a/docs/userguide/options/options_system.rst +++ b/docs/userguide/options/options_system.rst @@ -14,13 +14,13 @@ LANG_DEFAULT - ZoneMinder allows the web interface to use languages other than E OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder. -AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder. +AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authenticated 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured in ZoneMinder. AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways. -AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above +AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and passwords. Although these strings are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above. -AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failure. +AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failures. AUTH_HASH_TTL - Time before ZM auth will expire (does not apply to API tokens). The default has traditionally been 2 hours. A new hash will automatically be regenerated at half this value. @@ -34,11 +34,11 @@ OPT_USE_API - A global setting to enable/disable ZoneMinder APIs. If you are usi OPT_USE_LEGACY_AUTH - Starting version 1.34.0, ZoneMinder uses a more secure Authentication mechanism using JWT tokens. Older versions used a less secure MD5 based auth hash. It is recommended you turn this off after you are sure you don't need it. If you are using a 3rd party app that relies on the older API auth mechanisms, you will have to update that app if you turn this off. Note that zmNinja 1.3.057 onwards supports the new token system. -OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if its already installed. Please visit the `Event Notification Server project site `__ for installation instructions. +OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if it is already installed. Please visit the `Event Notification Server project site `__ for installation instructions. -OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid usernane and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reach out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen) +OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid username and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reaching out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen). -SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers: +SYSTEM_SHUTDOWN - this option puts a poweroff icon in the header of the ZM UI for users with System privilege accessi. This icon will allow the user to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers: :: @@ -46,9 +46,9 @@ SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full syst to perform the shutdown or reboot -OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system. +OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if youxr are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system. -FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. +FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. As of 1.34.0 filters should be automatically reloaded when saving a filter so this setting should have little effect. FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements. @@ -58,9 +58,9 @@ STATUS_UPDATE_INTERVAL - The zmstats daemon performs various db queries related WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked. -WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. +WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. Please note that some cameras can take up to 30 seconds to get a valid image, so this setting should be larger than that. -RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems. +RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems and run it manually if needed after a system crash. AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed. @@ -70,11 +70,11 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here. -CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable +CHECK_FOR_UPDATES - To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable. TELEMETRY_DATA - Enable collection of usage information of the local system and send it to the ZoneMinder development team. This data will be used to determine things like who and where our customers are, how big their systems are, the underlying hardware and operating system, etc. This is being done for the sole purpose of creating a better product for our target audience. This script is intended to be completely transparent to the end user, and can be disabled from the web console under Options. For more details on what information we collect, please refer to Zoneminder's privacy statement (available in the contextual help of TELEMETRY_DATA on your installation). -UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/`` +UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/``. SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored. From 76131d1887ec2584a3d365a6d299d34e0a321c7f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 1 Mar 2020 11:07:31 -0500 Subject: [PATCH 007/319] Fix timeline. Update parseFilterToTree to add missing operators. Fix logging calls to include ZM namespace. Update code style. --- .../classic/includes/timeline_functions.php | 379 +++++++++--------- web/skins/classic/views/timeline.php | 17 +- 2 files changed, 197 insertions(+), 199 deletions(-) diff --git a/web/skins/classic/includes/timeline_functions.php b/web/skins/classic/includes/timeline_functions.php index b5cc3909c..1c0995df4 100644 --- a/web/skins/classic/includes/timeline_functions.php +++ b/web/skins/classic/includes/timeline_functions.php @@ -1,6 +1,6 @@ $maxLines ) { @@ -29,20 +29,21 @@ function getYScale( $range, $minLines, $maxLines ) { } $scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1; - return( $scale ); + return $scale; } -function getSlotFrame( $slot ) { +function getSlotFrame($slot) { $slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1; + # FIXME what's with this false? if ( false && $slotFrame ) { $slotFrame -= $monitor['PreEventCount']; if ( $slotFrame < 1 ) $slotFrame = 1; } - return( $slotFrame ); + return $slotFrame; } -function parseFilterToTree( $filter ) { +function parseFilterToTree($filter) { if ( count($filter['terms']) <= 0 ) { return false; } @@ -68,73 +69,82 @@ function parseFilterToTree( $filter ) { 'or' => 4, ); - for ( $i = 0; $i <= count($terms); $i++ ) { - if ( !empty($terms[$i]['cnj']) ) { + for ( $i = 0; $i < count($terms); $i++ ) { + $term = $terms[$i]; + if ( !empty($term['cnj']) ) { while( true ) { if ( !count($postfixStack) ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; - } elseif ( $priorities[$terms[$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + } elseif ( $priorities[$term['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; } else { $postfixExpr[] = array_pop($postfixStack); } } - } - if ( !empty($terms[$i]['obr']) ) { - for ( $j = 0; $j < $terms[$i]['obr']; $j++ ) { - $postfixStack[] = array('type'=>'obr', 'value'=>$terms[$i]['obr']); + } # end if ! empty cnj + + if ( !empty($term['obr']) ) { + for ( $j = 0; $j < $term['obr']; $j++ ) { + $postfixStack[] = array('type'=>'obr', 'value'=>$term['obr']); } } - if ( !empty($terms[$i]['attr']) ) { + if ( !empty($term['attr']) ) { $dtAttr = false; - switch ( $terms[$i]['attr']) { + switch ( $term['attr']) { case 'MonitorName': - $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $terms[$i]['attr']); + $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $term['attr']); break; case 'ServerId': $sqlValue .= 'M.ServerId'; - break; + break; + case 'StorageServerId': + $sqlValue .= 'S.ServerId'; + break; + case 'FilterServerId': + $sqlValue .= ZM_SERVER_ID; + break; case 'DateTime': case 'StartDateTime': - $sqlValue = "E.StartTime"; + $sqlValue = 'E.StartTime'; $dtAttr = true; break; case 'Date': case 'StartDate': - $sqlValue = "to_days( E.StartTime )"; + $sqlValue = 'to_days(E.StartTime)'; $dtAttr = true; break; case 'Time': case 'StartTime': - $sqlValue = "extract( hour_second from E.StartTime )"; + $sqlValue = 'extract(hour_second from E.StartTime)'; break; case 'Weekday': case 'StartWeekday': - $sqlValue = "weekday( E.StartTime )"; + $sqlValue = 'weekday(E.StartTime)'; break; case 'EndDateTime': - $sqlValue = "E.EndTime"; + $sqlValue = 'E.EndTime'; $dtAttr = true; break; case 'EndDate': - $sqlValue = "to_days( E.EndTime )"; + $sqlValue = 'to_days(E.EndTime)'; $dtAttr = true; break; case 'EndTime': - $sqlValue = "extract( hour_second from E.EndTime )"; + $sqlValue = 'extract(hour_second from E.EndTime)'; break; case 'EndWeekday': - $sqlValue = "weekday( E.EndTime )"; + $sqlValue = 'weekday(E.EndTime)'; break; case 'Id': case 'Name': case 'MonitorId': case 'StorageId': + case 'SecondaryStorageId': case 'Length': case 'Frames': case 'AlarmFrames': @@ -145,7 +155,7 @@ function parseFilterToTree( $filter ) { case 'Notes': case 'StateId': case 'Archived': - $sqlValue = "E.".$terms[$i]['attr']; + $sqlValue = 'E.'.$term['attr']; break; case 'DiskPercent': // Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH @@ -174,53 +184,66 @@ function parseFilterToTree( $filter ) { $sqlValue = getDiskBlocks($StorageArea); break; default : - $sqlValue = $terms[$i]['attr']; + $sqlValue = $term['attr']; break; } if ( $dtAttr ) { - $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true); + $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true); } else { - $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue); + $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue); } } # end if attr - if ( isset($terms[$i]['op']) ) { - if ( empty($terms[$i]['op']) ) { - $terms[$i]['op'] = '='; + if ( isset($term['op']) ) { + if ( empty($term['op']) ) { + $term['op'] = '='; } - switch ( $terms[$i]['op']) { + switch ( $term['op']) { case '=' : case '!=' : case '>=' : case '>' : case '<' : case '<=' : - $sqlValue = $terms[$i]['op']; + case 'LIKE' : + case 'NOT LIKE': + $sqlValue = $term['op']; break; - case '=~' : + case '=~' : $sqlValue = 'regexp'; break; case '!~' : $sqlValue = 'not regexp'; break; case '=[]' : + case 'IN' : $sqlValue = 'in ('; break; case '![]' : $sqlValue = 'not in ('; break; + case 'IS' : + case 'IS NOT' : + if ( $term['val'] == 'Odd' ) { + $sqlValue .= ' % 2 = 1'; + } else if ( $term['val'] == 'Even' ) { + $sqlValue .= ' % 2 = 0'; + } else { + $sqlValue .= ' '.$term['op']; + } + break; default : - Error('Unknown operator in filter '. $terms[$i]['op']); + ZM\Error('Unknown operator in filter '.$term['op']); } while( true ) { if ( !count($postfixStack) ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue); + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue); + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); break; - } elseif ( $priorities[$terms[$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue ); + } elseif ( $priorities[$term['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue ); break; } else { $postfixExpr[] = array_pop($postfixStack); @@ -228,17 +251,23 @@ function parseFilterToTree( $filter ) { } // end while } // end if operator - if ( isset($terms[$i]['val']) ) { + if ( isset($term['val']) ) { $valueList = array(); - foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'])) as $value ) { - switch ( $terms[$i]['attr'] ) { + foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { + switch ( $term['attr'] ) { case 'MonitorName': case 'Name': case 'Cause': case 'Notes': - $value = "'$value'"; - break; - case 'ServerId': + if ( $term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE' ) { + $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' ) { @@ -260,17 +289,17 @@ function parseFilterToTree( $filter ) { case 'Date': case 'EndDate': case 'StartDate': - $value = "to_days('".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; case 'Time': case 'EndTime': case 'StartTime': - $value = "extract( hour_second from '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; case 'Weekday': case 'EndWeekday': case 'StartWeekday': - $value = "weekday( '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'weekday(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; default : if ( $value != 'NULL' ) @@ -278,11 +307,11 @@ function parseFilterToTree( $filter ) { } // end switch attribute $valueList[] = $value; } // end foreach value - $postfixExpr[] = array('type'=>'val', 'value'=>$terms[$i]['val'], 'sqlValue'=>join(',', $valueList)); + $postfixExpr[] = array('type'=>'val', 'value'=>$term['val'], 'sqlValue'=>join(',', $valueList)); } // end if has val - if ( !empty($terms[$i]['cbr']) ) { - for ( $j = 0; $j < $terms[$i]['cbr']; $j++ ) { + if ( !empty($term['cbr']) ) { + for ( $j = 0; $j < $term['cbr']; $j++ ) { while ( count($postfixStack) ) { $element = array_pop($postfixStack); if ( $element['type'] == 'obr' ) { @@ -292,8 +321,9 @@ function parseFilterToTree( $filter ) { $postfixExpr[] = $element; } } - } - } + } #end if cbr + } # end foreach term + while ( count($postfixStack) ) { $postfixExpr[] = array_pop($postfixStack); } @@ -314,11 +344,11 @@ function parseFilterToTree( $filter ) { $node = array('data'=>$element, 'count'=>2+$left['count']+$right['count'], 'right'=>$right, 'left'=>$left); $exprStack[] = $node; } else { - Fatal("Unexpected element type '".$element['type']."', value '".$element['value']."'"); + ZM\Fatal('Unexpected element type \''.$element['type'].'\', value \''.$element['value'].'\''); } } if ( count($exprStack) != 1 ) { - Fatal('Expression stack has '.count($exprStack).' elements'); + ZM\Fatal('Expression stack has '.count($exprStack).' elements'); } return array_pop($exprStack); } @@ -345,188 +375,153 @@ function parseTreeToInfix($tree) { return _parseTreeToInfix($tree); } -function _parseTreeToSQL( $node, $cbr=false ) -{ +function _parseTreeToSQL($node, $cbr=false) { $expression = ''; - if ( $node ) - { - if ( isset($node['left']) ) - { - if ( !empty($node['data']['bracket']) ) - $expression .= '( '; - $expression .= _parseTreeToSQL( $node['left'] ); - } - $inExpr = $node['data']['type'] == 'op' && ($node['data']['value'] == '=[]' || $node['data']['value'] == '![]'); - $expression .= $node['data']['sqlValue']; - if ( !$inExpr ) - $expression .= ' '; - if ( $cbr ) - $expression .= ') '; - if ( isset($node['right']) ) - { - $expression .= _parseTreeToSQL( $node['right'], $inExpr ); - if ( !empty($node['data']['bracket']) ) - $expression .= ') '; - } - } - return( $expression ); + if ( !$node ) + return $expression; + + if ( isset($node['left']) ) { + if ( !empty($node['data']['bracket']) ) + $expression .= '( '; + $expression .= _parseTreeToSQL($node['left']); + } + $inExpr = $node['data']['type'] == 'op' && ( + $node['data']['value'] == '=[]' + or + $node['data']['value'] == '![]' + or + $node['data']['value'] == 'IN' + or + $node['data']['value'] == 'NOT IN' + ); + $expression .= $node['data']['sqlValue']; + if ( !$inExpr ) + $expression .= ' '; + if ( $cbr ) + $expression .= ') '; + if ( isset($node['right']) ) { + $expression .= _parseTreeToSQL($node['right'], $inExpr); + if ( !empty($node['data']['bracket']) ) + $expression .= ') '; + } # end if right + return $expression; } -function parseTreeToSQL( $tree ) -{ - return( _parseTreeToSQL( $tree ) ); +function parseTreeToSQL($tree) { + return _parseTreeToSQL($tree); } -function _parseTreeToFilter( $node, &$terms, &$level ) -{ +function _parseTreeToFilter($node, &$terms, &$level) { $elements = array(); - if ( $node ) - { - if ( isset($node['left']) ) - { + if ( $node ) { + if ( isset($node['left']) ) { if ( !empty($node['data']['bracket']) ) $terms[$level]['obr'] = 1; _parseTreeToFilter( $node['left'], $terms, $level ); } - if ( $node['data']['type'] == 'cnj' ) - { + if ( $node['data']['type'] == 'cnj' ) { $level++; } $terms[$level][$node['data']['type']] = $node['data']['value']; - if ( isset($node['right']) ) - { - _parseTreeToFilter( $node['right'], $terms, $level ); + if ( isset($node['right']) ) { + _parseTreeToFilter($node['right'], $terms, $level); if ( !empty($node['data']['bracket']) ) $terms[$level]['cbr'] = 1; } } } -function parseTreeToFilter( $tree ) -{ +function parseTreeToFilter($tree) { $terms = array(); - if ( isset($tree) ) - { + if ( isset($tree) ) { $level = 0; - _parseTreeToFilter( $tree, $terms, $level ); + _parseTreeToFilter($tree, $terms, $level); } - return( array( 'Query' => array( 'terms' => $terms ) ) ); + return array('Query' => array('terms' => $terms)); } -function parseTreeToQuery( $tree ) -{ - $filter = parseTreeToFilter( $tree ); - parseFilter( $filter, false, '&' ); - return( $filter['query'] ); +function parseTreeToQuery($tree) { + $filter = parseTreeToFilter($tree); + parseFilter($filter, false, '&'); + return $filter['query']; } -function _drawTree( $node, $level ) -{ - if ( isset($node['left']) ) - { - _drawTree( $node['left'], $level+1 ); +function _drawTree($node, $level) { + if ( isset($node['left']) ) { + _drawTree($node['left'], $level+1); } - echo str_repeat( ".", $level*2 ).$node['data']['value']."
"; - if ( isset($node['right']) ) - { - _drawTree( $node['right'], $level+1 ); + echo str_repeat('.', $level*2).$node['data']['value'].'
'; + if ( isset($node['right']) ) { + _drawTree($node['right'], $level+1); } } -function drawTree( $tree ) -{ - _drawTree( $tree, 0 ); +function drawTree($tree) { + _drawTree($tree, 0); } -function _extractDatetimeRange( &$node, &$minTime, &$maxTime, &$expandable, $subOr ) -{ +function _extractDatetimeRange(&$node, &$minTime, &$maxTime, &$expandable, $subOr) { $pruned = $leftPruned = $rightPruned = false; - if ( $node ) - { - if ( isset($node['left']) && isset($node['right']) ) - { - if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) - { - $subOr = true; - } - elseif ( !empty($node['left']['data']['dtAttr']) ) - { - if ( $subOr ) - { - $expandable = false; - } - elseif ( $node['data']['type'] == 'op' ) - { - if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) - { - if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) - { - $minTime = $node['right']['data']['value']; - return( true ); - } - } - if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) - { - if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) - { - $maxTime = $node['right']['data']['value']; - return( true ); - } - } - } - else - { - Fatal( "Unexpected node type '".$node['data']['type']."'" ); - } - return( false ); - } + if ( !($node and isset($node['left']) and isset($node['right']) ) ) { + return $pruned; + } - $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); - $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); + if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) { + $subOr = true; + } else if ( !empty($node['left']['data']['dtAttr']) ) { + if ( $subOr ) { + $expandable = false; + } elseif ( $node['data']['type'] == 'op' ) { + if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) { + if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) { + $minTime = $node['right']['data']['value']; + return true; + } + } else if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) { + if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) { + $maxTime = $node['right']['data']['value']; + return true; + } + } + } else { + ZM\Fatal("Unexpected node type '".$node['data']['type']."'"); + } + return false; + } - if ( $leftPruned && $rightPruned ) - { - $pruned = true; - } - elseif ( $leftPruned ) - { - $node = $node['right']; - } - elseif ( $rightPruned ) - { - $node = $node['left']; - } - } - } - return( $pruned ); + $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); + $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); + + if ( $leftPruned && $rightPruned ) { + $pruned = true; + } else if ( $leftPruned ) { + $node = $node['right']; + } else if ( $rightPruned ) { + $node = $node['left']; + } + return $pruned; } -function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) -{ - $minTime = ""; - $maxTime = ""; +function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) { + $minTime = ''; + $maxTime = ''; $expandable = true; _extractDateTimeRange( $tree, $minTime, $maxTime, $expandable, false ); } -function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) -{ +function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); - if ( isset($tree) ) - { + if ( isset($tree) ) { $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); $tree = $cnjNode; - } - else - { + } else { $tree = $opNode; } - if ( $maxTime ) - { + if ( $maxTime ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php index 2fe419efd..794b99c20 100644 --- a/web/skins/classic/views/timeline.php +++ b/web/skins/classic/views/timeline.php @@ -212,11 +212,14 @@ if ( isset($minTime) && isset($maxTime) ) { if ( !isset($minTime) || !isset($maxTime) ) { // Dynamically determine range $row = dbFetchOne($rangeSql); - - if ( !isset($minTime) ) - $minTime = $row['MinTime']; - if ( !isset($maxTime) ) - $maxTime = $row['MaxTime']; + if ( $row ) { + if ( !isset($minTime) ) + $minTime = $row['MinTime']; + if ( !isset($maxTime) ) + $maxTime = $row['MaxTime']; + } else { + # Errors will be reported by db functions + } } if ( empty($minTime) ) @@ -307,7 +310,7 @@ $monEventSlots = array(); $monFrameSlots = array(); $events_result = dbQuery($eventsSql); if ( !$events_result ) { - Fatal('SQL-ERR'); + ZM\Fatal('SQL-ERR'); return; } @@ -552,7 +555,7 @@ if ( $mode == 'overlay' ) { $top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1; } } else { - Warning("No mode $mode"); + ZM\Warning("No mode $mode"); } preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches); From 7c21f065bd8c57622afcd6b648fa90f19641f342 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 11:50:40 -0500 Subject: [PATCH 008/319] Checking AuthHashGeneratedAt is no good because it gets updated before we check it. Instead check the auth hash in session against the one in and sensd the update if they are different --- web/ajax/status.php | 11 +++++++---- web/ajax/stream.php | 22 ++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/web/ajax/status.php b/web/ajax/status.php index 2725dcce8..1b1fb19a3 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -1,8 +1,11 @@ 0 ) { if ( count($rSockets) != 1 ) { - ZM\Error('Bogus return from select, '.count($rSockets).' sockets available'); ajaxError('Bogus return from select, '.count($rSockets).' sockets available'); } } @@ -124,10 +121,12 @@ if ( sem_acquire($semaphore,1) !== false ) { $data['delay'] = round( $data['delay'], 2 ); $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 ); if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) { - $time = time(); - // Regenerate auth hash after half the lifetime of the hash - if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) { - $data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS); + $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS); + if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) { + $data['auth'] = $auth_hash; + ZM\Logger::Debug("including nw auth hash " . $data['auth']); + } else { + ZM\Logger::Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash); } } ajaxResponse(array('status'=>$data)); @@ -141,12 +140,11 @@ if ( sem_acquire($semaphore,1) !== false ) { $data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg); } $data['rate'] /= RATE_BASE; - $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 ); + $data['zoom'] = round($data['zoom']/SCALE_BASE, 1); if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) { - $time = time(); - // Regenerate auth hash after half the lifetime of the hash - if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) { - $data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS); + $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS); + if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) { + $data['auth'] = $auth_hash; } } ajaxResponse(array('status'=>$data)); From bd59ae8856e9eb01aebe913c02314cf7ba19b21e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 14:59:36 -0500 Subject: [PATCH 009/319] Move the Basic Auth login code from skin.php to includes/auth.php --- web/includes/auth.php | 8 ++++++-- web/skins/classic/skin.php | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/web/includes/auth.php b/web/includes/auth.php index f958463c3..51e4c0789 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -263,8 +263,6 @@ if ( ZM_OPT_USE_AUTH ) { } else { // Non token based auth - $user = userFromSession(); - if ( ZM_AUTH_HASH_LOGINS && empty($user) && !empty($_REQUEST['auth']) ) { $user = getAuthUser($_REQUEST['auth']); } else if ( @@ -280,6 +278,12 @@ if ( ZM_OPT_USE_AUTH ) { return; } $user = $ret[0]; + } else if ( (ZM_AUTH_TYPE == 'remote') and !empty($_SERVER['REMOTE_USER']) ) { + $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; + // local user, shouldn't affect the global user + $user = dbFetchOne($sql, NULL, array($_SERVER['REMOTE_USER'])); + } else { + $user = userFromSession(); } if ( !empty($user) ) { diff --git a/web/skins/classic/skin.php b/web/skins/classic/skin.php index b24811066..9a6940887 100644 --- a/web/skins/classic/skin.php +++ b/web/skins/classic/skin.php @@ -39,11 +39,6 @@ if ( empty($view) ) { $view = isset($user)?'console':'login'; } -if ( !isset($user) && ZM_OPT_USE_AUTH && ZM_AUTH_TYPE == 'remote' && !empty( $_SERVER['REMOTE_USER']) ) { - $view = 'postlogin'; - $action = 'login'; - $_REQUEST['username'] = $_SERVER['REMOTE_USER']; -} if ( isset($user) ) { // Bandwidth Limiter From 2f48c442cca979d814e3abb732817995ec0c21c1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:15:35 -0500 Subject: [PATCH 010/319] add Enabled to Storage --- db/zm_create.sql.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 920884cbf..ce504a422 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -733,13 +733,14 @@ CREATE TABLE `Storage` ( `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `ServerId` int(10) unsigned, `DoDelete` BOOLEAN NOT NULL DEFAULT true, + `Enabled` BOOLEAN NOT NULL DEFAULT true, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Create a default storage location -- -insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true ); +insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true, true ); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; From d59bc4ca6dad9896dd007e88ac6876ddbb4146f2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:15:44 -0500 Subject: [PATCH 011/319] add Enabled to Storage --- db/zm_update-1.35.1.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 db/zm_update-1.35.1.sql diff --git a/db/zm_update-1.35.1.sql b/db/zm_update-1.35.1.sql new file mode 100644 index 000000000..d250cf751 --- /dev/null +++ b/db/zm_update-1.35.1.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Storage' + AND column_name = 'Enabled' + ) > 0, +"SELECT 'Column Enabled already exists in Storage'", +"ALTER TABLE `Storage` ADD `Enabled` BOOLEAN NOT NULL default true AFTER `DoDelete`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; From 0b267fbdeeeb1150eb36bd20b4750f13ef67687a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:46:16 -0500 Subject: [PATCH 012/319] Add Enabled to Storage --- web/includes/Storage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 695da80c7..8a397db97 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -16,6 +16,7 @@ class Storage extends ZM_Object { 'Scheme' => 'Medium', 'ServerId' => 0, 'DoDelete' => 1, + 'Enabled' => 1, ); public static function find($parameters = array(), $options = array()) { return ZM_Object::_find(get_class(), $parameters, $options); From 077b8888f9bc0176993dbf2e8a2cae38e9194463 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:46:32 -0500 Subject: [PATCH 013/319] Use Storage object and add Enabled --- web/skins/classic/views/storage.php | 45 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index 08e41892f..44cf2fc0d 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -1,7 +1,7 @@ $_REQUEST['id'])) ) ) { $view = 'error'; return; - $newStorage['ServerId'] = ''; } } else { - $newStorage = array(); - $newStorage['Name'] = translate('NewStorage'); - $newStorage['Path'] = ''; - $newStorage['Type'] = 'local'; - $newStorage['Url'] = ''; - $newStorage['Scheme'] = 'Medium'; - $newStorage['StorageId'] = ''; - $newStorage['ServerId'] = ''; - $newStorage['DoDelete'] = 1; + $newStorage = new Storage(); + $newStorage->Name(translate('NewStorage')); } $type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') ); @@ -55,12 +47,12 @@ foreach ( $servers as $S ) { } $focusWindow = true; -xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage['Name']); +xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage->Name()); ?>
@@ -71,33 +63,40 @@ xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage['Name']); - + - + - + - 'Remote / No Specific Server') + $ServersById, $newStorage['ServerId']); ?> + 'Remote / No Specific Server') + $ServersById, $newStorage->ServerId()); ?> - + Type()); ?> - + Scheme()); ?> - />Yes - />No + DoDelete() ? 'checked="checked"' : '' ?>/>Yes + DoDelete() ? '' : 'checked="checked"' ?>/>No + + + + + + Enabled() ? 'checked="checked"' : '' ?>/>Yes + Enabled() ? '' : 'checked="checked"' ?>/>No From ac00ca0ad89002bfd9f54e5338068aad499ca0cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:46:59 -0500 Subject: [PATCH 014/319] Only list enabled Storage Areas --- web/skins/classic/includes/functions.php | 4 ++-- web/skins/classic/views/monitor.php | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 3042a5488..6372aeb5f 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -352,7 +352,7 @@ if ( ZM_OPT_USE_AUTH and $user ) { ?>
  • : true)); $storage_paths = null; $storage_areas_with_no_server_id = array(); foreach ( $storage_areas as $area ) { diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 928bffc13..ad9da17d8 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -901,9 +901,12 @@ if ( $monitor->Type() == 'Local' ) { Type() == 'Remote' ) { ?> - Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> () RTSPDescribe() ) { ?> checked="checked"/> + Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> +  () + RTSPDescribe() ) { ?> checked="checked"/> + Type() == 'Remote' break; } case 'storage' : @@ -913,7 +916,7 @@ if ( $monitor->Type() == 'Local' ) { 'Default'); - foreach ( ZM\Storage::find(NULL, array('order'=>'lower(Name)')) as $Storage ) { + foreach ( ZM\Storage::find(array('Enabled'=>true), array('order'=>'lower(Name)')) as $Storage ) { $storage_areas[$Storage->Id()] = $Storage->Name(); } echo htmlSelect('newMonitor[StorageId]', $storage_areas, $monitor->StorageId()); From 9f696186a8eda14f03670d1f401ec7d2868397c5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 10:48:02 -0500 Subject: [PATCH 015/319] Increase height of storage popup for Enabled --- web/skins/classic/js/base.js | 2 +- web/skins/classic/js/classic.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 48747b98d..d70cd3b6a 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -63,7 +63,7 @@ var popupSizes = { 'shutdown': {'width': 400, 'height': 400}, 'state': {'width': 400, 'height': 170}, 'stats': {'width': 840, 'height': 200}, - 'storage': {'width': 600, 'height': 405}, + 'storage': {'width': 600, 'height': 425}, 'timeline': {'width': 760, 'height': 540}, 'user': {'width': 460, 'height': 720}, 'version': {'width': 360, 'height': 210}, diff --git a/web/skins/classic/js/classic.js b/web/skins/classic/js/classic.js index b3b72d967..447f31aae 100644 --- a/web/skins/classic/js/classic.js +++ b/web/skins/classic/js/classic.js @@ -63,7 +63,7 @@ var popupSizes = { 'settings': {'width': 220, 'height': 225}, 'state': {'width': 370, 'height': 134}, 'stats': {'width': 840, 'height': 200}, - 'storage': {'width': 600, 'height': 405}, + 'storage': {'width': 600, 'height': 425}, 'timeline': {'width': 760, 'height': 540}, 'user': {'width': 360, 'height': 720}, 'version': {'width': 360, 'height': 140}, From 53f262a4cb30aa0b184dd4a695bbccb5247eb67f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 11:21:48 -0500 Subject: [PATCH 016/319] Merge pull request #2870 from pliablepixels/dev #2866 - fixes incorrect token type comparison --- web/includes/auth.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/web/includes/auth.php b/web/includes/auth.php index 51e4c0789..c3a34405d 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -123,12 +123,16 @@ function validateToken($token, $allowed_token_type='access') { // convert from stdclass to array $jwt_payload = json_decode(json_encode($decoded_token), true); - - $type = $jwt_payload['type']; - if ( $type != $allowed_token_type ) { - ZM\Error("Token type mismatch. Expected $allowed_token_type but got $type"); - return array(false, 'Incorrect token type'); + if ($allowed_token_type != 'any') { + $type = $jwt_payload['type']; + if ( $type != $allowed_token_type ) { + ZM\Error("Token type mismatch. Expected $allowed_token_type but got $type"); + return array(false, 'Incorrect token type'); + } + } else { + ZM\Logger::Debug('Not comparing token types as [any] was passed'); } + $username = $jwt_payload['user']; $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; $saved_user_details = dbFetchOne($sql, NULL, array($username)); @@ -258,7 +262,10 @@ function userFromSession() { if ( ZM_OPT_USE_AUTH ) { if ( !empty($_REQUEST['token']) ) { - $ret = validateToken($_REQUEST['token'], 'access'); + // we only need to get the username here + // don't know the token type. That will + // be checked later + $ret = validateToken($_REQUEST['token'], 'any'); $user = $ret[0]; } else { // Non token based auth From 4f8745809a02226c265fa325d83b177e15f3b2df Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Wed, 4 Mar 2020 11:42:55 -0500 Subject: [PATCH 017/319] move the recommended install method before what is discouraged --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b9d215a40..e1ed37087 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,6 @@ https://github.com/ZoneMinder/zmdockerfiles ## Installation Methods -### Building from Source is Discouraged - -Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. - -Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. - ### Install from a Package Repository This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros: @@ -43,6 +37,13 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own. +### Building from Source is Discouraged + +Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. + +Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. + + ### Building a ZoneMinder Package ### Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier. From 424677165f3c46253d1c00de66946cecee37f9b5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 29 Feb 2020 08:57:35 -0500 Subject: [PATCH 018/319] Use ppa:iconnor/zoneminder-master instead of ppa:iconnor/zoneminder --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7cfe96854..b085414b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ addons: ssh_known_hosts: zmrepo.zoneminder.com apt: sources: - - sourceline: ppa:iconnor/zoneminder + - sourceline: ppa:iconnor/zoneminder-master - key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04 packages: - gdebi From 2091ad8c92e3262679d011fe354522696d843f93 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 5 Mar 2020 09:29:27 -0800 Subject: [PATCH 019/319] Add libjwt and remove gnutls-openssl wrapper --- CMakeLists.txt | 75 +- cmake/Modules/FindLibJWT.cmake | 28 + src/jwt-cpp/include/jwt-cpp/jwt_cpp.h | 1593 +++++++++++++++++++++++++ src/zm_crypt.cpp | 91 +- src/zm_rtsp_auth.h | 3 - src/zm_user.cpp | 3 - zoneminder-config.cmake | 2 +- 7 files changed, 1751 insertions(+), 44 deletions(-) create mode 100644 cmake/Modules/FindLibJWT.cmake create mode 100644 src/jwt-cpp/include/jwt-cpp/jwt_cpp.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b43680b0b..f8f177a10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,19 +347,48 @@ else(JPEG_FOUND) "ZoneMinder requires jpeg but it was not found on your system") endif(JPEG_FOUND) +# LIBJWT +find_package(LibJWT) +if(LIBJWT_FOUND) + set(HAVE_LIBJWT 1) + set(optlibsfound "${optlibsfound} LIBJWT") + list(APPEND ZM_BIN_LIBS "${LIBJWT_LIBRARY}") +else(LIBJWT_FOUND) + set(optlibsnotfound "${optlibsnotfound} LIBJWT") +endif(LIBJWT_FOUND) + +# gnutls (using find_library and find_path) +find_library(GNUTLS_LIBRARIES gnutls) +if(GNUTLS_LIBRARIES) + set(HAVE_LIBGNUTLS 1) + list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") + find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) + if(GNUTLS_INCLUDE_DIR) + include_directories("${GNUTLS_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") + endif(GNUTLS_INCLUDE_DIR) + mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) + check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) + set(optlibsfound "${optlibsfound} GnuTLS") +else(GNUTLS_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} GnuTLS") +endif(GNUTLS_LIBRARIES) + # OpenSSL -find_package(OpenSSL) -if(OPENSSL_FOUND) - set(HAVE_LIBOPENSSL 1) - set(HAVE_LIBCRYPTO 1) - list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") - include_directories("${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) - set(optlibsfound "${optlibsfound} OpenSSL") -else(OPENSSL_FOUND) - set(optlibsnotfound "${optlibsnotfound} OpenSSL") -endif(OPENSSL_FOUND) +if(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(HAVE_LIBOPENSSL 1) + set(HAVE_LIBCRYPTO 1) + list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") + include_directories("${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) + set(optlibsfound "${optlibsfound} OpenSSL") + else(OPENSSL_FOUND) + set(optlibsnotfound "${optlibsnotfound} OpenSSL") + endif(OPENSSL_FOUND) +endif(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) # pthread (using find_library and find_path) find_library(PTHREAD_LIBRARIES pthread) @@ -416,28 +445,6 @@ else(GCRYPT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} GCrypt") endif(GCRYPT_LIBRARIES) -# gnutls (using find_library and find_path) -find_library(GNUTLS_LIBRARIES gnutls-openssl) -if(NOT GNUTLS_LIBRARIES) - find_library(GNUTLS_LIBRARIES gnutls) -endif(NOT GNUTLS_LIBRARIES) - -if(GNUTLS_LIBRARIES) - set(HAVE_LIBGNUTLS 1) - list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") - find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) - if(GNUTLS_INCLUDE_DIR) - include_directories("${GNUTLS_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - endif(GNUTLS_INCLUDE_DIR) - mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) - check_include_file("gnutls/openssl.h" HAVE_GNUTLS_OPENSSL_H) - check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) - set(optlibsfound "${optlibsfound} GnuTLS") -else(GNUTLS_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} GnuTLS") -endif(GNUTLS_LIBRARIES) - # mysqlclient (using find_library and find_path) find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql) if(MYSQLCLIENT_LIBRARIES) diff --git a/cmake/Modules/FindLibJWT.cmake b/cmake/Modules/FindLibJWT.cmake new file mode 100644 index 000000000..e0c834609 --- /dev/null +++ b/cmake/Modules/FindLibJWT.cmake @@ -0,0 +1,28 @@ +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBJWT QUIET libjwt) + +find_path(LIBJWT_INCLUDE_DIR + NAMES jwt.h + HINTS ${PC_LIBJWT_INCLUDEDIR} ${PC_LIBJWT_INCLUDE_DIRS} + ) + +find_library(LIBJWT_LIBRARY + NAMES jwt-gnutls libjwt-gnutls liblibjwt-gnutls + HINTS ${PC_LIBJWT_LIBDIR} ${PC_LIBJWT_LIBRARY_DIR} + ) + +find_package_handle_standard_args(LibJWT + REQUIRED_VARS LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY + ) + +if(LIBJWT_FOUND) + add_library(libjwt STATIC IMPORTED GLOBAL) + set_target_properties(libjwt PROPERTIES + IMPORTED_LOCATION "${LIBJWT_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBJWT_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY) \ No newline at end of file diff --git a/src/jwt-cpp/include/jwt-cpp/jwt_cpp.h b/src/jwt-cpp/include/jwt-cpp/jwt_cpp.h new file mode 100644 index 000000000..c8c3c8719 --- /dev/null +++ b/src/jwt-cpp/include/jwt-cpp/jwt_cpp.h @@ -0,0 +1,1593 @@ +#pragma once +#define PICOJSON_USE_INT64 +#include "picojson.h" +#include "base.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//If openssl version less than 1.1 +#if OPENSSL_VERSION_NUMBER < 269484032 +#define OPENSSL10 +#endif + +#ifndef JWT_CLAIM_EXPLICIT +#define JWT_CLAIM_EXPLICIT 0 +#endif + +namespace jwt { + using date = std::chrono::system_clock::time_point; + + struct signature_verification_exception : public std::runtime_error { + signature_verification_exception() + : std::runtime_error("signature verification failed") + {} + explicit signature_verification_exception(const std::string& msg) + : std::runtime_error(msg) + {} + explicit signature_verification_exception(const char* msg) + : std::runtime_error(msg) + {} + }; + struct signature_generation_exception : public std::runtime_error { + signature_generation_exception() + : std::runtime_error("signature generation failed") + {} + explicit signature_generation_exception(const std::string& msg) + : std::runtime_error(msg) + {} + explicit signature_generation_exception(const char* msg) + : std::runtime_error(msg) + {} + }; + struct rsa_exception : public std::runtime_error { + explicit rsa_exception(const std::string& msg) + : std::runtime_error(msg) + {} + explicit rsa_exception(const char* msg) + : std::runtime_error(msg) + {} + }; + struct ecdsa_exception : public std::runtime_error { + explicit ecdsa_exception(const std::string& msg) + : std::runtime_error(msg) + {} + explicit ecdsa_exception(const char* msg) + : std::runtime_error(msg) + {} + }; + struct token_verification_exception : public std::runtime_error { + token_verification_exception() + : std::runtime_error("token verification failed") + {} + explicit token_verification_exception(const std::string& msg) + : std::runtime_error("token verification failed: " + msg) + {} + }; + + namespace helper { + inline + std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { + // TODO: Cannot find the exact version this change happended +#if OPENSSL_VERSION_NUMBER <= 0x1000114fL + std::unique_ptr certbio(BIO_new_mem_buf(const_cast(certstr.data()), certstr.size()), BIO_free_all); +#else + std::unique_ptr certbio(BIO_new_mem_buf(certstr.data(), certstr.size()), BIO_free_all); +#endif + std::unique_ptr keybio(BIO_new(BIO_s_mem()), BIO_free_all); + + std::unique_ptr cert(PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast(pw.c_str())), X509_free); + if (!cert) throw rsa_exception("Error loading cert into memory"); + std::unique_ptr key(X509_get_pubkey(cert.get()), EVP_PKEY_free); + if(!key) throw rsa_exception("Error getting public key from certificate"); + if(!PEM_write_bio_PUBKEY(keybio.get(), key.get())) throw rsa_exception("Error writing public key data in PEM format"); + char* ptr = nullptr; + auto len = BIO_get_mem_data(keybio.get(), &ptr); + if(len <= 0 || ptr == nullptr) throw rsa_exception("Failed to convert pubkey to pem"); + std::string res(ptr, len); + return res; + } + } + + namespace algorithm { + /** + * "none" algorithm. + * + * Returns and empty signature and checks if the given signature is empty. + */ + struct none { + /// Return an empty string + std::string sign(const std::string&) const { + return ""; + } + /// Check if the given signature is empty. JWT's with "none" algorithm should not contain a signature. + void verify(const std::string&, const std::string& signature) const { + if (!signature.empty()) + throw signature_verification_exception(); + } + /// Get algorithm name + std::string name() const { + return "none"; + } + }; + /** + * Base class for HMAC family of algorithms + */ + struct hmacsha { + /** + * Construct new hmac algorithm + * \param key Key to use for HMAC + * \param md Pointer to hash function + * \param name Name of the algorithm + */ + hmacsha(std::string key, const EVP_MD*(*md)(), const std::string& name) + : secret(std::move(key)), md(md), alg_name(name) + {} + /** + * Sign jwt data + * \param data The data to sign + * \return HMAC signature for the given data + * \throws signature_generation_exception + */ + std::string sign(const std::string& data) const { + std::string res; + res.resize(EVP_MAX_MD_SIZE); + unsigned int len = res.size(); + if (HMAC(md(), secret.data(), secret.size(), (const unsigned char*)data.data(), data.size(), (unsigned char*)res.data(), &len) == nullptr) + throw signature_generation_exception(); + res.resize(len); + return res; + } + /** + * Check if signature is valid + * \param data The data to check signature against + * \param signature Signature provided by the jwt + * \throws signature_verification_exception If the provided signature does not match + */ + void verify(const std::string& data, const std::string& signature) const { + try { + auto res = sign(data); + bool matched = true; + for (size_t i = 0; i < std::min(res.size(), signature.size()); i++) + if (res[i] != signature[i]) + matched = false; + if (res.size() != signature.size()) + matched = false; + if (!matched) + throw signature_verification_exception(); + } + catch (const signature_generation_exception&) { + throw signature_verification_exception(); + } + } + /** + * Returns the algorithm name provided to the constructor + * \return Algorithmname + */ + std::string name() const { + return alg_name; + } + private: + /// HMAC secrect + const std::string secret; + /// HMAC hash generator + const EVP_MD*(*md)(); + /// Algorithmname + const std::string alg_name; + }; + /** + * Base class for RSA family of algorithms + */ + struct rsa { + /** + * Construct new rsa algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + * \param md Pointer to hash function + * \param name Name of the algorithm + */ + rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) + : md(md), alg_name(name) + { + + std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + + if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { + auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); + if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) + throw rsa_exception("failed to load public key: bio_write failed"); + } else { + if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) + throw rsa_exception("failed to load public key: bio_write failed"); + } + pkey.reset(PEM_read_bio_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EVP_PKEY_free); + if (!pkey) + throw rsa_exception("failed to load public key: PEM_read_bio_PUBKEY failed"); + + if (!private_key.empty()) { + std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) + throw rsa_exception("failed to load private key: bio_write failed"); + RSA* privkey = PEM_read_bio_RSAPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()); + if (privkey == nullptr) + throw rsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); + if (EVP_PKEY_assign_RSA(pkey.get(), privkey) == 0) { + RSA_free(privkey); + throw rsa_exception("failed to load private key: EVP_PKEY_assign_RSA failed"); + } + } + } + /** + * Sign jwt data + * \param data The data to sign + * \return RSA signature for the given data + * \throws signature_generation_exception + */ + std::string sign(const std::string& data) const { +#ifdef OPENSSL10 + std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); +#else + std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); +#endif + if (!ctx) + throw signature_generation_exception("failed to create signature: could not create context"); + if (!EVP_SignInit(ctx.get(), md())) + throw signature_generation_exception("failed to create signature: SignInit failed"); + + std::string res; + res.resize(EVP_PKEY_size(pkey.get())); + unsigned int len = 0; + + if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) + throw signature_generation_exception(); + if (!EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get())) + throw signature_generation_exception(); + + res.resize(len); + return res; + } + /** + * Check if signature is valid + * \param data The data to check signature against + * \param signature Signature provided by the jwt + * \throws signature_verification_exception If the provided signature does not match + */ + void verify(const std::string& data, const std::string& signature) const { +#ifdef OPENSSL10 + std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); +#else + std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); +#endif + if (!ctx) + throw signature_verification_exception("failed to verify signature: could not create context"); + if (!EVP_VerifyInit(ctx.get(), md())) + throw signature_verification_exception("failed to verify signature: VerifyInit failed"); + if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) + throw signature_verification_exception("failed to verify signature: VerifyUpdate failed"); + if (!EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get())) + throw signature_verification_exception(); + } + /** + * Returns the algorithm name provided to the constructor + * \return Algorithmname + */ + std::string name() const { + return alg_name; + } + private: + /// OpenSSL structure containing converted keys + std::shared_ptr pkey; + /// Hash generator + const EVP_MD*(*md)(); + /// Algorithmname + const std::string alg_name; + }; + /** + * Base class for ECDSA family of algorithms + */ + struct ecdsa { + /** + * Construct new ecdsa algorithm + * \param public_key ECDSA public key in PEM format + * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + * \param md Pointer to hash function + * \param name Name of the algorithm + */ + ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) + : md(md), alg_name(name) + { + if (private_key.empty()) { + std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { + auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); + if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) + throw ecdsa_exception("failed to load public key: bio_write failed"); + } else { + if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) + throw ecdsa_exception("failed to load public key: bio_write failed"); + } + + pkey.reset(PEM_read_bio_EC_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EC_KEY_free); + if (!pkey) + throw ecdsa_exception("failed to load public key: PEM_read_bio_EC_PUBKEY failed"); + } else { + std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) + throw ecdsa_exception("failed to load private key: bio_write failed"); + pkey.reset(PEM_read_bio_ECPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()), EC_KEY_free); + if (!pkey) + throw ecdsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); + } + + if(EC_KEY_check_key(pkey.get()) == 0) + throw ecdsa_exception("failed to load key: key is invalid"); + } + /** + * Sign jwt data + * \param data The data to sign + * \return ECDSA signature for the given data + * \throws signature_generation_exception + */ + std::string sign(const std::string& data) const { + const std::string hash = generate_hash(data); + + std::unique_ptr + sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); + if(!sig) + throw signature_generation_exception(); +#ifdef OPENSSL10 + + return bn2raw(sig->r) + bn2raw(sig->s); +#else + const BIGNUM *r; + const BIGNUM *s; + ECDSA_SIG_get0(sig.get(), &r, &s); + return bn2raw(r) + bn2raw(s); +#endif + } + /** + * Check if signature is valid + * \param data The data to check signature against + * \param signature Signature provided by the jwt + * \throws signature_verification_exception If the provided signature does not match + */ + void verify(const std::string& data, const std::string& signature) const { + const std::string hash = generate_hash(data); + auto r = raw2bn(signature.substr(0, signature.size() / 2)); + auto s = raw2bn(signature.substr(signature.size() / 2)); + +#ifdef OPENSSL10 + ECDSA_SIG sig; + sig.r = r.get(); + sig.s = s.get(); + + if(ECDSA_do_verify((const unsigned char*)hash.data(), hash.size(), &sig, pkey.get()) != 1) + throw signature_verification_exception("Invalid signature"); +#else + ECDSA_SIG *sig = ECDSA_SIG_new(); + + ECDSA_SIG_set0(sig, r.get(), s.get()); + + if(ECDSA_do_verify((const unsigned char*)hash.data(), hash.size(), sig, pkey.get()) != 1) + throw signature_verification_exception("Invalid signature"); +#endif + } + /** + * Returns the algorithm name provided to the constructor + * \return Algorithmname + */ + std::string name() const { + return alg_name; + } + private: + /** + * Convert a OpenSSL BIGNUM to a std::string + * \param bn BIGNUM to convert + * \return bignum as string + */ +#ifdef OPENSSL10 + static std::string bn2raw(BIGNUM* bn) +#else + static std::string bn2raw(const BIGNUM* bn) +#endif + { + std::string res; + res.resize(BN_num_bytes(bn)); + BN_bn2bin(bn, (unsigned char*)res.data()); + if(res.size()%2 == 1 && res[0] == 0x00) + return res.substr(1); + return res; + } + /** + * Convert an std::string to a OpenSSL BIGNUM + * \param raw String to convert + * \return BIGNUM representation + */ + static std::unique_ptr raw2bn(const std::string& raw) { + if(static_cast(raw[0]) >= 0x80) { + std::string str(1, 0x00); + str += raw; + return std::unique_ptr(BN_bin2bn((const unsigned char*)str.data(), str.size(), nullptr), BN_free); + } + return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), raw.size(), nullptr), BN_free); + } + + /** + * Hash the provided data using the hash function specified in constructor + * \param data Data to hash + * \return Hash of data + */ + std::string generate_hash(const std::string& data) const { +#ifdef OPENSSL10 + std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); +#else + std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); +#endif + if(EVP_DigestInit(ctx.get(), md()) == 0) + throw signature_generation_exception("EVP_DigestInit failed"); + if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) + throw signature_generation_exception("EVP_DigestUpdate failed"); + unsigned int len = 0; + std::string res; + res.resize(EVP_MD_CTX_size(ctx.get())); + if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) + throw signature_generation_exception("EVP_DigestFinal failed"); + res.resize(len); + return res; + } + + /// OpenSSL struct containing keys + std::shared_ptr pkey; + /// Hash generator function + const EVP_MD*(*md)(); + /// Algorithmname + const std::string alg_name; + }; + + /** + * Base class for PSS-RSA family of algorithms + */ + struct pss { + /** + * Construct new pss algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + * \param md Pointer to hash function + * \param name Name of the algorithm + */ + pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) + : md(md), alg_name(name) + { + std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { + auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); + if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) + throw rsa_exception("failed to load public key: bio_write failed"); + } else { + if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) + throw rsa_exception("failed to load public key: bio_write failed"); + } + pkey.reset(PEM_read_bio_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EVP_PKEY_free); + if (!pkey) + throw rsa_exception("failed to load public key: PEM_read_bio_PUBKEY failed"); + + if (!private_key.empty()) { + std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); + if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) + throw rsa_exception("failed to load private key: bio_write failed"); + RSA* privkey = PEM_read_bio_RSAPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()); + if (privkey == nullptr) + throw rsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); + if (EVP_PKEY_assign_RSA(pkey.get(), privkey) == 0) { + RSA_free(privkey); + throw rsa_exception("failed to load private key: EVP_PKEY_assign_RSA failed"); + } + } + } + /** + * Sign jwt data + * \param data The data to sign + * \return ECDSA signature for the given data + * \throws signature_generation_exception + */ + std::string sign(const std::string& data) const { + auto hash = this->generate_hash(data); + + std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); + const int size = RSA_size(key.get()); + + std::string padded(size, 0x00); + if (!RSA_padding_add_PKCS1_PSS_mgf1(key.get(), (unsigned char*)padded.data(), (const unsigned char*)hash.data(), md(), md(), -1)) + throw signature_generation_exception("failed to create signature: RSA_padding_add_PKCS1_PSS_mgf1 failed"); + + std::string res(size, 0x00); + if (RSA_private_encrypt(size, (const unsigned char*)padded.data(), (unsigned char*)res.data(), key.get(), RSA_NO_PADDING) < 0) + throw signature_generation_exception("failed to create signature: RSA_private_encrypt failed"); + return res; + } + /** + * Check if signature is valid + * \param data The data to check signature against + * \param signature Signature provided by the jwt + * \throws signature_verification_exception If the provided signature does not match + */ + void verify(const std::string& data, const std::string& signature) const { + auto hash = this->generate_hash(data); + + std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); + const int size = RSA_size(key.get()); + + std::string sig(size, 0x00); + if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) + throw signature_verification_exception("Invalid signature"); + + if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1)) + throw signature_verification_exception("Invalid signature"); + } + /** + * Returns the algorithm name provided to the constructor + * \return Algorithmname + */ + std::string name() const { + return alg_name; + } + private: + /** + * Hash the provided data using the hash function specified in constructor + * \param data Data to hash + * \return Hash of data + */ + std::string generate_hash(const std::string& data) const { +#ifdef OPENSSL10 + std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); +#else + std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); +#endif + if(EVP_DigestInit(ctx.get(), md()) == 0) + throw signature_generation_exception("EVP_DigestInit failed"); + if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) + throw signature_generation_exception("EVP_DigestUpdate failed"); + unsigned int len = 0; + std::string res; + res.resize(EVP_MD_CTX_size(ctx.get())); + if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) + throw signature_generation_exception("EVP_DigestFinal failed"); + res.resize(len); + return res; + } + + /// OpenSSL structure containing keys + std::shared_ptr pkey; + /// Hash generator function + const EVP_MD*(*md)(); + /// Algorithmname + const std::string alg_name; + }; + + /** + * HS256 algorithm + */ + struct hs256 : public hmacsha { + /** + * Construct new instance of algorithm + * \param key HMAC signing key + */ + explicit hs256(std::string key) + : hmacsha(std::move(key), EVP_sha256, "HS256") + {} + }; + /** + * HS384 algorithm + */ + struct hs384 : public hmacsha { + /** + * Construct new instance of algorithm + * \param key HMAC signing key + */ + explicit hs384(std::string key) + : hmacsha(std::move(key), EVP_sha384, "HS384") + {} + }; + /** + * HS512 algorithm + */ + struct hs512 : public hmacsha { + /** + * Construct new instance of algorithm + * \param key HMAC signing key + */ + explicit hs512(std::string key) + : hmacsha(std::move(key), EVP_sha512, "HS512") + {} + }; + /** + * RS256 algorithm + */ + struct rs256 : public rsa { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + rs256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256") + {} + }; + /** + * RS384 algorithm + */ + struct rs384 : public rsa { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + rs384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384") + {} + }; + /** + * RS512 algorithm + */ + struct rs512 : public rsa { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + rs512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512") + {} + }; + /** + * ES256 algorithm + */ + struct es256 : public ecdsa { + /** + * Construct new instance of algorithm + * \param public_key ECDSA public key in PEM format + * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + es256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256") + {} + }; + /** + * ES384 algorithm + */ + struct es384 : public ecdsa { + /** + * Construct new instance of algorithm + * \param public_key ECDSA public key in PEM format + * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + es384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "ES384") + {} + }; + /** + * ES512 algorithm + */ + struct es512 : public ecdsa { + /** + * Construct new instance of algorithm + * \param public_key ECDSA public key in PEM format + * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + es512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "ES512") + {} + }; + + /** + * PS256 algorithm + */ + struct ps256 : public pss { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") + {} + }; + /** + * PS384 algorithm + */ + struct ps384 : public pss { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") + {} + }; + /** + * PS512 algorithm + */ + struct ps512 : public pss { + /** + * Construct new instance of algorithm + * \param public_key RSA public key in PEM format + * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. + * \param public_key_password Password to decrypt public key pem. + * \param privat_key_password Password to decrypt private key pem. + */ + ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") + : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") + {} + }; + } + + /** + * Convenience wrapper for JSON value + */ + class claim { + picojson::value val; + public: + enum class type { + null, + boolean, + number, + string, + array, + object, + int64 + }; + + claim() + : val() + {} +#if JWT_CLAIM_EXPLICIT + explicit claim(std::string s) + : val(std::move(s)) + {} + explicit claim(const date& s) + : val(int64_t(std::chrono::system_clock::to_time_t(s))) + {} + explicit claim(const std::set& s) + : val(picojson::array(s.cbegin(), s.cend())) + {} + explicit claim(const picojson::value& val) + : val(val) + {} +#else + claim(std::string s) + : val(std::move(s)) + {} + claim(const date& s) + : val(int64_t(std::chrono::system_clock::to_time_t(s))) + {} + claim(const std::set& s) + : val(picojson::array(s.cbegin(), s.cend())) + {} + claim(const picojson::value& val) + : val(val) + {} +#endif + + /** + * Get wrapped json object + * \return Wrapped json object + */ + picojson::value to_json() const { + return val; + } + + /** + * Get type of contained object + * \return Type + * \throws std::logic_error An internal error occured + */ + type get_type() const { + if (val.is()) return type::null; + else if (val.is()) return type::boolean; + else if (val.is()) return type::int64; + else if (val.is()) return type::number; + else if (val.is()) return type::string; + else if (val.is()) return type::array; + else if (val.is()) return type::object; + else throw std::logic_error("internal error"); + } + + /** + * Get the contained object as a string + * \return content as string + * \throws std::bad_cast Content was not a string + */ + const std::string& as_string() const { + if (!val.is()) + throw std::bad_cast(); + return val.get(); + } + /** + * Get the contained object as a date + * \return content as date + * \throws std::bad_cast Content was not a date + */ + date as_date() const { + return std::chrono::system_clock::from_time_t(as_int()); + } + /** + * Get the contained object as an array + * \return content as array + * \throws std::bad_cast Content was not an array + */ + const picojson::array& as_array() const { + if (!val.is()) + throw std::bad_cast(); + return val.get(); + } + /** + * Get the contained object as a set of strings + * \return content as set of strings + * \throws std::bad_cast Content was not a set + */ + const std::set as_set() const { + std::set res; + for(auto& e : as_array()) { + if(!e.is()) + throw std::bad_cast(); + res.insert(e.get()); + } + return res; + } + /** + * Get the contained object as an integer + * \return content as int + * \throws std::bad_cast Content was not an int + */ + int64_t as_int() const { + if (!val.is()) + throw std::bad_cast(); + return val.get(); + } + /** + * Get the contained object as a bool + * \return content as bool + * \throws std::bad_cast Content was not a bool + */ + bool as_bool() const { + if (!val.is()) + throw std::bad_cast(); + return val.get(); + } + /** + * Get the contained object as a number + * \return content as double + * \throws std::bad_cast Content was not a number + */ + double as_number() const { + if (!val.is()) + throw std::bad_cast(); + return val.get(); + } + }; + + /** + * Base class that represents a token payload. + * Contains Convenience accessors for common claims. + */ + class payload { + protected: + std::unordered_map payload_claims; + public: + /** + * Check if issuer is present ("iss") + * \return true if present, false otherwise + */ + bool has_issuer() const noexcept { return has_payload_claim("iss"); } + /** + * Check if subject is present ("sub") + * \return true if present, false otherwise + */ + bool has_subject() const noexcept { return has_payload_claim("sub"); } + /** + * Check if audience is present ("aud") + * \return true if present, false otherwise + */ + bool has_audience() const noexcept { return has_payload_claim("aud"); } + /** + * Check if expires is present ("exp") + * \return true if present, false otherwise + */ + bool has_expires_at() const noexcept { return has_payload_claim("exp"); } + /** + * Check if not before is present ("nbf") + * \return true if present, false otherwise + */ + bool has_not_before() const noexcept { return has_payload_claim("nbf"); } + /** + * Check if issued at is present ("iat") + * \return true if present, false otherwise + */ + bool has_issued_at() const noexcept { return has_payload_claim("iat"); } + /** + * Check if token id is present ("jti") + * \return true if present, false otherwise + */ + bool has_id() const noexcept { return has_payload_claim("jti"); } + /** + * Get issuer claim + * \return issuer as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_issuer() const { return get_payload_claim("iss").as_string(); } + /** + * Get subject claim + * \return subject as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_subject() const { return get_payload_claim("sub").as_string(); } + /** + * Get audience claim + * \return audience as a set of strings + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a set (Should not happen in a valid token) + */ + std::set get_audience() const { + auto aud = get_payload_claim("aud"); + if(aud.get_type() == jwt::claim::type::string) return { aud.as_string()}; + else return aud.as_set(); + } + /** + * Get expires claim + * \return expires as a date in utc + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) + */ + const date get_expires_at() const { return get_payload_claim("exp").as_date(); } + /** + * Get not valid before claim + * \return nbf date in utc + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) + */ + const date get_not_before() const { return get_payload_claim("nbf").as_date(); } + /** + * Get issued at claim + * \return issued at as date in utc + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) + */ + const date get_issued_at() const { return get_payload_claim("iat").as_date(); } + /** + * Get id claim + * \return id as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_id() const { return get_payload_claim("jti").as_string(); } + /** + * Check if a payload claim is present + * \return true if claim was present, false otherwise + */ + bool has_payload_claim(const std::string& name) const noexcept { return payload_claims.count(name) != 0; } + /** + * Get payload claim + * \return Requested claim + * \throws std::runtime_error If claim was not present + */ + const claim& get_payload_claim(const std::string& name) const { + if (!has_payload_claim(name)) + throw std::runtime_error("claim not found"); + return payload_claims.at(name); + } + /** + * Get all payload claims + * \return map of claims + */ + std::unordered_map get_payload_claims() const { return payload_claims; } + }; + + /** + * Base class that represents a token header. + * Contains Convenience accessors for common claims. + */ + class header { + protected: + std::unordered_map header_claims; + public: + /** + * Check if algortihm is present ("alg") + * \return true if present, false otherwise + */ + bool has_algorithm() const noexcept { return has_header_claim("alg"); } + /** + * Check if type is present ("typ") + * \return true if present, false otherwise + */ + bool has_type() const noexcept { return has_header_claim("typ"); } + /** + * Check if content type is present ("cty") + * \return true if present, false otherwise + */ + bool has_content_type() const noexcept { return has_header_claim("cty"); } + /** + * Check if key id is present ("kid") + * \return true if present, false otherwise + */ + bool has_key_id() const noexcept { return has_header_claim("kid"); } + /** + * Get algorithm claim + * \return algorithm as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_algorithm() const { return get_header_claim("alg").as_string(); } + /** + * Get type claim + * \return type as a string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_type() const { return get_header_claim("typ").as_string(); } + /** + * Get content type claim + * \return content type as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_content_type() const { return get_header_claim("cty").as_string(); } + /** + * Get key id claim + * \return key id as string + * \throws std::runtime_error If claim was not present + * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) + */ + const std::string& get_key_id() const { return get_header_claim("kid").as_string(); } + /** + * Check if a header claim is present + * \return true if claim was present, false otherwise + */ + bool has_header_claim(const std::string& name) const noexcept { return header_claims.count(name) != 0; } + /** + * Get header claim + * \return Requested claim + * \throws std::runtime_error If claim was not present + */ + const claim& get_header_claim(const std::string& name) const { + if (!has_header_claim(name)) + throw std::runtime_error("claim not found"); + return header_claims.at(name); + } + /** + * Get all header claims + * \return map of claims + */ + std::unordered_map get_header_claims() const { return header_claims; } + }; + + /** + * Class containing all information about a decoded token + */ + class decoded_jwt : public header, public payload { + protected: + /// Unmodifed token, as passed to constructor + const std::string token; + /// Header part decoded from base64 + std::string header; + /// Unmodified header part in base64 + std::string header_base64; + /// Payload part decoded from base64 + std::string payload; + /// Unmodified payload part in base64 + std::string payload_base64; + /// Signature part decoded from base64 + std::string signature; + /// Unmodified signature part in base64 + std::string signature_base64; + public: + /** + * Constructor + * Parses a given token + * \param token The token to parse + * \throws std::invalid_argument Token is not in correct format + * \throws std::runtime_error Base64 decoding failed or invalid json + */ + explicit decoded_jwt(const std::string& token) + : token(token) + { + auto hdr_end = token.find('.'); + if (hdr_end == std::string::npos) + throw std::invalid_argument("invalid token supplied"); + auto payload_end = token.find('.', hdr_end + 1); + if (payload_end == std::string::npos) + throw std::invalid_argument("invalid token supplied"); + header = header_base64 = token.substr(0, hdr_end); + payload = payload_base64 = token.substr(hdr_end + 1, payload_end - hdr_end - 1); + signature = signature_base64 = token.substr(payload_end + 1); + + // Fix padding: JWT requires padding to get removed + auto fix_padding = [](std::string& str) { + switch (str.size() % 4) { + case 1: + str += alphabet::base64url::fill(); +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + case 2: + str += alphabet::base64url::fill(); +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + case 3: + str += alphabet::base64url::fill(); +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + default: + break; + } + }; + fix_padding(header); + fix_padding(payload); + fix_padding(signature); + + header = base::decode(header); + payload = base::decode(payload); + signature = base::decode(signature); + + auto parse_claims = [](const std::string& str) { + std::unordered_map res; + picojson::value val; + if (!picojson::parse(val, str).empty()) + throw std::runtime_error("Invalid json"); + + for (auto& e : val.get()) { res.insert({ e.first, claim(e.second) }); } + + return res; + }; + + header_claims = parse_claims(header); + payload_claims = parse_claims(payload); + } + + /** + * Get token string, as passed to constructor + * \return token as passed to constructor + */ + const std::string& get_token() const { return token; } + /** + * Get header part as json string + * \return header part after base64 decoding + */ + const std::string& get_header() const { return header; } + /** + * Get payload part as json string + * \return payload part after base64 decoding + */ + const std::string& get_payload() const { return payload; } + /** + * Get signature part as json string + * \return signature part after base64 decoding + */ + const std::string& get_signature() const { return signature; } + /** + * Get header part as base64 string + * \return header part before base64 decoding + */ + const std::string& get_header_base64() const { return header_base64; } + /** + * Get payload part as base64 string + * \return payload part before base64 decoding + */ + const std::string& get_payload_base64() const { return payload_base64; } + /** + * Get signature part as base64 string + * \return signature part before base64 decoding + */ + const std::string& get_signature_base64() const { return signature_base64; } + + }; + + /** + * Builder class to build and sign a new token + * Use jwt::create() to get an instance of this class. + */ + class builder { + std::unordered_map header_claims; + std::unordered_map payload_claims; + + builder() {} + friend builder create(); + public: + /** + * Set a header claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + builder& set_header_claim(const std::string& id, claim c) { header_claims[id] = std::move(c); return *this; } + /** + * Set a payload claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + builder& set_payload_claim(const std::string& id, claim c) { payload_claims[id] = std::move(c); return *this; } + /** + * Set algorithm claim + * You normally don't need to do this, as the algorithm is automatically set if you don't change it. + * \param str Name of algorithm + * \return *this to allow for method chaining + */ + builder& set_algorithm(const std::string& str) { return set_header_claim("alg", claim(str)); } + /** + * Set type claim + * \param str Type to set + * \return *this to allow for method chaining + */ + builder& set_type(const std::string& str) { return set_header_claim("typ", claim(str)); } + /** + * Set content type claim + * \param str Type to set + * \return *this to allow for method chaining + */ + builder& set_content_type(const std::string& str) { return set_header_claim("cty", claim(str)); } + /** + * Set key id claim + * \param str Key id to set + * \return *this to allow for method chaining + */ + builder& set_key_id(const std::string& str) { return set_header_claim("kid", claim(str)); } + /** + * Set issuer claim + * \param str Issuer to set + * \return *this to allow for method chaining + */ + builder& set_issuer(const std::string& str) { return set_payload_claim("iss", claim(str)); } + /** + * Set subject claim + * \param str Subject to set + * \return *this to allow for method chaining + */ + builder& set_subject(const std::string& str) { return set_payload_claim("sub", claim(str)); } + /** + * Set audience claim + * \param l Audience set + * \return *this to allow for method chaining + */ + builder& set_audience(const std::set& l) { return set_payload_claim("aud", claim(l)); } + /** + * Set audience claim + * \param aud Single audience + * \return *this to allow for method chaining + */ + builder& set_audience(const std::string& aud) { return set_payload_claim("aud", claim(aud)); } + /** + * Set expires at claim + * \param d Expires time + * \return *this to allow for method chaining + */ + builder& set_expires_at(const date& d) { return set_payload_claim("exp", claim(d)); } + /** + * Set not before claim + * \param d First valid time + * \return *this to allow for method chaining + */ + builder& set_not_before(const date& d) { return set_payload_claim("nbf", claim(d)); } + /** + * Set issued at claim + * \param d Issued at time, should be current time + * \return *this to allow for method chaining + */ + builder& set_issued_at(const date& d) { return set_payload_claim("iat", claim(d)); } + /** + * Set id claim + * \param str ID to set + * \return *this to allow for method chaining + */ + builder& set_id(const std::string& str) { return set_payload_claim("jti", claim(str)); } + + /** + * Sign token and return result + * \param algo Instance of an algorithm to sign the token with + * \return Final token as a string + */ + template + std::string sign(const T& algo) { + this->set_algorithm(algo.name()); + + picojson::object obj_header; + for (auto& e : header_claims) { + obj_header.insert({ e.first, e.second.to_json() }); + } + picojson::object obj_payload; + for (auto& e : payload_claims) { + obj_payload.insert({ e.first, e.second.to_json() }); + } + + auto encode = [](const std::string& data) { + auto base = base::encode(data); + auto pos = base.find(alphabet::base64url::fill()); + base = base.substr(0, pos); + return base; + }; + + std::string header = encode(picojson::value(obj_header).serialize()); + std::string payload = encode(picojson::value(obj_payload).serialize()); + + std::string token = header + "." + payload; + + return token + "." + encode(algo.sign(token)); + } + }; + + /** + * Verifier class used to check if a decoded token contains all claims required by your application and has a valid signature. + */ + template + class verifier { + struct algo_base { + virtual ~algo_base() {} + virtual void verify(const std::string& data, const std::string& sig) = 0; + }; + template + struct algo : public algo_base { + T alg; + explicit algo(T a) : alg(a) {} + virtual void verify(const std::string& data, const std::string& sig) override { + alg.verify(data, sig); + } + }; + + /// Required claims + std::unordered_map claims; + /// Leeway time for exp, nbf and iat + size_t default_leeway = 0; + /// Instance of clock type + Clock clock; + /// Supported algorithms + std::unordered_map> algs; + public: + /** + * Constructor for building a new verifier instance + * \param c Clock instance + */ + explicit verifier(Clock c) : clock(c) {} + + /** + * Set default leeway to use. + * \param leeway Default leeway to use if not specified otherwise + * \return *this to allow chaining + */ + verifier& leeway(size_t leeway) { default_leeway = leeway; return *this; } + /** + * Set leeway for expires at. + * If not specified the default leeway will be used. + * \param leeway Set leeway to use for expires at. + * \return *this to allow chaining + */ + verifier& expires_at_leeway(size_t leeway) { return with_claim("exp", claim(std::chrono::system_clock::from_time_t(leeway))); } + /** + * Set leeway for not before. + * If not specified the default leeway will be used. + * \param leeway Set leeway to use for not before. + * \return *this to allow chaining + */ + verifier& not_before_leeway(size_t leeway) { return with_claim("nbf", claim(std::chrono::system_clock::from_time_t(leeway))); } + /** + * Set leeway for issued at. + * If not specified the default leeway will be used. + * \param leeway Set leeway to use for issued at. + * \return *this to allow chaining + */ + verifier& issued_at_leeway(size_t leeway) { return with_claim("iat", claim(std::chrono::system_clock::from_time_t(leeway))); } + /** + * Set an issuer to check for. + * Check is casesensitive. + * \param iss Issuer to check for. + * \return *this to allow chaining + */ + verifier& with_issuer(const std::string& iss) { return with_claim("iss", claim(iss)); } + /** + * Set a subject to check for. + * Check is casesensitive. + * \param sub Subject to check for. + * \return *this to allow chaining + */ + verifier& with_subject(const std::string& sub) { return with_claim("sub", claim(sub)); } + /** + * Set an audience to check for. + * If any of the specified audiences is not present in the token the check fails. + * \param aud Audience to check for. + * \return *this to allow chaining + */ + verifier& with_audience(const std::set& aud) { return with_claim("aud", claim(aud)); } + /** + * Set an id to check for. + * Check is casesensitive. + * \param id ID to check for. + * \return *this to allow chaining + */ + verifier& with_id(const std::string& id) { return with_claim("jti", claim(id)); } + /** + * Specify a claim to check for. + * \param name Name of the claim to check for + * \param c Claim to check for + * \return *this to allow chaining + */ + verifier& with_claim(const std::string& name, claim c) { claims[name] = c; return *this; } + + /** + * Add an algorithm available for checking. + * \param alg Algorithm to allow + * \return *this to allow chaining + */ + template + verifier& allow_algorithm(Algorithm alg) { + algs[alg.name()] = std::make_shared>(alg); + return *this; + } + + /** + * Verify the given token. + * \param jwt Token to check + * \throws token_verification_exception Verification failed + */ + void verify(const decoded_jwt& jwt) const { + const std::string data = jwt.get_header_base64() + "." + jwt.get_payload_base64(); + const std::string sig = jwt.get_signature(); + const std::string& algo = jwt.get_algorithm(); + if (algs.count(algo) == 0) + throw token_verification_exception("wrong algorithm"); + algs.at(algo)->verify(data, sig); + + auto assert_claim_eq = [](const decoded_jwt& jwt, const std::string& key, const claim& c) { + if (!jwt.has_payload_claim(key)) + throw token_verification_exception("decoded_jwt is missing " + key + " claim"); + auto& jc = jwt.get_payload_claim(key); + if (jc.get_type() != c.get_type()) + throw token_verification_exception("claim " + key + " type mismatch"); + if (c.get_type() == claim::type::int64) { + if (c.as_date() != jc.as_date()) + throw token_verification_exception("claim " + key + " does not match expected"); + } + else if (c.get_type() == claim::type::array) { + auto s1 = c.as_set(); + auto s2 = jc.as_set(); + if (s1.size() != s2.size()) + throw token_verification_exception("claim " + key + " does not match expected"); + auto it1 = s1.cbegin(); + auto it2 = s2.cbegin(); + while (it1 != s1.cend() && it2 != s2.cend()) { + if (*it1++ != *it2++) + throw token_verification_exception("claim " + key + " does not match expected"); + } + } + else if (c.get_type() == claim::type::string) { + if (c.as_string() != jc.as_string()) + throw token_verification_exception("claim " + key + " does not match expected"); + } + else throw token_verification_exception("internal error"); + }; + + auto time = clock.now(); + + if (jwt.has_expires_at()) { + auto leeway = claims.count("exp") == 1 ? std::chrono::system_clock::to_time_t(claims.at("exp").as_date()) : default_leeway; + auto exp = jwt.get_expires_at(); + if (time > exp + std::chrono::seconds(leeway)) + throw token_verification_exception("token expired"); + } + if (jwt.has_issued_at()) { + auto leeway = claims.count("iat") == 1 ? std::chrono::system_clock::to_time_t(claims.at("iat").as_date()) : default_leeway; + auto iat = jwt.get_issued_at(); + if (time < iat - std::chrono::seconds(leeway)) + throw token_verification_exception("token expired"); + } + if (jwt.has_not_before()) { + auto leeway = claims.count("nbf") == 1 ? std::chrono::system_clock::to_time_t(claims.at("nbf").as_date()) : default_leeway; + auto nbf = jwt.get_not_before(); + if (time < nbf - std::chrono::seconds(leeway)) + throw token_verification_exception("token expired"); + } + for (auto& c : claims) + { + if (c.first == "exp" || c.first == "iat" || c.first == "nbf") { + // Nothing to do here, already checked + } + else if (c.first == "aud") { + if (!jwt.has_audience()) + throw token_verification_exception("token doesn't contain the required audience"); + auto aud = jwt.get_audience(); + auto expected = c.second.as_set(); + for (auto& e : expected) + if (aud.count(e) == 0) + throw token_verification_exception("token doesn't contain the required audience"); + } + else { + assert_claim_eq(jwt, c.first, c.second); + } + } + } + }; + + /** + * Create a verifier using the given clock + * \param c Clock instance to use + * \return verifier instance + */ + template + verifier verify(Clock c) { + return verifier(c); + } + + /** + * Default clock class using std::chrono::system_clock as a backend. + */ + struct default_clock { + std::chrono::system_clock::time_point now() const { + return std::chrono::system_clock::now(); + } + }; + + /** + * Create a verifier using the default clock + * \return verifier instance + */ + inline + verifier verify() { + return verify({}); + } + + /** + * Return a builder instance to create a new token + */ + inline + builder create() { + return builder(); + } + + /** + * Decode a token + * \param token Token to decode + * \return Decoded token + * \throws std::invalid_argument Token is not in correct format + * \throws std::runtime_error Base64 decoding failed or invalid json + */ + inline + decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); + } +} diff --git a/src/zm_crypt.cpp b/src/zm_crypt.cpp index 6b78e169b..8e42b3b3c 100644 --- a/src/zm_crypt.cpp +++ b/src/zm_crypt.cpp @@ -1,12 +1,81 @@ #include "zm.h" #include "zm_crypt.h" #include "BCrypt.hpp" -#include "jwt.h" +#if HAVE_LIBJWT +#include +#else +#include "jwt_cpp.h" +#endif #include +#if HAVE_LIBCRYPTO #include +#elif HAVE_GNUTLS_GNUTLS_H +#include +#include +#endif #include // returns username if valid, "" if not +#if HAVE_LIBJWT +std::pair verifyToken(std::string jwt_token_str, std::string key) { + std::string username = ""; + unsigned int token_issued_at = 0; + int err = 0; + jwt_t *jwt = nullptr; + + err = jwt_new(&jwt); + if( err ) { + Error("Unable to Allocate JWT object"); + return std::make_pair("", 0); + } + + err = jwt_set_alg(jwt, JWT_ALG_HS256, (const unsigned char*)key.c_str(), key.length()); + if( err ) { + jwt_free(jwt); + Error("Error setting Algorithm for JWT decode"); + return std::make_pair("", 0); + } + + err = jwt_decode(&jwt, jwt_token_str.c_str(), nullptr, 0); + if( err ) { + jwt_free(jwt); + Error("Could not decode JWT"); + return std::make_pair("", 0); + } + + const char *c_type = jwt_get_grant(jwt, (const char*)"type"); + if ( !c_type ) { + jwt_free(jwt); + Error("Missing token type. This should not happen"); + return std::make_pair("", 0); + } else if ( std::string(c_type) != "access" ) { + jwt_free(jwt); + Error("Only access tokens are allowed. Please do not use refresh tokens"); + return std::make_pair("", 0); + } + + const char *c_username = jwt_get_grant(jwt, (const char*)"user"); + if( !c_username ) { + jwt_free(jwt); + Error("User not found in claim"); + return std::make_pair("", 0); + } + + username = std::string(c_username); + Debug(1, "Got %s as user claim from token", username.c_str()); + + token_issued_at = (unsigned int)jwt_get_grant_int(jwt, "iat"); + if ( errno == ENOENT ) { + jwt_free(jwt); + Error("IAT not found in claim. This should not happen"); + return std::make_pair("", 0); + } + + Debug(1, "Got IAT token=%u", token_issued_at); + jwt_free(jwt); + return std::make_pair(username, token_issued_at); +} +#else // HAVE_LIBJWT std::pair verifyToken(std::string jwt_token_str, std::string key) { std::string username = ""; unsigned int token_issued_at = 0; @@ -58,6 +127,7 @@ std::pair verifyToken(std::string jwt_token_str, std } return std::make_pair(username, token_issued_at); } +#endif // HAVE_LIBJWT bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash) { bool password_correct = false; @@ -70,10 +140,16 @@ bool verifyPassword(const char *username, const char *input_password, const char // MYSQL PASSWORD Debug(1, "%s is using an MD5 encoded password", username); - SHA_CTX ctx1, ctx2; + #ifndef SHA_DIGEST_LENGTH + #define SHA_DIGEST_LENGTH 20 + #endif + unsigned char digest_interim[SHA_DIGEST_LENGTH]; unsigned char digest_final[SHA_DIGEST_LENGTH]; - + +#if HAVE_LIBCRYPTO + SHA_CTX ctx1, ctx2; + //get first iteration SHA1_Init(&ctx1); SHA1_Update(&ctx1, input_password, strlen(input_password)); @@ -83,6 +159,15 @@ bool verifyPassword(const char *username, const char *input_password, const char SHA1_Init(&ctx2); SHA1_Update(&ctx2, digest_interim,SHA_DIGEST_LENGTH); SHA1_Final (digest_final, &ctx2); +#elif HAVE_GNUTLS_GNUTLS_H + //get first iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, input_password, strlen(input_password), digest_interim); + //2nd iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, digest_interim, SHA_DIGEST_LENGTH, digest_final); +#else + Error("Authentication Error. ZoneMinder not built with GnuTLS or Openssl"); + return false; +#endif char final_hash[SHA_DIGEST_LENGTH * 2 +2]; final_hash[0] = '*'; diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 34056eee6..8e65746de 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -19,9 +19,6 @@ #ifndef ZM_RTSP_AUTH_H #define ZM_RTSP_AUTH_H -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 1ebd3f1ff..1c0eb6d51 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -27,9 +27,6 @@ #include #include -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif diff --git a/zoneminder-config.cmake b/zoneminder-config.cmake index 46cf28d46..320620aa0 100644 --- a/zoneminder-config.cmake +++ b/zoneminder-config.cmake @@ -38,7 +38,6 @@ #cmakedefine HAVE_LIBGCRYPT 1 #cmakedefine HAVE_GCRYPT_H 1 #cmakedefine HAVE_LIBGNUTLS 1 -#cmakedefine HAVE_GNUTLS_OPENSSL_H 1 #cmakedefine HAVE_GNUTLS_GNUTLS_H 1 #cmakedefine HAVE_LIBMYSQLCLIENT 1 #cmakedefine HAVE_MYSQL_H 1 @@ -66,6 +65,7 @@ #cmakedefine HAVE_MP4_H 1 #cmakedefine HAVE_MP4V2_H 1 #cmakedefine HAVE_MP4V2_MP4V2_H 1 +#cmakedefine HAVE_LIBJWT 1 /* Authenication checks */ #cmakedefine HAVE_MD5_OPENSSL 1 From 15aec7b1091740a362ff73fea81c446524a1d5d2 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 5 Mar 2020 09:51:33 -0800 Subject: [PATCH 020/319] Add libjwt --- src/jwt-cpp/include/jwt-cpp/jwt.h | 1593 ----------------------------- 1 file changed, 1593 deletions(-) delete mode 100644 src/jwt-cpp/include/jwt-cpp/jwt.h diff --git a/src/jwt-cpp/include/jwt-cpp/jwt.h b/src/jwt-cpp/include/jwt-cpp/jwt.h deleted file mode 100644 index c8c3c8719..000000000 --- a/src/jwt-cpp/include/jwt-cpp/jwt.h +++ /dev/null @@ -1,1593 +0,0 @@ -#pragma once -#define PICOJSON_USE_INT64 -#include "picojson.h" -#include "base.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//If openssl version less than 1.1 -#if OPENSSL_VERSION_NUMBER < 269484032 -#define OPENSSL10 -#endif - -#ifndef JWT_CLAIM_EXPLICIT -#define JWT_CLAIM_EXPLICIT 0 -#endif - -namespace jwt { - using date = std::chrono::system_clock::time_point; - - struct signature_verification_exception : public std::runtime_error { - signature_verification_exception() - : std::runtime_error("signature verification failed") - {} - explicit signature_verification_exception(const std::string& msg) - : std::runtime_error(msg) - {} - explicit signature_verification_exception(const char* msg) - : std::runtime_error(msg) - {} - }; - struct signature_generation_exception : public std::runtime_error { - signature_generation_exception() - : std::runtime_error("signature generation failed") - {} - explicit signature_generation_exception(const std::string& msg) - : std::runtime_error(msg) - {} - explicit signature_generation_exception(const char* msg) - : std::runtime_error(msg) - {} - }; - struct rsa_exception : public std::runtime_error { - explicit rsa_exception(const std::string& msg) - : std::runtime_error(msg) - {} - explicit rsa_exception(const char* msg) - : std::runtime_error(msg) - {} - }; - struct ecdsa_exception : public std::runtime_error { - explicit ecdsa_exception(const std::string& msg) - : std::runtime_error(msg) - {} - explicit ecdsa_exception(const char* msg) - : std::runtime_error(msg) - {} - }; - struct token_verification_exception : public std::runtime_error { - token_verification_exception() - : std::runtime_error("token verification failed") - {} - explicit token_verification_exception(const std::string& msg) - : std::runtime_error("token verification failed: " + msg) - {} - }; - - namespace helper { - inline - std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { - // TODO: Cannot find the exact version this change happended -#if OPENSSL_VERSION_NUMBER <= 0x1000114fL - std::unique_ptr certbio(BIO_new_mem_buf(const_cast(certstr.data()), certstr.size()), BIO_free_all); -#else - std::unique_ptr certbio(BIO_new_mem_buf(certstr.data(), certstr.size()), BIO_free_all); -#endif - std::unique_ptr keybio(BIO_new(BIO_s_mem()), BIO_free_all); - - std::unique_ptr cert(PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast(pw.c_str())), X509_free); - if (!cert) throw rsa_exception("Error loading cert into memory"); - std::unique_ptr key(X509_get_pubkey(cert.get()), EVP_PKEY_free); - if(!key) throw rsa_exception("Error getting public key from certificate"); - if(!PEM_write_bio_PUBKEY(keybio.get(), key.get())) throw rsa_exception("Error writing public key data in PEM format"); - char* ptr = nullptr; - auto len = BIO_get_mem_data(keybio.get(), &ptr); - if(len <= 0 || ptr == nullptr) throw rsa_exception("Failed to convert pubkey to pem"); - std::string res(ptr, len); - return res; - } - } - - namespace algorithm { - /** - * "none" algorithm. - * - * Returns and empty signature and checks if the given signature is empty. - */ - struct none { - /// Return an empty string - std::string sign(const std::string&) const { - return ""; - } - /// Check if the given signature is empty. JWT's with "none" algorithm should not contain a signature. - void verify(const std::string&, const std::string& signature) const { - if (!signature.empty()) - throw signature_verification_exception(); - } - /// Get algorithm name - std::string name() const { - return "none"; - } - }; - /** - * Base class for HMAC family of algorithms - */ - struct hmacsha { - /** - * Construct new hmac algorithm - * \param key Key to use for HMAC - * \param md Pointer to hash function - * \param name Name of the algorithm - */ - hmacsha(std::string key, const EVP_MD*(*md)(), const std::string& name) - : secret(std::move(key)), md(md), alg_name(name) - {} - /** - * Sign jwt data - * \param data The data to sign - * \return HMAC signature for the given data - * \throws signature_generation_exception - */ - std::string sign(const std::string& data) const { - std::string res; - res.resize(EVP_MAX_MD_SIZE); - unsigned int len = res.size(); - if (HMAC(md(), secret.data(), secret.size(), (const unsigned char*)data.data(), data.size(), (unsigned char*)res.data(), &len) == nullptr) - throw signature_generation_exception(); - res.resize(len); - return res; - } - /** - * Check if signature is valid - * \param data The data to check signature against - * \param signature Signature provided by the jwt - * \throws signature_verification_exception If the provided signature does not match - */ - void verify(const std::string& data, const std::string& signature) const { - try { - auto res = sign(data); - bool matched = true; - for (size_t i = 0; i < std::min(res.size(), signature.size()); i++) - if (res[i] != signature[i]) - matched = false; - if (res.size() != signature.size()) - matched = false; - if (!matched) - throw signature_verification_exception(); - } - catch (const signature_generation_exception&) { - throw signature_verification_exception(); - } - } - /** - * Returns the algorithm name provided to the constructor - * \return Algorithmname - */ - std::string name() const { - return alg_name; - } - private: - /// HMAC secrect - const std::string secret; - /// HMAC hash generator - const EVP_MD*(*md)(); - /// Algorithmname - const std::string alg_name; - }; - /** - * Base class for RSA family of algorithms - */ - struct rsa { - /** - * Construct new rsa algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - * \param md Pointer to hash function - * \param name Name of the algorithm - */ - rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) - : md(md), alg_name(name) - { - - std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - - if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { - auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); - if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) - throw rsa_exception("failed to load public key: bio_write failed"); - } else { - if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) - throw rsa_exception("failed to load public key: bio_write failed"); - } - pkey.reset(PEM_read_bio_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EVP_PKEY_free); - if (!pkey) - throw rsa_exception("failed to load public key: PEM_read_bio_PUBKEY failed"); - - if (!private_key.empty()) { - std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) - throw rsa_exception("failed to load private key: bio_write failed"); - RSA* privkey = PEM_read_bio_RSAPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()); - if (privkey == nullptr) - throw rsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); - if (EVP_PKEY_assign_RSA(pkey.get(), privkey) == 0) { - RSA_free(privkey); - throw rsa_exception("failed to load private key: EVP_PKEY_assign_RSA failed"); - } - } - } - /** - * Sign jwt data - * \param data The data to sign - * \return RSA signature for the given data - * \throws signature_generation_exception - */ - std::string sign(const std::string& data) const { -#ifdef OPENSSL10 - std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); -#else - std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); -#endif - if (!ctx) - throw signature_generation_exception("failed to create signature: could not create context"); - if (!EVP_SignInit(ctx.get(), md())) - throw signature_generation_exception("failed to create signature: SignInit failed"); - - std::string res; - res.resize(EVP_PKEY_size(pkey.get())); - unsigned int len = 0; - - if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) - throw signature_generation_exception(); - if (!EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get())) - throw signature_generation_exception(); - - res.resize(len); - return res; - } - /** - * Check if signature is valid - * \param data The data to check signature against - * \param signature Signature provided by the jwt - * \throws signature_verification_exception If the provided signature does not match - */ - void verify(const std::string& data, const std::string& signature) const { -#ifdef OPENSSL10 - std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); -#else - std::unique_ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); -#endif - if (!ctx) - throw signature_verification_exception("failed to verify signature: could not create context"); - if (!EVP_VerifyInit(ctx.get(), md())) - throw signature_verification_exception("failed to verify signature: VerifyInit failed"); - if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) - throw signature_verification_exception("failed to verify signature: VerifyUpdate failed"); - if (!EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get())) - throw signature_verification_exception(); - } - /** - * Returns the algorithm name provided to the constructor - * \return Algorithmname - */ - std::string name() const { - return alg_name; - } - private: - /// OpenSSL structure containing converted keys - std::shared_ptr pkey; - /// Hash generator - const EVP_MD*(*md)(); - /// Algorithmname - const std::string alg_name; - }; - /** - * Base class for ECDSA family of algorithms - */ - struct ecdsa { - /** - * Construct new ecdsa algorithm - * \param public_key ECDSA public key in PEM format - * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - * \param md Pointer to hash function - * \param name Name of the algorithm - */ - ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) - : md(md), alg_name(name) - { - if (private_key.empty()) { - std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { - auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); - if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) - throw ecdsa_exception("failed to load public key: bio_write failed"); - } else { - if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) - throw ecdsa_exception("failed to load public key: bio_write failed"); - } - - pkey.reset(PEM_read_bio_EC_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EC_KEY_free); - if (!pkey) - throw ecdsa_exception("failed to load public key: PEM_read_bio_EC_PUBKEY failed"); - } else { - std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) - throw ecdsa_exception("failed to load private key: bio_write failed"); - pkey.reset(PEM_read_bio_ECPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()), EC_KEY_free); - if (!pkey) - throw ecdsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); - } - - if(EC_KEY_check_key(pkey.get()) == 0) - throw ecdsa_exception("failed to load key: key is invalid"); - } - /** - * Sign jwt data - * \param data The data to sign - * \return ECDSA signature for the given data - * \throws signature_generation_exception - */ - std::string sign(const std::string& data) const { - const std::string hash = generate_hash(data); - - std::unique_ptr - sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); - if(!sig) - throw signature_generation_exception(); -#ifdef OPENSSL10 - - return bn2raw(sig->r) + bn2raw(sig->s); -#else - const BIGNUM *r; - const BIGNUM *s; - ECDSA_SIG_get0(sig.get(), &r, &s); - return bn2raw(r) + bn2raw(s); -#endif - } - /** - * Check if signature is valid - * \param data The data to check signature against - * \param signature Signature provided by the jwt - * \throws signature_verification_exception If the provided signature does not match - */ - void verify(const std::string& data, const std::string& signature) const { - const std::string hash = generate_hash(data); - auto r = raw2bn(signature.substr(0, signature.size() / 2)); - auto s = raw2bn(signature.substr(signature.size() / 2)); - -#ifdef OPENSSL10 - ECDSA_SIG sig; - sig.r = r.get(); - sig.s = s.get(); - - if(ECDSA_do_verify((const unsigned char*)hash.data(), hash.size(), &sig, pkey.get()) != 1) - throw signature_verification_exception("Invalid signature"); -#else - ECDSA_SIG *sig = ECDSA_SIG_new(); - - ECDSA_SIG_set0(sig, r.get(), s.get()); - - if(ECDSA_do_verify((const unsigned char*)hash.data(), hash.size(), sig, pkey.get()) != 1) - throw signature_verification_exception("Invalid signature"); -#endif - } - /** - * Returns the algorithm name provided to the constructor - * \return Algorithmname - */ - std::string name() const { - return alg_name; - } - private: - /** - * Convert a OpenSSL BIGNUM to a std::string - * \param bn BIGNUM to convert - * \return bignum as string - */ -#ifdef OPENSSL10 - static std::string bn2raw(BIGNUM* bn) -#else - static std::string bn2raw(const BIGNUM* bn) -#endif - { - std::string res; - res.resize(BN_num_bytes(bn)); - BN_bn2bin(bn, (unsigned char*)res.data()); - if(res.size()%2 == 1 && res[0] == 0x00) - return res.substr(1); - return res; - } - /** - * Convert an std::string to a OpenSSL BIGNUM - * \param raw String to convert - * \return BIGNUM representation - */ - static std::unique_ptr raw2bn(const std::string& raw) { - if(static_cast(raw[0]) >= 0x80) { - std::string str(1, 0x00); - str += raw; - return std::unique_ptr(BN_bin2bn((const unsigned char*)str.data(), str.size(), nullptr), BN_free); - } - return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), raw.size(), nullptr), BN_free); - } - - /** - * Hash the provided data using the hash function specified in constructor - * \param data Data to hash - * \return Hash of data - */ - std::string generate_hash(const std::string& data) const { -#ifdef OPENSSL10 - std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); -#else - std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); -#endif - if(EVP_DigestInit(ctx.get(), md()) == 0) - throw signature_generation_exception("EVP_DigestInit failed"); - if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) - throw signature_generation_exception("EVP_DigestUpdate failed"); - unsigned int len = 0; - std::string res; - res.resize(EVP_MD_CTX_size(ctx.get())); - if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) - throw signature_generation_exception("EVP_DigestFinal failed"); - res.resize(len); - return res; - } - - /// OpenSSL struct containing keys - std::shared_ptr pkey; - /// Hash generator function - const EVP_MD*(*md)(); - /// Algorithmname - const std::string alg_name; - }; - - /** - * Base class for PSS-RSA family of algorithms - */ - struct pss { - /** - * Construct new pss algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - * \param md Pointer to hash function - * \param name Name of the algorithm - */ - pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) - : md(md), alg_name(name) - { - std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { - auto pkey = helper::extract_pubkey_from_cert(public_key, public_key_password); - if ((size_t)BIO_write(pubkey_bio.get(), pkey.data(), pkey.size()) != pkey.size()) - throw rsa_exception("failed to load public key: bio_write failed"); - } else { - if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) - throw rsa_exception("failed to load public key: bio_write failed"); - } - pkey.reset(PEM_read_bio_PUBKEY(pubkey_bio.get(), nullptr, nullptr, (void*)public_key_password.c_str()), EVP_PKEY_free); - if (!pkey) - throw rsa_exception("failed to load public key: PEM_read_bio_PUBKEY failed"); - - if (!private_key.empty()) { - std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) - throw rsa_exception("failed to load private key: bio_write failed"); - RSA* privkey = PEM_read_bio_RSAPrivateKey(privkey_bio.get(), nullptr, nullptr, (void*)private_key_password.c_str()); - if (privkey == nullptr) - throw rsa_exception("failed to load private key: PEM_read_bio_RSAPrivateKey failed"); - if (EVP_PKEY_assign_RSA(pkey.get(), privkey) == 0) { - RSA_free(privkey); - throw rsa_exception("failed to load private key: EVP_PKEY_assign_RSA failed"); - } - } - } - /** - * Sign jwt data - * \param data The data to sign - * \return ECDSA signature for the given data - * \throws signature_generation_exception - */ - std::string sign(const std::string& data) const { - auto hash = this->generate_hash(data); - - std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); - const int size = RSA_size(key.get()); - - std::string padded(size, 0x00); - if (!RSA_padding_add_PKCS1_PSS_mgf1(key.get(), (unsigned char*)padded.data(), (const unsigned char*)hash.data(), md(), md(), -1)) - throw signature_generation_exception("failed to create signature: RSA_padding_add_PKCS1_PSS_mgf1 failed"); - - std::string res(size, 0x00); - if (RSA_private_encrypt(size, (const unsigned char*)padded.data(), (unsigned char*)res.data(), key.get(), RSA_NO_PADDING) < 0) - throw signature_generation_exception("failed to create signature: RSA_private_encrypt failed"); - return res; - } - /** - * Check if signature is valid - * \param data The data to check signature against - * \param signature Signature provided by the jwt - * \throws signature_verification_exception If the provided signature does not match - */ - void verify(const std::string& data, const std::string& signature) const { - auto hash = this->generate_hash(data); - - std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); - const int size = RSA_size(key.get()); - - std::string sig(size, 0x00); - if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) - throw signature_verification_exception("Invalid signature"); - - if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1)) - throw signature_verification_exception("Invalid signature"); - } - /** - * Returns the algorithm name provided to the constructor - * \return Algorithmname - */ - std::string name() const { - return alg_name; - } - private: - /** - * Hash the provided data using the hash function specified in constructor - * \param data Data to hash - * \return Hash of data - */ - std::string generate_hash(const std::string& data) const { -#ifdef OPENSSL10 - std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); -#else - std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); -#endif - if(EVP_DigestInit(ctx.get(), md()) == 0) - throw signature_generation_exception("EVP_DigestInit failed"); - if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) - throw signature_generation_exception("EVP_DigestUpdate failed"); - unsigned int len = 0; - std::string res; - res.resize(EVP_MD_CTX_size(ctx.get())); - if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) - throw signature_generation_exception("EVP_DigestFinal failed"); - res.resize(len); - return res; - } - - /// OpenSSL structure containing keys - std::shared_ptr pkey; - /// Hash generator function - const EVP_MD*(*md)(); - /// Algorithmname - const std::string alg_name; - }; - - /** - * HS256 algorithm - */ - struct hs256 : public hmacsha { - /** - * Construct new instance of algorithm - * \param key HMAC signing key - */ - explicit hs256(std::string key) - : hmacsha(std::move(key), EVP_sha256, "HS256") - {} - }; - /** - * HS384 algorithm - */ - struct hs384 : public hmacsha { - /** - * Construct new instance of algorithm - * \param key HMAC signing key - */ - explicit hs384(std::string key) - : hmacsha(std::move(key), EVP_sha384, "HS384") - {} - }; - /** - * HS512 algorithm - */ - struct hs512 : public hmacsha { - /** - * Construct new instance of algorithm - * \param key HMAC signing key - */ - explicit hs512(std::string key) - : hmacsha(std::move(key), EVP_sha512, "HS512") - {} - }; - /** - * RS256 algorithm - */ - struct rs256 : public rsa { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - rs256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256") - {} - }; - /** - * RS384 algorithm - */ - struct rs384 : public rsa { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - rs384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384") - {} - }; - /** - * RS512 algorithm - */ - struct rs512 : public rsa { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - rs512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512") - {} - }; - /** - * ES256 algorithm - */ - struct es256 : public ecdsa { - /** - * Construct new instance of algorithm - * \param public_key ECDSA public key in PEM format - * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - es256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256") - {} - }; - /** - * ES384 algorithm - */ - struct es384 : public ecdsa { - /** - * Construct new instance of algorithm - * \param public_key ECDSA public key in PEM format - * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - es384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "ES384") - {} - }; - /** - * ES512 algorithm - */ - struct es512 : public ecdsa { - /** - * Construct new instance of algorithm - * \param public_key ECDSA public key in PEM format - * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - es512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "ES512") - {} - }; - - /** - * PS256 algorithm - */ - struct ps256 : public pss { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") - {} - }; - /** - * PS384 algorithm - */ - struct ps384 : public pss { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") - {} - }; - /** - * PS512 algorithm - */ - struct ps512 : public pss { - /** - * Construct new instance of algorithm - * \param public_key RSA public key in PEM format - * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. - * \param public_key_password Password to decrypt public key pem. - * \param privat_key_password Password to decrypt private key pem. - */ - ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") - : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") - {} - }; - } - - /** - * Convenience wrapper for JSON value - */ - class claim { - picojson::value val; - public: - enum class type { - null, - boolean, - number, - string, - array, - object, - int64 - }; - - claim() - : val() - {} -#if JWT_CLAIM_EXPLICIT - explicit claim(std::string s) - : val(std::move(s)) - {} - explicit claim(const date& s) - : val(int64_t(std::chrono::system_clock::to_time_t(s))) - {} - explicit claim(const std::set& s) - : val(picojson::array(s.cbegin(), s.cend())) - {} - explicit claim(const picojson::value& val) - : val(val) - {} -#else - claim(std::string s) - : val(std::move(s)) - {} - claim(const date& s) - : val(int64_t(std::chrono::system_clock::to_time_t(s))) - {} - claim(const std::set& s) - : val(picojson::array(s.cbegin(), s.cend())) - {} - claim(const picojson::value& val) - : val(val) - {} -#endif - - /** - * Get wrapped json object - * \return Wrapped json object - */ - picojson::value to_json() const { - return val; - } - - /** - * Get type of contained object - * \return Type - * \throws std::logic_error An internal error occured - */ - type get_type() const { - if (val.is()) return type::null; - else if (val.is()) return type::boolean; - else if (val.is()) return type::int64; - else if (val.is()) return type::number; - else if (val.is()) return type::string; - else if (val.is()) return type::array; - else if (val.is()) return type::object; - else throw std::logic_error("internal error"); - } - - /** - * Get the contained object as a string - * \return content as string - * \throws std::bad_cast Content was not a string - */ - const std::string& as_string() const { - if (!val.is()) - throw std::bad_cast(); - return val.get(); - } - /** - * Get the contained object as a date - * \return content as date - * \throws std::bad_cast Content was not a date - */ - date as_date() const { - return std::chrono::system_clock::from_time_t(as_int()); - } - /** - * Get the contained object as an array - * \return content as array - * \throws std::bad_cast Content was not an array - */ - const picojson::array& as_array() const { - if (!val.is()) - throw std::bad_cast(); - return val.get(); - } - /** - * Get the contained object as a set of strings - * \return content as set of strings - * \throws std::bad_cast Content was not a set - */ - const std::set as_set() const { - std::set res; - for(auto& e : as_array()) { - if(!e.is()) - throw std::bad_cast(); - res.insert(e.get()); - } - return res; - } - /** - * Get the contained object as an integer - * \return content as int - * \throws std::bad_cast Content was not an int - */ - int64_t as_int() const { - if (!val.is()) - throw std::bad_cast(); - return val.get(); - } - /** - * Get the contained object as a bool - * \return content as bool - * \throws std::bad_cast Content was not a bool - */ - bool as_bool() const { - if (!val.is()) - throw std::bad_cast(); - return val.get(); - } - /** - * Get the contained object as a number - * \return content as double - * \throws std::bad_cast Content was not a number - */ - double as_number() const { - if (!val.is()) - throw std::bad_cast(); - return val.get(); - } - }; - - /** - * Base class that represents a token payload. - * Contains Convenience accessors for common claims. - */ - class payload { - protected: - std::unordered_map payload_claims; - public: - /** - * Check if issuer is present ("iss") - * \return true if present, false otherwise - */ - bool has_issuer() const noexcept { return has_payload_claim("iss"); } - /** - * Check if subject is present ("sub") - * \return true if present, false otherwise - */ - bool has_subject() const noexcept { return has_payload_claim("sub"); } - /** - * Check if audience is present ("aud") - * \return true if present, false otherwise - */ - bool has_audience() const noexcept { return has_payload_claim("aud"); } - /** - * Check if expires is present ("exp") - * \return true if present, false otherwise - */ - bool has_expires_at() const noexcept { return has_payload_claim("exp"); } - /** - * Check if not before is present ("nbf") - * \return true if present, false otherwise - */ - bool has_not_before() const noexcept { return has_payload_claim("nbf"); } - /** - * Check if issued at is present ("iat") - * \return true if present, false otherwise - */ - bool has_issued_at() const noexcept { return has_payload_claim("iat"); } - /** - * Check if token id is present ("jti") - * \return true if present, false otherwise - */ - bool has_id() const noexcept { return has_payload_claim("jti"); } - /** - * Get issuer claim - * \return issuer as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_issuer() const { return get_payload_claim("iss").as_string(); } - /** - * Get subject claim - * \return subject as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_subject() const { return get_payload_claim("sub").as_string(); } - /** - * Get audience claim - * \return audience as a set of strings - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a set (Should not happen in a valid token) - */ - std::set get_audience() const { - auto aud = get_payload_claim("aud"); - if(aud.get_type() == jwt::claim::type::string) return { aud.as_string()}; - else return aud.as_set(); - } - /** - * Get expires claim - * \return expires as a date in utc - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) - */ - const date get_expires_at() const { return get_payload_claim("exp").as_date(); } - /** - * Get not valid before claim - * \return nbf date in utc - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) - */ - const date get_not_before() const { return get_payload_claim("nbf").as_date(); } - /** - * Get issued at claim - * \return issued at as date in utc - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a date (Should not happen in a valid token) - */ - const date get_issued_at() const { return get_payload_claim("iat").as_date(); } - /** - * Get id claim - * \return id as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_id() const { return get_payload_claim("jti").as_string(); } - /** - * Check if a payload claim is present - * \return true if claim was present, false otherwise - */ - bool has_payload_claim(const std::string& name) const noexcept { return payload_claims.count(name) != 0; } - /** - * Get payload claim - * \return Requested claim - * \throws std::runtime_error If claim was not present - */ - const claim& get_payload_claim(const std::string& name) const { - if (!has_payload_claim(name)) - throw std::runtime_error("claim not found"); - return payload_claims.at(name); - } - /** - * Get all payload claims - * \return map of claims - */ - std::unordered_map get_payload_claims() const { return payload_claims; } - }; - - /** - * Base class that represents a token header. - * Contains Convenience accessors for common claims. - */ - class header { - protected: - std::unordered_map header_claims; - public: - /** - * Check if algortihm is present ("alg") - * \return true if present, false otherwise - */ - bool has_algorithm() const noexcept { return has_header_claim("alg"); } - /** - * Check if type is present ("typ") - * \return true if present, false otherwise - */ - bool has_type() const noexcept { return has_header_claim("typ"); } - /** - * Check if content type is present ("cty") - * \return true if present, false otherwise - */ - bool has_content_type() const noexcept { return has_header_claim("cty"); } - /** - * Check if key id is present ("kid") - * \return true if present, false otherwise - */ - bool has_key_id() const noexcept { return has_header_claim("kid"); } - /** - * Get algorithm claim - * \return algorithm as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_algorithm() const { return get_header_claim("alg").as_string(); } - /** - * Get type claim - * \return type as a string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_type() const { return get_header_claim("typ").as_string(); } - /** - * Get content type claim - * \return content type as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_content_type() const { return get_header_claim("cty").as_string(); } - /** - * Get key id claim - * \return key id as string - * \throws std::runtime_error If claim was not present - * \throws std::bad_cast Claim was present but not a string (Should not happen in a valid token) - */ - const std::string& get_key_id() const { return get_header_claim("kid").as_string(); } - /** - * Check if a header claim is present - * \return true if claim was present, false otherwise - */ - bool has_header_claim(const std::string& name) const noexcept { return header_claims.count(name) != 0; } - /** - * Get header claim - * \return Requested claim - * \throws std::runtime_error If claim was not present - */ - const claim& get_header_claim(const std::string& name) const { - if (!has_header_claim(name)) - throw std::runtime_error("claim not found"); - return header_claims.at(name); - } - /** - * Get all header claims - * \return map of claims - */ - std::unordered_map get_header_claims() const { return header_claims; } - }; - - /** - * Class containing all information about a decoded token - */ - class decoded_jwt : public header, public payload { - protected: - /// Unmodifed token, as passed to constructor - const std::string token; - /// Header part decoded from base64 - std::string header; - /// Unmodified header part in base64 - std::string header_base64; - /// Payload part decoded from base64 - std::string payload; - /// Unmodified payload part in base64 - std::string payload_base64; - /// Signature part decoded from base64 - std::string signature; - /// Unmodified signature part in base64 - std::string signature_base64; - public: - /** - * Constructor - * Parses a given token - * \param token The token to parse - * \throws std::invalid_argument Token is not in correct format - * \throws std::runtime_error Base64 decoding failed or invalid json - */ - explicit decoded_jwt(const std::string& token) - : token(token) - { - auto hdr_end = token.find('.'); - if (hdr_end == std::string::npos) - throw std::invalid_argument("invalid token supplied"); - auto payload_end = token.find('.', hdr_end + 1); - if (payload_end == std::string::npos) - throw std::invalid_argument("invalid token supplied"); - header = header_base64 = token.substr(0, hdr_end); - payload = payload_base64 = token.substr(hdr_end + 1, payload_end - hdr_end - 1); - signature = signature_base64 = token.substr(payload_end + 1); - - // Fix padding: JWT requires padding to get removed - auto fix_padding = [](std::string& str) { - switch (str.size() % 4) { - case 1: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - case 2: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - case 3: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - default: - break; - } - }; - fix_padding(header); - fix_padding(payload); - fix_padding(signature); - - header = base::decode(header); - payload = base::decode(payload); - signature = base::decode(signature); - - auto parse_claims = [](const std::string& str) { - std::unordered_map res; - picojson::value val; - if (!picojson::parse(val, str).empty()) - throw std::runtime_error("Invalid json"); - - for (auto& e : val.get()) { res.insert({ e.first, claim(e.second) }); } - - return res; - }; - - header_claims = parse_claims(header); - payload_claims = parse_claims(payload); - } - - /** - * Get token string, as passed to constructor - * \return token as passed to constructor - */ - const std::string& get_token() const { return token; } - /** - * Get header part as json string - * \return header part after base64 decoding - */ - const std::string& get_header() const { return header; } - /** - * Get payload part as json string - * \return payload part after base64 decoding - */ - const std::string& get_payload() const { return payload; } - /** - * Get signature part as json string - * \return signature part after base64 decoding - */ - const std::string& get_signature() const { return signature; } - /** - * Get header part as base64 string - * \return header part before base64 decoding - */ - const std::string& get_header_base64() const { return header_base64; } - /** - * Get payload part as base64 string - * \return payload part before base64 decoding - */ - const std::string& get_payload_base64() const { return payload_base64; } - /** - * Get signature part as base64 string - * \return signature part before base64 decoding - */ - const std::string& get_signature_base64() const { return signature_base64; } - - }; - - /** - * Builder class to build and sign a new token - * Use jwt::create() to get an instance of this class. - */ - class builder { - std::unordered_map header_claims; - std::unordered_map payload_claims; - - builder() {} - friend builder create(); - public: - /** - * Set a header claim. - * \param id Name of the claim - * \param c Claim to add - * \return *this to allow for method chaining - */ - builder& set_header_claim(const std::string& id, claim c) { header_claims[id] = std::move(c); return *this; } - /** - * Set a payload claim. - * \param id Name of the claim - * \param c Claim to add - * \return *this to allow for method chaining - */ - builder& set_payload_claim(const std::string& id, claim c) { payload_claims[id] = std::move(c); return *this; } - /** - * Set algorithm claim - * You normally don't need to do this, as the algorithm is automatically set if you don't change it. - * \param str Name of algorithm - * \return *this to allow for method chaining - */ - builder& set_algorithm(const std::string& str) { return set_header_claim("alg", claim(str)); } - /** - * Set type claim - * \param str Type to set - * \return *this to allow for method chaining - */ - builder& set_type(const std::string& str) { return set_header_claim("typ", claim(str)); } - /** - * Set content type claim - * \param str Type to set - * \return *this to allow for method chaining - */ - builder& set_content_type(const std::string& str) { return set_header_claim("cty", claim(str)); } - /** - * Set key id claim - * \param str Key id to set - * \return *this to allow for method chaining - */ - builder& set_key_id(const std::string& str) { return set_header_claim("kid", claim(str)); } - /** - * Set issuer claim - * \param str Issuer to set - * \return *this to allow for method chaining - */ - builder& set_issuer(const std::string& str) { return set_payload_claim("iss", claim(str)); } - /** - * Set subject claim - * \param str Subject to set - * \return *this to allow for method chaining - */ - builder& set_subject(const std::string& str) { return set_payload_claim("sub", claim(str)); } - /** - * Set audience claim - * \param l Audience set - * \return *this to allow for method chaining - */ - builder& set_audience(const std::set& l) { return set_payload_claim("aud", claim(l)); } - /** - * Set audience claim - * \param aud Single audience - * \return *this to allow for method chaining - */ - builder& set_audience(const std::string& aud) { return set_payload_claim("aud", claim(aud)); } - /** - * Set expires at claim - * \param d Expires time - * \return *this to allow for method chaining - */ - builder& set_expires_at(const date& d) { return set_payload_claim("exp", claim(d)); } - /** - * Set not before claim - * \param d First valid time - * \return *this to allow for method chaining - */ - builder& set_not_before(const date& d) { return set_payload_claim("nbf", claim(d)); } - /** - * Set issued at claim - * \param d Issued at time, should be current time - * \return *this to allow for method chaining - */ - builder& set_issued_at(const date& d) { return set_payload_claim("iat", claim(d)); } - /** - * Set id claim - * \param str ID to set - * \return *this to allow for method chaining - */ - builder& set_id(const std::string& str) { return set_payload_claim("jti", claim(str)); } - - /** - * Sign token and return result - * \param algo Instance of an algorithm to sign the token with - * \return Final token as a string - */ - template - std::string sign(const T& algo) { - this->set_algorithm(algo.name()); - - picojson::object obj_header; - for (auto& e : header_claims) { - obj_header.insert({ e.first, e.second.to_json() }); - } - picojson::object obj_payload; - for (auto& e : payload_claims) { - obj_payload.insert({ e.first, e.second.to_json() }); - } - - auto encode = [](const std::string& data) { - auto base = base::encode(data); - auto pos = base.find(alphabet::base64url::fill()); - base = base.substr(0, pos); - return base; - }; - - std::string header = encode(picojson::value(obj_header).serialize()); - std::string payload = encode(picojson::value(obj_payload).serialize()); - - std::string token = header + "." + payload; - - return token + "." + encode(algo.sign(token)); - } - }; - - /** - * Verifier class used to check if a decoded token contains all claims required by your application and has a valid signature. - */ - template - class verifier { - struct algo_base { - virtual ~algo_base() {} - virtual void verify(const std::string& data, const std::string& sig) = 0; - }; - template - struct algo : public algo_base { - T alg; - explicit algo(T a) : alg(a) {} - virtual void verify(const std::string& data, const std::string& sig) override { - alg.verify(data, sig); - } - }; - - /// Required claims - std::unordered_map claims; - /// Leeway time for exp, nbf and iat - size_t default_leeway = 0; - /// Instance of clock type - Clock clock; - /// Supported algorithms - std::unordered_map> algs; - public: - /** - * Constructor for building a new verifier instance - * \param c Clock instance - */ - explicit verifier(Clock c) : clock(c) {} - - /** - * Set default leeway to use. - * \param leeway Default leeway to use if not specified otherwise - * \return *this to allow chaining - */ - verifier& leeway(size_t leeway) { default_leeway = leeway; return *this; } - /** - * Set leeway for expires at. - * If not specified the default leeway will be used. - * \param leeway Set leeway to use for expires at. - * \return *this to allow chaining - */ - verifier& expires_at_leeway(size_t leeway) { return with_claim("exp", claim(std::chrono::system_clock::from_time_t(leeway))); } - /** - * Set leeway for not before. - * If not specified the default leeway will be used. - * \param leeway Set leeway to use for not before. - * \return *this to allow chaining - */ - verifier& not_before_leeway(size_t leeway) { return with_claim("nbf", claim(std::chrono::system_clock::from_time_t(leeway))); } - /** - * Set leeway for issued at. - * If not specified the default leeway will be used. - * \param leeway Set leeway to use for issued at. - * \return *this to allow chaining - */ - verifier& issued_at_leeway(size_t leeway) { return with_claim("iat", claim(std::chrono::system_clock::from_time_t(leeway))); } - /** - * Set an issuer to check for. - * Check is casesensitive. - * \param iss Issuer to check for. - * \return *this to allow chaining - */ - verifier& with_issuer(const std::string& iss) { return with_claim("iss", claim(iss)); } - /** - * Set a subject to check for. - * Check is casesensitive. - * \param sub Subject to check for. - * \return *this to allow chaining - */ - verifier& with_subject(const std::string& sub) { return with_claim("sub", claim(sub)); } - /** - * Set an audience to check for. - * If any of the specified audiences is not present in the token the check fails. - * \param aud Audience to check for. - * \return *this to allow chaining - */ - verifier& with_audience(const std::set& aud) { return with_claim("aud", claim(aud)); } - /** - * Set an id to check for. - * Check is casesensitive. - * \param id ID to check for. - * \return *this to allow chaining - */ - verifier& with_id(const std::string& id) { return with_claim("jti", claim(id)); } - /** - * Specify a claim to check for. - * \param name Name of the claim to check for - * \param c Claim to check for - * \return *this to allow chaining - */ - verifier& with_claim(const std::string& name, claim c) { claims[name] = c; return *this; } - - /** - * Add an algorithm available for checking. - * \param alg Algorithm to allow - * \return *this to allow chaining - */ - template - verifier& allow_algorithm(Algorithm alg) { - algs[alg.name()] = std::make_shared>(alg); - return *this; - } - - /** - * Verify the given token. - * \param jwt Token to check - * \throws token_verification_exception Verification failed - */ - void verify(const decoded_jwt& jwt) const { - const std::string data = jwt.get_header_base64() + "." + jwt.get_payload_base64(); - const std::string sig = jwt.get_signature(); - const std::string& algo = jwt.get_algorithm(); - if (algs.count(algo) == 0) - throw token_verification_exception("wrong algorithm"); - algs.at(algo)->verify(data, sig); - - auto assert_claim_eq = [](const decoded_jwt& jwt, const std::string& key, const claim& c) { - if (!jwt.has_payload_claim(key)) - throw token_verification_exception("decoded_jwt is missing " + key + " claim"); - auto& jc = jwt.get_payload_claim(key); - if (jc.get_type() != c.get_type()) - throw token_verification_exception("claim " + key + " type mismatch"); - if (c.get_type() == claim::type::int64) { - if (c.as_date() != jc.as_date()) - throw token_verification_exception("claim " + key + " does not match expected"); - } - else if (c.get_type() == claim::type::array) { - auto s1 = c.as_set(); - auto s2 = jc.as_set(); - if (s1.size() != s2.size()) - throw token_verification_exception("claim " + key + " does not match expected"); - auto it1 = s1.cbegin(); - auto it2 = s2.cbegin(); - while (it1 != s1.cend() && it2 != s2.cend()) { - if (*it1++ != *it2++) - throw token_verification_exception("claim " + key + " does not match expected"); - } - } - else if (c.get_type() == claim::type::string) { - if (c.as_string() != jc.as_string()) - throw token_verification_exception("claim " + key + " does not match expected"); - } - else throw token_verification_exception("internal error"); - }; - - auto time = clock.now(); - - if (jwt.has_expires_at()) { - auto leeway = claims.count("exp") == 1 ? std::chrono::system_clock::to_time_t(claims.at("exp").as_date()) : default_leeway; - auto exp = jwt.get_expires_at(); - if (time > exp + std::chrono::seconds(leeway)) - throw token_verification_exception("token expired"); - } - if (jwt.has_issued_at()) { - auto leeway = claims.count("iat") == 1 ? std::chrono::system_clock::to_time_t(claims.at("iat").as_date()) : default_leeway; - auto iat = jwt.get_issued_at(); - if (time < iat - std::chrono::seconds(leeway)) - throw token_verification_exception("token expired"); - } - if (jwt.has_not_before()) { - auto leeway = claims.count("nbf") == 1 ? std::chrono::system_clock::to_time_t(claims.at("nbf").as_date()) : default_leeway; - auto nbf = jwt.get_not_before(); - if (time < nbf - std::chrono::seconds(leeway)) - throw token_verification_exception("token expired"); - } - for (auto& c : claims) - { - if (c.first == "exp" || c.first == "iat" || c.first == "nbf") { - // Nothing to do here, already checked - } - else if (c.first == "aud") { - if (!jwt.has_audience()) - throw token_verification_exception("token doesn't contain the required audience"); - auto aud = jwt.get_audience(); - auto expected = c.second.as_set(); - for (auto& e : expected) - if (aud.count(e) == 0) - throw token_verification_exception("token doesn't contain the required audience"); - } - else { - assert_claim_eq(jwt, c.first, c.second); - } - } - } - }; - - /** - * Create a verifier using the given clock - * \param c Clock instance to use - * \return verifier instance - */ - template - verifier verify(Clock c) { - return verifier(c); - } - - /** - * Default clock class using std::chrono::system_clock as a backend. - */ - struct default_clock { - std::chrono::system_clock::time_point now() const { - return std::chrono::system_clock::now(); - } - }; - - /** - * Create a verifier using the default clock - * \return verifier instance - */ - inline - verifier verify() { - return verify({}); - } - - /** - * Return a builder instance to create a new token - */ - inline - builder create() { - return builder(); - } - - /** - * Decode a token - * \param token Token to decode - * \return Decoded token - * \throws std::invalid_argument Token is not in correct format - * \throws std::runtime_error Base64 decoding failed or invalid json - */ - inline - decoded_jwt decode(const std::string& token) { - return decoded_jwt(token); - } -} From a53f67cd315cfbb9db684e825cd5863d8f8ca6d4 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 5 Mar 2020 09:53:07 -0800 Subject: [PATCH 021/319] Remove a extra file added by mistake --- src/jwt-cpp/vcpkg/fix-wolfssl.patch | 214 ---------------------------- 1 file changed, 214 deletions(-) delete mode 100644 src/jwt-cpp/vcpkg/fix-wolfssl.patch diff --git a/src/jwt-cpp/vcpkg/fix-wolfssl.patch b/src/jwt-cpp/vcpkg/fix-wolfssl.patch deleted file mode 100644 index cf535a1e2..000000000 --- a/src/jwt-cpp/vcpkg/fix-wolfssl.patch +++ /dev/null @@ -1,214 +0,0 @@ -diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h -index ed93fd5..977e6aa 100644 ---- a/include/jwt-cpp/jwt.h -+++ b/include/jwt-cpp/jwt.h -@@ -6,11 +6,13 @@ - #include - #include - #include --#include --#include --#include --#include --#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include - - //If openssl version less than 1.1 - #if OPENSSL_VERSION_NUMBER < 269484032 -@@ -280,7 +282,7 @@ namespace jwt { - throw signature_verification_exception("failed to verify signature: VerifyInit failed"); - if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) - throw signature_verification_exception("failed to verify signature: VerifyUpdate failed"); -- auto res = EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get()); -+ auto res = EVP_VerifyFinal(ctx.get(), (unsigned char*)signature.data(), signature.size(), pkey.get()); - if (res != 1) - throw signature_verification_exception("evp verify final failed: " + std::to_string(res) + " " + ERR_error_string(ERR_get_error(), NULL)); - } -@@ -342,8 +344,8 @@ namespace jwt { - if(!pkey) - throw rsa_exception("at least one of public or private key need to be present"); - -- if(EC_KEY_check_key(pkey.get()) == 0) -- throw ecdsa_exception("failed to load key: key is invalid"); -+ //if(EC_KEY_check_key(pkey.get()) == 0) -+ // throw ecdsa_exception("failed to load key: key is invalid"); - } - /** - * Sign jwt data -@@ -355,7 +357,7 @@ namespace jwt { - const std::string hash = generate_hash(data); - - std::unique_ptr -- sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); -+ sig(wolfSSL_ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); - if(!sig) - throw signature_generation_exception(); - #ifdef OPENSSL10 -@@ -470,109 +472,6 @@ namespace jwt { - const size_t signature_length; - }; - -- /** -- * Base class for PSS-RSA family of algorithms -- */ -- struct pss { -- /** -- * Construct new pss algorithm -- * \param public_key RSA public key in PEM format -- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. -- * \param public_key_password Password to decrypt public key pem. -- * \param privat_key_password Password to decrypt private key pem. -- * \param md Pointer to hash function -- * \param name Name of the algorithm -- */ -- pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name) -- : md(md), alg_name(name) -- { -- if (!private_key.empty()) { -- pkey = helper::load_private_key_from_string(private_key, private_key_password); -- } else if(!public_key.empty()) { -- pkey = helper::load_public_key_from_string(public_key, public_key_password); -- } else -- throw rsa_exception("at least one of public or private key need to be present"); -- } -- /** -- * Sign jwt data -- * \param data The data to sign -- * \return ECDSA signature for the given data -- * \throws signature_generation_exception -- */ -- std::string sign(const std::string& data) const { -- auto hash = this->generate_hash(data); -- -- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); -- const int size = RSA_size(key.get()); -- -- std::string padded(size, 0x00); -- if (!RSA_padding_add_PKCS1_PSS_mgf1(key.get(), (unsigned char*)padded.data(), (const unsigned char*)hash.data(), md(), md(), -1)) -- throw signature_generation_exception("failed to create signature: RSA_padding_add_PKCS1_PSS_mgf1 failed"); -- -- std::string res(size, 0x00); -- if (RSA_private_encrypt(size, (const unsigned char*)padded.data(), (unsigned char*)res.data(), key.get(), RSA_NO_PADDING) < 0) -- throw signature_generation_exception("failed to create signature: RSA_private_encrypt failed"); -- return res; -- } -- /** -- * Check if signature is valid -- * \param data The data to check signature against -- * \param signature Signature provided by the jwt -- * \throws signature_verification_exception If the provided signature does not match -- */ -- void verify(const std::string& data, const std::string& signature) const { -- auto hash = this->generate_hash(data); -- -- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free); -- const int size = RSA_size(key.get()); -- -- std::string sig(size, 0x00); -- if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) -- throw signature_verification_exception("Invalid signature"); -- -- if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1)) -- throw signature_verification_exception("Invalid signature"); -- } -- /** -- * Returns the algorithm name provided to the constructor -- * \return Algorithmname -- */ -- std::string name() const { -- return alg_name; -- } -- private: -- /** -- * Hash the provided data using the hash function specified in constructor -- * \param data Data to hash -- * \return Hash of data -- */ -- std::string generate_hash(const std::string& data) const { --#ifdef OPENSSL10 -- std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy); --#else -- std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); --#endif -- if(EVP_DigestInit(ctx.get(), md()) == 0) -- throw signature_generation_exception("EVP_DigestInit failed"); -- if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) -- throw signature_generation_exception("EVP_DigestUpdate failed"); -- unsigned int len = 0; -- std::string res; -- res.resize(EVP_MD_CTX_size(ctx.get())); -- if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0) -- throw signature_generation_exception("EVP_DigestFinal failed"); -- res.resize(len); -- return res; -- } -- -- /// OpenSSL structure containing keys -- std::shared_ptr pkey; -- /// Hash generator function -- const EVP_MD*(*md)(); -- /// Algorithmname -- const std::string alg_name; -- }; -- - /** - * HS256 algorithm - */ -@@ -700,51 +599,6 @@ namespace jwt { - {} - }; - -- /** -- * PS256 algorithm -- */ -- struct ps256 : public pss { -- /** -- * Construct new instance of algorithm -- * \param public_key RSA public key in PEM format -- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. -- * \param public_key_password Password to decrypt public key pem. -- * \param privat_key_password Password to decrypt private key pem. -- */ -- explicit ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") -- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") -- {} -- }; -- /** -- * PS384 algorithm -- */ -- struct ps384 : public pss { -- /** -- * Construct new instance of algorithm -- * \param public_key RSA public key in PEM format -- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. -- * \param public_key_password Password to decrypt public key pem. -- * \param privat_key_password Password to decrypt private key pem. -- */ -- explicit ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") -- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") -- {} -- }; -- /** -- * PS512 algorithm -- */ -- struct ps512 : public pss { -- /** -- * Construct new instance of algorithm -- * \param public_key RSA public key in PEM format -- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail. -- * \param public_key_password Password to decrypt public key pem. -- * \param privat_key_password Password to decrypt private key pem. -- */ -- explicit ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") -- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") -- {} -- }; - } - - /** From d30480e31eb32bda1406d390591b0a02e4b4ee65 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Mar 2020 14:10:35 -0500 Subject: [PATCH 022/319] add passive to addEventListener --- web/js/overlay.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/js/overlay.js b/web/js/overlay.js index 6517d2d7d..84547c53f 100644 --- a/web/js/overlay.js +++ b/web/js/overlay.js @@ -51,8 +51,8 @@ var Overlay = new Class({ }, show: function() { this.mask.show(); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); this.element.tween( 'opacity', [0, 1.0] ); this.element.show(); this.element.position(); @@ -80,8 +80,8 @@ var Overlay = new Class({ } updateOverlayLoading(); this.loading.setStyle( 'display', 'block' ); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); }, hideAnimation: function() { if ( this.loading ) { From 8300b94382fe021917125c18d9e0bfe2bcb5e686 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Mar 2020 15:50:02 -0500 Subject: [PATCH 023/319] bump version to 1.35.1 --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c0ac0af70..d3ffad793 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.0 +Version: 1.35.1 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 2aeaa11ee..7eee78574 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.0 +1.35.1 From f0791c81184bc4cc558fe84205cec9b196c9f1ac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Mar 2020 22:13:32 -0500 Subject: [PATCH 024/319] silence warning when no language is set --- web/includes/lang.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/lang.php b/web/includes/lang.php index a567f7273..78a64a0e1 100644 --- a/web/includes/lang.php +++ b/web/includes/lang.php @@ -33,7 +33,7 @@ function loadLanguage($prefix='') { if ( $prefix ) $prefix = $prefix.'/'; - if ( isset($user['Language']) ) { + if ( isset($user['Language']) and $user['Language'] ) { $userLangFile = $prefix.'lang/'.$user['Language'].'.php'; if ( file_exists($userLangFile) ) { From 3a129a88180f95014b557eabaef7ee7222075b58 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Mar 2020 15:30:27 -0500 Subject: [PATCH 025/319] Merge pull request #2874 from hax0kartik/2810-libjwt Add libjwt and remove gnutls-openssl wrapper --- CMakeLists.txt | 75 ++++++++------- cmake/Modules/FindLibJWT.cmake | 28 ++++++ .../include/jwt-cpp/{jwt.h => jwt_cpp.h} | 0 src/zm_crypt.cpp | 91 ++++++++++++++++++- src/zm_rtsp_auth.h | 3 - src/zm_user.cpp | 3 - zoneminder-config.cmake | 2 +- 7 files changed, 158 insertions(+), 44 deletions(-) create mode 100644 cmake/Modules/FindLibJWT.cmake rename src/jwt-cpp/include/jwt-cpp/{jwt.h => jwt_cpp.h} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b43680b0b..f8f177a10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,19 +347,48 @@ else(JPEG_FOUND) "ZoneMinder requires jpeg but it was not found on your system") endif(JPEG_FOUND) +# LIBJWT +find_package(LibJWT) +if(LIBJWT_FOUND) + set(HAVE_LIBJWT 1) + set(optlibsfound "${optlibsfound} LIBJWT") + list(APPEND ZM_BIN_LIBS "${LIBJWT_LIBRARY}") +else(LIBJWT_FOUND) + set(optlibsnotfound "${optlibsnotfound} LIBJWT") +endif(LIBJWT_FOUND) + +# gnutls (using find_library and find_path) +find_library(GNUTLS_LIBRARIES gnutls) +if(GNUTLS_LIBRARIES) + set(HAVE_LIBGNUTLS 1) + list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") + find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) + if(GNUTLS_INCLUDE_DIR) + include_directories("${GNUTLS_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") + endif(GNUTLS_INCLUDE_DIR) + mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) + check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) + set(optlibsfound "${optlibsfound} GnuTLS") +else(GNUTLS_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} GnuTLS") +endif(GNUTLS_LIBRARIES) + # OpenSSL -find_package(OpenSSL) -if(OPENSSL_FOUND) - set(HAVE_LIBOPENSSL 1) - set(HAVE_LIBCRYPTO 1) - list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") - include_directories("${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) - set(optlibsfound "${optlibsfound} OpenSSL") -else(OPENSSL_FOUND) - set(optlibsnotfound "${optlibsnotfound} OpenSSL") -endif(OPENSSL_FOUND) +if(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(HAVE_LIBOPENSSL 1) + set(HAVE_LIBCRYPTO 1) + list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") + include_directories("${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) + set(optlibsfound "${optlibsfound} OpenSSL") + else(OPENSSL_FOUND) + set(optlibsnotfound "${optlibsnotfound} OpenSSL") + endif(OPENSSL_FOUND) +endif(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) # pthread (using find_library and find_path) find_library(PTHREAD_LIBRARIES pthread) @@ -416,28 +445,6 @@ else(GCRYPT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} GCrypt") endif(GCRYPT_LIBRARIES) -# gnutls (using find_library and find_path) -find_library(GNUTLS_LIBRARIES gnutls-openssl) -if(NOT GNUTLS_LIBRARIES) - find_library(GNUTLS_LIBRARIES gnutls) -endif(NOT GNUTLS_LIBRARIES) - -if(GNUTLS_LIBRARIES) - set(HAVE_LIBGNUTLS 1) - list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") - find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) - if(GNUTLS_INCLUDE_DIR) - include_directories("${GNUTLS_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - endif(GNUTLS_INCLUDE_DIR) - mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) - check_include_file("gnutls/openssl.h" HAVE_GNUTLS_OPENSSL_H) - check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) - set(optlibsfound "${optlibsfound} GnuTLS") -else(GNUTLS_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} GnuTLS") -endif(GNUTLS_LIBRARIES) - # mysqlclient (using find_library and find_path) find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql) if(MYSQLCLIENT_LIBRARIES) diff --git a/cmake/Modules/FindLibJWT.cmake b/cmake/Modules/FindLibJWT.cmake new file mode 100644 index 000000000..e0c834609 --- /dev/null +++ b/cmake/Modules/FindLibJWT.cmake @@ -0,0 +1,28 @@ +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBJWT QUIET libjwt) + +find_path(LIBJWT_INCLUDE_DIR + NAMES jwt.h + HINTS ${PC_LIBJWT_INCLUDEDIR} ${PC_LIBJWT_INCLUDE_DIRS} + ) + +find_library(LIBJWT_LIBRARY + NAMES jwt-gnutls libjwt-gnutls liblibjwt-gnutls + HINTS ${PC_LIBJWT_LIBDIR} ${PC_LIBJWT_LIBRARY_DIR} + ) + +find_package_handle_standard_args(LibJWT + REQUIRED_VARS LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY + ) + +if(LIBJWT_FOUND) + add_library(libjwt STATIC IMPORTED GLOBAL) + set_target_properties(libjwt PROPERTIES + IMPORTED_LOCATION "${LIBJWT_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBJWT_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY) \ No newline at end of file diff --git a/src/jwt-cpp/include/jwt-cpp/jwt.h b/src/jwt-cpp/include/jwt-cpp/jwt_cpp.h similarity index 100% rename from src/jwt-cpp/include/jwt-cpp/jwt.h rename to src/jwt-cpp/include/jwt-cpp/jwt_cpp.h diff --git a/src/zm_crypt.cpp b/src/zm_crypt.cpp index 6b78e169b..8e42b3b3c 100644 --- a/src/zm_crypt.cpp +++ b/src/zm_crypt.cpp @@ -1,12 +1,81 @@ #include "zm.h" #include "zm_crypt.h" #include "BCrypt.hpp" -#include "jwt.h" +#if HAVE_LIBJWT +#include +#else +#include "jwt_cpp.h" +#endif #include +#if HAVE_LIBCRYPTO #include +#elif HAVE_GNUTLS_GNUTLS_H +#include +#include +#endif #include // returns username if valid, "" if not +#if HAVE_LIBJWT +std::pair verifyToken(std::string jwt_token_str, std::string key) { + std::string username = ""; + unsigned int token_issued_at = 0; + int err = 0; + jwt_t *jwt = nullptr; + + err = jwt_new(&jwt); + if( err ) { + Error("Unable to Allocate JWT object"); + return std::make_pair("", 0); + } + + err = jwt_set_alg(jwt, JWT_ALG_HS256, (const unsigned char*)key.c_str(), key.length()); + if( err ) { + jwt_free(jwt); + Error("Error setting Algorithm for JWT decode"); + return std::make_pair("", 0); + } + + err = jwt_decode(&jwt, jwt_token_str.c_str(), nullptr, 0); + if( err ) { + jwt_free(jwt); + Error("Could not decode JWT"); + return std::make_pair("", 0); + } + + const char *c_type = jwt_get_grant(jwt, (const char*)"type"); + if ( !c_type ) { + jwt_free(jwt); + Error("Missing token type. This should not happen"); + return std::make_pair("", 0); + } else if ( std::string(c_type) != "access" ) { + jwt_free(jwt); + Error("Only access tokens are allowed. Please do not use refresh tokens"); + return std::make_pair("", 0); + } + + const char *c_username = jwt_get_grant(jwt, (const char*)"user"); + if( !c_username ) { + jwt_free(jwt); + Error("User not found in claim"); + return std::make_pair("", 0); + } + + username = std::string(c_username); + Debug(1, "Got %s as user claim from token", username.c_str()); + + token_issued_at = (unsigned int)jwt_get_grant_int(jwt, "iat"); + if ( errno == ENOENT ) { + jwt_free(jwt); + Error("IAT not found in claim. This should not happen"); + return std::make_pair("", 0); + } + + Debug(1, "Got IAT token=%u", token_issued_at); + jwt_free(jwt); + return std::make_pair(username, token_issued_at); +} +#else // HAVE_LIBJWT std::pair verifyToken(std::string jwt_token_str, std::string key) { std::string username = ""; unsigned int token_issued_at = 0; @@ -58,6 +127,7 @@ std::pair verifyToken(std::string jwt_token_str, std } return std::make_pair(username, token_issued_at); } +#endif // HAVE_LIBJWT bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash) { bool password_correct = false; @@ -70,10 +140,16 @@ bool verifyPassword(const char *username, const char *input_password, const char // MYSQL PASSWORD Debug(1, "%s is using an MD5 encoded password", username); - SHA_CTX ctx1, ctx2; + #ifndef SHA_DIGEST_LENGTH + #define SHA_DIGEST_LENGTH 20 + #endif + unsigned char digest_interim[SHA_DIGEST_LENGTH]; unsigned char digest_final[SHA_DIGEST_LENGTH]; - + +#if HAVE_LIBCRYPTO + SHA_CTX ctx1, ctx2; + //get first iteration SHA1_Init(&ctx1); SHA1_Update(&ctx1, input_password, strlen(input_password)); @@ -83,6 +159,15 @@ bool verifyPassword(const char *username, const char *input_password, const char SHA1_Init(&ctx2); SHA1_Update(&ctx2, digest_interim,SHA_DIGEST_LENGTH); SHA1_Final (digest_final, &ctx2); +#elif HAVE_GNUTLS_GNUTLS_H + //get first iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, input_password, strlen(input_password), digest_interim); + //2nd iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, digest_interim, SHA_DIGEST_LENGTH, digest_final); +#else + Error("Authentication Error. ZoneMinder not built with GnuTLS or Openssl"); + return false; +#endif char final_hash[SHA_DIGEST_LENGTH * 2 +2]; final_hash[0] = '*'; diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 34056eee6..8e65746de 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -19,9 +19,6 @@ #ifndef ZM_RTSP_AUTH_H #define ZM_RTSP_AUTH_H -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 1ebd3f1ff..1c0eb6d51 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -27,9 +27,6 @@ #include #include -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif diff --git a/zoneminder-config.cmake b/zoneminder-config.cmake index 46cf28d46..320620aa0 100644 --- a/zoneminder-config.cmake +++ b/zoneminder-config.cmake @@ -38,7 +38,6 @@ #cmakedefine HAVE_LIBGCRYPT 1 #cmakedefine HAVE_GCRYPT_H 1 #cmakedefine HAVE_LIBGNUTLS 1 -#cmakedefine HAVE_GNUTLS_OPENSSL_H 1 #cmakedefine HAVE_GNUTLS_GNUTLS_H 1 #cmakedefine HAVE_LIBMYSQLCLIENT 1 #cmakedefine HAVE_MYSQL_H 1 @@ -66,6 +65,7 @@ #cmakedefine HAVE_MP4_H 1 #cmakedefine HAVE_MP4V2_H 1 #cmakedefine HAVE_MP4V2_MP4V2_H 1 +#cmakedefine HAVE_LIBJWT 1 /* Authenication checks */ #cmakedefine HAVE_MD5_OPENSSL 1 From 6d787fd77e1281bdf9f2fc59538d6ac1addd30d0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 6 Mar 2020 14:52:38 -0500 Subject: [PATCH 026/319] Bump version for 1.34.6 --- db/zm_update-1.34.6.sql | 5 +++++ distros/redhat/zoneminder.spec | 2 +- version | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/zm_update-1.34.6.sql diff --git a/db/zm_update-1.34.6.sql b/db/zm_update-1.34.6.sql new file mode 100644 index 000000000..1a58bee1f --- /dev/null +++ b/db/zm_update-1.34.6.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.5 database to 1.34.6 +-- +-- No changes required +-- diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index d4854b4ac..60701c429 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.5 +Version: 1.34.6 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 8a7cc82d5..6fef6c580 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.5 +1.34.6 From bb72e3000cae1dca929c196990ecd671db9359c1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 12:00:11 -0500 Subject: [PATCH 027/319] Merge pull request #2867 from veprbl/pr/cmake_install_full cmake: use CMAKE_INSTALL_FULL_ --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8f177a10..018fc0eb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,9 +138,9 @@ set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH "Location of temporary files, default: /tmp/zm") set(ZM_LOGDIR "/var/log/zm" CACHE PATH "Location of generated log files, default: /var/log/zm") -set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH +set(ZM_WEBDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/www" CACHE PATH "Location of the web files, default: /${CMAKE_INSTALL_DATADIR}/zoneminder/www") -set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH +set(ZM_CGIDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH "Location of the cgi-bin files, default: /${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH "Location of the web server cache busting files, default: /var/cache/zoneminder") From e56976c8000786397a793f094090be8862a93775 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 8 Mar 2020 17:23:48 -0400 Subject: [PATCH 028/319] Triggers cannot be null, it is '' instead --- web/skins/classic/views/monitor.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 928bffc13..76614decb 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -724,22 +724,19 @@ switch ( $tab ) { $breakCount = (int)(ceil(count($optTriggers))); $breakCount = min(3, $breakCount); $optCount = 0; - foreach( $optTriggers as $optTrigger ) { - if ( !ZM_OPT_X10 && $optTrigger == 'X10' ) + foreach ( $optTriggers as $optTrigger ) { + if ( $optTrigger == 'X10' and !ZM_OPT_X10 ) continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
    '; -?> - Triggers() ) && in_array( $optTrigger, $monitor->Triggers() ) ) { ?> checked="checked"/>  -Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"':''). '/> '. $optTrigger; $optCount ++; - } - if ( !$optCount ) { - ?> - - + } # end foreach trigger option + if ( !$optCount ) { + echo ''. translate('NoneAvailable') .''; + } +?> Date: Mon, 9 Mar 2020 11:09:20 -0400 Subject: [PATCH 029/319] return global frame IDs of snapshot and alarm frames for a specific event --- web/api/app/Controller/EventsController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index d9ad29b0d..71d9633a0 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -149,6 +149,10 @@ class EventsController extends AppController { )); $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->loadModel('Frame'); + $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'Score','Score DESC')['Frame']['Score']; + $event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['Id']; $this->set(array( 'event' => $event, From 3611d147e6ba7952a24a54e46d4b53a73c992c78 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 9 Mar 2020 11:25:07 -0400 Subject: [PATCH 030/319] fix score frameID to return ID not Score --- web/api/app/Controller/EventsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 71d9633a0..2a0f735eb 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -151,7 +151,7 @@ class EventsController extends AppController { $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; $this->loadModel('Frame'); - $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'Score','Score DESC')['Frame']['Score']; + $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'Id','Score DESC')['Frame']['Id']; $event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['Id']; $this->set(array( From a261efe0a7ddda110dbb2c7fed62a35184b14ac2 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 9 Mar 2020 12:04:40 -0400 Subject: [PATCH 031/319] return relative frameId, also sort score frame by lowest frame to avoid bulk frames incase score is 0 --- web/api/app/Controller/EventsController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 2a0f735eb..5188c18b4 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -151,8 +151,8 @@ class EventsController extends AppController { $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; $this->loadModel('Frame'); - $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'Id','Score DESC')['Frame']['Id']; - $event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['Id']; + $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'FrameId',array('Score'=>'desc','FrameId'=>'asc'))['Frame']['FrameId']; + $event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['FrameId']; $this->set(array( 'event' => $event, From ffed88454fda50c1d497b48610ffc9a602d1304a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 13:14:09 -0400 Subject: [PATCH 032/319] Provide a more useful error message when the monitor's function is set to NONE. Fixes #2877 --- src/zm_stream.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index ee874cf64..d355ef4c2 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -44,7 +44,12 @@ bool StreamBase::loadMonitor(int monitor_id) { Error("Unable to load monitor id %d for streaming", monitor_id); return false; } - if ( ! monitor->connect() ) { + if ( monitor->GetFunction() == Monitor::NONE ) { + Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); + return false; + } + + if ( !monitor->connect() ) { Error("Unable to connect to monitor id %d for streaming", monitor_id); return false; } From a0bd602339c03f74b20b347b0761fb16103f0e1f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 14:39:22 -0400 Subject: [PATCH 033/319] Use === when testing for empty value and setting defaults --- web/includes/Object.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index 6f2e4d8bf..9c71ed953 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -216,7 +216,8 @@ Logger::Debug("$k => Have default for $v: "); } } } # end foreach default - } + } # end if defaults + foreach ( $new_values as $field => $value ) { if ( method_exists($this, $field) ) { @@ -299,7 +300,7 @@ Logger::Debug("$k => Have default for $v: "); # Set defaults. Note that we only replace "" with null, not other values # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value foreach ( $this->defaults as $field => $default ) { - if ( (!property_exists($this, $field)) or ($this->{$field} == '') ) { + if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) { if ( is_array($default) ) { $this->{$field} = $default['default']; } else if ( $default == null ) { From 3d993eef809aa4194290e4ac7c0e086fba2b483f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 14:39:57 -0400 Subject: [PATCH 034/319] Fix triggers code --- web/skins/classic/views/monitor.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 2d472e6ef..20139c85a 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -639,7 +639,7 @@ switch ( $tab ) { - Enabled() ) { ?> checked="checked"/> + Enabled() ? ' checked="checked"' : '' ?>/> Type() != 'WebSite' ) { @@ -729,8 +729,8 @@ switch ( $tab ) { continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
    '; - echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"':''). '/> '. $optTrigger; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"':''). '/> '. $optTrigger; $optCount ++; } # end foreach trigger option if ( !$optCount ) { From e8553babfa3311bc2a7f665c454115bae5fa691f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 14:43:54 -0400 Subject: [PATCH 035/319] remove extra spaces, fix eslint --- web/skins/classic/views/js/video.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/video.js b/web/skins/classic/views/js/video.js index 70b6c48ca..0aea4482c 100644 --- a/web/skins/classic/views/js/video.js +++ b/web/skins/classic/views/js/video.js @@ -23,7 +23,7 @@ function generateVideoResponse( respObj, respText ) { window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0) ); } -function generateVideo( ) { +function generateVideo() { form = $j('#contentForm')[0]; var parms = 'view=request&request=event&action=video'; parms += '&'+$(form).toQueryString(); From 6bc8012e048ff6119f995e8d61743af1f76499c3 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 9 Mar 2020 15:56:34 -0400 Subject: [PATCH 036/319] add support to render gif image that may be created by object detection --- web/views/image.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index b05cc79d9..ecf24c93d 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -57,6 +57,7 @@ $filename = ''; $Frame = null; $Event = null; $path = null; +$image_type='image/jpeg'; if ( empty($_REQUEST['path']) ) { @@ -76,7 +77,16 @@ if ( empty($_REQUEST['path']) ) { return; } - if ( $_REQUEST['fid'] == 'objdetect' ) { + if ( $_REQUEST['fid'] == 'objdetectgif' ) { + $path = $Event->Path().'/objdetect.gif'; + if ( !file_exists($path) ) { + header('HTTP/1.0 404 Not Found'); + ZM\Fatal("File $path does not exist. Please make sure store_frame_in_zm is enabled in the object detection config"); + } + $Frame = new ZM\Frame(); + $Frame->Id('objdetect'); + $image_type = 'image/gif'; + } else if ( $_REQUEST['fid'] == 'objdetect' ) { $path = $Event->Path().'/objdetect.jpg'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); @@ -281,7 +291,7 @@ if ( !empty($_REQUEST['height']) ) { if ( $errorText ) { ZM\Error($errorText); } else { - header('Content-type: image/jpeg'); + header("Content-type: $image_type"); if ( ( $scale==0 || $scale==100 ) && ($width==0) && ($height==0) ) { # This is so that Save Image As give a useful filename if ( $Event ) { From 38b519c8a6681e51eec4f2a68026f1f5258a5ce9 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 9 Mar 2020 15:58:57 -0400 Subject: [PATCH 037/319] better error log --- web/views/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/views/image.php b/web/views/image.php index ecf24c93d..ef971c3f4 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -81,7 +81,7 @@ if ( empty($_REQUEST['path']) ) { $path = $Event->Path().'/objdetect.gif'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); - ZM\Fatal("File $path does not exist. Please make sure store_frame_in_zm is enabled in the object detection config"); + ZM\Fatal("File $path does not exist. You might not have enabled GIF creation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); } $Frame = new ZM\Frame(); $Frame->Id('objdetect'); From 49f23cb35be16c93d14f12477e69c2735e8593fe Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Tue, 10 Mar 2020 13:51:55 -0400 Subject: [PATCH 038/319] Added options to render objdetect, objdetectanim and objdetectimage --- web/views/image.php | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index ef971c3f4..27afb7580 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -77,16 +77,36 @@ if ( empty($_REQUEST['path']) ) { return; } - if ( $_REQUEST['fid'] == 'objdetectgif' ) { - $path = $Event->Path().'/objdetect.gif'; - if ( !file_exists($path) ) { - header('HTTP/1.0 404 Not Found'); - ZM\Fatal("File $path does not exist. You might not have enabled GIF creation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); - } - $Frame = new ZM\Frame(); - $Frame->Id('objdetect'); - $image_type = 'image/gif'; - } else if ( $_REQUEST['fid'] == 'objdetect' ) { + if ( $_REQUEST['fid'] == 'objdetect' ) { + // if animation file is found, return that, else return image + $path_anim = $Event->Path().'/objdetect.gif'; + $path_image = $Event->Path().'/objdetect.jpg'; + if ( file_exists($path_anim)) { + // we found the animation/gif file + $path = $path_anim; + ZM\Logger::Debug("Animation file found at $path"); + $image_type = 'image/gif'; + } else if (file_exists($path_image)) { + // animation/gif not found, but image found + ZM\Logger::Debug("Image file found at $path"); + $path = $path_image; + } else { + // neither animation/gif nor image found + header('HTTP/1.0 404 Not Found'); + ZM\Fatal("Object detection animation and image not found for this event"); + } + $Frame = new ZM\Frame(); + $Frame->Id('objdetect'); + } else if ( $_REQUEST['fid'] == 'objdetectanim' ) { + $path = $Event->Path().'/objdetect.gif'; + if ( !file_exists($path) ) { + header('HTTP/1.0 404 Not Found'); + ZM\Fatal("File $path does not exist. You might not have enabled GIF creation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); + } + $Frame = new ZM\Frame(); + $Frame->Id('objdetect'); + $image_type = 'image/gif'; + } else if ( $_REQUEST['fid'] == 'objdetectimage' ) { $path = $Event->Path().'/objdetect.jpg'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); From 59bd658a96671b5fdda4b1a250544fd38d0fccbe Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 10 Mar 2020 18:46:20 -0400 Subject: [PATCH 039/319] replace vjsPause with pauseCLicked. Fix behaviour with rate dropdown and reverse/pause etc. --- web/skins/classic/views/js/event.js | 72 +++++++++++++++++------------ 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 30cc5057c..c569e08e2 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -1,4 +1,7 @@ var vid = null; +var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. +var intervalRewind; +var revSpeed = .5; // Function called when video.js hits the end of the video function vjsReplay() { @@ -39,7 +42,7 @@ function vjsReplay() { streamNext(true); break; } -} +} // end function vjsReplay $j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON. @@ -67,14 +70,14 @@ function renderAlarmCues(containerEl) { var spanTimeStart = 0; var spanTimeEnd = 0; var alarmed = 0; - var alarmHtml = ""; + var alarmHtml = ''; var pixSkew = 0; var skip = 0; var num_cueFrames = cueFrames.length; for ( var i = 0; i < num_cueFrames; i++ ) { skip = 0; frame = cueFrames[i]; - if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm. + if ( (frame.Type == 'Alarm') && (alarmed == 0) ) { //From nothing to alarm. End nothing and start alarm. alarmed = 1; if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan spanTimeEnd = frame.Delta * 100; @@ -88,24 +91,24 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing. + } else if ( (frame.Type !== 'Alarm') && (alarmed == 1) ) { //from alarm to nothing. End alarm and start nothing. futNone = 0; indexPlus = i+1; if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { //alarm is too short and there is more event continue; } - while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan - if (indexPlus >= cueFrames.length) break; //check if end of event. + while ( futNone < minAlarm ) { //check ahead to see if there's enough for a nonespan + if ( indexPlus >= cueFrames.length ) break; //check if end of event. futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); - if (cueFrames[indexPlus].Type == "Alarm") { + if ( cueFrames[indexPlus].Type == 'Alarm' ) { i = --indexPlus; skip = 1; break; } indexPlus++; } - if (skip == 1) continue; //javascript doesn't support continue 2; + if ( skip == 1 ) continue; //javascript doesn't support continue 2; spanTimeEnd = frame.Delta *100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -118,7 +121,7 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm + } else if ( (frame.Type == 'Alarm') && (alarmed == 1) && (i + 1 >= cueFrames.length) ) { //event ends on an alarm spanTimeEnd = frame.Delta * 100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -155,12 +158,12 @@ function changeScale() { var eventViewer; var alarmCue = $j('div.alarmCue'); var bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus'); - if (streamMode == 'stills') { + if ( streamMode == 'stills' ) { eventViewer = $j('#eventThumbs'); } else { eventViewer = $j(vid ? '#videoobj' : '#evtStream'); } - if ( scale == "auto" ) { + if ( scale == 'auto' ) { var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; @@ -201,17 +204,31 @@ function changeReplayMode() { } function changeRate() { - var rate = $j('select[name="rate"]').val(); + var rate = parseInt($j('select[name="rate"]').val()); if ( ! rate ) { pauseClicked(); - } else { + } else if ( rate < 0 ) { + if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. + revSpeed = rates[rates.indexOf(-1*rate)-1]/100; + clearInterval(intervalRewind); + intervalRewind = setInterval(function() { + if ( vid.currentTime() <= 0 ) { + clearInterval(intervalRewind); + vid.pause(); + } else { + vid.playbackRate(0); + vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms. + } + }, 500); //500ms is a compromise between smooth reverse and realistic performance + } // end if vid + + } else { // Forward rate if ( vid ) { vid.playbackRate(rate/100); - Cookie.write('zmEventRate', rate, {duration: 10*365}); } } -} - + Cookie.write('zmEventRate', rate, {duration: 10*365}); +} // end function changeRate var streamParms = "view=request&request=stream&connkey="+connKey; if ( auth_hash ) { @@ -294,21 +311,18 @@ var streamReq = new Request.JSON( { function pauseClicked() { if ( vid ) { + if ( intervalRewind ) { + stopFastRev(); + } vid.pause(); } else { streamReq.send(streamParms+"&command="+CMD_PAUSE); - streamPause(); - } -} - -function vjsPause() { - if ( intervalRewind ) { - stopFastRev(); } streamPause(); } function streamPause( ) { + $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); @@ -319,6 +333,10 @@ function streamPause( ) { } function playClicked( ) { + var rate_select = $j('select[name="rate"]'); + if ( ! rate_select.val() ) { + $j('select[name="rate"]').val(100); + } if ( vid ) { if ( vid.paused() ) { vid.play(); @@ -327,8 +345,8 @@ function playClicked( ) { } } else { streamReq.send(streamParms+"&command="+CMD_PLAY); - streamPlay(); } + streamPlay(); } function vjsPlay() { //catches if we change mode programatically @@ -341,7 +359,6 @@ function vjsPlay() { //catches if we change mode programatically } function streamPlay( ) { - $j('#modeValue').html('Replay'); setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'active' ); setButtonState( $('fastFwdBtn'), 'inactive' ); @@ -370,9 +387,6 @@ function streamFastFwd( action ) { } } -var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. -var intervalRewind; -var revSpeed = .5; function streamSlowFwd( action ) { if ( vid ) { @@ -1073,7 +1087,7 @@ function initPage() { $j('.vjs-progress-control').append('
    ');//add a place for videojs only on first load vid.on('ended', vjsReplay); vid.on('play', vjsPlay); - vid.on('pause', vjsPause); + vid.on('pause', pauseClicked); vid.on('click', function(event) { handleClick(event); }); From c90655d178046b6199cf7dba928a06475a6adb21 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Mar 2020 10:54:35 -0400 Subject: [PATCH 040/319] fix build on libavtools < 12 --- src/zm_ffmpeg.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index c65bb38a0..f8fc519ed 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -40,6 +40,7 @@ extern "C" { #include #include #include "libavutil/audio_fifo.h" +#include "libavutil/pixfmt.h" /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) @@ -321,6 +322,7 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#if LIBAVUTIL_VERSION_CHECK(54, 4, 0, 74, 100) #define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ text, \ frame->format, \ @@ -331,6 +333,18 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#else +#define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ + text, \ + frame->format, \ + "unsupported", \ + frame->width, \ + frame->height, \ + frame->linesize[0], frame->linesize[1], \ + frame->pts \ + ); +#endif + #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #define zm_av_packet_unref( packet ) av_packet_unref( packet ) #define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src ) From 11b1bf1100eb6e8d5c721974c756494e262044cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Mar 2020 10:54:52 -0400 Subject: [PATCH 041/319] undo random addition --- src/zm_ffmpeg.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index f8fc519ed..73afff3e3 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -40,7 +40,6 @@ extern "C" { #include #include #include "libavutil/audio_fifo.h" -#include "libavutil/pixfmt.h" /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) From ff7defa84e66071fc78e402c307828d0703278ed Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Fri, 13 Mar 2020 14:46:20 -0400 Subject: [PATCH 042/319] re-enable fragmented mp4 recordings --- src/zm_videostore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index ad04124fa..6acee0cfd 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -409,7 +409,7 @@ bool VideoStore::open() { AVDictionary *opts = NULL; // av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); // Shiboleth reports that this may break seeking in mp4 before it downloads - //av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); + av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); // av_dict_set(&opts, "movflags", // "frag_keyframe+empty_moov+default_base_moof", 0); if ( (ret = avformat_write_header(oc, &opts)) < 0 ) { From 6b61f4ac8481226f11983829135aec098c800b39 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Fri, 13 Mar 2020 14:47:22 -0400 Subject: [PATCH 043/319] when an event is created, write video name if applicable so image.php can extract frames while recording is in progress --- src/zm_event.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e564479f3..875335d02 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -108,6 +108,25 @@ Event::Event( return; } id = mysql_insert_id(&dbconn); + // + + /* Now update event with DefaultVideo name if applicable, so index.php + can read frames while the video is being recorded, since MP4 is created + using fragments */ + + if ( monitor->GetOptVideoWriter() != 0 ) { + video_name[0] = 0; + snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4"); + Debug(1, "Updating inserted event with DefaultVideo=%s",video_name); + snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name,id); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); + db_mutex.unlock(); + return; + } + } else { + Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo"); + } db_mutex.unlock(); if ( untimedEvent ) { Warning("Event %d has zero time, setting to current", id); From dd76ee2c9f7ff27e58f11efb9559384ab412298d Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Fri, 13 Mar 2020 15:01:19 -0400 Subject: [PATCH 044/319] spacing --- src/zm_event.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 875335d02..ed2395a9c 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -120,9 +120,9 @@ Event::Event( Debug(1, "Updating inserted event with DefaultVideo=%s",video_name); snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name,id); if ( mysql_query(&dbconn, sql) ) { - Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); - db_mutex.unlock(); - return; + Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); + db_mutex.unlock(); + return; } } else { Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo"); From 30d45aee8fc3ad9aae1bc3c204997e925cde0d20 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Fri, 13 Mar 2020 15:11:00 -0400 Subject: [PATCH 045/319] formatting/comment --- src/zm_event.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index ed2395a9c..7605f1ca9 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -110,10 +110,8 @@ Event::Event( id = mysql_insert_id(&dbconn); // - /* Now update event with DefaultVideo name if applicable, so index.php - can read frames while the video is being recorded, since MP4 is created - using fragments */ - + /* Update event record with DefaultVideo name if possible so image.php can extract frames + if needed, while recording is in progress */ if ( monitor->GetOptVideoWriter() != 0 ) { video_name[0] = 0; snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4"); @@ -123,7 +121,7 @@ Event::Event( Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); db_mutex.unlock(); return; - } + } } else { Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo"); } From 9a31d545d4ebbf739538c53d1aa4d6da5f224f1f Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Sat, 14 Mar 2020 08:08:52 -0400 Subject: [PATCH 046/319] replace GIF function with MP4 --- web/views/image.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index 27afb7580..e714df6a3 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -79,33 +79,33 @@ if ( empty($_REQUEST['path']) ) { if ( $_REQUEST['fid'] == 'objdetect' ) { // if animation file is found, return that, else return image - $path_anim = $Event->Path().'/objdetect.gif'; + $path_anim = $Event->Path().'/objdetect.mp4'; $path_image = $Event->Path().'/objdetect.jpg'; if ( file_exists($path_anim)) { - // we found the animation/gif file + // we found the animation file $path = $path_anim; ZM\Logger::Debug("Animation file found at $path"); - $image_type = 'image/gif'; + $image_type = 'video/mp4'; } else if (file_exists($path_image)) { - // animation/gif not found, but image found + // animation not found, but image found ZM\Logger::Debug("Image file found at $path"); $path = $path_image; } else { - // neither animation/gif nor image found + // neither animation nor image found header('HTTP/1.0 404 Not Found'); ZM\Fatal("Object detection animation and image not found for this event"); } $Frame = new ZM\Frame(); $Frame->Id('objdetect'); } else if ( $_REQUEST['fid'] == 'objdetectanim' ) { - $path = $Event->Path().'/objdetect.gif'; + $path = $Event->Path().'/objdetect.mp4'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); - ZM\Fatal("File $path does not exist. You might not have enabled GIF creation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); + ZM\Fatal("File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); } $Frame = new ZM\Frame(); $Frame->Id('objdetect'); - $image_type = 'image/gif'; + $image_type = 'video/mp4'; } else if ( $_REQUEST['fid'] == 'objdetectimage' ) { $path = $Event->Path().'/objdetect.jpg'; if ( !file_exists($path) ) { From f8fa0340fc338b6c21b157269d73923b4dc74dbe Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 14 Mar 2020 09:27:07 -0400 Subject: [PATCH 047/319] fix eslint --- web/skins/classic/views/js/event.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 8b33fdedc..8b51e0cec 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -209,7 +209,6 @@ function changeRate() { } }, 500); //500ms is a compromise between smooth reverse and realistic performance } // end if vid - } else { // Forward rate if ( vid ) { vid.playbackRate(rate/100); @@ -310,7 +309,6 @@ function pauseClicked() { } function streamPause( ) { - $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); From 4d5f9f7de0f4d84f249b0df7dee0073a6a320687 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Sat, 14 Mar 2020 14:18:25 -0400 Subject: [PATCH 048/319] add back gif, make nomenclature consistent of objdetect_subtypes --- web/views/image.php | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index e714df6a3..18eca08cc 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -79,13 +79,18 @@ if ( empty($_REQUEST['path']) ) { if ( $_REQUEST['fid'] == 'objdetect' ) { // if animation file is found, return that, else return image - $path_anim = $Event->Path().'/objdetect.mp4'; + $path_anim_mp4 = $Event->Path().'/objdetect.mp4'; + $path_anim_gif = $Event->Path().'/objdetect.gif'; $path_image = $Event->Path().'/objdetect.jpg'; - if ( file_exists($path_anim)) { - // we found the animation file - $path = $path_anim; + if ( file_exists($path_anim_mp4)) { + // we found the animation mp4 file + $path = $path_anim_mp4; ZM\Logger::Debug("Animation file found at $path"); $image_type = 'video/mp4'; + } else if (file_exists($path_anim_gif)) { + // we found the animation gif file + ZM\Logger::Debug("Animation file found at $path"); + $path = $path_anim_gif; } else if (file_exists($path_image)) { // animation not found, but image found ZM\Logger::Debug("Image file found at $path"); @@ -97,16 +102,22 @@ if ( empty($_REQUEST['path']) ) { } $Frame = new ZM\Frame(); $Frame->Id('objdetect'); - } else if ( $_REQUEST['fid'] == 'objdetectanim' ) { + } else if ( $_REQUEST['fid'] == 'objdetect_mp4' ) { $path = $Event->Path().'/objdetect.mp4'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); ZM\Fatal("File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); - } - $Frame = new ZM\Frame(); - $Frame->Id('objdetect'); - $image_type = 'video/mp4'; - } else if ( $_REQUEST['fid'] == 'objdetectimage' ) { + } + } else if ( $_REQUEST['fid'] == 'objdetect_gif' ) { + $path = $Event->Path().'/objdetect.mp4'; + if ( !file_exists($path) ) { + header('HTTP/1.0 404 Not Found'); + ZM\Fatal("File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); + } + $Frame = new ZM\Frame(); + $Frame->Id('objdetect'); + $image_type = 'video/mp4'; + } else if ( $_REQUEST['fid'] == 'objdetect_jpg' ) { $path = $Event->Path().'/objdetect.jpg'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); From 4342506e5b96e14b599c704bb55b6d1a0e963085 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Sat, 14 Mar 2020 14:24:39 -0400 Subject: [PATCH 049/319] fix media type allocations --- web/views/image.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index 18eca08cc..653a5e256 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -57,7 +57,7 @@ $filename = ''; $Frame = null; $Event = null; $path = null; -$image_type='image/jpeg'; +$media_type='image/jpeg'; if ( empty($_REQUEST['path']) ) { @@ -86,9 +86,10 @@ if ( empty($_REQUEST['path']) ) { // we found the animation mp4 file $path = $path_anim_mp4; ZM\Logger::Debug("Animation file found at $path"); - $image_type = 'video/mp4'; + $media_type = 'video/mp4'; } else if (file_exists($path_anim_gif)) { // we found the animation gif file + $media_type = 'image/gif'; ZM\Logger::Debug("Animation file found at $path"); $path = $path_anim_gif; } else if (file_exists($path_image)) { @@ -108,15 +109,18 @@ if ( empty($_REQUEST['path']) ) { header('HTTP/1.0 404 Not Found'); ZM\Fatal("File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); } + $Frame = new ZM\Frame(); + $Frame->Id('objdetect'); + $media_type = 'video/mp4'; } else if ( $_REQUEST['fid'] == 'objdetect_gif' ) { - $path = $Event->Path().'/objdetect.mp4'; + $path = $Event->Path().'/objdetect.gif'; if ( !file_exists($path) ) { header('HTTP/1.0 404 Not Found'); ZM\Fatal("File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation"); } $Frame = new ZM\Frame(); $Frame->Id('objdetect'); - $image_type = 'video/mp4'; + $media_type = 'image/gif'; } else if ( $_REQUEST['fid'] == 'objdetect_jpg' ) { $path = $Event->Path().'/objdetect.jpg'; if ( !file_exists($path) ) { From 921c4c5bbdd747035f433ad25c79e317c8ecb5f0 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Sat, 14 Mar 2020 15:00:33 -0400 Subject: [PATCH 050/319] don't return mp4 if objdetect is used --- web/views/image.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/web/views/image.php b/web/views/image.php index 653a5e256..ff26682c7 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -79,15 +79,12 @@ if ( empty($_REQUEST['path']) ) { if ( $_REQUEST['fid'] == 'objdetect' ) { // if animation file is found, return that, else return image - $path_anim_mp4 = $Event->Path().'/objdetect.mp4'; + // we are only looking for GIF or jpg here, not mp4 + // as most often, browsers asking for this link will be expecting + // media types that can be rendered as $path_anim_gif = $Event->Path().'/objdetect.gif'; $path_image = $Event->Path().'/objdetect.jpg'; - if ( file_exists($path_anim_mp4)) { - // we found the animation mp4 file - $path = $path_anim_mp4; - ZM\Logger::Debug("Animation file found at $path"); - $media_type = 'video/mp4'; - } else if (file_exists($path_anim_gif)) { + if (file_exists($path_anim_gif)) { // we found the animation gif file $media_type = 'image/gif'; ZM\Logger::Debug("Animation file found at $path"); @@ -326,7 +323,7 @@ if ( !empty($_REQUEST['height']) ) { if ( $errorText ) { ZM\Error($errorText); } else { - header("Content-type: $image_type"); + header("Content-type: $media_type"); if ( ( $scale==0 || $scale==100 ) && ($width==0) && ($height==0) ) { # This is so that Save Image As give a useful filename if ( $Event ) { From 090cd845b09cfaf3da8126f9d9e9579176eb650a Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 16 Mar 2020 14:49:27 -0400 Subject: [PATCH 051/319] add ES status to telemetry and --show option to display what is being sent to ZM --- scripts/zmtelemetry.pl.in | 47 ++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index 3a6e81320..a3debabdb 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -45,6 +45,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $help = 0; my $force = 0; +my $show = 0; # Interval between version checks my $interval; my $version; @@ -52,6 +53,7 @@ my $version; GetOptions( force => \$force, help => \$help, + show => \$show, interval => \$interval, version => \$version ); @@ -59,6 +61,14 @@ if ( $version ) { print( ZoneMinder::Base::ZM_VERSION . "\n"); exit(0); } +if ($show) { + my %telemetry; + my $dbh = zmDbConnect(); + collectData($dbh, \%telemetry); + my $result = jsonEncode(\%telemetry); + print ($result); + exit(0); +} if ( $help ) { pod2usage(-exitstatus => -1); } @@ -89,21 +99,9 @@ while( 1 ) { my $dbh = zmDbConnect(); # Build the telemetry hash # We should keep *BSD systems in mind when calling system commands + my %telemetry; - $telemetry{uuid} = getUUID($dbh); - @telemetry{qw(city region country latitude longitude)} = getGeo(); - $telemetry{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); - $telemetry{monitor_count} = countQuery($dbh,'Monitors'); - $telemetry{event_count} = countQuery($dbh,'Events'); - $telemetry{architecture} = runSysCmd('uname -p'); - ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); - $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; - $telemetry{system_memory} = totalmem(); - $telemetry{processor_count} = cpu_count(); - $telemetry{monitors} = getMonitorRef($dbh); - - Info('Sending data to ZoneMinder Telemetry server.'); - + collectData($dbh,\%telemetry); my $result = jsonEncode(\%telemetry); if ( sendData($result) ) { @@ -126,6 +124,24 @@ print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', loc # SUBROUTINES # ############### +# collect data to send +sub collectData { + my $dbh = shift; + my $telemetry = shift; + $telemetry->{uuid} = getUUID($dbh); + ($telemetry->{city},$telemetry->{region},$telemetry->{country},$telemetry->{latitude},$telemetry->{longitude})=getGeo(); + $telemetry->{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); + $telemetry->{monitor_count} = countQuery($dbh,'Monitors'); + $telemetry->{event_count} = countQuery($dbh,'Events'); + $telemetry->{architecture} = runSysCmd('uname -p'); + ($telemetry->{kernel}, $telemetry->{distro}, $telemetry->{version}) = getDistro(); + $telemetry->{zm_version} = ZoneMinder::Base::ZM_VERSION; + $telemetry->{system_memory} = totalmem(); + $telemetry->{processor_count} = cpu_count(); + $telemetry->{use_event_server} = $Config{ZM_OPT_USE_EVENTNOTIFICATION}; + $telemetry->{monitors} = getMonitorRef($dbh); +} + # Find, verify, then run the supplied system command sub runSysCmd { my $msg = shift; @@ -365,7 +381,7 @@ zmtelemetry.pl - Send usage information to the ZoneMinder development team =head1 SYNOPSIS - zmtelemetry.pl [--force] [--help] [--interval=seconds] [--version] + zmtelemetry.pl [--force] [--help] [--show] [--interval=seconds] [--version] =head1 DESCRIPTION @@ -382,6 +398,7 @@ console under Options. --force Force the script to upload it's data instead of waiting for the defined interval since last upload. --help Display usage information + --show Displays telemetry data that is sent to zoneminder --interval Override the default configured interval since last upload. The value should be given in seconds, but can be an expression such as 24*60*60. From ad61a0e385769ddf91a0a012316f3ed14af06d3e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 16 Mar 2020 21:06:31 -0400 Subject: [PATCH 052/319] Escape Function to fix mysql8. Fixes #2885 --- src/zmu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zmu.cpp b/src/zmu.cpp index b435b7483..394770666 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -708,11 +708,11 @@ int main(int argc, char *argv[]) { } if ( function & ZMU_LIST ) { - std::string sql = "select Id, Function+0 from Monitors"; + std::string sql = "SELECT `Id`, `Function`+0 FROM `Monitors`"; if ( !verbose ) { - sql += "where Function != 'None'"; + sql += "WHERE `Function` != 'None'"; } - sql += " order by Id asc"; + sql += " ORDER BY Id ASC"; if ( mysql_query(&dbconn, sql.c_str()) ) { Error("Can't run query: %s", mysql_error(&dbconn)); From ecaaae90f3e266518498344630f20ec201107c84 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Mar 2020 11:06:01 -0400 Subject: [PATCH 053/319] fix streamImg loading when using applet. Also don't alter actParms as it is not a global. --- web/skins/classic/views/js/watch.js | 39 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 242ec80f0..83eae3475 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -54,7 +54,7 @@ function changeScale() { streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale)); } else { - console.error('No element found for liveStream.'); + console.error('No element found for liveStream'+monitorId); } } @@ -239,7 +239,6 @@ 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); - actParms = actParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } // end if have a new auth hash } // end if respObj.status @@ -722,10 +721,14 @@ function handleClick( event ) { function appletRefresh() { if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) { - var streamImg = $('liveStream'); - var parent = streamImg.getParent(); - streamImg.dispose(); - streamImg.inject( parent ); + var streamImg = $('liveStream'+monitorId); + if ( streamImg ) { + var parent = streamImg.getParent(); + streamImg.dispose(); + streamImg.inject( parent ); + } else { + console.error("Nothing found for liveStream"+monitorId); + } if ( appletRefreshTime ) { appletRefresh.delay( appletRefreshTime*1000 ); } @@ -767,7 +770,7 @@ function reloadWebSite() { function initPage() { if ( monitorType != 'WebSite' ) { - if ( streamMode == "single" ) { + if ( streamMode == 'single' ) { statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); watchdogCheck.pass('status').periodical(statusRefreshTimeout*2); } else { @@ -778,20 +781,24 @@ function initPage() { eventCmdTimer = eventCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); watchdogCheck.pass('event').periodical(eventsRefreshTimeout*2); - if ( canStreamNative || streamMode == "single" ) { + if ( canStreamNative || (streamMode == 'single') ) { var streamImg = $('imageFeed').getElement('img'); if ( !streamImg ) { streamImg = $('imageFeed').getElement('object'); } - if ( streamMode == "single" ) { - streamImg.addEvent('click', fetchImage.pass(streamImg)); - fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + if ( !streamImg ) { + console.error('No streamImg found for imageFeed'); } else { - streamImg.addEvent('click', function(event) { - handleClick(event); - }); - } - } + if ( streamMode == 'single' ) { + streamImg.addEvent('click', fetchImage.pass(streamImg)); + fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + } else { + streamImg.addEvent('click', function(event) { + handleClick(event); + }); + } + } // end if have streamImg + } // streamMode native or single if ( refreshApplet && appletRefreshTime ) { appletRefresh.delay(appletRefreshTime*1000); From d7ea798c096de9fce500be50aba581bf60a3082d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 20 Mar 2020 11:06:32 -0400 Subject: [PATCH 054/319] add modern stracktrace to log messages --- web/js/logger.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/js/logger.js b/web/js/logger.js index 0a1195043..78dc84543 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -49,6 +49,8 @@ function logReport( level, message, file, line ) { /* eslint-disable no-caller */ if ( arguments && arguments.callee && arguments.callee.caller && arguments.callee.caller.caller && arguments.callee.caller.caller.name ) { message += ' - '+arguments.callee.caller.caller.name+'()'; + } else { + message += new Error().stack; } /* eslint-enable no-caller */ From d3df0defc9ea891496168a105beed3571003b212 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 21 Mar 2020 15:28:18 -0400 Subject: [PATCH 055/319] Support missing openssl_random_pseudo_bytes by using alternate functions --- web/includes/functions.php | 15 +++++++++++++++ web/index.php | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 13b347893..537a9fa8e 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -2620,4 +2620,19 @@ function random_colour() { str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT); } +function zm_random_bytes($length = 32){ + if ( !isset($length) || intval($length) <= 8 ) { + $length = 32; + } + if ( function_exists('random_bytes') ) { + return random_bytes($length); + } + if ( function_exists('mcrypt_create_iv') ) { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } + if ( function_exists('openssl_random_pseudo_bytes') ) { + return openssl_random_pseudo_bytes($length); + } + ZM\Error('No random_bytes function found.'); +} ?> diff --git a/web/index.php b/web/index.php index 2f1a6cf62..d5f261742 100644 --- a/web/index.php +++ b/web/index.php @@ -177,7 +177,7 @@ if ( isset($_REQUEST['view']) ) # Add CSP Headers -$cspNonce = bin2hex(openssl_random_pseudo_bytes(16)); +$cspNonce = bin2hex(zm_random_bytes(16)); $request = null; if ( isset($_REQUEST['request']) ) From a70ab85407e15af60ab2aff4f2521d2e9edfcb87 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Tue, 24 Mar 2020 09:29:19 -0700 Subject: [PATCH 056/319] Fix 2892 --- CMakeLists.txt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05d726937..df1702328 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -739,14 +739,6 @@ if(HAVE_OPENSSL_MD5_H) "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) endif(HAVE_OPENSSL_MD5_H) -if(HAVE_GNUTLS_OPENSSL_H) - set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition( - MD5 - "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" - HAVE_MD5_GNUTLS) -endif(HAVE_GNUTLS_OPENSSL_H) if(HAVE_GNUTLS_GNUTLS_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") @@ -755,13 +747,13 @@ if(HAVE_GNUTLS_GNUTLS_H) "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT) endif(HAVE_GNUTLS_GNUTLS_H) -if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +if(HAVE_MD5_OPENSSL) set(HAVE_DECL_MD5 1) -else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +else(HAVE_MD5_OPENSSL) message(AUTHOR_WARNING "ZoneMinder requires a working MD5 function for hashed authenication but none were found - hashed authenication will not be available") -endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +endif(HAVE_MD5_OPENSSL) # Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available. # This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) From 0b5ebbbe59e861a1c1b15c715b2caa503fd3ac3a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 14:56:25 -0400 Subject: [PATCH 057/319] add focal to ubuntu builds --- utils/do_debian_package.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 539205e3b..af9921294 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -80,7 +80,7 @@ fi; if [ "$DISTROS" == "" ]; then if [ "$RELEASE" != "" ]; then - DISTROS="xenial,bionic,disco,eoan,trusty" + DISTROS="xenial,bionic,disco,eoan,focal,trusty" else DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`; fi; From 7fcd71e684a30dce8a9802c54c15d8f717a1d6d0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 15:19:16 -0400 Subject: [PATCH 058/319] Use fputc instead of printf for separator to be a tiny bit more efficient. Report current state when forcing alarm on --- src/zmu.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/zmu.cpp b/src/zmu.cpp index 394770666..12f8f9338 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -476,14 +476,12 @@ int main(int argc, char *argv[]) { } // end if auth if ( mon_id > 0 ) { - //fprintf(stderr,"Monitor %d\n", mon_id); Monitor *monitor = Monitor::Load(mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY); if ( monitor ) { - //fprintf(stderr,"Monitor %d(%s)\n", monitor->Id(), monitor->Name()); if ( verbose ) { printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name()); } - if ( ! monitor->connect() ) { + if ( !monitor->connect() ) { Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name()); exit_zmu(-1); } @@ -495,13 +493,13 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle")); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", state); have_output = true; } } if ( function & ZMU_TIME ) { - struct timeval timestamp = monitor->GetTimestamp( image_idx ); + struct timeval timestamp = monitor->GetTimestamp(image_idx); if ( verbose ) { char timestamp_str[64] = "None"; if ( timestamp.tv_sec ) @@ -511,7 +509,7 @@ int main(int argc, char *argv[]) { else printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000); have_output = true; } @@ -520,7 +518,7 @@ int main(int argc, char *argv[]) { if ( verbose ) printf("Last read index: %d\n", monitor->GetLastReadIndex()); else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", monitor->GetLastReadIndex()); have_output = true; } @@ -529,7 +527,7 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Last write index: %d\n", monitor->GetLastWriteIndex()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", monitor->GetLastWriteIndex()); have_output = true; } @@ -538,16 +536,16 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%" PRIu64, monitor->GetLastEventId()); have_output = true; } } if ( function & ZMU_FPS ) { - if ( verbose ) + if ( verbose ) { printf("Current capture rate: %.2f frames per second\n", monitor->GetFPS()); - else { - if ( have_output ) printf("%c", separator); + } else { + if ( have_output ) fputc(separator, stdout); printf("%.2f", monitor->GetFPS()); have_output = true; } @@ -573,10 +571,16 @@ int main(int argc, char *argv[]) { if ( monitor->GetFunction() == Monitor::Function::MONITOR ) { printf("A Monitor in monitor mode cannot handle alarms. Please use NoDect\n"); } else { - if ( verbose ) - printf("Forcing alarm on\n"); + Monitor::State state = monitor->GetState(); + + if ( verbose ) { + printf("Forcing alarm on current state: %s, event %" PRIu64 "\n", + state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"), + monitor->GetLastEventId() + ); + } monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web"); - while ( (monitor->GetState() != Monitor::ALARM) && !zm_terminate ) { + while ( ((state = monitor->GetState()) != Monitor::ALARM) && !zm_terminate ) { // Wait for monitor to notice. usleep(1000); } @@ -630,7 +634,7 @@ int main(int argc, char *argv[]) { else printf("Current brightness: %d\n", monitor->actionBrightness()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( brightness >= 0 ) printf("%d", monitor->actionBrightness(brightness)); else @@ -645,7 +649,7 @@ int main(int argc, char *argv[]) { else printf("Current contrast: %d\n", monitor->actionContrast()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( contrast >= 0 ) printf("%d", monitor->actionContrast(contrast)); else @@ -660,7 +664,7 @@ int main(int argc, char *argv[]) { else printf("Current hue: %d\n", monitor->actionHue()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( hue >= 0 ) printf("%d", monitor->actionHue(hue)); else @@ -675,7 +679,7 @@ int main(int argc, char *argv[]) { else printf("Current colour: %d\n", monitor->actionColour()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( colour >= 0 ) printf("%d", monitor->actionColour(colour)); else From 0ecd35e1c6c96072f3bfe5bc5401559b801311b3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 15:30:16 -0400 Subject: [PATCH 059/319] fix warnings because length() returns long unsigned int, but gnutls_datum_t size is unsigned int. --- src/zm_rtsp_auth.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 81aa13c27..2f8b2c3ce 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -144,7 +144,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; + gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), (unsigned int)ha1Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -159,7 +159,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; + gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), (unsigned int)ha2Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -180,7 +180,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; + gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), (unsigned int)digestData.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { From adf953dcdd6e0df5155eda84d3d8a10f19ec53ec Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 15:30:52 -0400 Subject: [PATCH 060/319] use bool instead of my_bool to fix #2886 --- src/zm_db.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 77c149f03..8e6258b0b 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -40,7 +40,7 @@ bool zmDbConnect() { Error("Can't initialise database connection: %s", mysql_error(&dbconn)); return false; } - my_bool reconnect = 1; + 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() ) From b611e2616bb63666c0d8a67c50fced9b17c62f7b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 16:15:24 -0400 Subject: [PATCH 061/319] handle scale=auto --- web/includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 87dc6c042..7c18d59a2 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -955,7 +955,7 @@ function reScale($dimension, $dummy) { $new_dimension = $dimension; for ( $i = 1; $i < func_num_args(); $i++ ) { $scale = func_get_arg($i); - if ( !empty($scale) && ($scale != '0') && ($scale != SCALE_BASE) ) + if ( !empty($scale) && ($scale != '0') && ($scale != 'auto') && ($scale != SCALE_BASE) ) $new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE); } return $new_dimension; From 2bd805c1f155b7824c806ce47d0de88bf76761e7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 16:16:32 -0400 Subject: [PATCH 062/319] minor code cleanup, adding monitor click event in a previous for loop instead of iterating again --- web/skins/classic/views/js/montagereview.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 2d482f771..fe9ccd3cd 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -971,6 +971,7 @@ function initPage() { imagedone(this, this.monId, false); }; loadImage2Monitor(monId, monitorImageURL[monId]); + monitorCanvasObj[monId].addEventListener('click', clickMonitor, false); } } // end foreach monitor @@ -986,15 +987,6 @@ function initPage() { ctx = canvas.getContext('2d'); drawGraph(); } - for ( i=0, len=monitorPtr.length; i < len; i += 1 ) { - var monitor_id = monitorPtr[i]; - monitor_canvas = $('Monitor'+monitor_id); - if ( ! monitor_canvas ) { - console.log("No canvas found for monitor " + monitor_id); - continue; - } - monitor_canvas.addEventListener('click', clickMonitor, false); - } setSpeed(speedIndex); //setFit(fitMode); // will redraw //setLive(liveMode); // will redraw From 1d3ad05bb5b5c07c12853a8f097d24e3da96d3da Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Wed, 25 Mar 2020 23:21:34 -0700 Subject: [PATCH 063/319] Start implementing VNC --- db/zm_create.sql.in | 6 +- src/CMakeLists.txt | 2 +- src/zm_camera.h | 3 +- src/zm_libvnc_camera.cpp | 128 ++++++++++++++++++++++++++++ src/zm_libvnc_camera.h | 64 ++++++++++++++ src/zm_monitor.cpp | 19 +++++ src/zm_monitor.h | 1 + web/api/app/Model/Monitor.php | 2 +- web/skins/classic/views/monitor.php | 14 ++- 9 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 src/zm_libvnc_camera.cpp create mode 100644 src/zm_libvnc_camera.h diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index ce504a422..ce462ac4c 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', @@ -406,7 +406,7 @@ DROP TABLE IF EXISTS `MonitorPresets`; CREATE TABLE `MonitorPresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Device` tinytext, `Channel` tinyint(3) unsigned default NULL, `Format` int(10) unsigned default NULL, @@ -440,7 +440,7 @@ CREATE TABLE `Monitors` ( `Notes` TEXT, `ServerId` int(10) unsigned, `StorageId` smallint(5) unsigned default 0, - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255), diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 331f9e039..2f75c5c04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) -set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp) +set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp) # A fix for cmake recompiling the source files for every target. diff --git a/src/zm_camera.h b/src/zm_camera.h index a6f576af2..2742ef1d8 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -35,7 +35,7 @@ class Camera; // class Camera { protected: - typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; + typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC, VNC_SRC } SourceType; unsigned int monitor_id; Monitor * monitor; // Null on instantiation, set as soon as possible. @@ -68,6 +68,7 @@ public: bool IsFfmpeg() const { return type == FFMPEG_SRC; } bool IsLibvlc() const { return type == LIBVLC_SRC; } bool IscURL() const { return type == CURL_SRC; } + bool IsVNC() const { return type == VNC_SRC; } unsigned int Width() const { return width; } unsigned int Height() const { return height; } unsigned int Colours() const { return colours; } diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp new file mode 100644 index 000000000..efd2954cd --- /dev/null +++ b/src/zm_libvnc_camera.cpp @@ -0,0 +1,128 @@ +#include "zm.h" +#include "zm_signal.h" +#include "zm_libvnc_camera.h" + +static int TAG_0; +static int TAG_1; +static int TAG_2; + +static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, int h){ + VncPrivateData *data = (VncPrivateData *)rfbClientGetClientData(rfb, &TAG_0); + data->buffer = rfb->frameBuffer; +} + +static char* GetPasswordCallback(rfbClient* cl){ + return strdup((const char *)rfbClientGetClientData(cl, &TAG_1)); +} + +static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ + rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); + if(credentialType != rfbCredentialTypeUser) { + return NULL; + } + + c->userCredential.password = strdup((const char *)rfbClientGetClientData(cl, &TAG_1)); + c->userCredential.username = strdup((const char *)rfbClientGetClientData(cl, &TAG_2)); + return c; +} + +VncCamera::VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ) : + Camera( + p_monitor_id, + VNC_SRC, + p_width, + p_height, + p_colours, + ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), + p_brightness, + p_contrast, + p_hue, + p_colour, + p_capture, + p_record_audio + ), + mHost(host), + mPort(port), + mUser(user), + mPass(pass) +{ + Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); + if( capture ) + Initialise(); +} + + +VncCamera::~VncCamera() { + if( capture ) + Terminate(); +} + +void VncCamera::Initialise() { + Debug(2, "Initializing Client"); + mRfb = rfbGetClient(8, 3, 4); + rfbClientSetClientData(mRfb, &TAG_0, &mVncData); + rfbClientSetClientData(mRfb, &TAG_1, (void *)mPass.c_str()); + rfbClientSetClientData(mRfb, &TAG_2, (void *)mUser.c_str()); + + mVncData.bufferSize = width * height * 4; + mVncData.buffer = (uint8_t *)malloc(mVncData.bufferSize * sizeof(uint8_t)); + + 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()); + rfbInitClient(mRfb, 0, nullptr); +} + +void VncCamera::Terminate() { + if(mVncData.buffer) + free(mVncData.buffer); + return; +} + +int VncCamera::PrimeCapture() { + Info("Priming capture from %s", mHost.c_str()); + return 0; +} + +int VncCamera::PreCapture() { + WaitForMessage(mRfb, 500); + rfbBool res = HandleRFBServerMessage(mRfb); + return res == TRUE ? 1 : -1 ; +} + +int VncCamera::Capture(Image &image) { + Debug(2, "Capturing"); + image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); + return 1; +} + +int VncCamera::PostCapture() { + return 0; +} + +int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_directory) { + return 0; +} + +int VncCamera::Close() { + rfbClientCleanup(mRfb); + return 0; +} \ No newline at end of file diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h new file mode 100644 index 000000000..667111bc5 --- /dev/null +++ b/src/zm_libvnc_camera.h @@ -0,0 +1,64 @@ + +#ifndef ZN_LIBVNC_CAMERA_H +#define ZN_LIBVNC_CAMERA_H + +#include "zm_buffer.h" +#include "zm_camera.h" +#include "zm_thread.h" + +#include + +// Used by vnc callbacks +struct VncPrivateData +{ + uint8_t *buffer; + uint8_t *prevBuffer; + uint32_t bufferSize; + Mutex mutex; + ThreadData newImage; +}; + +class VncCamera : public Camera { +protected: + rfbClient *mRfb; + VncPrivateData mVncData; + int mBpp; + int mSpp; + int mBps; + char** mOptArgvs; + std::string mHost; + std::string mPort; + std::string mUser; + std::string mPass; + time_t secs; +public: + VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ); + + ~VncCamera(); + + void Initialise(); + void Terminate(); + + int PreCapture(); + int PrimeCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, timeval recording, char* event_directory ); + int Close(); +}; + +#endif \ No newline at end of file diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2f0e4826b..291fe039d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -51,6 +51,7 @@ #if HAVE_LIBCURL #include "zm_curl_camera.h" #endif // HAVE_LIBCURL +#include "zm_libvnc_camera.h" #if ZM_MEM_MAPPED #include @@ -89,6 +90,7 @@ std::string CameraType_Strings[] = { "Ffmpeg", "LibVLC", "CURL", + "VNC", }; @@ -2332,6 +2334,23 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { #else // HAVE_LIBCURL Fatal("You must have libcurl installed to use ffmpeg cameras for monitor %d", id); #endif // HAVE_LIBCURL + } else if ( type == "VNC" ) { + camera = new VncCamera( + id, + host.c_str(), + port.c_str(), + user.c_str(), + pass.c_str(), + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + record_audio + ); } else { Fatal("Bogus monitor type '%s' for monitor %d", type.c_str(), id); } // end if type diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 9109c9c72..e1d66950c 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -75,6 +75,7 @@ public: FFMPEG, LIBVLC, CURL, + VNC, } CameraType; typedef enum { diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index aa959f429..2de0b6fcb 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -119,7 +119,7 @@ class Monitor extends AppModel { ); public $actsAs = array( 'CakePHP-Enum-Behavior.Enum' => array( - 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite'), + 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite', 'VNC'), 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), 'Orientation' => array('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT'), 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 20139c85a..413d5a517 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -123,7 +123,8 @@ $sourceTypes = array( 'Libvlc' => translate('Libvlc'), 'cURL' => 'cURL (HTTP(S) only)', 'WebSite'=> 'Web Site', - 'NVSocket' => translate('NVSocket') + 'NVSocket' => translate('NVSocket'), + 'VNC' => 'VNC' ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -779,6 +780,13 @@ switch ( $tab ) { } else if ( $monitor->Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); + } else if ( $monitor->Type() == 'VNC' ) { +?> + + + + +Type() == 'Remote' ) { ?> Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> @@ -811,8 +819,8 @@ include('_monitor_source_nvsocket.php'); ?> () - () - + () + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> From 5b1409d8df0f4c7e3f70bc53bb0cfaa09f522e70 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Wed, 25 Mar 2020 23:29:22 -0700 Subject: [PATCH 064/319] Add cmake rules to check for libvnc --- CMakeLists.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index df1702328..dfc1f7b75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,8 @@ set(ZM_NO_MMAP "OFF" CACHE BOOL experience problems with the shared memory. default: OFF") set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") +set(ZM_NO_LIBVNC "OFF" CACHE BOOL +"Set to ON to skip libvnc checks and force building ZM without libvnc. default: OFF") set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") set(ZM_NO_X10 "OFF" CACHE BOOL @@ -674,6 +676,25 @@ if(NOT ZM_NO_LIBVLC) endif(LIBVLC_LIBRARIES) endif(NOT ZM_NO_LIBVLC) +if(NOT ZM_NO_LIBVNC) + # libvncclient (using find_library and find_path) + find_library(LIBVNC_LIBRARIES vncclient) + if(LIBVNC_LIBRARIES) + set(HAVE_LIBVNC 1) + list(APPEND ZM_BIN_LIBS "${LIBVNC_LIBRARIES}") + find_path(LIBVNC_INCLUDE_DIR "rfb/rfb.h") + if(LIBVNC_INCLUDE_DIR) + include_directories("${LIBVNC_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${LIBVNC_INCLUDE_DIR}") + endif(LIBVNC_INCLUDE_DIR) + mark_as_advanced(FORCE LIBVNC_LIBRARIES LIBVNC_INCLUDE_DIR) + check_include_file("rfb/rfb.h" HAVE_RFB_RFB_H) + set(optlibsfound "${optlibsfound} libVNC") + else(LIBVNC_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} libVNC") + endif(LIBVNC_LIBRARIES) +endif(NOT ZM_NO_LIBVNC) + #find_package(Boost 1.36.0) #if(Boost_FOUND) #include_directories(${Boost_INCLUDE_DIRS}) From 156728c9b6a1c9dd74c19c1cdf7e444ae50fb02d Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 26 Mar 2020 00:08:00 -0700 Subject: [PATCH 065/319] Use preprocessors to ensure libvnc monitor is only built when libvnc is found --- src/zm_libvnc_camera.cpp | 5 ++++- src/zm_libvnc_camera.h | 3 ++- src/zm_monitor.cpp | 6 ++++++ zoneminder-config.cmake | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index efd2954cd..c72b0b340 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -2,6 +2,8 @@ #include "zm_signal.h" #include "zm_libvnc_camera.h" +#if HAVE_LIBVNC + static int TAG_0; static int TAG_1; static int TAG_2; @@ -125,4 +127,5 @@ int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_dir int VncCamera::Close() { rfbClientCleanup(mRfb); return 0; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 667111bc5..6d9285114 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -6,6 +6,7 @@ #include "zm_camera.h" #include "zm_thread.h" +#if HAVE_LIBVNC #include // Used by vnc callbacks @@ -61,4 +62,4 @@ public: int Close(); }; -#endif \ No newline at end of file +#endif // HAVE_LIBVNC diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 291fe039d..1186c75f7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -51,7 +51,9 @@ #if HAVE_LIBCURL #include "zm_curl_camera.h" #endif // HAVE_LIBCURL +#if HAVE_LIBVNC #include "zm_libvnc_camera.h" +#endif // HAVE_LIBVNC #if ZM_MEM_MAPPED #include @@ -2335,6 +2337,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { Fatal("You must have libcurl installed to use ffmpeg cameras for monitor %d", id); #endif // HAVE_LIBCURL } else if ( type == "VNC" ) { +#if HAVE_LIBVNC camera = new VncCamera( id, host.c_str(), @@ -2351,6 +2354,9 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { purpose==CAPTURE, record_audio ); +#else // HAVE_LIBVNC + Fatal("You must have libvnc installed to use VNC cameras for monitor id %d", id); +#endif // HAVE_LIBVNC } else { Fatal("Bogus monitor type '%s' for monitor %d", type.c_str(), id); } // end if type diff --git a/zoneminder-config.cmake b/zoneminder-config.cmake index 320620aa0..edcb42ff6 100644 --- a/zoneminder-config.cmake +++ b/zoneminder-config.cmake @@ -59,6 +59,8 @@ #cmakedefine HAVE_LIBAVRESAMPLE_AVRESAMPLE_H 1 #cmakedefine HAVE_LIBVLC 1 #cmakedefine HAVE_VLC_VLC_H 1 +#cmakedefine HAVE_LIBVNC 1 +#cmakedefine HAVE_RFB_RFB_H 1 #cmakedefine HAVE_LIBX264 1 #cmakedefine HAVE_X264_H 1 #cmakedefine HAVE_LIBMP4V2 1 From 8f5a3c1bf2c98dfdd90a27bf13a0c995ea78ba1f Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 26 Mar 2020 00:52:34 -0700 Subject: [PATCH 066/319] Add a missing endif --- src/zm_libvnc_camera.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 6d9285114..f57d12813 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -63,3 +63,4 @@ public: }; #endif // HAVE_LIBVNC +#endif // ZN_LIBVNC_CAMERA_H From cbda9848ab8154a25c4a5092372d2ef9f203766c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 12:06:07 -0400 Subject: [PATCH 067/319] Fix SLOW FWD not working --- src/zm_eventstream.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index bb61ca357..4e2eeb1b7 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -76,7 +76,7 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) { curr_frame_id = 1; // curr_frame_id is 1-based if ( event_time >= event_data->start_time ) { Debug(2, "event time is after event start"); - for (unsigned int i = 0; i < event_data->frame_count; i++ ) { + for ( unsigned int i = 0; i < event_data->frame_count; i++ ) { //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); if ( event_data->frames[i].timestamp >= event_time ) { curr_frame_id = i+1; @@ -378,6 +378,8 @@ void EventStream::processCommand(const CmdMsg *msg) { paused = true; replay_rate = ZM_RATE_BASE; step = 1; + if ( (unsigned int)curr_frame_id < event_data->frame_count ) + curr_frame_id += 1; break; case CMD_SLOWREV : Debug(1, "Got SLOW REV command"); @@ -848,20 +850,15 @@ void EventStream::runStream() { // commands may set send_frame to true while ( checkCommandQueue() && !zm_terminate ) { // The idea is to loop here processing all commands before proceeding. - Debug(1, "Have command queue"); } - Debug(2, "Done command queue"); // Update modified time of the socket .lock file so that we can tell which ones are stale. if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) { touch(sock_path_lock); last_comm_update = now; } - } else { - Debug(2, "Not checking command queue"); } - // Get current frame data FrameData *frame_data = &event_data->frames[curr_frame_id-1]; @@ -1017,7 +1014,8 @@ void EventStream::runStream() { curr_frame_id += step; // Detects when we hit end of event and will load the next event or previous event - checkEventLoaded(); + if ( !paused ) + checkEventLoaded(); } // end while ! zm_terminate #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) From a5ec89ab1b2408a740a25428682d0aebec0f486a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 15:30:52 -0400 Subject: [PATCH 068/319] use bool instead of my_bool to fix #2886 --- src/zm_db.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 77c149f03..8e6258b0b 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -40,7 +40,7 @@ bool zmDbConnect() { Error("Can't initialise database connection: %s", mysql_error(&dbconn)); return false; } - my_bool reconnect = 1; + 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() ) From ea7bea4c6e20e4e0cd558c86c4f3006cfede50a6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 15:30:16 -0400 Subject: [PATCH 069/319] fix warnings because length() returns long unsigned int, but gnutls_datum_t size is unsigned int. --- src/zm_rtsp_auth.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 81aa13c27..2f8b2c3ce 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -144,7 +144,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; + gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), (unsigned int)ha1Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -159,7 +159,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; + gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), (unsigned int)ha2Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -180,7 +180,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; + gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), (unsigned int)digestData.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { From eed45ae66c449af305c1f24a944535b1c6c8e4fd Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Tue, 24 Mar 2020 09:29:19 -0700 Subject: [PATCH 070/319] Fix 2892 --- CMakeLists.txt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 018fc0eb0..a15267582 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -738,14 +738,6 @@ if(HAVE_OPENSSL_MD5_H) "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) endif(HAVE_OPENSSL_MD5_H) -if(HAVE_GNUTLS_OPENSSL_H) - set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition( - MD5 - "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" - HAVE_MD5_GNUTLS) -endif(HAVE_GNUTLS_OPENSSL_H) if(HAVE_GNUTLS_GNUTLS_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") @@ -754,13 +746,13 @@ if(HAVE_GNUTLS_GNUTLS_H) "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT) endif(HAVE_GNUTLS_GNUTLS_H) -if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +if(HAVE_MD5_OPENSSL) set(HAVE_DECL_MD5 1) -else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +else(HAVE_MD5_OPENSSL) message(AUTHOR_WARNING "ZoneMinder requires a working MD5 function for hashed authenication but none were found - hashed authenication will not be available") -endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +endif(HAVE_MD5_OPENSSL) # Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available. # This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) From cf6b24b432c4492b24117042e22e7b8242793b72 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Mon, 16 Mar 2020 14:49:27 -0400 Subject: [PATCH 071/319] add ES status to telemetry and --show option to display what is being sent to ZM --- scripts/zmtelemetry.pl.in | 47 ++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index 9b5e7c89d..82ceeac96 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -45,6 +45,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $help = 0; my $force = 0; +my $show = 0; # Interval between version checks my $interval; my $version; @@ -52,6 +53,7 @@ my $version; GetOptions( force => \$force, help => \$help, + show => \$show, interval => \$interval, version => \$version ); @@ -59,6 +61,14 @@ if ( $version ) { print( ZoneMinder::Base::ZM_VERSION . "\n"); exit(0); } +if ($show) { + my %telemetry; + my $dbh = zmDbConnect(); + collectData($dbh, \%telemetry); + my $result = jsonEncode(\%telemetry); + print ($result); + exit(0); +} if ( $help ) { pod2usage(-exitstatus => -1); } @@ -89,21 +99,9 @@ while( 1 ) { my $dbh = zmDbConnect(); # Build the telemetry hash # We should keep *BSD systems in mind when calling system commands + my %telemetry; - $telemetry{uuid} = getUUID($dbh); - @telemetry{qw(city region country latitude longitude)} = getGeo(); - $telemetry{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); - $telemetry{monitor_count} = countQuery($dbh,'Monitors'); - $telemetry{event_count} = countQuery($dbh,'Events'); - $telemetry{architecture} = runSysCmd('uname -p'); - ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); - $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; - $telemetry{system_memory} = totalmem(); - $telemetry{processor_count} = cpu_count(); - $telemetry{monitors} = getMonitorRef($dbh); - - Info('Sending data to ZoneMinder Telemetry server.'); - + collectData($dbh,\%telemetry); my $result = jsonEncode(\%telemetry); if ( sendData($result) ) { @@ -126,6 +124,24 @@ print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', loc # SUBROUTINES # ############### +# collect data to send +sub collectData { + my $dbh = shift; + my $telemetry = shift; + $telemetry->{uuid} = getUUID($dbh); + ($telemetry->{city},$telemetry->{region},$telemetry->{country},$telemetry->{latitude},$telemetry->{longitude})=getGeo(); + $telemetry->{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); + $telemetry->{monitor_count} = countQuery($dbh,'Monitors'); + $telemetry->{event_count} = countQuery($dbh,'Events'); + $telemetry->{architecture} = runSysCmd('uname -p'); + ($telemetry->{kernel}, $telemetry->{distro}, $telemetry->{version}) = getDistro(); + $telemetry->{zm_version} = ZoneMinder::Base::ZM_VERSION; + $telemetry->{system_memory} = totalmem(); + $telemetry->{processor_count} = cpu_count(); + $telemetry->{use_event_server} = $Config{ZM_OPT_USE_EVENTNOTIFICATION}; + $telemetry->{monitors} = getMonitorRef($dbh); +} + # Find, verify, then run the supplied system command sub runSysCmd { my $msg = shift; @@ -365,7 +381,7 @@ zmtelemetry.pl - Send usage information to the ZoneMinder development team =head1 SYNOPSIS - zmtelemetry.pl [--force] [--help] [--interval=seconds] [--version] + zmtelemetry.pl [--force] [--help] [--show] [--interval=seconds] [--version] =head1 DESCRIPTION @@ -382,6 +398,7 @@ console under Options. --force Force the script to upload it's data instead of waiting for the defined interval since last upload. --help Display usage information + --show Displays telemetry data that is sent to zoneminder --interval Override the default configured interval since last upload. The value should be given in seconds, but can be an expression such as 24*60*60. From 45299955a1b07b0ad5c3bf7fe84db0ac4a8fb761 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 12:06:07 -0400 Subject: [PATCH 072/319] Fix SLOW FWD not working --- src/zm_eventstream.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 034ff778a..ee495a2e0 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -76,7 +76,7 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) { curr_frame_id = 1; // curr_frame_id is 1-based if ( event_time >= event_data->start_time ) { Debug(2, "event time is after event start"); - for (unsigned int i = 0; i < event_data->frame_count; i++ ) { + for ( unsigned int i = 0; i < event_data->frame_count; i++ ) { //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); if ( event_data->frames[i].timestamp >= event_time ) { curr_frame_id = i+1; @@ -377,6 +377,8 @@ void EventStream::processCommand(const CmdMsg *msg) { paused = true; replay_rate = ZM_RATE_BASE; step = 1; + if ( (unsigned int)curr_frame_id < event_data->frame_count ) + curr_frame_id += 1; break; case CMD_SLOWREV : Debug(1, "Got SLOW REV command"); @@ -845,20 +847,15 @@ void EventStream::runStream() { // commands may set send_frame to true while ( checkCommandQueue() && !zm_terminate ) { // The idea is to loop here processing all commands before proceeding. - Debug(1, "Have command queue"); } - Debug(2, "Done command queue"); // Update modified time of the socket .lock file so that we can tell which ones are stale. if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) { touch(sock_path_lock); last_comm_update = now; } - } else { - Debug(2, "Not checking command queue"); } - // Get current frame data FrameData *frame_data = &event_data->frames[curr_frame_id-1]; @@ -1014,7 +1011,8 @@ void EventStream::runStream() { curr_frame_id += step; // Detects when we hit end of event and will load the next event or previous event - checkEventLoaded(); + if ( !paused ) + checkEventLoaded(); } // end while ! zm_terminate #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) From af36cc3e52c100244051b2c4de0503b68887120a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 13:57:00 -0400 Subject: [PATCH 073/319] Fix starting and stopping zmcontrol processes across servers. --- web/includes/Monitor.php | 12 ++++++++---- web/includes/actions/monitor.php | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index e2eee7ba9..8c45c0d27 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -497,6 +497,10 @@ class Monitor extends ZM_Object { if ( !count($options) ) { if ( $command == 'quit' ) { $options['command'] = 'quit'; + } else if ( $command == 'start' ) { + $options['command'] = 'start'; + } else if ( $command == 'stop' ) { + $options['command'] = 'stop'; } else { Warning("No commands to send to zmcontrol from $command"); return false; @@ -531,7 +535,7 @@ class Monitor extends ZM_Object { } else if ( $this->ServerId() ) { $Server = $this->Server(); - $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmcontrol.json'; + $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS ); @@ -547,12 +551,12 @@ class Monitor extends ZM_Object { $context = stream_context_create(); try { $result = file_get_contents($url, false, $context); - if ($result === FALSE) { /* Handle error */ - Error("Error restarting zma using $url"); + if ( $result === FALSE ) { /* Handle error */ + Error("Error sending command using $url"); return false; } } catch ( Exception $e ) { - Error("Except $e thrown trying to restart zma"); + Error("Exception $e thrown trying to send command to $url"); return false; } } else { diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index 94e527e29..78b64e13c 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -92,6 +92,9 @@ if ( $action == 'monitor' ) { if ( $monitor->Type() != 'WebSite' ) { $monitor->zmaControl('stop'); $monitor->zmcControl('stop'); + if ( $monitor->Controllable() ) { + $monitor->sendControlCommand('stop'); + } } # These are used in updating zones @@ -264,8 +267,7 @@ if ( $action == 'monitor' ) { $monitor->zmaControl('start'); if ( $monitor->Controllable() ) { - require_once('includes/control_functions.php'); - $monitor->sendControlCommand('quit'); + $monitor->sendControlCommand('start'); } } // really should thump zmwatch and maybe zmtrigger too. From b354641df3dd4538724b87ddf18809ba183fb711 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 13:57:19 -0400 Subject: [PATCH 074/319] fix syntax errors in triggers in monitor view --- web/skins/classic/views/monitor.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 76614decb..a78c6f615 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -729,12 +729,12 @@ switch ( $tab ) { continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
    '; - echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"':''). '/> '. $optTrigger; - $optCount ++; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; + $optCount ++; } # end foreach trigger option if ( !$optCount ) { - echo ''. translate('NoneAvailable') .''; + echo ''.translate('NoneAvailable').''; } ?> From 3e55795cad2e5dc1de6a9f8e108ef6b761c75d8b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 13:57:43 -0400 Subject: [PATCH 075/319] Handle different command line syntax for zmcontrol.pl --- web/api/app/Controller/MonitorsController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 92114a558..3818f69d3 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -379,6 +379,8 @@ class MonitorsController extends AppController { $args = ''; if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) { $args = '-d ' . $monitor['Device']; + } else if ( $daemon == 'zmcontrol.pl' ) { + $args = '--id '.$id; } else { $args = '-m ' . $id; } From e98f4227306773c10ed2a48200703e59a8c8133e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 15:04:28 -0400 Subject: [PATCH 076/319] Use %d for values that are actually integer in debug statements --- src/zm_local_camera.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index c6030f08a..f06f9f958 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -55,7 +55,7 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette) #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { - switch( palette ) { + switch ( palette ) { #if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444) case V4L2_PIX_FMT_RGB444 : pixFormat = AV_PIX_FMT_RGB444; @@ -745,12 +745,12 @@ void LocalCamera::Initialise() { Debug(4, " v4l2_data.fmt.type = %08x\n" - " v4l2_data.fmt.fmt.pix.width = %08x\n" - " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %d\n" + " v4l2_data.fmt.fmt.pix.height = %d\n" " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" " v4l2_data.fmt.fmt.pix.field = %08x\n" - " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" - " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %d\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %d\n" " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" " v4l2_data.fmt.fmt.pix.priv = %08x\n" , v4l2_data.fmt.type @@ -788,12 +788,12 @@ void LocalCamera::Initialise() { /* Note VIDIOC_S_FMT may change width and height. */ Debug(4, " v4l2_data.fmt.type = %08x\n" - " v4l2_data.fmt.fmt.pix.width = %08x\n" - " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %d\n" + " v4l2_data.fmt.fmt.pix.height = %d\n" " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" " v4l2_data.fmt.fmt.pix.field = %08x\n" - " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" - " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %d\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %d\n" " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" " v4l2_data.fmt.fmt.pix.priv = %08x\n" , v4l2_data.fmt.type From 79ad2ec87d63e806913cb80d695869dfdebea8d3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 15:08:32 -0400 Subject: [PATCH 077/319] better debug logging for SLOWFWD and SLOWREV listing new frame_id --- src/zm_eventstream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 4e2eeb1b7..8739dfd54 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -374,20 +374,20 @@ void EventStream::processCommand(const CmdMsg *msg) { } break; case CMD_SLOWFWD : - Debug(1, "Got SLOW FWD command"); paused = true; replay_rate = ZM_RATE_BASE; step = 1; if ( (unsigned int)curr_frame_id < event_data->frame_count ) curr_frame_id += 1; + Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id); break; case CMD_SLOWREV : - Debug(1, "Got SLOW REV command"); paused = true; replay_rate = ZM_RATE_BASE; step = -1; curr_frame_id -= 1; if ( curr_frame_id < 1 ) curr_frame_id = 1; + Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id); break; case CMD_FASTREV : Debug(1, "Got FAST REV command"); From bc950c9a34f414e6dabc88893a749b0f5968e171 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 15:11:39 -0400 Subject: [PATCH 078/319] Remove and as they are not used --- src/zm_eventstream.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index d0b5827e7..62be73015 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -20,9 +20,6 @@ #ifndef ZM_EVENTSTREAM_H #define ZM_EVENTSTREAM_H -#include -#include - #include "zm_image.h" #include "zm_stream.h" #include "zm_video.h" @@ -97,7 +94,7 @@ class EventStream : public StreamBase { public: EventStream() { mode = DEFAULT_MODE; - replay_rate = DEFAULT_RATE; + replay_rate = DEFAULT_RATE; forceEventChange = false; @@ -121,8 +118,8 @@ class EventStream : public StreamBase { void runStream(); Image *getImage(); private: - AVCodecContext *input_codec_context; - AVCodec *input_codec; + AVCodecContext *input_codec_context; + AVCodec *input_codec; }; #endif // ZM_EVENTSTREAM_H From c880793fdc411558236e8d8c55ec29c455e6c416 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 26 Mar 2020 15:34:36 -0400 Subject: [PATCH 079/319] bump version to 1.34.7 --- db/zm_update-1.34.7.sql | 5 +++++ distros/redhat/zoneminder.spec | 2 +- version | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/zm_update-1.34.7.sql diff --git a/db/zm_update-1.34.7.sql b/db/zm_update-1.34.7.sql new file mode 100644 index 000000000..ba86b1202 --- /dev/null +++ b/db/zm_update-1.34.7.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.6 database to 1.34.7 +-- +-- No changes required +-- diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 60701c429..39d6353f5 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.6 +Version: 1.34.7 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 6fef6c580..ca380088a 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.6 +1.34.7 From 17fbafd7dee9196a697bdf4d0146118f45494fc9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Feb 2020 17:42:02 -0500 Subject: [PATCH 080/319] including the language files must come after auth in order to user the user's language setting --- web/index.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/index.php b/web/index.php index d5f261742..18da56e63 100644 --- a/web/index.php +++ b/web/index.php @@ -153,7 +153,6 @@ if ( } -require_once('includes/lang.php'); # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -184,10 +183,14 @@ if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); require_once('includes/auth.php'); + # Only one request can open the session file at a time, so let's close the session here to improve concurrency. # Any file/page that sets session variables must re-open it. session_write_close(); +// lang references $user[Language] so must come after auth +require_once('includes/lang.php'); + foreach ( getSkinIncludes('skin.php') as $includeFile ) { require_once $includeFile; } From 1ab294e2917df0811257aed3323591fc943c826e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 12:43:21 -0400 Subject: [PATCH 081/319] Fix selecting layout after save by using value in session. When applying layout, if height is auto, set the img height to auto. --- web/skins/classic/views/js/montage.js | 9 ++++++--- web/skins/classic/views/montage.php | 12 ++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 00130f108..68a2acf2c 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -210,7 +210,6 @@ function Monitor(monitorData) { * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - console.log(element); layout = $j(element).val(); if ( layout_id = parseInt(layout) ) { @@ -221,8 +220,8 @@ function selectLayout(element) { // Need to clear the current positioning, and apply the new monitor_frame = $j('#monitorFrame'+monitor.id); - if ( ! monitor_frame ) { - console.log("Error finding frame for " + monitor.id); + if ( !monitor_frame ) { + console.log('Error finding frame for ' + monitor.id); continue; } @@ -262,6 +261,10 @@ function selectLayout(element) { if ( streamImg.nodeName == 'IMG' ) { var src = streamImg.src; src = src.replace(/width=[\.\d]+/i, 'width=0' ); + if ( $j('#height').val() == 'auto' ) { + src = src.replace(/height=[\.\d]+/i, 'height=0' ); + streamImg.style.height = 'auto'; + } if ( src != streamImg.src ) { streamImg.src = ''; streamImg.src = src; diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index 45ebcb0cc..0009b9929 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -69,17 +69,19 @@ foreach ( $layouts as $l ) { } } foreach ( $layouts as $l ) { - if ( $l->Name() != "Freeform" ) + if ( $l->Name() != 'Freeform' ) $layoutsById[$l->Id()] = $l; } -session_start(); +zm_session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; -#} elseif ( isset($_SESSION['zmMontageLayout']) ) { - #$layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id"); +} elseif ( isset($_SESSION['zmMontageLayout']) ) { + $layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id from session"); } $options = array(); @@ -88,6 +90,8 @@ $Positions = ''; if ( $layout_id and is_numeric($layout_id) and isset($layoutsById[$layout_id]) ) { $Layout = $layoutsById[$layout_id]; $Positions = json_decode($Layout->Positions(), true); +} else { + ZM\Logger::Debug("Layout not found"); } if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { // Use layout instead of other options From 50700031b19eb94bb0ea955fb775ed4f7a016461 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 13:05:12 -0400 Subject: [PATCH 082/319] Use zm_session_start instead of session_start --- web/includes/actions/montage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/actions/montage.php b/web/includes/actions/montage.php index 207c9a0f0..cd9a41aaf 100644 --- a/web/includes/actions/montage.php +++ b/web/includes/actions/montage.php @@ -36,7 +36,7 @@ if ( isset($_REQUEST['object']) ) { } $Layout->Positions($_REQUEST['Positions']); $Layout->save(); - session_start(); + zm_session_start(); $_SESSION['zmMontageLayout'] = $Layout->Id(); setcookie('zmMontageLayout', $Layout->Id(), 1); session_write_close(); From bfa08c342252f6f18f0e5b9246bcc40af4e8150e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 13:06:10 -0400 Subject: [PATCH 083/319] MOstly spaces and quotes, remove duplicated db version update and only prepare the sth once. --- scripts/zmupdate.pl.in | 107 +++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index b0b63757a..3662b6655 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -847,9 +847,9 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version eq "1.24.4" ) { + if ( $cascade || $version eq '1.24.4' ) { # Patch the database - patchDB( $dbh, "1.24.4" ); + patchDB($dbh, '1.24.4'); # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; @@ -863,12 +863,12 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version lt "1.26.0" ) { - my $sth = $dbh->prepare_cached( 'select * from Monitors LIMIT 0,1' ); + if ( $cascade || $version lt '1.26.0' ) { + my $sth = $dbh->prepare_cached('SELECT * FROM Monitors LIMIT 0,1'); die "Error: " . $dbh->errstr . "\n" unless ($sth); die "Error: " . $sth->errstr . "\n" unless ($sth->execute); - my $columns = $sth->{'NAME'}; + my $columns = $sth->{NAME}; if ( ! grep(/^Colours$/, @$columns ) ) { $dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;}); } # end if @@ -898,28 +898,31 @@ if ( $version ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if + my $sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; #PP make sure we use version compare - if ( version->parse('v' . $v) > version->parse('v' . $version) ) { - print( "Upgrading DB to $v from $version\n" ); - patchDB( $dbh, $v ); - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + if ( version->parse('v'.$v) > version->parse('v'.$version) ) { + print("Upgrading DB to $v from $version\n"); + if ( patchDB($dbh, $v) ) { + my $res = $sth->execute($version) or die( "Can't execute: ".$sth->errstr() ); + } #patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch ); } # end if newer version } # end foreach patchfile + + $sth->finish(); $cascade = !undef; } # end if if ( $cascade ) { - my $installed_version = ZM_VERSION; - my $sql = 'update Config set Value = ? where Name = ?'; + # This is basically here so that we don't need zm-update-blah.sql files for versions without db changes + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); - $res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_DB_VERSION') or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_CURR_VERSION') or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); } else { zmDbDisconnect(); @@ -930,41 +933,42 @@ if ( $version ) { #my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); #my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() ); #$sth->finish(); - print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" ); -} + print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n"); +} # end if version + zmDbDisconnect(); -exit( 0 ); +exit(0); sub patchDB_using_do { my ( $dbh, $version, $file ) = @_; - open( my $fh, '<', $file ) or die "Unable to open $file $!"; + open(my $fh, '<', $file) or die "Unable to open $file $!"; $/ = undef; my $sql = <$fh>; close $fh; if ( $sql ) { - $dbh->{'AutoCommit'} = 0; + $dbh->{AutoCommit} = 0; $dbh->do($sql); if ( $dbh->errstr() ) { $dbh->rollback(); - die "Error: " . $dbh->errstr(). ". Rolled back.\n"; + die 'Error: '.$dbh->errstr().". Rolled back.\n"; } # end if error - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = \'ZM_DYN_DB_VERSION\''; + my $sth = $dbh->prepare_cached($sql) or die "Can't prepare '$sql': ".$dbh->errstr(); + my $res = $sth->execute($version) or die 'Can\'t execute: '.$sth->errstr(); $sth->finish(); - $dbh->{'AutoCommit'} = 1; + $dbh->{AutoCommit} = 1; } else { Warning("Empty db update file at $file"); } -} +} # end sub patchDB_using_do + sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; if ( defined($portOrSocket) ) { @@ -988,39 +992,38 @@ sub patchDB { } $command .= '/zm_update-'.$version.'.sql'; - print( "Executing '$command'\n" ) if ( logDebugging() ); + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { - chomp( $output ); - print( "Output: $output\n" ); + chomp($output); + print("Output: $output\n"); } if ( $status ) { - die( "Command '$command' exited with status: $status\n" ); + die("Command '$command' exited with status: $status\n"); } - print( "\nDatabase successfully upgraded to version $version.\n" ); - -} + print("\nDatabase successfully upgraded to version $version.\n"); +} # end sub patchDB sub migratePasswords { - print ("Migratings passwords, if any...\n"); - my $sql = "select * from Users"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - while( my $user = $sth->fetchrow_hashref() ) { - my $scheme = substr($user->{Password}, 0, 1); - if ($scheme eq "*") { - print ("-->".$user->{Username}. " password will be migrated\n"); - my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); - my $settings = '$2a$10$'.$salt; - my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); - my $new_pass_hash = "-ZM-".$pass_hash; - $sql = "UPDATE Users SET PASSWORD=? WHERE Username=?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($new_pass_hash, $user->{Username}) or die( "Can't execute: ".$sth->errstr() ); - } + print ("Migratings passwords, if any...\n"); + my $sql = 'SELECT * FROM `Users`'; + my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); + while( my $user = $sth->fetchrow_hashref() ) { + my $scheme = substr($user->{Password}, 0, 1); + if ($scheme eq '*') { + print ('-->'.$user->{Username}." password will be migrated\n"); + my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); + my $settings = '$2a$10$'.$salt; + my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); + my $new_pass_hash = '-ZM-'.$pass_hash; + $sql = 'UPDATE Users SET `Password`=? WHERE `Username`=?'; + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($new_pass_hash, $user->{Username}) or die("Can't execute: ".$sth->errstr()); } -} + } +} # end sub migratePasswords sub migratePaths { From a555e09cad328e9991b9245bad8409460ee69018 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 13:15:11 -0400 Subject: [PATCH 084/319] Bump version to 1.34.8. Please note that the lack of zm-update-1.34.8.sql is intentional. It is not needed as there are no db updates --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 39d6353f5..a38e41a03 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.7 +Version: 1.34.8 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index ca380088a..bc4697cae 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.7 +1.34.8 From 23205cd07f1032d63d6936d9f4c19bea5a70b6c5 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Fri, 27 Mar 2020 11:15:15 -0700 Subject: [PATCH 085/319] WIP scaling --- src/zm_libvnc_camera.cpp | 53 ++++++++++++++++++++++++++--- src/zm_libvnc_camera.h | 11 +++--- web/skins/classic/views/monitor.php | 2 +- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index c72b0b340..a9863f450 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -1,6 +1,11 @@ #include "zm.h" #include "zm_signal.h" #include "zm_libvnc_camera.h" +extern "C" { + #include + #include + #include +} #if HAVE_LIBVNC @@ -76,13 +81,11 @@ VncCamera::~VncCamera() { void VncCamera::Initialise() { Debug(2, "Initializing Client"); mRfb = rfbGetClient(8, 3, 4); + rfbClientSetClientData(mRfb, &TAG_0, &mVncData); rfbClientSetClientData(mRfb, &TAG_1, (void *)mPass.c_str()); rfbClientSetClientData(mRfb, &TAG_2, (void *)mUser.c_str()); - mVncData.bufferSize = width * height * 4; - mVncData.buffer = (uint8_t *)malloc(mVncData.bufferSize * sizeof(uint8_t)); - mRfb->GotFrameBufferUpdate = GotFrameBufferUpdateCallback; mRfb->GetPassword = GetPasswordCallback; mRfb->GetCredential = GetCredentialsCallback; @@ -94,13 +97,15 @@ void VncCamera::Initialise() { } void VncCamera::Terminate() { - if(mVncData.buffer) - free(mVncData.buffer); return; } int VncCamera::PrimeCapture() { Info("Priming capture from %s", mHost.c_str()); + if(mRfb->si.framebufferWidth != width || mRfb->si.framebufferHeight != height) { + Info("Expected screen resolution does not match with the provided resolution, using scaling"); + mScale = true; + } return 0; } @@ -112,11 +117,49 @@ int VncCamera::PreCapture() { int VncCamera::Capture(Image &image) { Debug(2, "Capturing"); + int srcLineSize[4]; + int dstLineSize[4]; + int dstSize; + if(mScale) { + sws = sws_getContext(mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, + width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + if(!sws) { + Error("Could not scale image"); + return -1; + } + + if (av_image_fill_arrays(srcbuf, srcLineSize, mVncData.buffer, AV_PIX_FMT_RGBA, + mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, 16) < 0) { + sws_freeContext(sws); + Error("Could not allocate source image. Scaling failed"); + return -1; + } + + if ((dstSize = av_image_alloc(dstbuf, dstLineSize, width, height, + AV_PIX_FMT_RGBA, 1)) < 0) { + av_freep(&srcbuf[0]); + sws_freeContext(sws); + Error("Could not allocate dest image. Scaling failed"); + return -1; + } + + sws_scale(sws, (const uint8_t* const*)srcbuf, srcLineSize, 0, mRfb->si.framebufferHeight, + dstbuf, dstLineSize); + + } + else{ + dstbuf[0] = mVncData.buffer; + } image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); return 1; } int VncCamera::PostCapture() { + if(mScale) { + av_freep(&srcbuf[0]); + av_freep(&dstbuf[0]); + sws_freeContext(sws); + } return 0; } diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index f57d12813..9502f8330 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -13,10 +13,8 @@ struct VncPrivateData { uint8_t *buffer; - uint8_t *prevBuffer; - uint32_t bufferSize; - Mutex mutex; - ThreadData newImage; + uint8_t width; + uint8_t height; }; class VncCamera : public Camera { @@ -26,12 +24,13 @@ protected: int mBpp; int mSpp; int mBps; - char** mOptArgvs; + bool mScale; + uint8_t *srcbuf[4], *dstbuf[4]; + struct SwsContext *sws; std::string mHost; std::string mPort; std::string mUser; std::string mPass; - time_t secs; public: VncCamera( unsigned int p_monitor_id, diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 413d5a517..aabc97f34 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -785,7 +785,7 @@ include('_monitor_source_nvsocket.php'); - + Type() == 'Remote' ) { ?> From 4eed0a9c291f69a87f95164f43c89688b5a9b3a6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 12:43:21 -0400 Subject: [PATCH 086/319] Fix selecting layout after save by using value in session. When applying layout, if height is auto, set the img height to auto. --- web/skins/classic/views/js/montage.js | 9 ++++++--- web/skins/classic/views/montage.php | 12 ++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index d294ed813..5d3ce33b0 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -210,7 +210,6 @@ function Monitor(monitorData) { * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - console.log(element); layout = $j(element).val(); if ( layout_id = parseInt(layout) ) { @@ -221,8 +220,8 @@ function selectLayout(element) { // Need to clear the current positioning, and apply the new monitor_frame = $j('#monitorFrame'+monitor.id); - if ( ! monitor_frame ) { - console.log("Error finding frame for " + monitor.id); + if ( !monitor_frame ) { + console.log('Error finding frame for ' + monitor.id); continue; } @@ -262,6 +261,10 @@ function selectLayout(element) { if ( streamImg.nodeName == 'IMG' ) { var src = streamImg.src; src = src.replace(/width=[\.\d]+/i, 'width=0' ); + if ( $j('#height').val() == 'auto' ) { + src = src.replace(/height=[\.\d]+/i, 'height=0' ); + streamImg.style.height = 'auto'; + } if ( src != streamImg.src ) { streamImg.src = ''; streamImg.src = src; diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index d2cd18323..d6a51732e 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -66,17 +66,19 @@ foreach ( $layouts as $l ) { } } foreach ( $layouts as $l ) { - if ( $l->Name() != "Freeform" ) + if ( $l->Name() != 'Freeform' ) $layoutsById[$l->Id()] = $l; } -session_start(); +zm_session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; -#} elseif ( isset($_SESSION['zmMontageLayout']) ) { - #$layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id"); +} elseif ( isset($_SESSION['zmMontageLayout']) ) { + $layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id from session"); } $options = array(); @@ -85,6 +87,8 @@ $Positions = ''; if ( $layout_id and is_numeric($layout_id) and isset($layoutsById[$layout_id]) ) { $Layout = $layoutsById[$layout_id]; $Positions = json_decode($Layout->Positions(), true); +} else { + ZM\Logger::Debug("Layout not found"); } if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { // Use layout instead of other options From c3bcdcff6426726397c3b3e5d93fe89bc9356fe1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 13:05:12 -0400 Subject: [PATCH 087/319] Use zm_session_start instead of session_start --- web/includes/actions/montage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/actions/montage.php b/web/includes/actions/montage.php index 207c9a0f0..cd9a41aaf 100644 --- a/web/includes/actions/montage.php +++ b/web/includes/actions/montage.php @@ -36,7 +36,7 @@ if ( isset($_REQUEST['object']) ) { } $Layout->Positions($_REQUEST['Positions']); $Layout->save(); - session_start(); + zm_session_start(); $_SESSION['zmMontageLayout'] = $Layout->Id(); setcookie('zmMontageLayout', $Layout->Id(), 1); session_write_close(); From ae80b3b8595599bbf7255e836c394dcd410808aa Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Wed, 25 Mar 2020 23:21:34 -0700 Subject: [PATCH 088/319] Start implementing VNC --- db/zm_create.sql.in | 6 +- src/CMakeLists.txt | 2 +- src/zm_camera.h | 3 +- src/zm_libvnc_camera.cpp | 128 ++++++++++++++++++++++++++++ src/zm_libvnc_camera.h | 64 ++++++++++++++ src/zm_monitor.cpp | 19 +++++ src/zm_monitor.h | 1 + web/api/app/Model/Monitor.php | 2 +- web/skins/classic/views/monitor.php | 14 ++- 9 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 src/zm_libvnc_camera.cpp create mode 100644 src/zm_libvnc_camera.h diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index ce504a422..ce462ac4c 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', @@ -406,7 +406,7 @@ DROP TABLE IF EXISTS `MonitorPresets`; CREATE TABLE `MonitorPresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Device` tinytext, `Channel` tinyint(3) unsigned default NULL, `Format` int(10) unsigned default NULL, @@ -440,7 +440,7 @@ CREATE TABLE `Monitors` ( `Notes` TEXT, `ServerId` int(10) unsigned, `StorageId` smallint(5) unsigned default 0, - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255), diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 331f9e039..2f75c5c04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) -set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp) +set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp) # A fix for cmake recompiling the source files for every target. diff --git a/src/zm_camera.h b/src/zm_camera.h index a6f576af2..2742ef1d8 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -35,7 +35,7 @@ class Camera; // class Camera { protected: - typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; + typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC, VNC_SRC } SourceType; unsigned int monitor_id; Monitor * monitor; // Null on instantiation, set as soon as possible. @@ -68,6 +68,7 @@ public: bool IsFfmpeg() const { return type == FFMPEG_SRC; } bool IsLibvlc() const { return type == LIBVLC_SRC; } bool IscURL() const { return type == CURL_SRC; } + bool IsVNC() const { return type == VNC_SRC; } unsigned int Width() const { return width; } unsigned int Height() const { return height; } unsigned int Colours() const { return colours; } diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp new file mode 100644 index 000000000..efd2954cd --- /dev/null +++ b/src/zm_libvnc_camera.cpp @@ -0,0 +1,128 @@ +#include "zm.h" +#include "zm_signal.h" +#include "zm_libvnc_camera.h" + +static int TAG_0; +static int TAG_1; +static int TAG_2; + +static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, int h){ + VncPrivateData *data = (VncPrivateData *)rfbClientGetClientData(rfb, &TAG_0); + data->buffer = rfb->frameBuffer; +} + +static char* GetPasswordCallback(rfbClient* cl){ + return strdup((const char *)rfbClientGetClientData(cl, &TAG_1)); +} + +static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ + rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); + if(credentialType != rfbCredentialTypeUser) { + return NULL; + } + + c->userCredential.password = strdup((const char *)rfbClientGetClientData(cl, &TAG_1)); + c->userCredential.username = strdup((const char *)rfbClientGetClientData(cl, &TAG_2)); + return c; +} + +VncCamera::VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ) : + Camera( + p_monitor_id, + VNC_SRC, + p_width, + p_height, + p_colours, + ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), + p_brightness, + p_contrast, + p_hue, + p_colour, + p_capture, + p_record_audio + ), + mHost(host), + mPort(port), + mUser(user), + mPass(pass) +{ + Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); + if( capture ) + Initialise(); +} + + +VncCamera::~VncCamera() { + if( capture ) + Terminate(); +} + +void VncCamera::Initialise() { + Debug(2, "Initializing Client"); + mRfb = rfbGetClient(8, 3, 4); + rfbClientSetClientData(mRfb, &TAG_0, &mVncData); + rfbClientSetClientData(mRfb, &TAG_1, (void *)mPass.c_str()); + rfbClientSetClientData(mRfb, &TAG_2, (void *)mUser.c_str()); + + mVncData.bufferSize = width * height * 4; + mVncData.buffer = (uint8_t *)malloc(mVncData.bufferSize * sizeof(uint8_t)); + + 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()); + rfbInitClient(mRfb, 0, nullptr); +} + +void VncCamera::Terminate() { + if(mVncData.buffer) + free(mVncData.buffer); + return; +} + +int VncCamera::PrimeCapture() { + Info("Priming capture from %s", mHost.c_str()); + return 0; +} + +int VncCamera::PreCapture() { + WaitForMessage(mRfb, 500); + rfbBool res = HandleRFBServerMessage(mRfb); + return res == TRUE ? 1 : -1 ; +} + +int VncCamera::Capture(Image &image) { + Debug(2, "Capturing"); + image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); + return 1; +} + +int VncCamera::PostCapture() { + return 0; +} + +int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_directory) { + return 0; +} + +int VncCamera::Close() { + rfbClientCleanup(mRfb); + return 0; +} \ No newline at end of file diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h new file mode 100644 index 000000000..667111bc5 --- /dev/null +++ b/src/zm_libvnc_camera.h @@ -0,0 +1,64 @@ + +#ifndef ZN_LIBVNC_CAMERA_H +#define ZN_LIBVNC_CAMERA_H + +#include "zm_buffer.h" +#include "zm_camera.h" +#include "zm_thread.h" + +#include + +// Used by vnc callbacks +struct VncPrivateData +{ + uint8_t *buffer; + uint8_t *prevBuffer; + uint32_t bufferSize; + Mutex mutex; + ThreadData newImage; +}; + +class VncCamera : public Camera { +protected: + rfbClient *mRfb; + VncPrivateData mVncData; + int mBpp; + int mSpp; + int mBps; + char** mOptArgvs; + std::string mHost; + std::string mPort; + std::string mUser; + std::string mPass; + time_t secs; +public: + VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ); + + ~VncCamera(); + + void Initialise(); + void Terminate(); + + int PreCapture(); + int PrimeCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, timeval recording, char* event_directory ); + int Close(); +}; + +#endif \ No newline at end of file diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2f0e4826b..291fe039d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -51,6 +51,7 @@ #if HAVE_LIBCURL #include "zm_curl_camera.h" #endif // HAVE_LIBCURL +#include "zm_libvnc_camera.h" #if ZM_MEM_MAPPED #include @@ -89,6 +90,7 @@ std::string CameraType_Strings[] = { "Ffmpeg", "LibVLC", "CURL", + "VNC", }; @@ -2332,6 +2334,23 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { #else // HAVE_LIBCURL Fatal("You must have libcurl installed to use ffmpeg cameras for monitor %d", id); #endif // HAVE_LIBCURL + } else if ( type == "VNC" ) { + camera = new VncCamera( + id, + host.c_str(), + port.c_str(), + user.c_str(), + pass.c_str(), + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + record_audio + ); } else { Fatal("Bogus monitor type '%s' for monitor %d", type.c_str(), id); } // end if type diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 9109c9c72..e1d66950c 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -75,6 +75,7 @@ public: FFMPEG, LIBVLC, CURL, + VNC, } CameraType; typedef enum { diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index aa959f429..2de0b6fcb 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -119,7 +119,7 @@ class Monitor extends AppModel { ); public $actsAs = array( 'CakePHP-Enum-Behavior.Enum' => array( - 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite'), + 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite', 'VNC'), 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), 'Orientation' => array('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT'), 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 81ab90b96..b3a2dfc79 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -123,7 +123,8 @@ $sourceTypes = array( 'Libvlc' => translate('Libvlc'), 'cURL' => 'cURL (HTTP(S) only)', 'WebSite'=> 'Web Site', - 'NVSocket' => translate('NVSocket') + 'NVSocket' => translate('NVSocket'), + 'VNC' => 'VNC' ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -779,6 +780,13 @@ switch ( $tab ) { } else if ( $monitor->Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); + } else if ( $monitor->Type() == 'VNC' ) { +?> + + + + +Type() == 'Remote' ) { ?> Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> @@ -811,8 +819,8 @@ include('_monitor_source_nvsocket.php'); ?> () - () - + () + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> From 457f3e41beeafd9db4c80241030b8b4ad370d05b Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Wed, 25 Mar 2020 23:29:22 -0700 Subject: [PATCH 089/319] Add cmake rules to check for libvnc --- CMakeLists.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index df1702328..dfc1f7b75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,8 @@ set(ZM_NO_MMAP "OFF" CACHE BOOL experience problems with the shared memory. default: OFF") set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") +set(ZM_NO_LIBVNC "OFF" CACHE BOOL +"Set to ON to skip libvnc checks and force building ZM without libvnc. default: OFF") set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") set(ZM_NO_X10 "OFF" CACHE BOOL @@ -674,6 +676,25 @@ if(NOT ZM_NO_LIBVLC) endif(LIBVLC_LIBRARIES) endif(NOT ZM_NO_LIBVLC) +if(NOT ZM_NO_LIBVNC) + # libvncclient (using find_library and find_path) + find_library(LIBVNC_LIBRARIES vncclient) + if(LIBVNC_LIBRARIES) + set(HAVE_LIBVNC 1) + list(APPEND ZM_BIN_LIBS "${LIBVNC_LIBRARIES}") + find_path(LIBVNC_INCLUDE_DIR "rfb/rfb.h") + if(LIBVNC_INCLUDE_DIR) + include_directories("${LIBVNC_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${LIBVNC_INCLUDE_DIR}") + endif(LIBVNC_INCLUDE_DIR) + mark_as_advanced(FORCE LIBVNC_LIBRARIES LIBVNC_INCLUDE_DIR) + check_include_file("rfb/rfb.h" HAVE_RFB_RFB_H) + set(optlibsfound "${optlibsfound} libVNC") + else(LIBVNC_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} libVNC") + endif(LIBVNC_LIBRARIES) +endif(NOT ZM_NO_LIBVNC) + #find_package(Boost 1.36.0) #if(Boost_FOUND) #include_directories(${Boost_INCLUDE_DIRS}) From d61e5e42ca58b693fc4fe74bc75b7f00ab990ff4 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 26 Mar 2020 00:08:00 -0700 Subject: [PATCH 090/319] Use preprocessors to ensure libvnc monitor is only built when libvnc is found --- src/zm_libvnc_camera.cpp | 5 ++++- src/zm_libvnc_camera.h | 3 ++- src/zm_monitor.cpp | 6 ++++++ zoneminder-config.cmake | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index efd2954cd..c72b0b340 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -2,6 +2,8 @@ #include "zm_signal.h" #include "zm_libvnc_camera.h" +#if HAVE_LIBVNC + static int TAG_0; static int TAG_1; static int TAG_2; @@ -125,4 +127,5 @@ int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_dir int VncCamera::Close() { rfbClientCleanup(mRfb); return 0; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 667111bc5..6d9285114 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -6,6 +6,7 @@ #include "zm_camera.h" #include "zm_thread.h" +#if HAVE_LIBVNC #include // Used by vnc callbacks @@ -61,4 +62,4 @@ public: int Close(); }; -#endif \ No newline at end of file +#endif // HAVE_LIBVNC diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 291fe039d..1186c75f7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -51,7 +51,9 @@ #if HAVE_LIBCURL #include "zm_curl_camera.h" #endif // HAVE_LIBCURL +#if HAVE_LIBVNC #include "zm_libvnc_camera.h" +#endif // HAVE_LIBVNC #if ZM_MEM_MAPPED #include @@ -2335,6 +2337,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { Fatal("You must have libcurl installed to use ffmpeg cameras for monitor %d", id); #endif // HAVE_LIBCURL } else if ( type == "VNC" ) { +#if HAVE_LIBVNC camera = new VncCamera( id, host.c_str(), @@ -2351,6 +2354,9 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { purpose==CAPTURE, record_audio ); +#else // HAVE_LIBVNC + Fatal("You must have libvnc installed to use VNC cameras for monitor id %d", id); +#endif // HAVE_LIBVNC } else { Fatal("Bogus monitor type '%s' for monitor %d", type.c_str(), id); } // end if type diff --git a/zoneminder-config.cmake b/zoneminder-config.cmake index 320620aa0..edcb42ff6 100644 --- a/zoneminder-config.cmake +++ b/zoneminder-config.cmake @@ -59,6 +59,8 @@ #cmakedefine HAVE_LIBAVRESAMPLE_AVRESAMPLE_H 1 #cmakedefine HAVE_LIBVLC 1 #cmakedefine HAVE_VLC_VLC_H 1 +#cmakedefine HAVE_LIBVNC 1 +#cmakedefine HAVE_RFB_RFB_H 1 #cmakedefine HAVE_LIBX264 1 #cmakedefine HAVE_X264_H 1 #cmakedefine HAVE_LIBMP4V2 1 From b615eada41b2f0308a4dcba8b96899a0a734bdce Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Thu, 26 Mar 2020 00:52:34 -0700 Subject: [PATCH 091/319] Add a missing endif --- src/zm_libvnc_camera.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 6d9285114..f57d12813 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -63,3 +63,4 @@ public: }; #endif // HAVE_LIBVNC +#endif // ZN_LIBVNC_CAMERA_H From a781cc2c87c46ee56665c5dece3b58656e5a7e66 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Fri, 27 Mar 2020 11:15:15 -0700 Subject: [PATCH 092/319] WIP scaling --- src/zm_libvnc_camera.cpp | 53 ++++++++++++++++++++++++++--- src/zm_libvnc_camera.h | 11 +++--- web/skins/classic/views/monitor.php | 2 +- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index c72b0b340..a9863f450 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -1,6 +1,11 @@ #include "zm.h" #include "zm_signal.h" #include "zm_libvnc_camera.h" +extern "C" { + #include + #include + #include +} #if HAVE_LIBVNC @@ -76,13 +81,11 @@ VncCamera::~VncCamera() { void VncCamera::Initialise() { Debug(2, "Initializing Client"); mRfb = rfbGetClient(8, 3, 4); + rfbClientSetClientData(mRfb, &TAG_0, &mVncData); rfbClientSetClientData(mRfb, &TAG_1, (void *)mPass.c_str()); rfbClientSetClientData(mRfb, &TAG_2, (void *)mUser.c_str()); - mVncData.bufferSize = width * height * 4; - mVncData.buffer = (uint8_t *)malloc(mVncData.bufferSize * sizeof(uint8_t)); - mRfb->GotFrameBufferUpdate = GotFrameBufferUpdateCallback; mRfb->GetPassword = GetPasswordCallback; mRfb->GetCredential = GetCredentialsCallback; @@ -94,13 +97,15 @@ void VncCamera::Initialise() { } void VncCamera::Terminate() { - if(mVncData.buffer) - free(mVncData.buffer); return; } int VncCamera::PrimeCapture() { Info("Priming capture from %s", mHost.c_str()); + if(mRfb->si.framebufferWidth != width || mRfb->si.framebufferHeight != height) { + Info("Expected screen resolution does not match with the provided resolution, using scaling"); + mScale = true; + } return 0; } @@ -112,11 +117,49 @@ int VncCamera::PreCapture() { int VncCamera::Capture(Image &image) { Debug(2, "Capturing"); + int srcLineSize[4]; + int dstLineSize[4]; + int dstSize; + if(mScale) { + sws = sws_getContext(mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, + width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + if(!sws) { + Error("Could not scale image"); + return -1; + } + + if (av_image_fill_arrays(srcbuf, srcLineSize, mVncData.buffer, AV_PIX_FMT_RGBA, + mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, 16) < 0) { + sws_freeContext(sws); + Error("Could not allocate source image. Scaling failed"); + return -1; + } + + if ((dstSize = av_image_alloc(dstbuf, dstLineSize, width, height, + AV_PIX_FMT_RGBA, 1)) < 0) { + av_freep(&srcbuf[0]); + sws_freeContext(sws); + Error("Could not allocate dest image. Scaling failed"); + return -1; + } + + sws_scale(sws, (const uint8_t* const*)srcbuf, srcLineSize, 0, mRfb->si.framebufferHeight, + dstbuf, dstLineSize); + + } + else{ + dstbuf[0] = mVncData.buffer; + } image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); return 1; } int VncCamera::PostCapture() { + if(mScale) { + av_freep(&srcbuf[0]); + av_freep(&dstbuf[0]); + sws_freeContext(sws); + } return 0; } diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index f57d12813..9502f8330 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -13,10 +13,8 @@ struct VncPrivateData { uint8_t *buffer; - uint8_t *prevBuffer; - uint32_t bufferSize; - Mutex mutex; - ThreadData newImage; + uint8_t width; + uint8_t height; }; class VncCamera : public Camera { @@ -26,12 +24,13 @@ protected: int mBpp; int mSpp; int mBps; - char** mOptArgvs; + bool mScale; + uint8_t *srcbuf[4], *dstbuf[4]; + struct SwsContext *sws; std::string mHost; std::string mPort; std::string mUser; std::string mPass; - time_t secs; public: VncCamera( unsigned int p_monitor_id, diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index b3a2dfc79..6e08cd072 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -785,7 +785,7 @@ include('_monitor_source_nvsocket.php'); - + Type() == 'Remote' ) { ?> From fc25a777fd3393f723b308d80cf0e813050b0c9c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 14:47:59 -0400 Subject: [PATCH 093/319] Bump version for db update adding VNC Monitor Type --- db/zm_update-1.35.2.sql | 1 + version | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 db/zm_update-1.35.2.sql diff --git a/db/zm_update-1.35.2.sql b/db/zm_update-1.35.2.sql new file mode 100644 index 000000000..b94ab38c5 --- /dev/null +++ b/db/zm_update-1.35.2.sql @@ -0,0 +1 @@ +ALTER TABLE Monitors MODIFY `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','NVSocket','VNC') NOT NULL default 'Local'; diff --git a/version b/version index 7eee78574..0035f2a76 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.1 +1.35.2 From dd9c578be6e21fff6707a769ecfbf5b5872043c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 14:48:20 -0400 Subject: [PATCH 094/319] bumpv ersion to 1.35.2 --- 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 d3ffad793..923e0c068 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.1 +Version: 1.35.2 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons From 599960ef056a023e83977987fdd258b7e4e856cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 10:09:13 -0400 Subject: [PATCH 095/319] Update group save action, using Group object methods. Fixes errors on new MariaDB --- web/includes/actions/group.php | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php index 9aa988540..b23659d18 100644 --- a/web/includes/actions/group.php +++ b/web/includes/actions/group.php @@ -21,42 +21,31 @@ // Group edit actions # Should probably verify that each monitor id is a valid monitor, that we have access to. # However at the moment, you have to have System permissions to do this -if ( ! canEdit('Groups') ) { +if ( !canEdit('Groups') ) { ZM\Warning('Need group edit permissions to edit groups'); return; } if ( $action == 'Save' ) { - $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $group_id = null; - if ( !empty($_POST['gid']) ) { + if ( !empty($_POST['gid']) ) $group_id = $_POST['gid']; - dbQuery( - 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + $group = new ZM\Group($group_id); + $group->save( array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - $group_id, + 'Name'=> $_POST['newGroup']['Name'], + 'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), ) ); - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id)); - } else { - dbQuery( - 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - ) - ); - $group_id = dbInsertId(); - } + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id)); + $group_id = $group->Id(); if ( $group_id ) { foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); + dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid)); } } $view = 'none'; $refreshParent = true; $closePopup = true; -} +} ?> From 38c018ed18bad71caee66c1dcffaadde5c644107 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 11:19:06 -0400 Subject: [PATCH 096/319] Add a warning when an email exceeds 10Mb --- scripts/zmfilter.pl.in | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 18b08cdf1..5b478316d 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -829,14 +829,20 @@ sub sendEmail { Data => $body ); ### Add the attachments + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Disposition => 'attachment' ); } + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); + } ### Send the Message if ( $Config{ZM_SSMTP_MAIL} ) { my $ssmtp_location = $Config{ZM_SSMTP_PATH}; @@ -860,20 +866,27 @@ sub sendEmail { } } else { my $mail = MIME::Entity->build( - From => $Config{ZM_FROM_EMAIL}, - To => $$filter{EmailTo}, - Subject => $subject, - Type => (($body=~/ $body - ); + From => $Config{ZM_FROM_EMAIL}, + To => $$filter{EmailTo}, + Subject => $subject, + Type => (($body=~/ $body + ); + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); + $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Encoding => 'base64' - ); + Path => $attachment->{path}, + Type => $attachment->{type}, + Encoding => 'base64' + ); + } # end foreach attachment + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); } $mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL}); } From e894102cb7c65a7c239e98d87477b1f9de00beee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 11:59:05 -0400 Subject: [PATCH 097/319] debug --- src/zm_libvnc_camera.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index a9863f450..6bc62d7c0 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -110,8 +110,11 @@ int VncCamera::PrimeCapture() { } int VncCamera::PreCapture() { + Debug(2, "PreCapture"); WaitForMessage(mRfb, 500); + Debug(2, "After Wait "); rfbBool res = HandleRFBServerMessage(mRfb); + Debug(2, "After Handle "); return res == TRUE ? 1 : -1 ; } @@ -120,10 +123,12 @@ int VncCamera::Capture(Image &image) { int srcLineSize[4]; int dstLineSize[4]; int dstSize; - if(mScale) { - sws = sws_getContext(mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, - width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); - if(!sws) { + if ( mScale ) { + sws = sws_getContext( + mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, + width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); + if ( !sws ) { Error("Could not scale image"); return -1; } @@ -171,4 +176,4 @@ int VncCamera::Close() { rfbClientCleanup(mRfb); return 0; } -#endif \ No newline at end of file +#endif From 98598f1441910b404777b9f4c1c8fe1ea6806da5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 11:19:06 -0400 Subject: [PATCH 098/319] Add a warning when an email exceeds 10Mb --- scripts/zmfilter.pl.in | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 18b08cdf1..5b478316d 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -829,14 +829,20 @@ sub sendEmail { Data => $body ); ### Add the attachments + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Disposition => 'attachment' ); } + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); + } ### Send the Message if ( $Config{ZM_SSMTP_MAIL} ) { my $ssmtp_location = $Config{ZM_SSMTP_PATH}; @@ -860,20 +866,27 @@ sub sendEmail { } } else { my $mail = MIME::Entity->build( - From => $Config{ZM_FROM_EMAIL}, - To => $$filter{EmailTo}, - Subject => $subject, - Type => (($body=~/ $body - ); + From => $Config{ZM_FROM_EMAIL}, + To => $$filter{EmailTo}, + Subject => $subject, + Type => (($body=~/ $body + ); + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); + $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Encoding => 'base64' - ); + Path => $attachment->{path}, + Type => $attachment->{type}, + Encoding => 'base64' + ); + } # end foreach attachment + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); } $mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL}); } From 17a818e2fa6e8d23fbe0ebcd0f351035e046a3dd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 10:09:13 -0400 Subject: [PATCH 099/319] Update group save action, using Group object methods. Fixes errors on new MariaDB --- web/includes/actions/group.php | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php index 9aa988540..b23659d18 100644 --- a/web/includes/actions/group.php +++ b/web/includes/actions/group.php @@ -21,42 +21,31 @@ // Group edit actions # Should probably verify that each monitor id is a valid monitor, that we have access to. # However at the moment, you have to have System permissions to do this -if ( ! canEdit('Groups') ) { +if ( !canEdit('Groups') ) { ZM\Warning('Need group edit permissions to edit groups'); return; } if ( $action == 'Save' ) { - $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $group_id = null; - if ( !empty($_POST['gid']) ) { + if ( !empty($_POST['gid']) ) $group_id = $_POST['gid']; - dbQuery( - 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + $group = new ZM\Group($group_id); + $group->save( array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - $group_id, + 'Name'=> $_POST['newGroup']['Name'], + 'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), ) ); - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id)); - } else { - dbQuery( - 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - ) - ); - $group_id = dbInsertId(); - } + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id)); + $group_id = $group->Id(); if ( $group_id ) { foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); + dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid)); } } $view = 'none'; $refreshParent = true; $closePopup = true; -} +} ?> From b93f5d970cc467a0647df6231275479f05029539 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 16:03:52 -0400 Subject: [PATCH 100/319] sws_scale directly into the image. Allocate and de-allocate sws in Prime and Close. Seems to fix scaling. --- src/zm_libvnc_camera.cpp | 67 +++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index 6bc62d7c0..b18ebda97 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -24,7 +24,7 @@ static char* GetPasswordCallback(rfbClient* cl){ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); - if(credentialType != rfbCredentialTypeUser) { + if ( credentialType != rfbCredentialTypeUser ) { return NULL; } @@ -68,7 +68,7 @@ VncCamera::VncCamera( mPass(pass) { Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); - if( capture ) + if ( capture ) Initialise(); } @@ -102,9 +102,18 @@ void VncCamera::Terminate() { int VncCamera::PrimeCapture() { Info("Priming capture from %s", mHost.c_str()); - if(mRfb->si.framebufferWidth != width || mRfb->si.framebufferHeight != height) { - Info("Expected screen resolution does not match with the provided resolution, using scaling"); + if ( mRfb->si.framebufferWidth != width || mRfb->si.framebufferHeight != height ) { + Info("Expected screen resolution (%dx%d) does not match the provided resolution (%dx%d), using scaling", + width, height, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight); mScale = true; + sws = sws_getContext( + mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, + width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); + if ( !sws ) { + Error("Could not scale image"); + return -1; + } } return 0; } @@ -123,48 +132,39 @@ int VncCamera::Capture(Image &image) { int srcLineSize[4]; int dstLineSize[4]; int dstSize; + if ( mScale ) { - sws = sws_getContext( - mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, - width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, - NULL, NULL, NULL); - if ( !sws ) { - Error("Could not scale image"); + uint8_t* directbuffer; + + /* Request a writeable buffer of the target image */ + directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + if ( directbuffer == NULL ) { + Error("Failed requesting writeable buffer for the captured image."); return -1; } - if (av_image_fill_arrays(srcbuf, srcLineSize, mVncData.buffer, AV_PIX_FMT_RGBA, + if ( av_image_fill_arrays(dstbuf, dstLineSize, directbuffer, AV_PIX_FMT_RGBA, + width, height, 16) < 0) { + Error("Could not allocate dst image. Scaling failed"); + return -1; + } + + if ( av_image_fill_arrays(srcbuf, srcLineSize, mVncData.buffer, AV_PIX_FMT_RGBA, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, 16) < 0) { - sws_freeContext(sws); Error("Could not allocate source image. Scaling failed"); return -1; } - if ((dstSize = av_image_alloc(dstbuf, dstLineSize, width, height, - AV_PIX_FMT_RGBA, 1)) < 0) { - av_freep(&srcbuf[0]); - sws_freeContext(sws); - Error("Could not allocate dest image. Scaling failed"); - return -1; - } - sws_scale(sws, (const uint8_t* const*)srcbuf, srcLineSize, 0, mRfb->si.framebufferHeight, dstbuf, dstLineSize); + } else { + image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); } - else{ - dstbuf[0] = mVncData.buffer; - } - image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); return 1; } int VncCamera::PostCapture() { - if(mScale) { - av_freep(&srcbuf[0]); - av_freep(&dstbuf[0]); - sws_freeContext(sws); - } return 0; } @@ -173,6 +173,15 @@ int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_dir } int VncCamera::Close() { +#if HAVE_LIBSWSCALE + if ( mScale ) { + av_freep(&srcbuf[0]); + av_freep(&dstbuf[0]); + sws_freeContext(sws); + sws = NULL; + } +#endif + rfbClientCleanup(mRfb); return 0; } From 6886b2b375879875786b7999e5081a1d37c55b03 Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Sat, 28 Mar 2020 15:17:19 -0700 Subject: [PATCH 101/319] Modify Convert to scale as well --- src/zm_libvnc_camera.cpp | 83 +++++++++++----------------------------- src/zm_libvnc_camera.h | 14 ++++--- src/zm_swscale.cpp | 22 ++++++----- src/zm_swscale.h | 5 ++- 4 files changed, 47 insertions(+), 77 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index b18ebda97..9117d85cd 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -1,11 +1,7 @@ #include "zm.h" #include "zm_signal.h" #include "zm_libvnc_camera.h" -extern "C" { - #include - #include - #include -} +#include "zm_swscale.h" #if HAVE_LIBVNC @@ -25,6 +21,7 @@ static char* GetPasswordCallback(rfbClient* cl){ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); if ( credentialType != rfbCredentialTypeUser ) { + free(c); return NULL; } @@ -68,6 +65,23 @@ VncCamera::VncCamera( mPass(pass) { Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); + + if ( colours == ZM_COLOUR_RGB32 ) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + mImgPixFmt = AV_PIX_FMT_RGBA; + mBpp = 4; + } else if ( colours == ZM_COLOUR_RGB24 ) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + mImgPixFmt = AV_PIX_FMT_RGB24; + mBpp = 3; + } else if ( colours == ZM_COLOUR_GRAY8 ) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + mImgPixFmt = AV_PIX_FMT_GRAY8; + mBpp = 1; + } else { + Panic("Unexpected colours: %d", colours); + } + if ( capture ) Initialise(); } @@ -94,6 +108,7 @@ void VncCamera::Initialise() { mRfb->serverHost = strdup(mHost.c_str()); mRfb->serverPort = atoi(mPort.c_str()); rfbInitClient(mRfb, 0, nullptr); + scale.init(); } void VncCamera::Terminate() { @@ -102,65 +117,20 @@ void VncCamera::Terminate() { int VncCamera::PrimeCapture() { Info("Priming capture from %s", mHost.c_str()); - if ( mRfb->si.framebufferWidth != width || mRfb->si.framebufferHeight != height ) { - Info("Expected screen resolution (%dx%d) does not match the provided resolution (%dx%d), using scaling", - width, height, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight); - mScale = true; - sws = sws_getContext( - mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, AV_PIX_FMT_RGBA, - width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, - NULL, NULL, NULL); - if ( !sws ) { - Error("Could not scale image"); - return -1; - } - } return 0; } int VncCamera::PreCapture() { Debug(2, "PreCapture"); WaitForMessage(mRfb, 500); - Debug(2, "After Wait "); rfbBool res = HandleRFBServerMessage(mRfb); - Debug(2, "After Handle "); return res == TRUE ? 1 : -1 ; } int VncCamera::Capture(Image &image) { Debug(2, "Capturing"); - int srcLineSize[4]; - int dstLineSize[4]; - int dstSize; - - if ( mScale ) { - uint8_t* directbuffer; - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if ( directbuffer == NULL ) { - Error("Failed requesting writeable buffer for the captured image."); - return -1; - } - - if ( av_image_fill_arrays(dstbuf, dstLineSize, directbuffer, AV_PIX_FMT_RGBA, - width, height, 16) < 0) { - Error("Could not allocate dst image. Scaling failed"); - return -1; - } - - if ( av_image_fill_arrays(srcbuf, srcLineSize, mVncData.buffer, AV_PIX_FMT_RGBA, - mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, 16) < 0) { - Error("Could not allocate source image. Scaling failed"); - return -1; - } - - sws_scale(sws, (const uint8_t* const*)srcbuf, srcLineSize, 0, mRfb->si.framebufferHeight, - dstbuf, dstLineSize); - - } else { - image.Assign(width, height, colours, subpixelorder, mVncData.buffer, width * height * 4); - } + uint8_t *directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + scale.Convert(mVncData.buffer, mRfb->si.framebufferHeight * mRfb->si.framebufferWidth * 4, directbuffer, width * height * mBpp, AV_PIX_FMT_RGBA, mImgPixFmt, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, width, height); return 1; } @@ -173,15 +143,6 @@ int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_dir } int VncCamera::Close() { -#if HAVE_LIBSWSCALE - if ( mScale ) { - av_freep(&srcbuf[0]); - av_freep(&dstbuf[0]); - sws_freeContext(sws); - sws = NULL; - } -#endif - rfbClientCleanup(mRfb); return 0; } diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 9502f8330..37525f35e 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -5,10 +5,17 @@ #include "zm_buffer.h" #include "zm_camera.h" #include "zm_thread.h" +#include "zm_swscale.h" #if HAVE_LIBVNC #include +extern "C" { + #include + #include + #include +} + // Used by vnc callbacks struct VncPrivateData { @@ -22,11 +29,8 @@ protected: rfbClient *mRfb; VncPrivateData mVncData; int mBpp; - int mSpp; - int mBps; - bool mScale; - uint8_t *srcbuf[4], *dstbuf[4]; - struct SwsContext *sws; + SWScale scale; + AVPixelFormat mImgPixFmt; std::string mHost; std::string mPort; std::string mUser; diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index 1e91029ad..04c37ace7 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -84,7 +84,7 @@ int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, return 0; } -int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { +int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height, unsigned int new_width, unsigned int new_height) { /* Parameter checking */ if(in_buffer == NULL || out_buffer == NULL) { Error("NULL Input or output buffer"); @@ -94,7 +94,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint // Error("Invalid input or output pixel formats"); // return -2; // } - if (!width || !height) { + if (!width || !height || !new_height || !new_width) { Error("Invalid width or height"); return -3; } @@ -111,7 +111,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint /* Check the buffer sizes */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t insize = av_image_get_buffer_size(in_pf, width, height,1); + size_t insize = av_image_get_buffer_size(in_pf, width, height, 1); #else size_t insize = avpicture_get_size(in_pf, width, height); #endif @@ -120,9 +120,9 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint return -4; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t outsize = av_image_get_buffer_size(out_pf, width, height,1); + size_t outsize = av_image_get_buffer_size(out_pf, new_width, new_height, 1); #else - size_t outsize = avpicture_get_size(out_pf, width, height); + size_t outsize = avpicture_get_size(out_pf, new_width, new_height); #endif if(outsize < out_buffer_size) { @@ -131,7 +131,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint } /* Get the context */ - swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); + swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, new_width, new_height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); if(swscale_ctx == NULL) { Error("Failed getting swscale context"); return -6; @@ -150,10 +150,10 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, - out_buffer, out_pf, width, height, 1) <= 0) { + out_buffer, out_pf, new_width, new_height, 1) <= 0) { #else - if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width, - height) <= 0) { + if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width, + new_height) <= 0) { #endif Error("Failed filling output frame with output buffer"); return -8; @@ -168,6 +168,10 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint return 0; } +int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { + return Convert(in_buffer, in_buffer_size, out_buffer, out_buffer_size, in_pf, out_pf, width, height, width, height); +} + int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { if(img->Width() != width) { Error("Source image width differs. Source: %d Output: %d",img->Width(), width); diff --git a/src/zm_swscale.h b/src/zm_swscale.h index 62f99e342..4d559b4fc 100644 --- a/src/zm_swscale.h +++ b/src/zm_swscale.h @@ -10,13 +10,14 @@ class SWScale { public: SWScale(); ~SWScale(); - bool init(); + bool init(); int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - + int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height, unsigned int new_width, unsigned int new_height); + protected: bool gotdefaults; struct SwsContext* swscale_ctx; From 2f0b991761352b6bd852e3bf20715026e9cc3df3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:05:19 -0400 Subject: [PATCH 102/319] If can't determine latest stable release, then quit --- utils/do_debian_package.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index af9921294..8fdd115eb 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -110,7 +110,12 @@ else fi; if [ "$SNAPSHOT" == "stable" ]; then if [ "$BRANCH" == "" ]; then - BRANCH=$(git describe --tags $(git rev-list --tags --max-count=1)); + #REV=$(git rev-list --tags --max-count=1) + BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`; + if [ "$BRANCH" == "" ]; then + echo "Unable to determine latest stable branch!" + exit 0; + fi echo "Latest stable branch is $BRANCH"; fi; else From ff061bd48ed80d7e8659c5fded32b74f21ab5f5e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:09:53 -0400 Subject: [PATCH 103/319] Fixup VNC input options. Remove some hard coded sizes, spacing and typos. Convert inputs to number inputs as appropriate --- web/skins/classic/views/monitor.php | 186 +++++++++++++++++++--------- 1 file changed, 130 insertions(+), 56 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 6e08cd072..c1aef81fe 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -124,7 +124,7 @@ $sourceTypes = array( 'cURL' => 'cURL (HTTP(S) only)', 'WebSite'=> 'Web Site', 'NVSocket' => translate('NVSocket'), - 'VNC' => 'VNC' + 'VNC' => translate('VNC'), ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -782,28 +782,43 @@ switch ( $tab ) { include('_monitor_source_nvsocket.php'); } else if ( $monitor->Type() == 'VNC' ) { ?> - - - - + + + + + + + + + + + + + + + + Type() == 'Remote' ) { ?> Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> + + + +Protocol() || $monitor->Protocol() == 'http' ) { -?> - Method() ); ?> -Method() ); } else { -?> - Method() ); ?> -Method() ); } ?> - + + + - + Type() == 'File' ) { ?> @@ -811,23 +826,44 @@ include('_monitor_source_nvsocket.php'); Type() == 'cURL' ) { ?> - - - + + + Type() == 'WebSite' ) { ?> - - () - () - + + + + + + () + + + + () + + + + + + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { + } else if ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> - -  ()Method() ); ?> + + + + + + ) + Method()) ?> + -  (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) +  (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { + if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { ?> - Colours() ); ?> - + + + Colours()) ?> + () @@ -892,18 +930,22 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { Orientation());?> Type() == 'Local' ) { + } + if ( $monitor->Type() == 'Local' ) { ?> - - + + Deinterlacing())?> + +Type() != 'WebSite' ) { ?> - + + + Deinterlacing())?> + - Type() == 'Remote' ) { ?> Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> @@ -950,15 +992,17 @@ if ( $monitor->Type() == 'Local' ) { $videowriteropts[1] = 'X264 Encode'; - if ($monitor->Type() == 'Ffmpeg' ) + if ( $monitor->Type() == 'Ffmpeg' ) $videowriteropts[2] = 'H264 Camera Passthrough'; else $videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1); - echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() ); + echo htmlselect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); ?> - + + + Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> @@ -973,22 +1017,52 @@ if ( $monitor->Type() == 'Local' ) { case 'timestamp' : { ?> - - - - + + + + + + + + + + + + + + + LabelSize()) ?> + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + Type() == 'Local' ) { ?> - Controllable() ) { ?> checked="checked"/> + Controllable() ) { ?> checked="checked"/> + ControlId()); @@ -1022,20 +1097,19 @@ if ( canEdit('Control') ) { TrackMotion() ) { ?> checked="checked"/> - translate('None'), - '0' => translate('Home'), - '1' => translate('Preset').' 1', - ); -?> - ReturnLocation()); ?> + translate('None'), + '0' => translate('Home'), + '1' => translate('Preset').' 1', + ); +echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnLocation()); ?> From eec634c1b06d87fa2190fb8984b2fee999916fd4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:10:06 -0400 Subject: [PATCH 104/319] Add Source() support for VNC type --- web/includes/Monitor.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 8c45c0d27..de9bcf7c4 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -441,14 +441,17 @@ class Monitor extends ZM_Object { $source = ''; if ( $this->{'Type'} == 'Local' ) { $source = $this->{'Device'}.' ('.$this->{'Channel'}.')'; - } elseif ( $this->{'Type'} == 'Remote' ) { + } else if ( $this->{'Type'} == 'Remote' ) { $source = preg_replace( '/^.*@/', '', $this->{'Host'} ); if ( $this->{'Port'} != '80' and $this->{'Port'} != '554' ) { $source .= ':'.$this->{'Port'}; } - } elseif ( $this->{'Type'} == 'File' || $this->{'Type'} == 'cURL' ) { - $source = preg_replace( '/^.*\//', '', $this->{'Path'} ); - } elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { + } else if ( $this->{'Type'} == 'VNC' ) { + $source = preg_replace( '/^.*@/', '', $this->{'Host'} ); + if ( $this->{'Port'} != '5900' ) { + $source .= ':'.$this->{'Port'}; + } + } else if ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { $url_parts = parse_url( $this->{'Path'} ); if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { # Filter out everything but the hostname @@ -457,7 +460,7 @@ class Monitor extends ZM_Object { } else { $source = $this->{'Path'}; } - } elseif ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) { + } else if ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) { # Filter out sensitive and common items unset($url_parts['user']); unset($url_parts['pass']); From 3117f29f716193114bd3b1fb7aa48742dbe4d663 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:32:26 -0400 Subject: [PATCH 105/319] code style and correct license. Author was contacted and ok'd the change. Fies #2883 --- .../lib/ZoneMinder/Control/DCS5020L.pm | 388 ++++++++---------- 1 file changed, 171 insertions(+), 217 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm index 59d9e3550..4f14e787a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm @@ -46,243 +46,187 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); -sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +sub open { + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/' . ZoneMinder::Base::ZM_VERSION); + $self->{state} = 'open'; } -our $AUTOLOAD; - -sub AUTOLOAD -{ - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); +sub close { + my $self = shift; + $self->{state} = 'closed'; } -sub open -{ - my $self = shift; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $cgi = shift; - $self->loadMonitor(); + my $result = undef; - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/" . ZoneMinder::Base::ZM_VERSION ); - $self->{state} = 'open'; + printMsg($cmd, 'Tx'); + + my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); + $req->content($cmd); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + } else { + Error("Error check failed: '".$res->status_line()."'"); + } + + return $result; } -sub close -{ - my $self = shift; - $self->{state} = 'closed'; +sub move { + my $self = shift; + my $dir = shift; + my $panStep = shift; + my $tiltStep = shift; + my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; + $self->sendCmd($cmd, 'pantiltcontrol'); } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); +sub moveRel { + my $self = shift; + my $params = shift; + my $panStep = $self->getParam($params, 'panstep', 0); + my $tiltStep = $self->getParam($params, 'tiltstep', 0); + my $dir = shift; + $self->move( $dir, $panStep, $tiltStep ); } -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - my $cgi = shift; - - my $result = undef; - - printMsg( $cmd, "Tx" ); - - my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); - $req->content($cmd); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "Error check failed: '".$res->status_line()."'" ); - } - - return( $result ); +sub moveRelUpLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 0); } -sub move -{ - my $self = shift; - my $dir = shift; - my $panStep = shift; - my $tiltStep = shift; - my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub moveRelUp { + my $self = shift; + my $params = shift; + $self->moveRel($params, 1); } -sub moveRel -{ - my $self = shift; - my $params = shift; - my $panStep = $self->getParam($params, 'panstep', 0); - my $tiltStep = $self->getParam($params, 'tiltstep', 0); - my $dir = shift; - $self->move( $dir, $panStep, $tiltStep ); +sub moveRelUpRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 2); } -sub moveRelUpLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 0 ); +sub moveRelLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 3); } -sub moveRelUp -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 1 ); +sub moveRelRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 5); } -sub moveRelUpRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 2 ); +sub moveRelDownLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 6); } -sub moveRelLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 3 ); +sub moveRelDown { + my $self = shift; + my $params = shift; + $self->moveRel($params, 7); } -sub moveRelRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 5 ); -} - -sub moveRelDownLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 6 ); -} - -sub moveRelDown -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 7 ); -} - -sub moveRelDownRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 8 ); +sub moveRelDownRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 8); } # moves the camera to center on the point that the user clicked on in the video image. # This isn't extremely accurate but good enough for most purposes -sub moveMap -{ - # if the camera moves too much or too little, try increasing or decreasing this value - my $f = 11; +sub moveMap { + # if the camera moves too much or too little, try increasing or decreasing this value + my $f = 11; - my $self = shift; - my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); + my $self = shift; + my $params = shift; + my $xcoord = $self->getParam( $params, 'xcoord' ); + my $ycoord = $self->getParam( $params, 'ycoord' ); - my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; - my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; - - my $direction; - my $horSteps; - my $verSteps; - if ($hor < 50 && $ver < 50) { - # up left - $horSteps = (50 - $hor) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 0; - } elsif ($hor >= 50 && $ver < 50) { - # up right - $horSteps = ($hor - 50) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 2; - } elsif ($hor < 50 && $ver >= 50) { - # down left - $horSteps = (50 - $hor) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 6; - } elsif ($hor >= 50 && $ver >= 50) { - # down right - $horSteps = ($hor - 50) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 8; - } - my $v = int($verSteps + .5); - my $h = int($horSteps + .5); - Debug( "Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction" ); - $self->move( $direction, $h, $v ); + my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; + my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; + + my $direction; + my $horSteps; + my $verSteps; + if ($hor < 50 && $ver < 50) { + # up left + $horSteps = (50 - $hor) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 0; + } elsif ($hor >= 50 && $ver < 50) { + # up right + $horSteps = ($hor - 50) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 2; + } elsif ($hor < 50 && $ver >= 50) { + # down left + $horSteps = (50 - $hor) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 6; + } elsif ($hor >= 50 && $ver >= 50) { + # down right + $horSteps = ($hor - 50) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 8; + } + my $v = int($verSteps + .5); + my $h = int($horSteps + .5); + Debug("Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction"); + $self->move($direction, $h, $v); } -sub presetClear -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); - my $cmd = "ClearPosition=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetClear { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Clear Preset $preset" ); + my $cmd = "ClearPosition=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Set Preset $preset" ); + my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "PanTiltPresetPositionMove=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Goto Preset $preset" ); + my $cmd = "PanTiltPresetPositionMove=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - $self->move( 4, 0, 0 ); +sub presetHome { + my $self = shift; + Debug( "Home Preset" ); + $self->move( 4, 0, 0 ); } - # IR Controls # # wake = IR on @@ -290,40 +234,36 @@ sub presetHome # reset = IR auto sub setDayNightMode { - my $self = shift; - my $mode = shift; - my $cmd = "DayNightMode=$mode&ConfigReboot=No"; - $self->sendCmd( $cmd, 'daynight' ); + my $self = shift; + my $mode = shift; + my $cmd = "DayNightMode=$mode&ConfigReboot=No"; + $self->sendCmd($cmd, 'daynight'); } -sub wake -{ - my $self = shift; - Debug( "Wake - IR on" ); - $self->setDayNightMode(2); +sub wake { + my $self = shift; + Debug('Wake - IR on'); + $self->setDayNightMode(2); } -sub sleep -{ - my $self = shift; - Debug( "Sleep - IR off" ); - $self->setDayNightMode(3); +sub sleep { + my $self = shift; + Debug('Sleep - IR off'); + $self->setDayNightMode(3); } -sub reset -{ - my $self = shift; - Debug( "Reset - IR auto" ); - $self->setDayNightMode(0); +sub reset { + my $self = shift; + Debug('Reset - IR auto'); + $self->setDayNightMode(0); } 1; __END__ -# Below is stub documentation for your module. You'd better edit it! =head1 NAME -ZoneMinder::Database - Perl extension for DCS-5020L +ZoneMinder::Control::DCS5020L - Perl extension for DCS-5020L =head1 SYNOPSIS @@ -351,6 +291,20 @@ Art Scheel ascheel (at) gmail =head1 COPYRIGHT AND LICENSE -LGPLv3 +Copyright (C) 2018 ZoneMinder LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut From 593ea24cc60213c81879ef12809eedf351eacd56 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 12:42:27 -0400 Subject: [PATCH 106/319] add libvnc dependencies for VNC monitor type --- distros/ubuntu1604/control | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 617b3e852..00a8bd9ac 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -33,6 +33,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,libssl-dev ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncserver-dev # Unbundled (dh_linktree): ,libjs-jquery ,libjs-mootools @@ -82,6 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libssl | libssl1.0.0 | libssl1.1 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncclient1 Recommends: ${misc:Recommends} ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,mysql-server | mariadb-server | virtual-mysql-server From 04a6d5e42e4dc60ed7f2555396c0b50dc1dca31f Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Mon, 30 Mar 2020 10:11:42 -0700 Subject: [PATCH 107/319] Fix a memory leak, shift rfbClientCleanup to Terminate() --- src/zm_libvnc_camera.cpp | 4 +++- src/zm_libvnc_camera.h | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index 9117d85cd..ac074ae1f 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -112,6 +112,9 @@ void VncCamera::Initialise() { } void VncCamera::Terminate() { + if(mRfb->frameBuffer) + free(mRfb->frameBuffer); + rfbClientCleanup(mRfb); return; } @@ -143,7 +146,6 @@ int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_dir } int VncCamera::Close() { - rfbClientCleanup(mRfb); return 0; } #endif diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 37525f35e..250cfaa2e 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -9,13 +9,6 @@ #if HAVE_LIBVNC #include - -extern "C" { - #include - #include - #include -} - // Used by vnc callbacks struct VncPrivateData { From 2fe0096b21b5debac0aa40ada57b7bb345c4e9a2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 18:34:43 -0400 Subject: [PATCH 108/319] correct label_size array and add translations --- web/skins/classic/views/monitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index c1aef81fe..9437cdde3 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -368,8 +368,8 @@ $fastblendopts_alarm = array( ); $label_size = array( - 'Default' => 1, - 'Large' => 2 + 1 => translate('Default'), + 2 => translate('Large'), ); $codecs = array( From 4dcba55ab6a3fef7259dd29d008063f81888c2da Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 31 Mar 2020 08:53:32 -0400 Subject: [PATCH 109/319] Include Storage.php and Server.php --- web/skins/classic/views/storage.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index 44cf2fc0d..c849b2349 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -23,6 +23,9 @@ if ( !canEdit('System') ) { return; } +require_once('includes/Server.php'); +require_once('includes/Storage.php'); + if ( $_REQUEST['id'] ) { if ( !($newStorage = ZM\Storage::find_one(array('Id'=>$_REQUEST['id'])) ) ) { $view = 'error'; From ebac665d4d796a09ff52ee1236067c9cdf6db52f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 31 Mar 2020 08:54:47 -0400 Subject: [PATCH 110/319] Must use namespace ZM --- web/skins/classic/views/storage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index c849b2349..e1642b142 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -32,7 +32,7 @@ if ( $_REQUEST['id'] ) { return; } } else { - $newStorage = new Storage(); + $newStorage = new ZM\Storage(); $newStorage->Name(translate('NewStorage')); } From 3bfcd8eb334c92b32989687f8cc68cbdd205b672 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 2 Apr 2020 14:00:36 -0400 Subject: [PATCH 111/319] Check for definition of ZM_TIMEZONE before using it. Fixes #2895 --- web/includes/config.php.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 57064f22d..7052a061a 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -193,7 +193,7 @@ if ( ! defined('ZM_SERVER_ID') ) { } } -if ( ZM_TIMEZONE ) +if ( defined('ZM_TIMEZONE') and ZM_TIMEZONE ) ini_set('date.timezone', ZM_TIMEZONE); function process_configfile($configFile) { From 87069b99b668b8d18a40d592757c16310cce82d8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 2 Apr 2020 14:13:28 -0400 Subject: [PATCH 112/319] Fix inline-script errors on groups page referenced in #2890 --- web/skins/classic/views/groups.php | 2 +- web/skins/classic/views/js/groups.js | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/groups.php b/web/skins/classic/views/groups.php index cba26aaea..4ffe47f7d 100644 --- a/web/skins/classic/views/groups.php +++ b/web/skins/classic/views/groups.php @@ -69,7 +69,7 @@ function group_line( $Group ) { $html .= str_repeat(' ', $Group->depth()); $html .= ''; if ( canEdit('Groups') ) { - $html .= ''. validHtmlStr($Group->Id() . ' ' . $Group->Name()).''; + $html .= ''.validHtmlStr($Group->Id().' '.$Group->Name()).''; } else { $html .= validHtmlStr($Group->Name()); } diff --git a/web/skins/classic/views/js/groups.js b/web/skins/classic/views/js/groups.js index 31c9402a2..8ff1b61ff 100644 --- a/web/skins/classic/views/js/groups.js +++ b/web/skins/classic/views/js/groups.js @@ -8,8 +8,13 @@ function setGroup( element ) { form.submit(); } -function editGroup( gid ) { - createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' ); +function editGroup( element ) { + var gid = element.getAttribute('data-group-id'); + if ( !gid ) { + console.log('No group id found in editGroup'); + } else { + createPopup('?view=group&gid='+gid, 'zmGroup'+gid, 'group'); + } } function deleteGroup( element ) { From 1cc60f0857b5ba53eac5c58b0c49d68f61a27c8a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 2 Apr 2020 16:06:27 -0400 Subject: [PATCH 113/319] fix action on cycle play button --- web/skins/classic/views/cycle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index a36390b52..b7281a719 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -167,7 +167,7 @@ xhtmlHeaders(__FILE__, translate('CycleWatch'));
    - +
    From ef9fbac90cbf9158ea73ce161bccccf9d21a0350 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 2 Apr 2020 18:14:14 -0400 Subject: [PATCH 114/319] Add Noscript warnings. Whitespace, improve some logic --- .../classic/includes/export_functions.php | 154 +++++++++++------- 1 file changed, 98 insertions(+), 56 deletions(-) diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index d43c697e3..8fa7ff3fa 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -20,11 +20,12 @@ function exportHeader($title) { ?> + <?php echo $title ?> - -

    : Name()) ?> ()

    +

    Name()).( (!empty($otherlinks)) .' ('.$otherlinks.') ':'') ?>

    DefaultVideo() ) { @@ -252,7 +283,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { } ?>
    -
  • >
  • >
  • keyboard_arrow_
  • - + +
    From b384d233ae7287eb98383d9b22dd65bf191579ae Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Apr 2020 09:20:33 -0400 Subject: [PATCH 187/319] add async and defer to recaptcha load --- web/skins/classic/views/login.php | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/web/skins/classic/views/login.php b/web/skins/classic/views/login.php index 3b1a9d690..296a54887 100644 --- a/web/skins/classic/views/login.php +++ b/web/skins/classic/views/login.php @@ -22,19 +22,17 @@ xhtmlHeaders(__FILE__, translate('Login')); - - '; - echo ''; - } ?> - + + '; +} ?> - From 264ba6525b937cea90f8b8ba0ed5304c8113acd0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Apr 2020 12:19:18 -0400 Subject: [PATCH 188/319] Remove debug --- web/skins/classic/views/js/monitor.js.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 04fa06dcb..6a6db3ce9 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -138,11 +138,7 @@ function validateForm( form ) { if ( (form.elements['newMonitor[SaveJPEGs]'].value == '0') && (form.elements['newMonitor[VideoWriter]'].value == '0') ) { warnings[warnings.length] = ""; } -console.log(form.elements['newMonitor[SaveJPEGs]'].value); -console.log(form.elements['newMonitor[VideoWriter]'].value); - } -console.log(warnings); if ( warnings.length ) { if ( !confirm(warnings.join("\n")) ) { return false; From 10d89ed82e0c88e9aad3300a955ce7946f9c575d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 10:00:11 -0400 Subject: [PATCH 189/319] Spacing, quotes, improve debug messages. --- .../ZoneMinder/lib/ZoneMinder/Memory.pm.in | 10 ++++------ .../lib/ZoneMinder/Memory/Mapped.pm | 20 ++++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 53da1659b..13cae4d4b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -301,7 +301,7 @@ sub zmMemVerify { } return !undef; -} +} # end sub zmMemVerify sub zmMemRead { my $monitor = shift; @@ -375,10 +375,8 @@ sub zmMemInvalidate { my $mem_key = zmMemKey($monitor); if ( $mem_key ) { zmMemDetach($monitor); - } else { - Warning('no memkey in zmMemInvalidate'); } -} +} # end sub zmMemInvalidate sub zmMemTidy { zmMemClean(); @@ -504,10 +502,10 @@ sub zmHasAlarmed { my $last_event_id = shift; my ( $state, $last_event ) = zmMemRead($monitor, - ['shared_data:state' ,'shared_data:last_event'] + ['shared_data:state', 'shared_data:last_event'] ); - if ( $state == STATE_ALARM || $state == STATE_ALERT ) { + if ( $state == STATE_ALARM or $state == STATE_ALERT ) { return $last_event; } elsif( $last_event != $last_event_id ) { return $last_event; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm index b9a8b6a1c..cb7920913 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm @@ -51,7 +51,7 @@ our %EXPORT_TAGS = ( ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); our @EXPORT = @EXPORT_OK; @@ -77,17 +77,17 @@ sub zmMemAttach { my ( $monitor, $size ) = @_; if ( !$size ) { - Error("No size passed to zmMemAttach for monitor $$monitor{Id}"); + Error('No size passed to zmMemAttach for monitor '.$$monitor{Id}); return undef; } if ( defined($monitor->{MMapAddr}) ) { - Debug("zmMemAttach already attached at $monitor->{MMapAddr}"); + Debug("zmMemAttach already attached at $monitor->{MMapAddr} for $$monitor{Id}"); return !undef; } my $mmap_file = $Config{ZM_PATH_MAP}.'/zm.mmap.'.$monitor->{Id}; if ( ! -e $mmap_file ) { - Error("Memory map file '$mmap_file' does not exist. zmc might not be running."); + Error("Memory map file '$mmap_file' does not exist in zmMemAttach. zmc might not be running."); return undef; } my $mmap_file_size = -s $mmap_file; @@ -119,18 +119,24 @@ sub zmMemDetach { if ( $monitor->{MMap} ) { if ( ! munmap(${$monitor->{MMap}}) ) { - Warn( "Unable to munmap for monitor $$monitor{Id}\n"); + Warn("Unable to munmap for monitor $$monitor{Id}"); } delete $monitor->{MMap}; + } else { + Warn("No MMap for $$monitor{Id}"); } if ( $monitor->{MMapAddr} ) { delete $monitor->{MMapAddr}; + } else { + Warn("No MMapAddr in $$monitor{Id}"); } if ( $monitor->{MMapHandle} ) { close($monitor->{MMapHandle}); delete $monitor->{MMapHandle}; + } else { + Warn("No MMapHandle in $$monitor{Id}"); } -} +} # end sub zmMemDetach sub zmMemGet { my $monitor = shift; @@ -162,7 +168,7 @@ sub zmMemPut { } sub zmMemClean { - Debug("Removing memory map files"); + Debug('Removing memory map files'); my $mapPath = $Config{ZM_PATH_MAP}.'/zm.mmap.*'; foreach my $mapFile( glob( $mapPath ) ) { ( $mapFile ) = $mapFile =~ /^(.+)$/; From 055c20c3cc9070db5779d2245a561ce9e84dbacb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 10:23:46 -0400 Subject: [PATCH 190/319] fix eslint --- web/skins/classic/js/skin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index aa04bd4df..2c21c0503 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -454,7 +454,7 @@ function convertLabelFormat(LabelFormat, monitorName) { //https://raw.githubusercontent.com/benjaminoakes/moment-strftime/master/lib/moment-strftime.js //added %f and %N below (TODO: add %Q) var replacements = { - 'a': 'ddd', + 'a': 'ddd', 'A': 'dddd', 'b': 'MMM', 'B': 'MMMM', From a7b553cdbe63a55838f88a3aab476913bfad4d29 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:32:26 -0400 Subject: [PATCH 191/319] code style and correct license. Author was contacted and ok'd the change. Fies #2883 --- .../lib/ZoneMinder/Control/DCS5020L.pm | 388 ++++++++---------- 1 file changed, 171 insertions(+), 217 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm index 59d9e3550..4f14e787a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm @@ -46,243 +46,187 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); -sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +sub open { + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/' . ZoneMinder::Base::ZM_VERSION); + $self->{state} = 'open'; } -our $AUTOLOAD; - -sub AUTOLOAD -{ - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); +sub close { + my $self = shift; + $self->{state} = 'closed'; } -sub open -{ - my $self = shift; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $cgi = shift; - $self->loadMonitor(); + my $result = undef; - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/" . ZoneMinder::Base::ZM_VERSION ); - $self->{state} = 'open'; + printMsg($cmd, 'Tx'); + + my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); + $req->content($cmd); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + } else { + Error("Error check failed: '".$res->status_line()."'"); + } + + return $result; } -sub close -{ - my $self = shift; - $self->{state} = 'closed'; +sub move { + my $self = shift; + my $dir = shift; + my $panStep = shift; + my $tiltStep = shift; + my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; + $self->sendCmd($cmd, 'pantiltcontrol'); } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); +sub moveRel { + my $self = shift; + my $params = shift; + my $panStep = $self->getParam($params, 'panstep', 0); + my $tiltStep = $self->getParam($params, 'tiltstep', 0); + my $dir = shift; + $self->move( $dir, $panStep, $tiltStep ); } -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - my $cgi = shift; - - my $result = undef; - - printMsg( $cmd, "Tx" ); - - my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); - $req->content($cmd); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "Error check failed: '".$res->status_line()."'" ); - } - - return( $result ); +sub moveRelUpLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 0); } -sub move -{ - my $self = shift; - my $dir = shift; - my $panStep = shift; - my $tiltStep = shift; - my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub moveRelUp { + my $self = shift; + my $params = shift; + $self->moveRel($params, 1); } -sub moveRel -{ - my $self = shift; - my $params = shift; - my $panStep = $self->getParam($params, 'panstep', 0); - my $tiltStep = $self->getParam($params, 'tiltstep', 0); - my $dir = shift; - $self->move( $dir, $panStep, $tiltStep ); +sub moveRelUpRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 2); } -sub moveRelUpLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 0 ); +sub moveRelLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 3); } -sub moveRelUp -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 1 ); +sub moveRelRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 5); } -sub moveRelUpRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 2 ); +sub moveRelDownLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 6); } -sub moveRelLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 3 ); +sub moveRelDown { + my $self = shift; + my $params = shift; + $self->moveRel($params, 7); } -sub moveRelRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 5 ); -} - -sub moveRelDownLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 6 ); -} - -sub moveRelDown -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 7 ); -} - -sub moveRelDownRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 8 ); +sub moveRelDownRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 8); } # moves the camera to center on the point that the user clicked on in the video image. # This isn't extremely accurate but good enough for most purposes -sub moveMap -{ - # if the camera moves too much or too little, try increasing or decreasing this value - my $f = 11; +sub moveMap { + # if the camera moves too much or too little, try increasing or decreasing this value + my $f = 11; - my $self = shift; - my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); + my $self = shift; + my $params = shift; + my $xcoord = $self->getParam( $params, 'xcoord' ); + my $ycoord = $self->getParam( $params, 'ycoord' ); - my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; - my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; - - my $direction; - my $horSteps; - my $verSteps; - if ($hor < 50 && $ver < 50) { - # up left - $horSteps = (50 - $hor) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 0; - } elsif ($hor >= 50 && $ver < 50) { - # up right - $horSteps = ($hor - 50) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 2; - } elsif ($hor < 50 && $ver >= 50) { - # down left - $horSteps = (50 - $hor) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 6; - } elsif ($hor >= 50 && $ver >= 50) { - # down right - $horSteps = ($hor - 50) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 8; - } - my $v = int($verSteps + .5); - my $h = int($horSteps + .5); - Debug( "Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction" ); - $self->move( $direction, $h, $v ); + my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; + my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; + + my $direction; + my $horSteps; + my $verSteps; + if ($hor < 50 && $ver < 50) { + # up left + $horSteps = (50 - $hor) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 0; + } elsif ($hor >= 50 && $ver < 50) { + # up right + $horSteps = ($hor - 50) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 2; + } elsif ($hor < 50 && $ver >= 50) { + # down left + $horSteps = (50 - $hor) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 6; + } elsif ($hor >= 50 && $ver >= 50) { + # down right + $horSteps = ($hor - 50) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 8; + } + my $v = int($verSteps + .5); + my $h = int($horSteps + .5); + Debug("Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction"); + $self->move($direction, $h, $v); } -sub presetClear -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); - my $cmd = "ClearPosition=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetClear { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Clear Preset $preset" ); + my $cmd = "ClearPosition=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Set Preset $preset" ); + my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "PanTiltPresetPositionMove=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Goto Preset $preset" ); + my $cmd = "PanTiltPresetPositionMove=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - $self->move( 4, 0, 0 ); +sub presetHome { + my $self = shift; + Debug( "Home Preset" ); + $self->move( 4, 0, 0 ); } - # IR Controls # # wake = IR on @@ -290,40 +234,36 @@ sub presetHome # reset = IR auto sub setDayNightMode { - my $self = shift; - my $mode = shift; - my $cmd = "DayNightMode=$mode&ConfigReboot=No"; - $self->sendCmd( $cmd, 'daynight' ); + my $self = shift; + my $mode = shift; + my $cmd = "DayNightMode=$mode&ConfigReboot=No"; + $self->sendCmd($cmd, 'daynight'); } -sub wake -{ - my $self = shift; - Debug( "Wake - IR on" ); - $self->setDayNightMode(2); +sub wake { + my $self = shift; + Debug('Wake - IR on'); + $self->setDayNightMode(2); } -sub sleep -{ - my $self = shift; - Debug( "Sleep - IR off" ); - $self->setDayNightMode(3); +sub sleep { + my $self = shift; + Debug('Sleep - IR off'); + $self->setDayNightMode(3); } -sub reset -{ - my $self = shift; - Debug( "Reset - IR auto" ); - $self->setDayNightMode(0); +sub reset { + my $self = shift; + Debug('Reset - IR auto'); + $self->setDayNightMode(0); } 1; __END__ -# Below is stub documentation for your module. You'd better edit it! =head1 NAME -ZoneMinder::Database - Perl extension for DCS-5020L +ZoneMinder::Control::DCS5020L - Perl extension for DCS-5020L =head1 SYNOPSIS @@ -351,6 +291,20 @@ Art Scheel ascheel (at) gmail =head1 COPYRIGHT AND LICENSE -LGPLv3 +Copyright (C) 2018 ZoneMinder LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut From 67b35967d494161c817f7f326e72259be608f401 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Mar 2020 13:06:10 -0400 Subject: [PATCH 192/319] MOstly spaces and quotes, remove duplicated db version update and only prepare the sth once. --- scripts/zmupdate.pl.in | 107 +++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index b0b63757a..3662b6655 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -847,9 +847,9 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version eq "1.24.4" ) { + if ( $cascade || $version eq '1.24.4' ) { # Patch the database - patchDB( $dbh, "1.24.4" ); + patchDB($dbh, '1.24.4'); # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; @@ -863,12 +863,12 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version lt "1.26.0" ) { - my $sth = $dbh->prepare_cached( 'select * from Monitors LIMIT 0,1' ); + if ( $cascade || $version lt '1.26.0' ) { + my $sth = $dbh->prepare_cached('SELECT * FROM Monitors LIMIT 0,1'); die "Error: " . $dbh->errstr . "\n" unless ($sth); die "Error: " . $sth->errstr . "\n" unless ($sth->execute); - my $columns = $sth->{'NAME'}; + my $columns = $sth->{NAME}; if ( ! grep(/^Colours$/, @$columns ) ) { $dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;}); } # end if @@ -898,28 +898,31 @@ if ( $version ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if + my $sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; #PP make sure we use version compare - if ( version->parse('v' . $v) > version->parse('v' . $version) ) { - print( "Upgrading DB to $v from $version\n" ); - patchDB( $dbh, $v ); - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + if ( version->parse('v'.$v) > version->parse('v'.$version) ) { + print("Upgrading DB to $v from $version\n"); + if ( patchDB($dbh, $v) ) { + my $res = $sth->execute($version) or die( "Can't execute: ".$sth->errstr() ); + } #patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch ); } # end if newer version } # end foreach patchfile + + $sth->finish(); $cascade = !undef; } # end if if ( $cascade ) { - my $installed_version = ZM_VERSION; - my $sql = 'update Config set Value = ? where Name = ?'; + # This is basically here so that we don't need zm-update-blah.sql files for versions without db changes + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); - $res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_DB_VERSION') or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_CURR_VERSION') or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); } else { zmDbDisconnect(); @@ -930,41 +933,42 @@ if ( $version ) { #my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); #my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() ); #$sth->finish(); - print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" ); -} + print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n"); +} # end if version + zmDbDisconnect(); -exit( 0 ); +exit(0); sub patchDB_using_do { my ( $dbh, $version, $file ) = @_; - open( my $fh, '<', $file ) or die "Unable to open $file $!"; + open(my $fh, '<', $file) or die "Unable to open $file $!"; $/ = undef; my $sql = <$fh>; close $fh; if ( $sql ) { - $dbh->{'AutoCommit'} = 0; + $dbh->{AutoCommit} = 0; $dbh->do($sql); if ( $dbh->errstr() ) { $dbh->rollback(); - die "Error: " . $dbh->errstr(). ". Rolled back.\n"; + die 'Error: '.$dbh->errstr().". Rolled back.\n"; } # end if error - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = \'ZM_DYN_DB_VERSION\''; + my $sth = $dbh->prepare_cached($sql) or die "Can't prepare '$sql': ".$dbh->errstr(); + my $res = $sth->execute($version) or die 'Can\'t execute: '.$sth->errstr(); $sth->finish(); - $dbh->{'AutoCommit'} = 1; + $dbh->{AutoCommit} = 1; } else { Warning("Empty db update file at $file"); } -} +} # end sub patchDB_using_do + sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; if ( defined($portOrSocket) ) { @@ -988,39 +992,38 @@ sub patchDB { } $command .= '/zm_update-'.$version.'.sql'; - print( "Executing '$command'\n" ) if ( logDebugging() ); + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { - chomp( $output ); - print( "Output: $output\n" ); + chomp($output); + print("Output: $output\n"); } if ( $status ) { - die( "Command '$command' exited with status: $status\n" ); + die("Command '$command' exited with status: $status\n"); } - print( "\nDatabase successfully upgraded to version $version.\n" ); - -} + print("\nDatabase successfully upgraded to version $version.\n"); +} # end sub patchDB sub migratePasswords { - print ("Migratings passwords, if any...\n"); - my $sql = "select * from Users"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - while( my $user = $sth->fetchrow_hashref() ) { - my $scheme = substr($user->{Password}, 0, 1); - if ($scheme eq "*") { - print ("-->".$user->{Username}. " password will be migrated\n"); - my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); - my $settings = '$2a$10$'.$salt; - my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); - my $new_pass_hash = "-ZM-".$pass_hash; - $sql = "UPDATE Users SET PASSWORD=? WHERE Username=?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($new_pass_hash, $user->{Username}) or die( "Can't execute: ".$sth->errstr() ); - } + print ("Migratings passwords, if any...\n"); + my $sql = 'SELECT * FROM `Users`'; + my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); + while( my $user = $sth->fetchrow_hashref() ) { + my $scheme = substr($user->{Password}, 0, 1); + if ($scheme eq '*') { + print ('-->'.$user->{Username}." password will be migrated\n"); + my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); + my $settings = '$2a$10$'.$salt; + my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); + my $new_pass_hash = '-ZM-'.$pass_hash; + $sql = 'UPDATE Users SET `Password`=? WHERE `Username`=?'; + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($new_pass_hash, $user->{Username}) or die("Can't execute: ".$sth->errstr()); } -} + } +} # end sub migratePasswords sub migratePaths { From 7046612b65f801c271d6c3e267ded45c9d29a611 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 09:48:55 -0500 Subject: [PATCH 193/319] Code style updates. Quote the word Function in SQL for newer mysql --- scripts/zmx10.pl.in | 1144 +++++++++++++++++++------------------------ 1 file changed, 514 insertions(+), 630 deletions(-) diff --git a/scripts/zmx10.pl.in b/scripts/zmx10.pl.in index 89dee5d00..de1f74929 100644 --- a/scripts/zmx10.pl.in +++ b/scripts/zmx10.pl.in @@ -83,72 +83,64 @@ my $unit_code; my $version; GetOptions( - 'command=s' =>\$command, - 'unit-code=i' =>\$unit_code, - 'version' =>\$version + 'command=s' =>\$command, + 'unit-code=i' =>\$unit_code, + 'version' =>\$version ) or pod2usage(-exitstatus => -1); if ( $version ) { - print ZoneMinder::Base::ZM_VERSION; - exit(0); + print ZoneMinder::Base::ZM_VERSION; + exit(0); } -die( 'No command given' ) unless( $command ); -die( 'No unit code given' ) - unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); +die 'No command given' unless $command; +die 'No unit code given' +unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); -if ( $command eq 'start' ) -{ +if ( $command eq 'start' ) { + X10Server::runServer(); + exit(); +} + +socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) + or Fatal("Can't open socket: $!"); + +my $saddr = sockaddr_un(SOCK_FILE); + +if ( !connect(CLIENT, $saddr) ) { + # The server isn't there + print("Unable to connect, starting server\n"); + close(CLIENT); + + if ( my $cpid = fork() ) { + # Parent process just sleep and fall through + sleep(2); + logReinit(); + socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) + or Fatal("Can't open socket: $!"); + connect(CLIENT, $saddr) + or Fatal("Can't connect: $!"); + } elsif ( defined($cpid) ) { + setpgrp(); + + logReinit(); X10Server::runServer(); - exit(); -} - -socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) - or Fatal( "Can't open socket: $!" ); - -my $saddr = sockaddr_un( SOCK_FILE ); - -if ( !connect( CLIENT, $saddr ) ) -{ - # The server isn't there - print( "Unable to connect, starting server\n" ); - close( CLIENT ); - - if ( my $cpid = fork() ) - { - # Parent process just sleep and fall through - sleep( 2 ); - logReinit(); - socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) - or Fatal( "Can't open socket: $!" ); - connect( CLIENT, $saddr ) - or Fatal( "Can't connect: $!" ); - } - elsif ( defined($cpid) ) - { - setpgrp(); - - logReinit(); - X10Server::runServer(); - } - else - { - Fatal( "Can't fork: $!" ); - } + } else { + Fatal("Can't fork: $!"); + } } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); -my $message = "$command"; -$message .= ";$unit_code" if ( $unit_code ); -print( CLIENT $message ); -shutdown( CLIENT, 1 ); -while ( my $line = ) -{ - chomp( $line ); - print( "$line\n" ); +my $message = $command; +$message .= ';'.$unit_code if $unit_code; +print(CLIENT $message); +shutdown(CLIENT, 1); +while ( my $line = ) { + chomp($line); + print("$line\n"); } -close( CLIENT ); +close(CLIENT); #print( "Finished writing, bye\n" ); exit; @@ -178,605 +170,497 @@ our %monitor_hash; our %device_hash; our %pending_tasks; -sub runServer -{ - Info( "X10 server starting\n" ); +sub runServer { + Info('X10 server starting'); - socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) - or Fatal( "Can't open socket: $!" ); - unlink( main::SOCK_FILE ); - my $saddr = sockaddr_un( main::SOCK_FILE ); - bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); - listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); + socket(SERVER, PF_UNIX, SOCK_STREAM, 0) + or Fatal("Can't open socket: $!"); + unlink(main::SOCK_FILE); + my $saddr = sockaddr_un(main::SOCK_FILE); + bind(SERVER, $saddr) or Fatal("Can't bind: $!"); + listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!"); - $dbh = zmDbConnect(); + $dbh = zmDbConnect(); - $x10 = new X10::ActiveHome( port=>$Config{ZM_X10_DEVICE}, house_code=>$Config{ZM_X10_HOUSE_CODE}, debug=>0 ); + $x10 = new X10::ActiveHome( + port=>$Config{ZM_X10_DEVICE}, + house_code=>$Config{ZM_X10_HOUSE_CODE}, + debug=>0 + ); - loadTasks(); + loadTasks(); - $x10->register_listener( \&x10listen ); + $x10->register_listener(\&x10listen); - my $rin = ''; - vec( $rin, fileno(SERVER),1) = 1; - vec( $rin, $x10->select_fds(),1) = 1; - my $timeout = 0.2; - #print( 'F:'.fileno(SERVER)."\n" ); - my $reload = undef; - my $reload_count = 0; - my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout; - while( 1 ) - { - my $nfound = select( my $rout = $rin, undef, undef, $timeout ); - #print( "Off select, NF:$nfound, ER:$!\n" ); - #print( vec( $rout, fileno(SERVER),1)."\n" ); - #print( vec( $rout, $x10->select_fds(),1)."\n" ); - if ( $nfound > 0 ) - { - if ( vec( $rout, fileno(SERVER),1) ) - { - my $paddr = accept( CLIENT, SERVER ); - my $message = ; + my $rin = ''; + vec($rin, fileno(SERVER),1) = 1; + vec($rin, $x10->select_fds(),1) = 1; + my $timeout = 0.2; + #print( 'F:'.fileno(SERVER)."\n" ); + my $reload = undef; + my $reload_count = 0; + my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout; + while( 1 ) { + my $nfound = select(my $rout = $rin, undef, undef, $timeout); + #print( "Off select, NF:$nfound, ER:$!\n" ); + #print( vec( $rout, fileno(SERVER),1)."\n" ); + #print( vec( $rout, $x10->select_fds(),1)."\n" ); + if ( $nfound > 0 ) { + if ( vec($rout, fileno(SERVER),1) ) { + my $paddr = accept(CLIENT, SERVER); + my $message = ; - my ( $command, $unit_code ) = split( /;/, $message ); + my ($command, $unit_code) = split(';', $message); - my $device; - if ( defined($unit_code) ) - { - if ( $unit_code < 1 || $unit_code > 16 ) - { - dPrint( ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n" ); - next; - } + my $device; + if ( defined($unit_code) ) { + if ( $unit_code < 1 || $unit_code > 16 ) { + dPrint(ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n"); + next; + } - $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), - status=>'unknown' - }; - } - } + $device = $device_hash{$unit_code}; + if ( !$device ) { + $device = $device_hash{$unit_code} = { + appliance=>$x10->Appliance(unit_code=>$unit_code), + status=>'unknown' + }; + } + } # end if defined($unit_code) - my $result; - if ( $command eq 'on' ) - { - $result = $device->{appliance}->on(); - } - elsif ( $command eq 'off' ) - { - $result = $device->{appliance}->off(); - } - #elsif ( $command eq 'dim' ) - #{ - #$result = $device->{appliance}->dim(); - #} - #elsif ( $command eq 'bright' ) - #{ - #$result = $device->{appliance}->bright(); - #} - elsif ( $command eq 'status' ) - { - if ( $device ) - { - dPrint( ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n" ); - } - else - { - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - dPrint( ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n" ); - } - } - } - elsif ( $command eq 'shutdown' ) - { - last; - } - else - { - dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); - } - if ( defined($result) ) - { - if ( 1 || $result ) - { - $device->{status} = uc($command); - dPrint( ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n" ); - #x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) ); - } - else - { - dPrint( ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n" ); - } - } - close( CLIENT ); - } - elsif ( vec( $rout, $x10->select_fds(),1) ) - { - $x10->handle_input(); - } - else - { - Fatal( 'Bogus descriptor' ); - } + my $result; + if ( $command eq 'on' ) { + $result = $device->{appliance}->on(); + } elsif ( $command eq 'off' ) { + $result = $device->{appliance}->off(); } - elsif ( $nfound < 0 ) - { - if ( $! != EINTR ) - { - Fatal( "Can't select: $!" ); + #elsif ( $command eq 'dim' ) + #{ + #$result = $device->{appliance}->dim(); + #} + #elsif ( $command eq 'bright' ) + #{ + #$result = $device->{appliance}->bright(); + #} + elsif ( $command eq 'status' ) { + if ( $device ) { + dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n"); + } else { + foreach my $unit_code ( sort( keys(%device_hash) ) ) { + my $device = $device_hash{$unit_code}; + dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n"); } + } + } elsif ( $command eq 'shutdown' ) { + last; + } else { + dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n"); } - else - { - #print( "Select timed out\n" ); - # Check for state changes - foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) - { - my $monitor = $monitor_hash{$monitor_id}; - my $state = zmGetMonitorState( $monitor ); - if ( !defined($state) ) - { - $reload = !undef; - next; - } - if ( defined( $monitor->{LastState} ) ) - { - my $task_list; - if ( ($state == STATE_ALARM || $state == STATE_ALERT) - && ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE) - ) # Gone into alarm state - { - Debug( "Applying ON_list for $monitor_id\n" ); - $task_list = $monitor->{'ON_list'}; - } - elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) - || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) - ) # Come out of alarm state - { - Debug( "Applying OFF_list for $monitor_id\n" ); - $task_list = $monitor->{'OFF_list'}; - } - if ( $task_list ) - { - foreach my $task ( @$task_list ) - { - processTask( $task ); - } - } - } - $monitor->{LastState} = $state; - } - - # Check for pending tasks - my $now = time(); - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { - last if ( $activation_time > $now ); - my $pending_list = $pending_tasks{$activation_time}; - foreach my $task ( @$pending_list ) - { - processTask( $task ); - } - delete( $pending_tasks{$activation_time} ); - } - if ( $reload || ++$reload_count >= $reload_limit ) - { - loadTasks(); - $reload = undef; - $reload_count = 0; - } - } - } - Info( "X10 server exiting\n" ); - close( SERVER ); - exit(); -} - -sub addToDeviceList -{ - my $unit_code = shift; - my $event = shift; - my $monitor = shift; - my $function = shift; - my $limit = shift; - - Debug( "Adding to device list, uc:$unit_code, ev:$event, mo:" - .$monitor->{Id}.", fu:$function, li:$limit\n" - ); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), - status=>'unknown' - }; - } - - my $task = { type=>'device', - monitor=>$monitor, - address=>$device->{appliance}->address(), - function=>$function - }; - if ( $limit ) - { - $task->{limit} = $limit - } - - my $task_list = $device->{$event.'_list'}; - if ( !$task_list ) - { - $task_list = $device->{$event.'_list'} = []; - } - push( @$task_list, $task ); -} - -sub addToMonitorList -{ - my $monitor = shift; - my $event = shift; - my $unit_code = shift; - my $function = shift; - my $limit = shift; - - Debug( "Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id} - .", fu:$function, li:$limit\n" - ); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), - status=>'unknown' - }; - } - - my $task = { type=>'monitor', - device=>$device, - id=>$monitor->{Id}, - function=>$function - }; - if ( $limit ) - { - $task->{limit} = $limit; - } - - my $task_list = $monitor->{$event.'_list'}; - if ( !$task_list ) - { - $task_list = $monitor->{$event.'_list'} = []; - } - push( @$task_list, $task ); -} - -sub loadTasks -{ - %monitor_hash = (); - - Debug( "Loading tasks\n" ); - # Clear out all old device task lists - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - $device->{ON_list} = []; - $device->{OFF_list} = []; - } - - my $sql = "SELECT M.*,T.* from Monitors as M - INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId) - WHERE find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' ) - AND M.Enabled = 1 - AND find_IN_set( 'X10', M.Triggers )" - ; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { -# Check shared memory ok - if ( !zmMemVerify( $monitor ) ) { - zmMemInvalidate( $monitor ); - next ; + if ( defined($result) ) { + # FIXME + if ( 1 || $result ) { + $device->{status} = uc($command); + dPrint(ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n"); + #x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) ); + } else { + dPrint(ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n"); + } + } # end if defined result + close(CLIENT); + } elsif ( vec($rout, $x10->select_fds(),1) ) { + $x10->handle_input(); + } else { + Fatal('Bogus descriptor'); } - - $monitor_hash{$monitor->{Id}} = $monitor; - - if ( $monitor->{Activation} ) - { - Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{Activation} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) - = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToDeviceList( $unit_code, - 'ON', - $monitor, - !$invert ? 'start_active' - : 'stop_active', - $limit - ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToDeviceList( $unit_code, - 'OFF', - $monitor, - !$invert ? 'stop_active' - : 'start_active', - $limit - ); - } - } - } + } elsif ( $nfound < 0 ) { + if ( $! != EINTR ) { + Fatal("Can't select: $!"); + } + } else { + #print( "Select timed out\n" ); + # Check for state changes + foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) { + my $monitor = $monitor_hash{$monitor_id}; + my $state = zmGetMonitorState($monitor); + if ( !defined($state) ) { + $reload = !undef; + next; } - if ( $monitor->{AlarmInput} ) - { - Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) - = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToDeviceList( $unit_code, - 'ON', - $monitor, - !$invert ? 'start_alarm' - : 'stop_alarm', - $limit - ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToDeviceList( $unit_code, - 'OFF', - $monitor, - !$invert ? 'stop_alarm' - : 'start_alarm', - $limit - ); - } - } + if ( defined( $monitor->{LastState} ) ) { + my $task_list; + if ( ($state == STATE_ALARM || $state == STATE_ALERT) + && ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE) + ) # Gone into alarm state + { + Debug("Applying ON_list for $monitor_id"); + $task_list = $monitor->{ON_list}; + } elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) + || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) + ) # Come out of alarm state + { + Debug("Applying OFF_list for $monitor_id"); + $task_list = $monitor->{OFF_list}; + } + if ( $task_list ) { + foreach my $task ( @$task_list ) { + processTask($task); } - } - if ( $monitor->{AlarmOutput} ) - { - Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) - = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToMonitorList( $monitor, - 'ON', - $unit_code, - !$invert ? 'on' - : 'off', - $limit - ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToMonitorList( $monitor, - 'OFF', - $unit_code, - !$invert ? 'off' - : 'on', - $limit - ); - } - } - } - } - zmMemInvalidate( $monitor ); - } -} + } + } # end if defined laststate + $monitor->{LastState} = $state; + } # end foreach monitor -sub addPendingTask -{ - my $task = shift; - - # Check whether we are just extending a previous pending task - # and remove it if it's there - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { + # Check for pending tasks + my $now = time(); + foreach my $activation_time ( sort(keys(%pending_tasks) ) ) { + last if ( $activation_time > $now ); my $pending_list = $pending_tasks{$activation_time}; - my $new_pending_list = []; - foreach my $pending_task ( @$pending_list ) - { - if ( $task->{type} ne $pending_task->{type} ) - { - push( @$new_pending_list, $pending_task ) - } - elsif ( $task->{type} eq 'device' ) - { - if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) - || ( $task->{function} ne $pending_task->{function} )) - { - push( @$new_pending_list, $pending_task ) - } - } - elsif ( $task->{type} eq 'monitor' ) - { - if (( $task->{device}->{appliance}->unit_code() - != $pending_task->{device}->{appliance}->unit_code() - ) - || ( $task->{function} ne $pending_task->{function} ) - ) - { - push( @$new_pending_list, $pending_task ) - } - } - } - if ( @$new_pending_list ) - { - $pending_tasks{$activation_time} = $new_pending_list; - } - else - { - delete( $pending_tasks{$activation_time} ); + foreach my $task ( @$pending_list ) { + processTask($task); } + delete $pending_tasks{$activation_time}; + } + if ( $reload or (++$reload_count >= $reload_limit) ) { + loadTasks(); + $reload = undef; + $reload_count = 0; + } + } + } + Info("X10 server exiting"); + close(SERVER); + exit(); +} + +sub addToDeviceList { + my $unit_code = shift; + my $event = shift; + my $monitor = shift; + my $function = shift; + my $limit = shift; + + Debug("Adding to device list, uc:$unit_code, ev:$event, mo:" + .$monitor->{Id}.", fu:$function, li:$limit" + ); + my $device = $device_hash{$unit_code}; + if ( !$device ) { + $device = $device_hash{$unit_code} = { + appliance=>$x10->Appliance(unit_code=>$unit_code), + status=>'unknown' + }; + } + + my $task = { + type=>'device', + monitor=>$monitor, + address=>$device->{appliance}->address(), + function=>$function + }; + + if ( $limit ) { + $task->{limit} = $limit + } + + my $task_list = $device->{$event.'_list'}; + if ( !$task_list ) { + $task_list = $device->{$event.'_list'} = []; + } + push @$task_list, $task; +} # end sub addToDeviceList + +sub addToMonitorList { + my $monitor = shift; + my $event = shift; + my $unit_code = shift; + my $function = shift; + my $limit = shift; + + Debug("Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id} + .", fu:$function, li:$limit" + ); + my $device = $device_hash{$unit_code}; + if ( !$device ) { + $device = $device_hash{$unit_code} = { + appliance=>$x10->Appliance(unit_code=>$unit_code), + status=>'unknown' + }; + } + + my $task = { + type=>'monitor', + device=>$device, + id=>$monitor->{Id}, + function=>$function + }; + if ( $limit ) { + $task->{limit} = $limit; + } + + my $task_list = $monitor->{$event.'_list'}; + if ( !$task_list ) { + $task_list = $monitor->{$event.'_list'} = []; + } + push @$task_list, $task; +} # end sub addToMonitorList + +sub loadTasks { + %monitor_hash = (); + + Debug('Loading tasks'); + # Clear out all old device task lists + foreach my $unit_code ( sort keys(%device_hash) ) { + my $device = $device_hash{$unit_code}; + $device->{ON_list} = []; + $device->{OFF_list} = []; + } + + my $sql = 'SELECT M.*,T.* FROM Monitors as M + INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId) + WHERE find_in_set(M.`Function`, \'Modect,Record,Mocord,Nodect\') + AND M.`Enabled` = 1 + AND find_IN_set(\'X10\', M.Triggers)'; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute() + or Fatal("Can't execute: ".$sth->errstr()); + while( my $monitor = $sth->fetchrow_hashref() ) { + # Check shared memory ok + if ( !zmMemVerify($monitor) ) { + zmMemInvalidate($monitor); + next; } - my $end_time = time() + $task->{limit}; - my $pending_list = $pending_tasks{$end_time}; - if ( !$pending_list ) - { - $pending_list = $pending_tasks{$end_time} = []; + $monitor_hash{$monitor->{Id}} = $monitor; + + if ( $monitor->{Activation} ) { + Debug("$monitor->{Name} has active string '$monitor->{Activation}'"); + foreach my $code_string ( split(',', $monitor->{Activation}) ) { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if !$limit; + if ( $unit_code ) { + if ( !$modifier || $modifier eq '+' ) { + addToDeviceList( $unit_code, + 'ON', + $monitor, + (!$invert ? 'start_active' : 'stop_active'), + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) { + addToDeviceList( $unit_code, + 'OFF', + $monitor, + (!$invert ? 'stop_active' : 'start_active'), + $limit + ); + } + } # end if unit_code + } # end foreach code_string } - my $pending_task; - if ( $task->{type} eq 'device' ) - { - $pending_task = { type=>$task->{type}, - monitor=>$task->{monitor}, - function=>$task->{function} + if ( $monitor->{AlarmInput} ) { + Debug("$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'"); + foreach my $code_string ( split(',', $monitor->{AlarmInput}) ) { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if !$limit; + if ( $unit_code ) { + if ( !$modifier || $modifier eq '+' ) { + addToDeviceList( $unit_code, + 'ON', + $monitor, + (!$invert ? 'start_alarm' : 'stop_alarm'), + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) { + addToDeviceList( $unit_code, + 'OFF', + $monitor, + (!$invert ? 'stop_alarm' : 'start_alarm'), + $limit + ); + } + } # end if unit_code + } # end foreach code_string + } # end if AlarmInput + if ( $monitor->{AlarmOutput} ) { + Debug("$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'"); + foreach my $code_string ( split( ',', $monitor->{AlarmOutput} ) ) { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if !$limit; + if ( $unit_code ) { + if ( !$modifier || $modifier eq '+' ) { + addToMonitorList( $monitor, + 'ON', + $unit_code, + (!$invert ? 'on' : 'off'), + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) { + addToMonitorList( $monitor, + 'OFF', + $unit_code, + (!$invert ? 'off' : 'on'), + $limit + ); + } + } # end if unit_code + } # end foreach code_string + } # end if AlarmOutput + zmMemInvalidate($monitor); + } +} # end sub loadTasks + +sub addPendingTask { + my $task = shift; + + # Check whether we are just extending a previous pending task + # and remove it if it's there + foreach my $activation_time ( sort keys(%pending_tasks) ) { + my $pending_list = $pending_tasks{$activation_time}; + my $new_pending_list = []; + foreach my $pending_task ( @$pending_list ) { + if ( $task->{type} ne $pending_task->{type} ) { + push( @$new_pending_list, $pending_task ) + } elsif ( $task->{type} eq 'device' ) { + if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) + || ( $task->{function} ne $pending_task->{function} )) + { + push @$new_pending_list, $pending_task; + } + } elsif ( $task->{type} eq 'monitor' ) { + if (( $task->{device}->{appliance}->unit_code() + != $pending_task->{device}->{appliance}->unit_code() + ) + || ( $task->{function} ne $pending_task->{function} ) + ) { + push @$new_pending_list, $pending_task; + } + } # end switch task->type + } # end foreach pending_task + + if ( @$new_pending_list ) { + $pending_tasks{$activation_time} = $new_pending_list; + } else { + delete $pending_tasks{$activation_time}; + } + } # end foreach activation_time + + my $end_time = time() + $task->{limit}; + my $pending_list = $pending_tasks{$end_time}; + if ( !$pending_list ) { + $pending_list = $pending_tasks{$end_time} = []; + } + my $pending_task; + if ( $task->{type} eq 'device' ) { + $pending_task = { + type=>$task->{type}, + monitor=>$task->{monitor}, + function=>$task->{function} + }; + $pending_task->{function} =~ s/start/stop/; + } elsif ( $task->{type} eq 'monitor' ) { + $pending_task = { + type=>$task->{type}, + device=>$task->{device}, + function=>$task->{function} + }; + $pending_task->{function} =~ s/on/off/; + } + push @$pending_list, $pending_task; +} # end sub addPendingTask + +sub processTask { + my $task = shift; + + if ( $task->{type} eq 'device' ) { + my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); + + if ( $class eq 'active' ) { + if ( $instruction eq 'start' ) { + zmMonitorEnable($task->{monitor}); + if ( $task->{limit} ) { + addPendingTask($task); + } + } elsif( $instruction eq 'stop' ) { + zmMonitorDisable($task->{monitor}); + } + } elsif( $class eq 'alarm' ) { + if ( $instruction eq 'start' ) { + zmTriggerEventOn( + $task->{monitor}, + 0, + main::CAUSE_STRING, + $task->{address} + ); + if ( $task->{limit} ) { + addPendingTask($task); + } + } elsif( $instruction eq 'stop' ) { + zmTriggerEventCancel($task->{monitor}); + } + } # end switch class + } elsif( $task->{type} eq 'monitor' ) { + if ( $task->{function} eq 'on' ) { + $task->{device}->{appliance}->on(); + if ( $task->{limit} ) { + addPendingTask($task); + } + } elsif ( $task->{function} eq 'off' ) { + $task->{device}->{appliance}->off(); + } + } +} + +sub dPrint { + my $dbg_level = shift; + if ( fileno(CLIENT) ) { + print CLIENT @_ + } + if ( $dbg_level == ZoneMinder::Logger::DEBUG ) { + Debug(@_); + } elsif ( $dbg_level == ZoneMinder::Logger::INFO ) { + Info(@_); + } elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) { + Warning(@_); + } + elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) { + Error( @_ ); + } elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) { + Fatal( @_ ); + } +} + +sub x10listen { + foreach my $event ( @_ ) { + #print( Data::Dumper( $_ )."\n" ); + if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} ) { + my $unit_code = $event->unit_code(); + my $device = $device_hash{$unit_code}; + if ( !$device ) { + $device = $device_hash{$unit_code} = { + appliance=>$x10->Appliance(unit_code=>$unit_code), + status=>'unknown' }; - $pending_task->{function} =~ s/start/stop/; - } - elsif ( $task->{type} eq 'monitor' ) - { - $pending_task = { type=>$task->{type}, - device=>$task->{device}, - function=>$task->{function} - }; - $pending_task->{function} =~ s/on/off/; - } - push( @$pending_list, $pending_task ); -} - -sub processTask -{ - my $task = shift; - - if ( $task->{type} eq 'device' ) - { - my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); - - if ( $class eq 'active' ) - { - if ( $instruction eq 'start' ) - { - zmMonitorEnable( $task->{monitor} ); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq 'stop' ) - { - zmMonitorDisable( $task->{monitor} ); - } + } + next if ( $event->func() !~ /(?:ON|OFF)/ ); + $device->{status} = $event->func(); + my $task_list = $device->{$event->func().'_list'}; + if ( $task_list ) { + foreach my $task ( @$task_list ) { + processTask($task); } - elsif( $class eq 'alarm' ) - { - if ( $instruction eq 'start' ) - { - zmTriggerEventOn( $task->{monitor}, - 0, - main::CAUSE_STRING, - $task->{address} - ); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq 'stop' ) - { - zmTriggerEventCancel( $task->{monitor} ); - } - } - } - elsif( $task->{type} eq 'monitor' ) - { - if ( $task->{function} eq 'on' ) - { - $task->{device}->{appliance}->on(); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif ( $task->{function} eq 'off' ) - { - $task->{device}->{appliance}->off(); - } - } -} - -sub dPrint -{ - my $dbg_level = shift; - if ( fileno(CLIENT) ) - { - print CLIENT @_ - } - if ( $dbg_level == ZoneMinder::Logger::DEBUG ) - { - Debug( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::INFO ) - { - Info( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) - { - Warning( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) - { - Error( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) - { - Fatal( @_ ); - } -} - -sub x10listen -{ - foreach my $event ( @_ ) - { - #print( Data::Dumper( $_ )."\n" ); - if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} ) - { - my $unit_code = $event->unit_code(); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), - status=>'unknown' - }; - } - next if ( $event->func() !~ /(?:ON|OFF)/ ); - $device->{status} = $event->func(); - my $task_list = $device->{$event->func().'_list'}; - if ( $task_list ) - { - foreach my $task ( @$task_list ) - { - processTask( $task ); - } - } - } - Info( "Got event - ".$event->as_string()."\n" ); - } -} + } + } # end if correct house code + Info('Got event - '.$event->as_string()); + } +} # end sub x10listen 1; +__END__ From 576e3149d66918c3a0f059e7fe6e0cf8d241de12 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 3 Mar 2020 17:29:31 -0500 Subject: [PATCH 194/319] escape table columns for mysql8 --- scripts/zmtelemetry.pl.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index 82ceeac96..a3debabdb 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -214,7 +214,7 @@ sub getUUID { $uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array(); $sth->finish(); - $sql = q`UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`; + $sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); @@ -250,9 +250,9 @@ sub countQuery { my $dbh = shift; my $table = shift; - my $sql = "SELECT count(*) FROM $table"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $sql = "SELECT count(*) FROM `$table`"; + my $sth = $dbh->prepare_cached($sql) or die "Can't prepare '$sql': ".$dbh->errstr(); + my $res = $sth->execute() or die 'Can\'t execute: '.$sth->errstr(); my $count = $sth->fetchrow_array(); $sth->finish(); @@ -263,7 +263,7 @@ sub countQuery { sub getMonitorRef { my $dbh = shift; - my $sql = 'SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors'; + my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS` FROM `Monitors`'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); my $arrayref = $sth->fetchall_arrayref({}); From 57141fddeb4bba8917a3459d1c96400699e1d9c0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 26 Feb 2020 09:42:17 -0500 Subject: [PATCH 195/319] handle additional atributes on the html tag in html detection --- scripts/zmfilter.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index be9996fe2..77dfd1e08 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -860,7 +860,7 @@ sub sendEmail { From => $Config{ZM_FROM_EMAIL}, To => $Config{ZM_EMAIL_ADDRESS}, Subject => $subject, - Type => (($body=~//)?'text/html':'text/plain'), + Type => (($body=~/ $body ); @@ -962,7 +962,7 @@ sub sendMessage { From => $Config{ZM_FROM_EMAIL}, To => $Config{ZM_MESSAGE_ADDRESS}, Subject => $subject, - Type => (($body=~//)?'text/html':'text/plain'), + Type => (($body=~/ $body ); From 3ac6dc267b42878e1622b8bcac1ceb642ca711cc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 Feb 2020 09:42:54 -0500 Subject: [PATCH 196/319] out an error if name contains invalid characters --- scripts/zmcamtool.pl.in | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/zmcamtool.pl.in b/scripts/zmcamtool.pl.in index 7e345b79d..3d68b1408 100644 --- a/scripts/zmcamtool.pl.in +++ b/scripts/zmcamtool.pl.in @@ -352,9 +352,13 @@ sub exportsql { } my $name = $ARGV[0]; - if ($name && $name =~ /^([A-Za-z0-9 ,.&()\/\-]+)$/) { # Allow alphanumeric and " ,.&()/-" - $name = $1; - $command .= qq( --where="Name = '$name'"); + if ( $name ) { + if ( $name =~ /^([A-Za-z0-9 ,.&()\/\-]+)$/ ) { # Allow alphanumeric and " ,.&()/-" + $name = $1; + $command .= qq( --where="Name = '$name'"); + } else { + print "Invalid characters in Name\n"; + } } $command .= " zm Controls MonitorPresets"; From 59f9f37fffb11175b9b340a45218363e70d03433 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:10:57 -0400 Subject: [PATCH 197/319] cleanup. Don't output errors that break json response --- web/ajax/log.php | 142 +++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/web/ajax/log.php b/web/ajax/log.php index 36a29adc3..d5483b7ca 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -1,14 +1,15 @@ $value ) { if ( !in_array($field, $filterFields) ) { - ZM\Error("'$field' is not in valid filter fields " . print_r($filterField,true)); + ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true)); continue; } - if ( $field == 'Level' ){ + if ( $field == 'Level' ) { $where[] = $field.' <= ?'; $values[] = $value; } else { @@ -69,8 +70,8 @@ switch ( $_REQUEST['task'] ) { $string = $_POST['message']; - $file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : ''; - if ( !empty( $_POST['line'] ) ) { + $file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : ''; + if ( !empty($_POST['line']) ) { $line = validInt($_POST['line']); } else { $line = NULL; @@ -82,6 +83,8 @@ switch ( $_REQUEST['task'] ) { } $level = $levels[$_POST['level']]; ZM\Logger::fetch()->logPrint($level, $string, $file, $line); + } else { + ZM\Error('Invalid log create: '.print_r($_POST, true)); } ajaxResponse(); break; @@ -93,12 +96,7 @@ switch ( $_REQUEST['task'] ) { $query = buildLogQuery('DELETE'); $result = dbQuery($query['sql'], $query['values']); - ajaxResponse( array( - 'result'=>'Ok', - 'deleted'=>$result->rowCount(), - ) ); - - + ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount())); } case 'query' : { @@ -107,10 +105,12 @@ switch ( $_REQUEST['task'] ) { $total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total'); $query = buildLogQuery('SELECT *'); - $servers = ZM\Server::find(); + global $Servers; + if ( !$Servers ) + $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. - foreach ( $servers as $server ) { + foreach ( $Servers as $server ) { $servers_by_Id[$server->Id()] = $server; } @@ -120,11 +120,9 @@ switch ( $_REQUEST['task'] ) { foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) { $log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])); - #Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']); - #$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']); - foreach( $filterFields as $field ) { + foreach ( $filterFields as $field ) { if ( !isset($options[$field]) ) $options[$field] = array(); $value = $log[$field]; @@ -141,21 +139,21 @@ switch ( $_REQUEST['task'] ) { } } $logs[] = $log; - } + } # end foreach log db row foreach ( $options as $field => $values ) { asort($options[$field]); } $available = count($logs); - ajaxResponse( array( - 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG), + ajaxResponse(array( + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG), 'total' => $total, 'available' => isset($available) ? $available : $total, 'logs' => $logs, 'state' => logState(), 'options' => $options, - ) ); + )); break; } case 'export' : @@ -163,9 +161,9 @@ switch ( $_REQUEST['task'] ) { if ( !canView('System') ) ajaxError('Insufficient permissions to export logs'); - $minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL; - $maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL; - if ( !is_null($minTime) && !is_null($maxTime) && $minTime > $maxTime ) { + $minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL; + $maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL; + if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) { $tempTime = $minTime; $minTime = $maxTime; $maxTime = $tempTime; @@ -174,15 +172,17 @@ switch ( $_REQUEST['task'] ) { $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = 'TimeKey'; if ( isset($_POST['sortField']) ) { - if ( ! in_array( $_POST['sortField'], $filterFields ) and ( $_POST['sortField'] != 'TimeKey' ) ) { - ZM\Error("Invalid sort field " . $_POST['sortField'] ); + if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) { + ZM\Error('Invalid sort field '.$_POST['sortField']); } else { $sortField = $_POST['sortField']; } } - $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; + $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc'; - $servers = ZM\Server::find(); + global $Servers; + if ( !$Servers ) + $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { @@ -193,11 +193,9 @@ switch ( $_REQUEST['task'] ) { $where = array(); $values = array(); if ( $minTime ) { - ZM\Logger::Debug("MinTime: $minTime"); if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) { # This handles sub second precision $minTime = strtotime($matches[1]).$matches[2]; - ZM\Logger::Debug("MinTime: $minTime"); } else { $minTime = strtotime($minTime); } @@ -225,11 +223,11 @@ switch ( $_REQUEST['task'] ) { } } if ( count($where) ) - $sql.= ' WHERE '.join( ' AND ', $where ); + $sql.= ' WHERE '.join(' AND ', $where); $sql .= ' ORDER BY '.$sortField.' '.$sortOrder; //$sql .= " limit ".dbEscape($limit); - $format = isset($_POST['format'])?$_POST['format']:'text'; - switch( $format ) { + $format = isset($_POST['format']) ? $_POST['format'] : 'text'; + switch ( $format ) { case 'text' : $exportExt = 'txt'; break; @@ -245,43 +243,40 @@ switch ( $_REQUEST['task'] ) { default : ZM\Fatal("Unrecognised log export format '$format'"); } - $exportKey = substr(md5(rand()),0,8); - $exportFile = "zm-log.$exportExt"; - if ( ! file_exists(ZM_DIR_EXPORTS) ) { - ZM\Logger::Debug('Creating ' . ZM_DIR_EXPORTS); - if ( ! mkdir(ZM_DIR_EXPORTS) ) { - ZM\Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); - } + $exportKey = substr(md5(rand()), 0, 8); + $exportFile = 'zm-log.'.$exportExt; + if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) { + ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } - $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; + $exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt; ZM\Logger::Debug("Exporting to $exportPath"); if ( !($exportFP = fopen($exportPath, 'w')) ) ZM\Fatal("Unable to open log export file $exportPath"); $logs = array(); foreach ( dbFetchAll($sql, NULL, $values) as $log ) { - $log['DateTime'] = preg_replace('/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey']); + $log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } - ZM\Logger::Debug(count($logs)." lines being exported by $sql " . implode(',',$values)); + ZM\Logger::Debug(count($logs).' lines being exported by '.$sql.implode(',', $values)); switch( $format ) { case 'text' : { foreach ( $logs as $log ) { if ( $log['Line'] ) - fprintf( $exportFP, "%s %s[%d].%s-%s/%d [%s]\n", - $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message'] ); + fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n", + $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']); else - fprintf( $exportFP, "%s %s[%d].%s-%s [%s]\n", - $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message'] ); + fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n", + $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']); } break; } case 'tsv' : { # This line doesn't need fprintf, it could use fwrite - fprintf( $exportFP, join( "\t", + fprintf($exportFP, join("\t", translate('DateTime'), translate('Component'), translate('Server'), @@ -290,17 +285,17 @@ switch ( $_REQUEST['task'] ) { translate('Message'), translate('File'), translate('Line') - )."\n" ); + )."\n"); foreach ( $logs as $log ) { - fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", + $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']); } break; } case 'html' : { - fwrite( $exportFP, - ' - + fwrite($exportFP, +' '.translate('ZoneMinderLog').' @@ -339,34 +334,36 @@ switch ( $_REQUEST['task'] ) {

    '.translate('ZoneMinderLog').'

    -

    '.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'

    +

    '.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'

    '.count($logs).' '.translate('Logs').'

    - ' ); + '); foreach ( $logs as $log ) { $classLevel = $log['Level']; - if ( $classLevel < ZM\Logger::FATAL ) + if ( $classLevel < ZM\Logger::FATAL ) { $classLevel = ZM\Logger::FATAL; - elseif ( $classLevel > ZM\Logger::DEBUG ) + } else if ( $classLevel > ZM\Logger::DEBUG ) { $classLevel = ZM\Logger::DEBUG; + } $logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]); - fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf($exportFP, ' + ', $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']); } - fwrite( $exportFP, + fwrite($exportFP, '
    '.translate('DateTime').''.translate('Component').''.translate('Server').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').'
    %s%s%s%d%s%s%s%s
    %s%s%s%d%s%s%s%s
    - ' ); + '); break; } case 'xml' : { - fwrite( $exportFP, + fwrite($exportFP, ' - - '.$_POST['selector'].'' ); + + '.$_POST['selector'].''); foreach ( $filter as $field=>$value ) if ( $value != '' ) fwrite( $exportFP, @@ -381,7 +378,7 @@ switch ( $_REQUEST['task'] ) { ' ); foreach ( $logs as $log ) { fprintf( $exportFP, - " + ' %s %s %s @@ -390,7 +387,8 @@ switch ( $_REQUEST['task'] ) { %s %d - \n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); + +', $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); } fwrite( $exportFP, ' @@ -419,7 +417,7 @@ switch ( $_REQUEST['task'] ) { ZM\Fatal('No log export format given'); $format = $_REQUEST['format']; - switch( $format ) { + switch ( $format ) { case 'text' : $exportExt = 'txt'; break; @@ -436,15 +434,15 @@ switch ( $_REQUEST['task'] ) { ZM\Fatal("Unrecognised log export format '$format'"); } - $exportFile = "zm-log.$exportExt"; - $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; + $exportFile = 'zm-log.'.$exportExt; + $exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt; header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Cache-Control: private', false ); // required by certain browsers + header('Cache-Control: private', false); // required by certain browsers header('Content-Description: File Transfer'); - header('Content-Disposition: attachment; filename="'.$exportFile.'"' ); + header('Content-Disposition: attachment; filename="'.$exportFile.'"'); header('Content-Transfer-Encoding: binary'); header('Content-Type: application/force-download'); header('Content-Length: '.filesize($exportPath)); @@ -452,6 +450,6 @@ switch ( $_REQUEST['task'] ) { exit(0); break; } -} +} // end switch ( $_REQUEST['task'] ) ajaxError('Unrecognised action or insufficient permissions'); ?> From c3a80e7e401712c0a3b0b5599210382ef6869eee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:11:24 -0400 Subject: [PATCH 198/319] Add MonitorId to event data returned for status request --- web/ajax/status.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/ajax/status.php b/web/ajax/status.php index 1b1fb19a3..fea5943ca 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -96,6 +96,7 @@ $statusData = array( 'selector' => 'Events.MonitorId', 'elements' => array( 'Id' => true, + 'MonitorId' => true, 'Name' => true, 'Cause' => true, 'Notes' => true, From 9b77f77ac3db973312a81aea248ee20217683d7f Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Sun, 1 Mar 2020 14:02:44 -0500 Subject: [PATCH 199/319] better logs --- src/zm_user.cpp | 2 +- web/api/app/Controller/HostController.php | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 1c0eb6d51..0ad9f0672 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -200,7 +200,7 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { return NULL; } - Debug (1,"Got stored expiry time of %u",stored_iat); + Debug (1,"Got last token revoke time of: %u",stored_iat); Debug (1,"Authenticated user '%s' via token", username.c_str()); mysql_free_result(result); return user; diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index 7ddba57df..cf6e99b53 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -50,8 +50,10 @@ class HostController extends AppController { $cred_depr = []; if ( $username && $password ) { + ZM\Logger::Debug('Username and password provided, generating access and refresh tokens'); $cred = $this->_getCredentials(true, '', $username); // generate refresh } else { + ZM\Logger::Debug('Only generating access token'); $cred = $this->_getCredentials(false, $token); // don't generate refresh } @@ -69,6 +71,8 @@ class HostController extends AppController { $cred_depr = $this->_getCredentialsDeprecated(); $login_array['credentials'] = $cred_depr[0]; $login_array['append_password'] = $cred_depr[1]; + } else { + ZM\Logger::Debug('Legacy Auth is disabled, not generating auth= credentials'); } $login_array['version'] = $ver[0]; @@ -108,8 +112,11 @@ class HostController extends AppController { private function _getCredentials($generate_refresh_token=false, $token='', $username='') { - if ( !ZM_OPT_USE_AUTH ) + if ( !ZM_OPT_USE_AUTH ) { + ZM\Error('OPT_USE_AUTH is turned off. Tokens will be null'); return; + } + if ( !ZM_AUTH_HASH_SECRET ) throw new ForbiddenException(__('Please create a valid AUTH_HASH_SECRET in ZoneMinder')); From daff14df9b36886177e2be0fab5cb271a830b37e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 16:39:12 -0500 Subject: [PATCH 200/319] Allow users without System::View to read servers.json --- web/api/app/Controller/ServersController.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/web/api/app/Controller/ServersController.php b/web/api/app/Controller/ServersController.php index c30de038f..c3ac6fad7 100644 --- a/web/api/app/Controller/ServersController.php +++ b/web/api/app/Controller/ServersController.php @@ -17,12 +17,17 @@ class ServersController extends AppController { public function beforeFilter() { parent::beforeFilter(); + /* + * A user needs the server data to calculate how to view a monitor, and there really isn't anything sensitive in this data. + * So it has been decided for now to just let everyone read it. + global $user; $canView = (!$user) || ($user['System'] != 'None'); if ( !$canView ) { throw new UnauthorizedException(__('Insufficient Privileges')); return; } + */ } /** @@ -34,7 +39,7 @@ class ServersController extends AppController { $this->Server->recursive = 0; $options = ''; - $servers = $this->Server->find('all',$options); + $servers = $this->Server->find('all', $options); $this->set(array( 'servers' => $servers, '_serialize' => array('servers') @@ -50,13 +55,13 @@ class ServersController extends AppController { */ public function view($id = null) { $this->Server->recursive = 0; - if (!$this->Server->exists($id)) { + if ( !$this->Server->exists($id) ) { throw new NotFoundException(__('Invalid server')); } $restricted = ''; $options = array('conditions' => array( - array('Server.' . $this->Server->primaryKey => $id), + array('Server.'.$this->Server->primaryKey => $id), $restricted ) ); From 6f08322f7f28c803b3a80191b976b8b2c152786e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 12:17:09 -0400 Subject: [PATCH 201/319] add ability to set a filter_regexp in the defaults for an object to have new values be altered before saving --- web/includes/Object.php | 42 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index cc595cd74..9d037c014 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -39,10 +39,13 @@ class ZM_Object { public function __call($fn, array $args){ $type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar'; if ( count($args) ) { - if ( $type == 'set' and is_array($args[0]) ) + if ( $type == 'set' and is_array($args[0]) ) { $this->{$fn} = implode(',', $args[0]); - else + } else if ( array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn]) && isset($this->defaults[$fn]['filter_regexp']) ) { + $this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]); + } else { $this->{$fn} = $args[0]; + } } if ( property_exists($this, $fn) ) { @@ -63,7 +66,7 @@ class ZM_Object { public static function _find($class, $parameters = null, $options = null ) { $table = $class::$table; $filters = array(); - $sql = "SELECT * FROM `$table` "; + $sql = 'SELECT * FROM `'.$table.'` '; $values = array(); if ( $parameters ) { @@ -164,24 +167,11 @@ class ZM_Object { # perhaps should turn into a comma-separated string $this->{$k} = implode(',', $v); } else if ( is_string($v) ) { -if ( 0 ) { -# Remarking this out. We are setting a value, not asking for a default to be set. -# So don't do defaults here, do them somewhere else - if ( ($v == null) and array_key_exists($k, $this->defaults) ) { -Logger::Debug("$k => Have default for $v: "); - if ( is_array($this->defaults[$k]) ) { - $this->{$k} = $this->defaults[$k]['default']; - } else { - $this->{$k} = $this->defaults[$k]; - Logger::Debug("$k => Have default for $v: " . $this->{$k}); - } - } else { + if ( array_key_exists($k, $this->defaults) && is_array($this->defaults[$k]) && isset($this->defaults[$k]['filter_regexp']) ) { + $this->{$k} = preg_replace($this->defaults[$k]['filter_regexp'], '', trim($v)); + } else { $this->{$k} = trim($v); } -} else { - $this->{$k} = trim($v); -} - } else if ( is_integer($v) ) { $this->{$k} = $v; } else if ( is_bool($v) ) { @@ -250,6 +240,9 @@ Logger::Debug("$k => Have default for $v: "); # Input might be a command separated string, or an array } else { + if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) { + $value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value)); + } if ( $this->{$field} != $value ) { $changes[$field] = $value; } @@ -270,17 +263,6 @@ Logger::Debug("$k => Have default for $v: "); $changes[$field] = $value; } } - - #if ( (!array_key_exists($field, $this)) or ( $this->{$field} != $new_values[$field] ) ) { - #Logger::Debug("Checking default $field => $default_value changes becaause" . $new_values[$field].' != '.$new_values[$field]); - #$changes[$field] = $new_values[$field]; - ##} else if { - #Logger::Debug("Checking default $field => $default_value changes becaause " . $new_values[$field].' != '.$new_values[$field]); - ##array_push( $changes, [$field=>$defaults[$field]] ); - #} - #} else { - #Logger::Debug("Checking default $field => $default_value not in new_values"); - #} } # end foreach newvalue From f5bf474ba6d8e4f8952f4036313ce6c7a81cfbe6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 14:59:21 -0400 Subject: [PATCH 202/319] filter bad characters out of Monitor Name --- web/includes/Monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 8c45c0d27..c788b5fb8 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -11,7 +11,7 @@ class Monitor extends ZM_Object { protected $defaults = array( 'Id' => null, - 'Name' => '', + 'Name' => array('type'=>'text','filter_regexp'=>'/[^\w\-\.\(\)\:\/ ]/'), 'Notes' => '', 'ServerId' => 0, 'StorageId' => 0, From 38f559b34b94b5a740e1a2ebb9fc2a76dafba900 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 15:00:28 -0400 Subject: [PATCH 203/319] deprecate ubuntu trusty and precise --- distros/ubuntu1204/NEWS | 0 distros/ubuntu1204/README.Debian | 160 --------- distros/ubuntu1204/TODO.Debian | 12 - distros/ubuntu1204/changelog | 8 - distros/ubuntu1204/clean | 3 - distros/ubuntu1204/compat | 1 - .../ubuntu1204/conf/apache2/zoneminder.conf | 57 ---- distros/ubuntu1204/control | 158 --------- distros/ubuntu1204/copyright | 174 ---------- distros/ubuntu1204/examples/nginx.conf | 32 -- distros/ubuntu1204/gbp.conf | 7 - distros/ubuntu1204/libzoneminder-perl.install | 2 - distros/ubuntu1204/patches/series | 0 distros/ubuntu1204/rules | 93 ----- distros/ubuntu1204/source/format | 1 - distros/ubuntu1204/source/lintian-overrides | 9 - distros/ubuntu1204/watch | 7 - distros/ubuntu1204/zoneminder-doc.doc-base | 8 - distros/ubuntu1204/zoneminder-doc.install | 1 - distros/ubuntu1204/zoneminder-doc.links | 2 - distros/ubuntu1204/zoneminder.apache2 | 1 - distros/ubuntu1204/zoneminder.bug-presubj | 5 - distros/ubuntu1204/zoneminder.dirs | 9 - distros/ubuntu1204/zoneminder.docs | 1 - distros/ubuntu1204/zoneminder.examples | 2 - distros/ubuntu1204/zoneminder.init | 98 ------ distros/ubuntu1204/zoneminder.install | 11 - distros/ubuntu1204/zoneminder.links | 1 - distros/ubuntu1204/zoneminder.linktrees | 14 - .../ubuntu1204/zoneminder.lintian-overrides | 14 - distros/ubuntu1204/zoneminder.logrotate | 13 - distros/ubuntu1204/zoneminder.maintscript | 1 - distros/ubuntu1204/zoneminder.manpages | 1 - distros/ubuntu1204/zoneminder.postinst | 66 ---- distros/ubuntu1204/zoneminder.postrm | 14 - distros/ubuntu1204/zoneminder.preinst | 36 -- distros/ubuntu1204/zoneminder.service | 23 -- distros/ubuntu1204/zoneminder.tmpfile | 3 - distros/ubuntu1410/README.Debian | 51 --- distros/ubuntu1410/apache.conf | 9 - distros/ubuntu1410/changelog | 323 ------------------ distros/ubuntu1410/compat | 1 - distros/ubuntu1410/control | 122 ------- distros/ubuntu1410/copyright | 22 -- distros/ubuntu1410/docs | 1 - distros/ubuntu1410/libzoneminder-perl.install | 4 - distros/ubuntu1410/patches/series | 0 distros/ubuntu1410/po/POTFILES.in | 3 - distros/ubuntu1410/po/fr.po | 252 -------------- distros/ubuntu1410/po/templates.pot | 222 ------------ distros/ubuntu1410/rules | 154 --------- distros/ubuntu1410/source/format | 1 - distros/ubuntu1410/source/local-options | 0 distros/ubuntu1410/source/options | 1 - distros/ubuntu1410/zoneminder-core.config | 11 - distros/ubuntu1410/zoneminder-core.dirs | 4 - distros/ubuntu1410/zoneminder-core.install | 4 - distros/ubuntu1410/zoneminder-core.links | 3 - distros/ubuntu1410/zoneminder-core.postinst | 80 ----- distros/ubuntu1410/zoneminder-core.postrm | 37 -- distros/ubuntu1410/zoneminder-core.preinst | 33 -- distros/ubuntu1410/zoneminder-core.templates | 19 -- .../zoneminder-core.zoneminder.init | 90 ----- .../zoneminder-core.zoneminder.service | 19 -- .../zoneminder-core.zoneminder.tmpfile | 1 - distros/ubuntu1410/zoneminder-database.config | 142 -------- distros/ubuntu1410/zoneminder-database.dirs | 3 - .../ubuntu1410/zoneminder-database.install | 1 - .../ubuntu1410/zoneminder-database.postinst | 79 ----- distros/ubuntu1410/zoneminder-database.postrm | 34 -- distros/ubuntu1410/zoneminder-database.prerm | 22 -- .../ubuntu1410/zoneminder-database.templates | 58 ---- distros/ubuntu1410/zoneminder-ui-base.config | 20 -- distros/ubuntu1410/zoneminder-ui-base.install | 11 - distros/ubuntu1410/zoneminder-ui-base.links | 1 - .../ubuntu1410/zoneminder-ui-base.postinst | 48 --- distros/ubuntu1410/zoneminder-ui-base.postrm | 41 --- .../ubuntu1410/zoneminder-ui-base.templates | 7 - .../ubuntu1410/zoneminder-ui-classic.install | 1 - .../ubuntu1410/zoneminder-ui-mobile.install | 1 - distros/ubuntu1410/zoneminder-ui-xml.install | 1 - 81 files changed, 2985 deletions(-) delete mode 100644 distros/ubuntu1204/NEWS delete mode 100644 distros/ubuntu1204/README.Debian delete mode 100644 distros/ubuntu1204/TODO.Debian delete mode 100644 distros/ubuntu1204/changelog delete mode 100644 distros/ubuntu1204/clean delete mode 100644 distros/ubuntu1204/compat delete mode 100644 distros/ubuntu1204/conf/apache2/zoneminder.conf delete mode 100644 distros/ubuntu1204/control delete mode 100644 distros/ubuntu1204/copyright delete mode 100644 distros/ubuntu1204/examples/nginx.conf delete mode 100644 distros/ubuntu1204/gbp.conf delete mode 100644 distros/ubuntu1204/libzoneminder-perl.install delete mode 100644 distros/ubuntu1204/patches/series delete mode 100755 distros/ubuntu1204/rules delete mode 100644 distros/ubuntu1204/source/format delete mode 100644 distros/ubuntu1204/source/lintian-overrides delete mode 100644 distros/ubuntu1204/watch delete mode 100644 distros/ubuntu1204/zoneminder-doc.doc-base delete mode 100644 distros/ubuntu1204/zoneminder-doc.install delete mode 100644 distros/ubuntu1204/zoneminder-doc.links delete mode 100644 distros/ubuntu1204/zoneminder.apache2 delete mode 100644 distros/ubuntu1204/zoneminder.bug-presubj delete mode 100644 distros/ubuntu1204/zoneminder.dirs delete mode 100644 distros/ubuntu1204/zoneminder.docs delete mode 100644 distros/ubuntu1204/zoneminder.examples delete mode 100644 distros/ubuntu1204/zoneminder.init delete mode 100644 distros/ubuntu1204/zoneminder.install delete mode 100644 distros/ubuntu1204/zoneminder.links delete mode 100644 distros/ubuntu1204/zoneminder.linktrees delete mode 100644 distros/ubuntu1204/zoneminder.lintian-overrides delete mode 100644 distros/ubuntu1204/zoneminder.logrotate delete mode 100644 distros/ubuntu1204/zoneminder.maintscript delete mode 100644 distros/ubuntu1204/zoneminder.manpages delete mode 100644 distros/ubuntu1204/zoneminder.postinst delete mode 100644 distros/ubuntu1204/zoneminder.postrm delete mode 100644 distros/ubuntu1204/zoneminder.preinst delete mode 100644 distros/ubuntu1204/zoneminder.service delete mode 100644 distros/ubuntu1204/zoneminder.tmpfile delete mode 100644 distros/ubuntu1410/README.Debian delete mode 100644 distros/ubuntu1410/apache.conf delete mode 100644 distros/ubuntu1410/changelog delete mode 100644 distros/ubuntu1410/compat delete mode 100644 distros/ubuntu1410/control delete mode 100644 distros/ubuntu1410/copyright delete mode 100644 distros/ubuntu1410/docs delete mode 100644 distros/ubuntu1410/libzoneminder-perl.install delete mode 100644 distros/ubuntu1410/patches/series delete mode 100644 distros/ubuntu1410/po/POTFILES.in delete mode 100644 distros/ubuntu1410/po/fr.po delete mode 100644 distros/ubuntu1410/po/templates.pot delete mode 100755 distros/ubuntu1410/rules delete mode 100644 distros/ubuntu1410/source/format delete mode 100644 distros/ubuntu1410/source/local-options delete mode 100644 distros/ubuntu1410/source/options delete mode 100644 distros/ubuntu1410/zoneminder-core.config delete mode 100644 distros/ubuntu1410/zoneminder-core.dirs delete mode 100644 distros/ubuntu1410/zoneminder-core.install delete mode 100644 distros/ubuntu1410/zoneminder-core.links delete mode 100644 distros/ubuntu1410/zoneminder-core.postinst delete mode 100644 distros/ubuntu1410/zoneminder-core.postrm delete mode 100644 distros/ubuntu1410/zoneminder-core.preinst delete mode 100644 distros/ubuntu1410/zoneminder-core.templates delete mode 100644 distros/ubuntu1410/zoneminder-core.zoneminder.init delete mode 100644 distros/ubuntu1410/zoneminder-core.zoneminder.service delete mode 100644 distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile delete mode 100644 distros/ubuntu1410/zoneminder-database.config delete mode 100644 distros/ubuntu1410/zoneminder-database.dirs delete mode 100644 distros/ubuntu1410/zoneminder-database.install delete mode 100644 distros/ubuntu1410/zoneminder-database.postinst delete mode 100644 distros/ubuntu1410/zoneminder-database.postrm delete mode 100644 distros/ubuntu1410/zoneminder-database.prerm delete mode 100644 distros/ubuntu1410/zoneminder-database.templates delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.config delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.install delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.links delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.postinst delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.postrm delete mode 100644 distros/ubuntu1410/zoneminder-ui-base.templates delete mode 100644 distros/ubuntu1410/zoneminder-ui-classic.install delete mode 100644 distros/ubuntu1410/zoneminder-ui-mobile.install delete mode 100644 distros/ubuntu1410/zoneminder-ui-xml.install diff --git a/distros/ubuntu1204/NEWS b/distros/ubuntu1204/NEWS deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1204/README.Debian b/distros/ubuntu1204/README.Debian deleted file mode 100644 index 2ba809fe4..000000000 --- a/distros/ubuntu1204/README.Debian +++ /dev/null @@ -1,160 +0,0 @@ -Zoneminder for Debian ---------------------- - -Initializing database ---------------------- - - pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf -OR - cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf - - echo 'grant lock tables,alter,create,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ - | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql - -Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" -accordingly. - -The following command can help to ensure that zoneminder can read its -configuration file: - - chgrp -c www-data /etc/zm/zm.conf - - -Upgrading database ------------------- - -Prior to 1.28.1 database upgrade was performed automatically. -"zoneminder" service will refuse to start with outdated database. - -Assuming that database is on "localhost" then the following command can be -used to upgrade "zm" database: - - zmupdate.pl - -Additional permissions may be required to perform upgrade: - - echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ - | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql - -The following command prints the current version of zoneminder database: - - echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ - | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm - - -Enabling service ----------------- - -By default Zoneminder service is not starting automatically and need to be -manually activated once database is configured: - -On systemd: - - sudo systemctl enable zoneminder.service - -On SysV: - - sudo update-rc.d zoneminder enable - - -Web server set-up ------------------ - -There are few manual steps to get the web interface working: - -## Apache2 - -Apache can be configured as folder "/zm" using sample .conf: - - sudo a2enconf zoneminder - -Alternatively Apache web site configuration template can be used to setup -zoneminder as "http://zoneminder": - - sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ - sudo a2ensite zoneminder.conf - -Common configuration steps for Apache2: - - sudo a2enmod cgi - sudo service apache2 reload - - -## nginx / fcgiwrap - -Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package -for binary "cgi-bin" applications: - - sudo apt-get install php5-fpm fcgiwrap - -To enable a URL alias that makes Zoneminder available from - - http://yourserver/zm - -the following line is to be added to "server" section of a web site -configuration: - - include /usr/share/doc/zoneminder/examples/nginx.conf; - -For "default" web site it would be sufficient to include the above -statement to the file - - /etc/nginx/sites-enabled/default - -To avoid problems with feeds from multiple cameras "fcgiwrap" should be -configured to start at least as many processes as there are cameras. -It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". -Systemd users may be affected by the following bug: - - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 - - -## Note: - -When Zoneminder web site is running it may be necessary to set -Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web -site configuration. - - -Changing the location for images and events -------------------------------------------- - -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. -(thanks to vagrant@freegeek.org): - -These lines in fstab could allow you to bind-mount an alternate location - - /dev/sdX1 /otherdrive ext3 defaults 0 2 - /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 - /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 - -Access to /dev/video* ---------------------- - -For cameras which require access to /dev/video*, zoneminder may need the -www-data user added to the video group in order to see those cameras: - - adduser www-data video - -Note that all web applications running on the zoneminder server will then have -access to all video devices on the system. - - -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1204/TODO.Debian b/distros/ubuntu1204/TODO.Debian deleted file mode 100644 index 9dc59613b..000000000 --- a/distros/ubuntu1204/TODO.Debian +++ /dev/null @@ -1,12 +0,0 @@ - -## Separate substantial /usr/share into its own arch-all package. - -## Decide how to handle database updates. - - * Consider possibility that database may be on another machine (#469239). - * Consider dbconfig-common? Probably not (what if database is not on localhost?). - -### Run `zmupdate.pl` from service control scripts (init.d, service) on start? - - Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). - diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog deleted file mode 100644 index b19a3f228..000000000 --- a/distros/ubuntu1204/changelog +++ /dev/null @@ -1,8 +0,0 @@ -<<<<<<< HEAD -zoneminder (1.31.0-trusty) trusty; urgency=medium - - * placeholder - - -- Isaac Connor Fri, 13 May 2016 09:45:49 -0400 -======= ->>>>>>> master diff --git a/distros/ubuntu1204/clean b/distros/ubuntu1204/clean deleted file mode 100644 index 941ef2a3a..000000000 --- a/distros/ubuntu1204/clean +++ /dev/null @@ -1,3 +0,0 @@ -.gitattributes -web/api/.gitattributes -web/api/.gitignore diff --git a/distros/ubuntu1204/compat b/distros/ubuntu1204/compat deleted file mode 100644 index ec635144f..000000000 --- a/distros/ubuntu1204/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/distros/ubuntu1204/conf/apache2/zoneminder.conf b/distros/ubuntu1204/conf/apache2/zoneminder.conf deleted file mode 100644 index 8e2957cbf..000000000 --- a/distros/ubuntu1204/conf/apache2/zoneminder.conf +++ /dev/null @@ -1,57 +0,0 @@ -# Remember to enable cgi mod (i.e. "a2enmod cgi"). -ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" - - Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch - AllowOverride All - Require all granted - - -# Order matters. This Alias must come first -Alias /zm/cache /var/cache/zoneminder/cache - - Options -Indexes +FollowSymLinks - AllowOverride None - - # Apache 2.4 - Require all granted - - - # Apache 2.2 - Order deny,allow - Allow from all - - - -Alias /zm /usr/share/zoneminder/www - - php_flag register_globals off - Options -Indexes +FollowSymLinks - - DirectoryIndex index.php - - - -# For better visibility, the following directives have been migrated from the -# default .htaccess files included with the CakePHP project. -# Parameters not set here are inherited from the parent directive above. - - RewriteEngine on - RewriteRule ^$ app/webroot/ [L] - RewriteRule (.*) app/webroot/$1 [L] - RewriteBase /zm/api - - - - RewriteEngine on - RewriteRule ^$ webroot/ [L] - RewriteRule (.*) webroot/$1 [L] - RewriteBase /zm/api - - - - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^ index.php [L] - RewriteBase /zm/api - diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control deleted file mode 100644 index e5688a421..000000000 --- a/distros/ubuntu1204/control +++ /dev/null @@ -1,158 +0,0 @@ -Source: zoneminder -Section: net -Priority: optional -Maintainer: Isaac Connor -Uploaders: Isaac Connor -Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree - ,cmake - ,libx264-dev, libmp4v2-dev - ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev - ,libavresample-dev - ,libbz2-dev - ,libgcrypt-dev - ,libcurl4-gnutls-dev - ,libgnutls-openssl-dev - ,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, - ,libmysqlclient-dev - ,libpcre3-dev - ,libpolkit-gobject-1-dev - ,libv4l-dev (>= 0.8.3) [!hurd-any] - ,libvlc-dev - ,libdate-manip-perl - ,libdbd-mysql-perl - ,libphp-serialization-perl - ,libsys-mmap-perl [!hurd-any] - ,libwww-perl - ,libdata-uuid-perl - ,libssl-dev - ,libcrypt-eksblowfish-perl - ,libdata-entropy-perl -# Unbundled (dh_linktree): - ,libjs-jquery - ,libjs-mootools -Standards-Version: 3.9.4 -Homepage: http://www.zoneminder.com/ -Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git -Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git - -Package: zoneminder -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} - ,javascript-common - ,libmp4v2-2, libx264-142 - ,libav-tools|ffmpeg - ,libdate-manip-perl - ,libdbd-mysql-perl - ,libmime-lite-perl - ,libmime-tools-perl - ,libphp-serialization-perl - ,libmodule-load-conditional-perl - ,libnet-sftp-foreign-perl -# ,libzoneminder-perl (= ${source:Version}) - ,libarchive-zip-perl - ,libdbd-mysql-perl - ,libdevice-serialport-perl - ,libimage-info-perl - ,libjson-maybexs-perl - ,libsys-mmap-perl [!hurd-any] - ,liburi-encode-perl - ,libwww-perl - ,libdatetime-perl - ,libdata-uuid-perl - ,libnumber-bytes-human-perl - ,libfile-slurp-perl - ,mysql-client | virtual-mysql-client - ,perl-modules - ,php5-mysql, php5-gd, php5-apcu, php-apc - ,policykit-1 - ,rsyslog | system-log-daemon - ,zip - ,libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl - ,libio-socket-multicast-perl, libdigest-sha-perl - ,libsys-cpu-perl, libsys-meminfo-perl - ,libssl | libssl1.0.0 - ,libcrypt-eksblowfish-perl - ,libdata-entropy-perl -Recommends: ${misc:Recommends} - ,libapache2-mod-php5 | php5-fpm - ,mysql-server | virtual-mysql-server - ,zoneminder-doc (>= ${source:Version}) -Suggests: fcgiwrap, logrotate -Description: video camera security and surveillance solution - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - -#Package: libzoneminder-perl -#Section: perl -#Architecture: all -#Multi-Arch: foreign -#Depends: ${misc:Depends}, ${perl:Depends} -# ,libarchive-zip-perl -# ,libdbd-mysql-perl -# ,libdevice-serialport-perl -# ,libimage-info-perl -# ,libjson-maybexs-perl -# ,libsys-mmap-perl [!hurd-any] -# ,liburi-encode-perl -# ,libwww-perl -#Description: ZoneMinder Perl libraries -# ZoneMinder is intended for use in single or multi-camera video security -# applications, including commercial or home CCTV, theft prevention and child -# or family member or home monitoring and other care scenarios. It -# supports capture, analysis, recording, and monitoring of video data coming -# from one or more video or network cameras attached to a Linux system. -# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom -# cameras using a variety of protocols. It is suitable for use as a home -# video security system and for commercial or professional video security -# and surveillance. It can also be integrated into a home automation system -# via X.10 or other protocols. -# . -# This package provides ZoneMinder Perl libraries; it can be used to -# write custom interfaces as well. - -Package: zoneminder-doc -Section: doc -Architecture: all -Multi-Arch: foreign -Depends: ${misc:Depends}, ${sphinxdoc:Depends} -Suggests: www-browser -Description: ZoneMinder documentation - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - . - This package provides ZoneMinder documentation in HTML format. - -Package: zoneminder-dbg -Section: debug -Priority: extra -Architecture: any -Depends: zoneminder (= ${binary:Version}), ${misc:Depends} -Description: Zoneminder -- debugging symbols - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - . - This package provides debugging symbols diff --git a/distros/ubuntu1204/copyright b/distros/ubuntu1204/copyright deleted file mode 100644 index c48025a25..000000000 --- a/distros/ubuntu1204/copyright +++ /dev/null @@ -1,174 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: ZoneMinder -Upstream-Contact: Philip Coombes -Source: https://github.com/ZoneMinder/ZoneMinder -Comment: - This package was originally debianized by matrix - on Mon, 7 Mar 2005 02:07:57 -0500. - It was re-done for submission to the Debian project by Peter Howard - on Fri, 8 Dec 2006 10:19:43 +1100 -Files-Excluded: - web/skins/*/js/jquery-* - web/tools/mootools/*-yc.js - -Files: * -Copyright: 2001-2014 Philip Coombes - 2008 Brian Rudy - 2014 Vincent Giovannone - 2013 Tim Craig - 2003-2008 Corey DeLasaux - 2001-2010 Chris Kistner -License: GPL-2+ - -Files: distros/* -Copyright: 2001-2008 Philip Coombes - 2014 Isaac Connor - 2005 Serg Oskin -License: GPL-2+ - -Files: web/skins/*/js/jquery-* -Copyright: 2010 John Resig - 2010 The Dojo Foundation -License: GPL-2 or Expat -Comment: - Dual licensed under the MIT or GPL Version 2 licenses. - http://jquery.org/license - . - Includes Sizzle.js http://sizzlejs.com/ - Released under the MIT, BSD, and GPL Licenses. - -Files: web/tools/mootools/*.js -Copyright: 2009 Marcelo Jorge Vieira (metal) - 2006-2010 Valerio Proietti (http://mad4milk.net/) -License: Expat - -Files: web/api/* -Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) -License: Expat - -Files: - cmake/Modules/CheckPrototypeDefinition*.cmake - cmake/Modules/FindGLIB2.cmake - cmake/Modules/FindPolkit.cmake - cmake/Modules/GNUInstallDirs.cmake -Copyright: - 2005-2011 Kitware, Inc. - 2010-2011 Andreas Schneider - 2009 Dario Freddi - 2008 Laurent Montel, - 2011 Nikita Krupen'ko -License: BSD-3-clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - . - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - . - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - . - * The names of Kitware, Inc., the Insight Consortium, or the names of - any consortium members, or of any contributors, may not be used to - endorse or promote products derived from this software without - specific prior written permission. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Files: cmake/Modules/FindPerlModules.cmake -Copyright: 2012 Iowa State University -License: Boost-1.0 - Boost Software License - Version 1.0 - August 17th, 2003 - . - Permission is hereby granted, free of charge, to any person or organization - obtaining a copy of the software and accompanying documentation covered by - this license (the "Software") to use, reproduce, display, distribute, - execute, and transmit the Software, and to prepare derivative works of the - Software, and to permit third-parties to whom the Software is furnished to - do so, all subject to the following: - . - The copyright notices in the Software and this entire statement, including - the above license grant, this restriction and the following disclaimer, - must be included in all copies of the Software, in whole or in part, and - all derivative works of the Software, unless such copies or derivative - works are solely in the form of machine-executable object code generated by - a source language processor. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - -Files: debian/* -Copyright: 2015 Dmitry Smirnov - 2007-2014 Peter Howard - 2010-2012 Vagrant Cascadian - 2001-2008 Philip Coombes -License: GPL-2+ - -License: Expat - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - . - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - -License: GPL-2+ - This package is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - . - You should have received a copy of the GNU General Public - License along with this package; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - . - The complete text of the GNU General Public License version 2 - can be found in "/usr/share/common-licenses/GPL-2". - -License: GPL-2 - This package is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; version 2 of the License. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - . - You should have received a copy of the GNU General Public - License along with this package; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - . - The complete text of the GNU General Public License version 2 - can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/ubuntu1204/examples/nginx.conf b/distros/ubuntu1204/examples/nginx.conf deleted file mode 100644 index 5636ca3e1..000000000 --- a/distros/ubuntu1204/examples/nginx.conf +++ /dev/null @@ -1,32 +0,0 @@ -location /zm/cgi-bin { - gzip off; - alias /usr/lib/zoneminder/cgi-bin; - - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $request_filename; - fastcgi_pass unix:/var/run/fcgiwrap.socket; -} - -location /zm { -# if ($scheme ~ ^http:){ -# rewrite ^(.*)$ https://$host$1 permanent; -# } - - gzip off; - alias /usr/share/zoneminder/www; - index index.php; - - location ~ \.php$ { - if (!-f $request_filename) { return 404; } - expires epoch; - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $request_filename; - fastcgi_index index.php; - fastcgi_pass unix:/var/run/php5-fpm.sock; - } - - location ~ \.(jpg|jpeg|gif|png|ico)$ { - access_log off; - expires 33d; - } -} diff --git a/distros/ubuntu1204/gbp.conf b/distros/ubuntu1204/gbp.conf deleted file mode 100644 index 4608913d9..000000000 --- a/distros/ubuntu1204/gbp.conf +++ /dev/null @@ -1,7 +0,0 @@ - -[dch] -id-length = 0 - -[import-orig] -pristine-tar = False -merge = False diff --git a/distros/ubuntu1204/libzoneminder-perl.install b/distros/ubuntu1204/libzoneminder-perl.install deleted file mode 100644 index 67191d9cf..000000000 --- a/distros/ubuntu1204/libzoneminder-perl.install +++ /dev/null @@ -1,2 +0,0 @@ -usr/share/man/man3 -usr/share/perl5 diff --git a/distros/ubuntu1204/patches/series b/distros/ubuntu1204/patches/series deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules deleted file mode 100755 index 657697fcf..000000000 --- a/distros/ubuntu1204/rules +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -export DEB_BUILD_MAINT_OPTIONS = hardening=+all -export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed - -ifeq ($(DEB_BUILD_ARCH_OS),hurd) -ARGS:= -DZM_NO_MMAP=ON -endif - -%: - dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ - --with sphinxdoc,apache2,linktree - -override_dh_auto_configure: - dh_auto_configure -- $(ARGS) \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DZM_CONFIG_DIR="/etc/zm" \ - -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ - -DZM_RUNDIR="/var/run/zm" \ - -DZM_SOCKDIR="/var/run/zm" \ - -DZM_TMPDIR="/tmp/zm" \ - -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ - -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ - -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ - -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" - -override_dh_clean: - dh_clean $(MANPAGES1) - $(RM) -r docs/_build docs/installationguide - -build-indep: - #$(MAKE) -C docs text - $(MAKE) -C docs html - -MANPAGES1 = dbuild/scripts/zmupdate.pl.1 -$(MANPAGES1): - # generate man page(s): - pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@ - -## reproducible build: -LAST_CHANGE=$(shell dpkg-parsechangelog -S Date) -BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)") -override_dh_installman: $(MANPAGES1) - $(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\"" - dh_installman --language=C $(MANPAGES1) - -override_dh_auto_install: - dh_auto_install --destdir=$(CURDIR)/debian/tmp - # remove worthless files: - $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist - $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in - # remove empty directories: - find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n' - # remove extra-license-file: - $(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt - -override_dh_fixperms: - dh_fixperms - # - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf - chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf - -override_dh_installinit: - dh_installinit --no-start - -override_dh_apache2: - dh_apache2 --noenable - -override_dh_strip: - [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ - && dh_strip --dbg-package=zoneminder-dbg \ - || dh_strip - -#%: -# dh $@ --parallel --buildsystem=autoconf --with autoreconf -# -#override_dh_auto_configure: -# dh_auto_configure -- \ -# --sysconfdir=/etc/zm \ -# --with-mysql=/usr \ -# --with-webdir=/usr/share/zoneminder \ -# --with-ffmpeg=/usr \ -# --with-cgidir=/usr/lib/cgi-bin \ -# --with-webuser=www-data \ -# --with-webgroup=www-data \ -# --enable-mmap=yes diff --git a/distros/ubuntu1204/source/format b/distros/ubuntu1204/source/format deleted file mode 100644 index 163aaf8d8..000000000 --- a/distros/ubuntu1204/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/distros/ubuntu1204/source/lintian-overrides b/distros/ubuntu1204/source/lintian-overrides deleted file mode 100644 index 3669e5de8..000000000 --- a/distros/ubuntu1204/source/lintian-overrides +++ /dev/null @@ -1,9 +0,0 @@ -## Actually sources are there: "*-nc.js". -source-is-missing web/tools/mootools/mootools-*-yc.js - -## We're using "libjs-jquery" instead. -source-is-missing web/skins/*/js/jquery-1.4.2.min.js - -## Acknowledged, will repack eventually. -source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js -source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu1204/watch b/distros/ubuntu1204/watch deleted file mode 100644 index 7ee690edb..000000000 --- a/distros/ubuntu1204/watch +++ /dev/null @@ -1,7 +0,0 @@ -version=3 - -opts=\ -repacksuffix=+dfsg,\ -dversionmangle=s{\+dfsg\d*}{},\ - https://github.com/ZoneMinder/ZoneMinder/releases \ - .*/ZoneMinder/archive/v(.*).tar.gz diff --git a/distros/ubuntu1204/zoneminder-doc.doc-base b/distros/ubuntu1204/zoneminder-doc.doc-base deleted file mode 100644 index c43dc4336..000000000 --- a/distros/ubuntu1204/zoneminder-doc.doc-base +++ /dev/null @@ -1,8 +0,0 @@ -Document: zoneminder-doc -Title: Zoneminder documentation -Abstract: This document describes how to use Zoneminder. -Section: System/Administration - -Format: HTML -Index: /usr/share/doc/zoneminder-doc/html/index.html -Files: /usr/share/doc/zoneminder-doc/html/* diff --git a/distros/ubuntu1204/zoneminder-doc.install b/distros/ubuntu1204/zoneminder-doc.install deleted file mode 100644 index c19bc6f3a..000000000 --- a/distros/ubuntu1204/zoneminder-doc.install +++ /dev/null @@ -1 +0,0 @@ -docs/_build/html usr/share/doc/zoneminder-doc/ diff --git a/distros/ubuntu1204/zoneminder-doc.links b/distros/ubuntu1204/zoneminder-doc.links deleted file mode 100644 index cc09f6462..000000000 --- a/distros/ubuntu1204/zoneminder-doc.links +++ /dev/null @@ -1,2 +0,0 @@ -## Convenience symlink: -/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html diff --git a/distros/ubuntu1204/zoneminder.apache2 b/distros/ubuntu1204/zoneminder.apache2 deleted file mode 100644 index 466144fa7..000000000 --- a/distros/ubuntu1204/zoneminder.apache2 +++ /dev/null @@ -1 +0,0 @@ -conf debian/conf/apache2/zoneminder.conf nginx diff --git a/distros/ubuntu1204/zoneminder.bug-presubj b/distros/ubuntu1204/zoneminder.bug-presubj deleted file mode 100644 index 990fc1d94..000000000 --- a/distros/ubuntu1204/zoneminder.bug-presubj +++ /dev/null @@ -1,5 +0,0 @@ -Unless bug is specific to Debian please consider reporting it directly to -upstream developer(s): - - https://github.com/ZoneMinder/ZoneMinder/issues - diff --git a/distros/ubuntu1204/zoneminder.dirs b/distros/ubuntu1204/zoneminder.dirs deleted file mode 100644 index 79b2c66af..000000000 --- a/distros/ubuntu1204/zoneminder.dirs +++ /dev/null @@ -1,9 +0,0 @@ -var/log/zm -var/lib/zm -var/cache/zoneminder/events -var/cache/zoneminder/images -var/cache/zoneminder/temp -var/cache/zoneminder/cache -usr/share/zoneminder/db -etc/zm -etc/zm/conf.d diff --git a/distros/ubuntu1204/zoneminder.docs b/distros/ubuntu1204/zoneminder.docs deleted file mode 100644 index b43bf86b5..000000000 --- a/distros/ubuntu1204/zoneminder.docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/distros/ubuntu1204/zoneminder.examples b/distros/ubuntu1204/zoneminder.examples deleted file mode 100644 index 3b8befe7b..000000000 --- a/distros/ubuntu1204/zoneminder.examples +++ /dev/null @@ -1,2 +0,0 @@ -debian/examples/* -dbuild/misc/apache.conf diff --git a/distros/ubuntu1204/zoneminder.init b/distros/ubuntu1204/zoneminder.init deleted file mode 100644 index de552848b..000000000 --- a/distros/ubuntu1204/zoneminder.init +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: zoneminder -# Required-Start: $network $remote_fs $syslog -# Required-Stop: $network $remote_fs $syslog -# Should-Start: mysql -# Should-Stop: mysql -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Control ZoneMinder as a Service -# Description: ZoneMinder CCTV recording and surveillance system -### END INIT INFO -# chkconfig: 2345 20 20 - -# Source function library. -. /lib/lsb/init-functions - -prog=ZoneMinder -ZM_PATH_BIN="/usr/bin" -RUNDIR="/var/run/zm" -TMPDIR="/tmp/zm" -command="$ZM_PATH_BIN/zmpkg.pl" - -start() { - echo -n "Starting $prog: " - # Wait for mysqld to start. Continue if it takes too long. - count=0 - while [ ! -e /var/run/mysqld/mysqld.sock ] && [ $count -lt 60 ] - do - sleep 1 - count=$((count+1)) - done - export TZ=:/etc/localtime - mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" - mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" - $command start - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && touch /var/lock/zm - return $RETVAL -} -stop() { - echo -n "Stopping $prog: " - # - # Why is this status check being done? - # as $command stop returns 1 if zoneminder - # is stopped, which will result in - # this returning 1, which will stuff - # dpkg when it tries to stop zoneminder before - # uninstalling . . . - # - result=`$command status` - if [ ! "$result" = "running" ]; then - echo "Zoneminder already stopped" - echo - RETVAL=0 - else - $command stop - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && rm -f /var/lock/zm - fi -} -status() { - result=`$command status` - if [ "$result" = "running" ]; then - echo "ZoneMinder is running" - RETVAL=0 - else - echo "ZoneMinder is stopped" - RETVAL=1 - fi -} - -case "$1" in -'start') - start - ;; -'stop') - stop - ;; -'restart' | 'force-reload') - stop - start - ;; -'status') - status - ;; -*) - echo "Usage: $0 { start | stop | restart | status }" - RETVAL=1 - ;; -esac -exit $RETVAL diff --git a/distros/ubuntu1204/zoneminder.install b/distros/ubuntu1204/zoneminder.install deleted file mode 100644 index 67b135de5..000000000 --- a/distros/ubuntu1204/zoneminder.install +++ /dev/null @@ -1,11 +0,0 @@ -etc/zm/zm.conf -etc/zm/conf.d/* -usr/bin -usr/lib/zoneminder -usr/share/polkit-1 -usr/share/zoneminder/db -usr/share/zoneminder/www - -# libzoneminder-perl files: -usr/share/man/man3 -usr/share/perl5 diff --git a/distros/ubuntu1204/zoneminder.links b/distros/ubuntu1204/zoneminder.links deleted file mode 100644 index 373548919..000000000 --- a/distros/ubuntu1204/zoneminder.links +++ /dev/null @@ -1 +0,0 @@ -/tmp/zm /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.linktrees b/distros/ubuntu1204/zoneminder.linktrees deleted file mode 100644 index 2e843bbf1..000000000 --- a/distros/ubuntu1204/zoneminder.linktrees +++ /dev/null @@ -1,14 +0,0 @@ -## cakephp -#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake - -## libjs-mootools -replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js -replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js -replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js -replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js -replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js -replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js - -## libjs-jquery -replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js -replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu1204/zoneminder.lintian-overrides b/distros/ubuntu1204/zoneminder.lintian-overrides deleted file mode 100644 index 90be05a9f..000000000 --- a/distros/ubuntu1204/zoneminder.lintian-overrides +++ /dev/null @@ -1,14 +0,0 @@ -# Depends: policykit-1 -unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec - -# Intentionally not others-readable, #637685. -non-standard-file-perm etc/zm/zm.conf 0640 != 0644 - -# Bundled Cake PHP framework, not intended for direct execution: -script-not-executable usr/share/zoneminder/www/api/* - -# Annoying but seems to be too much troubles to fix; should be fixed upstream: -script-with-language-extension usr/bin/*.pl - -# dh-linktree: -package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/* diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/ubuntu1204/zoneminder.logrotate deleted file mode 100644 index 3195d0fb2..000000000 --- a/distros/ubuntu1204/zoneminder.logrotate +++ /dev/null @@ -1,13 +0,0 @@ -/var/log/zm/*log { - missingok - notifempty - sharedscripts - delaycompress - compress - postrotate - /usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || : - endscript - daily - rotate 7 - maxage 7 -} diff --git a/distros/ubuntu1204/zoneminder.maintscript b/distros/ubuntu1204/zoneminder.maintscript deleted file mode 100644 index 3aa20b3a0..000000000 --- a/distros/ubuntu1204/zoneminder.maintscript +++ /dev/null @@ -1 +0,0 @@ -rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/ubuntu1204/zoneminder.manpages b/distros/ubuntu1204/zoneminder.manpages deleted file mode 100644 index d2053d688..000000000 --- a/distros/ubuntu1204/zoneminder.manpages +++ /dev/null @@ -1 +0,0 @@ -docs/_build/man/*.1 diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst deleted file mode 100644 index 603786ff6..000000000 --- a/distros/ubuntu1204/zoneminder.postinst +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/sh - -set -e - -if [ "$1" = "configure" ]; then - - . /etc/zm/zm.conf - for i in /etc/zm/conf.d/*.conf; do - . $i - done; - - - # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group - chown www-data:root /var/log/zm - chown www-data:www-data /var/lib/zm - if [ -z "$2" ]; then - chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* - fi - - # Do this every time the package is installed or upgraded - # Ensure zoneminder is stopped - invoke-rc.d zoneminder stop || true - - if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/etc/init.d/mysql" ]; then - # - # Get mysql started if it isn't - # - if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then - invoke-rc.d mysql start - fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - if [ $? -ne 0 ]; then - echo "Error creating db." - exit 1; - fi - # This creates the user. - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - else - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - - zmupdate.pl --nointeractive - zmupdate.pl --nointeractive -f - - # Add any new PTZ control configurations to the database (will not overwrite) - zmcamtool.pl --import >/dev/null 2>&1 - - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' - fi - else - echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" - fi - echo "Done Updating, starting ZoneMinder" - invoke-rc.d zoneminder start || true -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.postrm b/distros/ubuntu1204/zoneminder.postrm deleted file mode 100644 index ba2066c8d..000000000 --- a/distros/ubuntu1204/zoneminder.postrm +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/sh - -set -e - -if [ "$1" = "purge" ]; then - echo " -Reminder: to completely remove \"zoneminder\" it may be necessary - * to delete database using the following sample command: - sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm - * to delete remaining data files in "/var/cache/zoneminder". -" -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.preinst b/distros/ubuntu1204/zoneminder.preinst deleted file mode 100644 index 3f75a1b3e..000000000 --- a/distros/ubuntu1204/zoneminder.preinst +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -set -e - -## Remove obsolete symlink which is in the way of dh_apache2: -ol="/etc/apache2/conf-available/zoneminder.conf" -if [ -h "${ol}" ]; then - [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" -fi - -abort=false -if [ -h /usr/share/zoneminder/www/events ]; then - l=$(readlink /usr/share/zoneminder/www/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -h /usr/share/zoneminder/www/images ]; then - l=$(readlink /usr/share/zoneminder/www/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.service b/distros/ubuntu1204/zoneminder.service deleted file mode 100644 index ac719b733..000000000 --- a/distros/ubuntu1204/zoneminder.service +++ /dev/null @@ -1,23 +0,0 @@ -# ZoneMinder systemd unit file -# This file is intended to work with Debian distributions - -[Unit] -Description=ZoneMinder CCTV recording and surveillance system -After=network.target mysql.service -# Remarked out so that it will start ZM on machines that don't have mysql installed -#Requires=mysql.service - -[Service] -#User=www-data -Type=forking -ExecStart=/usr/bin/zmpkg.pl start -ExecReload=/usr/bin/zmpkg.pl restart -ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid -Restart=always -RestartSec=10 -Environment=TZ=:/etc/localtime -TimeoutSec=600 - -[Install] -WantedBy=multi-user.target diff --git a/distros/ubuntu1204/zoneminder.tmpfile b/distros/ubuntu1204/zoneminder.tmpfile deleted file mode 100644 index 017955900..000000000 --- a/distros/ubuntu1204/zoneminder.tmpfile +++ /dev/null @@ -1,3 +0,0 @@ -d /var/run/zm 0755 www-data www-data -d /tmp/zm 0755 www-data www-data -d /var/tmp/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/README.Debian b/distros/ubuntu1410/README.Debian deleted file mode 100644 index a49b6be72..000000000 --- a/distros/ubuntu1410/README.Debian +++ /dev/null @@ -1,51 +0,0 @@ -zoneminder for Debian ---------------------- - -There is one manual step to get the web interface working. -You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, -then reload the apache config (i.e. /etc/init.d/apache2 reload) - -Changing the location for images and events -------------------------------------------- - -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. - -These lines would mount /dev/sdX1 to /video_storage, and then 'link' /video_storage -to the locations that ZoneMinder expects them to be at. - - /dev/sdX1 /video_storage ext4 defaults 0 2 - /video_storage/zoneminder/images /var/cache/zoneminder/images none bind 0 2 - /video_storage/zoneminder/events /var/cache/zoneminder/events none bind 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext4 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext4 defaults 0 2 - - - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 - -Access to /dev/video* ---------------------- - -For cameras which require access to /dev/video*, zoneminder may need the -www-data user added to the video group in order to see those cameras: - - adduser www-data video - -Note that all web applications running on the zoneminder server will then have -access to all video devices on the system. - - -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1410/apache.conf b/distros/ubuntu1410/apache.conf deleted file mode 100644 index 92a2b6414..000000000 --- a/distros/ubuntu1410/apache.conf +++ /dev/null @@ -1,9 +0,0 @@ -Alias /zm /usr/share/zoneminder - - - php_flag register_globals off - Options Indexes FollowSymLinks - - DirectoryIndex index.php - - diff --git a/distros/ubuntu1410/changelog b/distros/ubuntu1410/changelog deleted file mode 100644 index 55e3d17b0..000000000 --- a/distros/ubuntu1410/changelog +++ /dev/null @@ -1,323 +0,0 @@ -zoneminder (1.30.2-trusty-2016033001) trusty; urgency=medium - - * merge master - - -- Isaac Connor Wed, 30 Mar 2016 14:09:48 -0400 - -zoneminder (1.30.2-trusty-2016032901) trusty; urgency=medium - - * filter fixes, merge options rework by Kyle - - -- Isaac Connor Tue, 29 Mar 2016 12:27:57 -0400 - -zoneminder (1.30.2-trusty-2016030702) trusty; urgency=medium - - * - - -- Isaac Connor Mon, 07 Mar 2016 22:14:03 -0500 - -zoneminder (1.30.2-trusty-2016030701) trusty; urgency=medium - - * merge master. with telemetry - - -- Isaac Connor Mon, 07 Mar 2016 21:47:53 -0500 - -zoneminder (1.30.2-trusty-2016022101) trusty; urgency=medium - - * merge zmtrigger fix - - -- Isaac Connor Mon, 22 Feb 2016 09:15:53 -0500 - -zoneminder (1.30.2-trusty-2016021901) trusty; urgency=medium - - * zmtrigger improvements - - -- Isaac Connor Fri, 19 Feb 2016 11:09:57 -0500 - -zoneminder (1.30.2-trusty-2016021701) trusty; urgency=medium - - * printout id, and ip address when failing to connect - - -- Isaac Connor Wed, 17 Feb 2016 09:40:49 -0500 - -zoneminder (1.30.2-trusty-2016021001) trusty; urgency=medium - - * - - -- Isaac Connor Wed, 10 Feb 2016 13:06:09 -0500 - -zoneminder (1.29.111-trusty-2016020101) trusty; urgency=medium - - * Fix video download and use of Storage Areas - - -- Isaac Connor Mon, 01 Feb 2016 13:42:06 -0500 - -zoneminder (1.29.111-trusty-2016011401) trusty; urgency=medium - - * fix timeline view for storageareas - - -- Isaac Connor Thu, 14 Jan 2016 14:03:41 -0500 - -zoneminder (1.29.111-trusty-2016010801) trusty; urgency=medium - - * Add better debug and skip when event links are not just digits. Merge multi-server stuff from master. - - -- Isaac Connor Fri, 08 Jan 2016 10:37:16 -0500 - -zoneminder (1.29.111-trusty-2016010401) trusty; urgency=medium - - * include fix to rotate image dimensions when applying a rotation - - -- Isaac Connor Mon, 04 Jan 2016 13:24:42 -0500 - -zoneminder (1.29.111-trusty-2016010101) trusty; urgency=medium - - * fix logging with multi-server - - -- Isaac Connor Fri, 01 Jan 2016 17:11:09 -0500 - -zoneminder (1.29.111-trusty-2015123101) trusty; urgency=medium - - * Add log filtering from multi-server - - -- Isaac Connor Thu, 31 Dec 2015 10:18:03 -0500 - -zoneminder (1.29.109-trusty-2015122401) trusty; urgency=medium - - * fix delete events not in database in zmaudit.pl - - -- Isaac Connor Thu, 24 Dec 2015 12:38:05 -0500 - -zoneminder (1.29.109-trusty-2015122301) trusty; urgency=medium - - * todays release - - -- Isaac Connor Wed, 23 Dec 2015 09:33:46 -0500 - -zoneminder (1.29.109-trusty-2015122202) trusty; urgency=medium - - * more object work and zmaudit - - -- Isaac Connor Tue, 22 Dec 2015 13:13:56 -0500 - -zoneminder (1.29.109-trusty-2015122201) trusty; urgency=medium - - * merge multi-server, and master stuff, start work on zmaudit storage areas support - - -- Isaac Connor Tue, 22 Dec 2015 11:11:44 -0500 - -zoneminder (1.29.109-trusty-2015122103) trusty; urgency=medium - - * Fixes - - -- Isaac Connor Mon, 21 Dec 2015 15:20:15 -0500 - -zoneminder (1.29.109-trusty-2015122102) trusty; urgency=medium - - * fix deleting events - - -- Isaac Connor Mon, 21 Dec 2015 14:49:12 -0500 - -zoneminder (1.29.109-trusty-2015122101) trusty; urgency=medium - - * Make zmfilter work. - - -- Isaac Connor Mon, 21 Dec 2015 12:32:27 -0500 - -zoneminder (1.29.109-trusty-2015121803) trusty; urgency=medium - - * merge zmvideo improvements - - -- Isaac Connor Fri, 18 Dec 2015 14:17:56 -0500 - -zoneminder (1.29.109-trusty-2015121802) trusty; urgency=medium - - * Add some missing files to the autoconf build - - -- Isaac Connor Fri, 18 Dec 2015 12:14:08 -0500 - -zoneminder (1.29.109-trusty-2015121801) trusty; urgency=medium - - * - - -- Isaac Connor Fri, 18 Dec 2015 11:05:58 -0500 - -zoneminder (1.29.109-trusty-2015121701) trusty; urgency=medium - - * Merge master + better zmvideo - - -- Isaac Connor Thu, 17 Dec 2015 15:11:18 -0500 - -zoneminder (1.29.0-trusty-2015112301) trusty; urgency=medium - - * apply fix for zms crash - - -- Isaac Connor Mon, 23 Nov 2015 10:47:49 -0500 - -zoneminder (1.29.0-trusty-2015110601) trusty; urgency=medium - - * add a FIONREAD test on timeout - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015072201) trusty; urgency=medium - - * add AnalysisFPS - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015071601) trusty; urgency=medium - - * Merge master and zmtrigger - - -- Isaac Connor Thu, 16 Jul 2015 13:15:58 -0400 - -zoneminder (1.29.0-trusty-38) trusty; urgency=medium - - * Merge master - - -- Isaac Connor Tue, 14 Jul 2015 10:15:00 -0400 - -zoneminder (1.29.0-trusty-37) trusty; urgency=medium - - * merge master api stuff, set sleep after failure to capture to 5000 instead of 5000000 - - -- Isaac Connor Fri, 19 Jun 2015 09:59:54 -0400 - -zoneminder (1.29.0-trusty-36) trusty; urgency=medium - - * Detect select interuption when no action on our fd - - -- Isaac Connor Thu, 28 May 2015 09:35:59 -0400 - -zoneminder (1.29.0-trusty-35) trusty; urgency=medium - - * better logging - - -- Isaac Connor Fri, 22 May 2015 15:30:42 -0400 - -zoneminder (1.29.0-trusty-34) trusty; urgency=medium - - * Faster shutdown and changes to ReadData - - -- Isaac Connor Thu, 21 May 2015 15:54:47 -0400 - -zoneminder (1.29.0-trusty-33) trusty; urgency=medium - - * update zmaudit some more - - -- Isaac Connor Thu, 14 May 2015 13:44:35 -0400 - -zoneminder (1.29.0-trusty-32) trusty; urgency=medium - - * merge zmaudit_updates1 - - -- Isaac Connor Wed, 13 May 2015 15:51:56 -0400 - -zoneminder (1.29.0-trusty-31) trusty; urgency=medium - - * Merge some fixes and zmaudit improvements - - -- Isaac Connor Wed, 13 May 2015 15:16:19 -0400 - -zoneminder (1.29.0-trusty-27) trusty; urgency=medium - - * fflush logs, merge master. - - -- Isaac Connor Mon, 30 Mar 2015 19:28:05 -0400 - -zoneminder (1.29.0-vivid-26) vivid; urgency=medium - - * logging improvements, merge RedData changes - - -- Isaac Connor Wed, 04 Mar 2015 16:39:19 -0500 - -zoneminder (1.29.0-vivid-25) vivid; urgency=medium - - * some change to ReadData - - -- Isaac Connor Mon, 02 Mar 2015 12:57:16 -0500 - -zoneminder (1.29.0-utopic-24) utopic; urgency=medium - - * merge local_raw - - -- Isaac Connor Mon, 23 Feb 2015 17:49:36 -0500 - -zoneminder (1.29.0-trusty-23) trusty; urgency=medium - - * more onvif merges, fix to zmfilter - - -- Isaac Connor Sat, 21 Feb 2015 16:17:01 -0500 - -zoneminder (1.29.0-trusty-22) trusty; urgency=medium - - * updates from master: merge onvif, default to classic if skin not defined. - - -- Isaac Connor Thu, 19 Feb 2015 18:14:58 -0500 - -zoneminder (1.29.0-utopic-21) utopic; urgency=medium - - * updates from master, improve monitor probing and support TRENDnet cameras - - -- Isaac Connor Tue, 17 Feb 2015 14:18:52 -0500 - -zoneminder (1.29.0-wheezy-20) wheezy; urgency=medium - - * zmaudit.pl improvements - double check db before deleting fs event - - -- Isaac Connor Wed, 04 Feb 2015 11:09:22 -0500 - -zoneminder (1.29.0-utopic-18) utopic; urgency=medium - - * RTSP Timeout fixes - - -- Isaac Connor Wed, 28 Jan 2015 13:49:16 -0500 - -zoneminder (1.29.0-utopic-17) utopic; urgency=medium - - * Merge master, use new split-up debian build - - -- Isaac Connor Mon, 26 Jan 2015 11:21:07 -0500 - -zoneminder (1.28.0+nmu1) testing; urgency=medium - - * Non-maintainer upload - * Split the debian package into several packages - * Switch to native source format - - -- Emmanuel Papin Thu, 15 Jan 2015 20:00:08 +0100 - -zoneminder (1.28.0-0.2) testing; urgency=medium - - * Non-maintainer upload. - * Upstream release for debian jessie - * Package dependencies updated - * debhelper version upgraded - * Standards-Version upgraded - * Use debhelper commands instead of standard commands - * Install man pages in /usr/share/man (patch added) - * Switch to quilt - * Switch to systemd - * Some lintian fixes - - -- Emmanuel Papin Wed, 26 Nov 2014 00:26:01 +0100 - -zoneminder (1.28.0-0.1) stable; urgency=medium - - * Release - - -- Isaac Connor Fri, 17 Oct 2014 09:27:22 -0400 - -zoneminder (1.27.99+1-testing-SNAPSHOT2014072901) testing; urgency=medium - - * improve error messages - * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates - - -- Isaac Connor Tue, 29 Jul 2014 14:50:20 -0400 - -zoneminder (1.27.0+1-testing-v4ltomonitor-1) testing; urgency=high - - * Snapshot release - - - -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 diff --git a/distros/ubuntu1410/compat b/distros/ubuntu1410/compat deleted file mode 100644 index ec635144f..000000000 --- a/distros/ubuntu1410/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control deleted file mode 100644 index 5a873f133..000000000 --- a/distros/ubuntu1410/control +++ /dev/null @@ -1,122 +0,0 @@ -Source: zoneminder -Section: net -Priority: optional -Maintainer: Isaac Connor -Build-Depends: debhelper (>= 9), po-debconf (>= 1.0), autoconf, automake, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, libavdevice-dev, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev | libgcrypt20-dev, libpolkit-gobject-1-dev, libdbi-perl, libnet-sftp-foreign-perl, libexpect-perl, libmime-tools-perl, libx264-dev, libmp4v2-dev, libpcre3-dev -Standards-Version: 3.9.6 - -Package: zoneminder -Section: metapackages -Architecture: all -Depends: ${misc:Depends}, - libzoneminder-perl (>= ${source:Version}), - zoneminder-database (>= ${source:Version}), - zoneminder-core (>= ${binary:Version}), - zoneminder-ui-base (>= ${source:Version}), - zoneminder-ui-classic (>= ${source:Version}), - zoneminder-ui-mobile (>= ${source:Version}), - zoneminder-ui-xml (>= ${source:Version}) -Description: Video camera security and surveillance solution (metapackage) - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - -Package: libzoneminder-perl -Section: perl -Architecture: all -Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl, - libsys-mmap-perl, liburi-encode-perl, libwww-perl -Description: Perl libraries for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the libraries for the perl scripts, it can be used to - write custom interfaces as well. - -Package: zoneminder-database -Section: database -Architecture: all -Depends: ${misc:Depends}, debconf, dbconfig-common, - mysql-client | mariadb-client -Recommends: mysql-server | mariadb-server -Description: Database management package for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the sql files and maintenance scripts to perform all the - database operations (installation, upgrade or removal) on a local or a remote - server. - -Package: zoneminder-core -Section: video -Architecture: any -Depends: libzoneminder-perl (= ${source:Version}), - zoneminder-database (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, - ${perl:Depends}, libarchive-tar-perl, libarchive-zip-perl, libdate-manip-perl, - libdbi-perl, libmodule-load-conditional-perl, libmime-lite-perl, - libmime-tools-perl, libnet-sftp-foreign-perl, libphp-serialization-perl, - debconf, ffmpeg | libav-tools, rsyslog | system-log-daemon, zip, - policykit-1, apache2, libmp4v2-2, libpcre++0 -Description: Core binaries and perl scripts for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the executable compiled binaries which do the main video - processing work and the perl scripts which perform helper and/or external - interface tasks. - -Package: zoneminder-core-dbg -Priority: extra -Section: debug -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${misc:Depends} -Description: Debugging symbols for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the debugging symbols for the executable compiled - binaries. - -Package: zoneminder-ui-base -Section: web -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends}, - ${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, - php5, php5-mysql | php5-mysqlnd, php5-gd -Description: Essential files for ZoneMinder's web user interface - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the essential web files and maintenance scripts to set up - a basic web environment. - -Package: zoneminder-ui-classic -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Classic web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the classic web user interface. - -Package: zoneminder-ui-mobile -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Mobile web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the web user interface for mobile devices. - -Package: zoneminder-ui-xml -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: XML interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides a XML interface mainly intended for use with the eyeZm - iPhone Application, but can be used with any other custom programs as well. diff --git a/distros/ubuntu1410/copyright b/distros/ubuntu1410/copyright deleted file mode 100644 index a177502a0..000000000 --- a/distros/ubuntu1410/copyright +++ /dev/null @@ -1,22 +0,0 @@ -Copyright: - -Copyright 2002 Philip Coombes - -License: - -This package is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. - -This package is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public -License along with this package; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -On Debian GNU/Linux systems, the text of the GPL can be found in -/usr/share/common-licenses/GPL. diff --git a/distros/ubuntu1410/docs b/distros/ubuntu1410/docs deleted file mode 100644 index b43bf86b5..000000000 --- a/distros/ubuntu1410/docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/distros/ubuntu1410/libzoneminder-perl.install b/distros/ubuntu1410/libzoneminder-perl.install deleted file mode 100644 index 792ffc15e..000000000 --- a/distros/ubuntu1410/libzoneminder-perl.install +++ /dev/null @@ -1,4 +0,0 @@ -usr/share/perl5/ZoneMinder -usr/share/perl5/ZoneMinder.pm -debian/tmp/usr/share/man/man3/ZoneMinder.3pm -debian/tmp/usr/share/man/man3/ZoneMinder::* diff --git a/distros/ubuntu1410/patches/series b/distros/ubuntu1410/patches/series deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1410/po/POTFILES.in b/distros/ubuntu1410/po/POTFILES.in deleted file mode 100644 index 5b155907e..000000000 --- a/distros/ubuntu1410/po/POTFILES.in +++ /dev/null @@ -1,3 +0,0 @@ -[type: gettext/rfc822deb] zoneminder-core.templates -[type: gettext/rfc822deb] zoneminder-database.templates -[type: gettext/rfc822deb] zoneminder-ui-base.templates diff --git a/distros/ubuntu1410/po/fr.po b/distros/ubuntu1410/po/fr.po deleted file mode 100644 index 85ced7fd2..000000000 --- a/distros/ubuntu1410/po/fr.po +++ /dev/null @@ -1,252 +0,0 @@ -# debconf french translation file for ZoneMinder. -# Copyright (C) 2001-2008 Philip Coombes -# This file is distributed under the same license as the zoneminder package. -# First author: Emmanuel Papin , 2014. -# -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: 2014-12-07 00:40+0100\n" -"Last-Translator: Emmanuel Papin \n" -"Language-Team: French \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "Supprimer ce répertoire non vide ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" -"Une purge du paquet ZoneMinder est en cours mais le répertoire '/var/cache/" -"zoneminder' n'est pas vide et sera donc conservé." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" -"Veuillez considérer que ce répertoire est conçu pour contenir des données " -"résultants de la détection d'événements. Par conséquent, des preuves " -"pourraient être perdues !" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" -"Si vous n'êtes pas sûr de votre décision, veuillez conserver ce répertoire " -"et effectuer une vérification manuelle." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "Supression confirmée ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" -"Vous avez autorisé la suppression du répertoire '/var/cache/zoneminder' bien " -"qu'il puisse contenir des données critiques." - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "local" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "distant" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "Emplacement de la base de donnée :" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" -"Un serveur de base de données est requis pour ZoneMinder. La base de donnée " -"peut être installée localement ou à distance sur une machine de votre réseau." - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" -"Si vous choisissez un emplacement distant, vous devrez sélectionner la " -"méthode de connexion 'tcp/ip' et entrer le nom réseau ou l'adresse ip de la " -"machine distante dans les écrans de configuration suivants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "Aucun serveur local de base de données n'est disponible :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" -"Actuellement ZoneMinder supporte les serveurs de base de données mysql et " -"mariadb mais aucun d'entre eux n'est installé sur cette machine." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" -"Afin de compléter l'installation de ZoneMinder, après la fermeture de cet " -"assitant, veuillez installer un serveur de base de données compatible et " -"ensuite redémarrez l'assistant en invoquant :" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "$ sudo dpkg-reconfigure zoneminder" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "Les serveurs de base de données distants ne sont pas autorisés :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" -"La configuration actuelle de dbconfig-common ne permet pas l'installation de " -"bases de données sur des serveurs distants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" -"Afin de reconfigurer dbconfig-common, veuillez invoquer la commande suivante " -"après la fermeture de cet assitant :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "$ sudo dpkg-reconfigure dbconfig-common" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" -"Ensuite, pour compléter l'installation de ZoneMinder, veuillez redémarrer " -"cet assistant en invoquant :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "Nouveau mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "Veuillez entrer le mot de passe du compte administrateur par défaut." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" -"Bien que cela ne soit pas obligatoire, il est fortement recommandé de " -"fournir un mot de passe personnalisé pour le compte administrateur 'admin'." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "Si le champ est laissé vide, le mot de passe ne sera pas changé." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "Répéter le mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "Erreur de mot de passe" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" -"Les deux mots de passe saisis ne sont pas les mêmes. Veuillez essayer à " -"nouveau." - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "Serveur web à reconfigurer automatiquement :" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" -"Veuillez choisir le serveur web à reconfigurer automatiquement pour l'accès " -"au portail web de ZoneMinder." diff --git a/distros/ubuntu1410/po/templates.pot b/distros/ubuntu1410/po/templates.pot deleted file mode 100644 index 941a4094e..000000000 --- a/distros/ubuntu1410/po/templates.pot +++ /dev/null @@ -1,222 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" diff --git a/distros/ubuntu1410/rules b/distros/ubuntu1410/rules deleted file mode 100755 index 49d3549f1..000000000 --- a/distros/ubuntu1410/rules +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - -CFLAGS = -Wall -CPPFLAGS = -D__STDC_CONSTANT_MACROS -CXXFLAGS = -DHAVE_LIBCRYPTO - -ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) -DEBOPT = --enable-debug -CFLAGS += -g -CXXFLAGS += -g -else -DEBOPT = -endif - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) -CFLAGS += -O0 -else -CFLAGS += -O2 -endif - -# These are used to get the most recent version of the original sources from github -UURL = $(shell git config --get remote.origin.url) -BRANCH = $(shell git rev-parse --abbrev-ref HEAD) -HEAD = $(shell git rev-parse HEAD) -PKD = $(abspath $(dir $(MAKEFILE_LIST))) -PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source)) -VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-|\+nmu\d+.*)};') -DTYPE = -TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz - -%: - dh $@ --with autoreconf - -override_dh_auto_configure: - CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- \ - --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \ - --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man \ - --infodir=\$${prefix}/share/info --with-mysql=/usr \ - --with-mariadb=/usr --with-webdir=/usr/share/zoneminder \ - --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \ - --with-webuser=www-data --with-webgroup=www-data \ - --enable-mmap=yes $(DEBOPT) - -override_dh_clean: - # Add here commands to clean up after the build process. - [ ! -f Makefile ] || $(MAKE) distclean - dh_clean src/zm_config_defines.h - # - # Delete remaining auto-generated Makefile if Makefile.in exists - find $(CURDIR)/ -type f -name "Makefile" | while read file; do \ - [ -f $$file.in ] && rm -f $$file; \ - done || true - # - # Delete remaining auto-generated Makefile.in if Makefile.am exists - find $(CURDIR)/ -type f -name "Makefile.in" | while read filein; do \ - fileam=`echo $$filein | sed 's/\(.*\)\.in/\1\.am/'`; \ - [ -f $$fileam ] && rm -f $$filein; \ - done || true - -override_dh_install: - dh_install --fail-missing - # - # NOTE: This is a short-term kludge; hopefully changes in the next - # upstream version will render this unnecessary. - rm -rf debian/zoneminder/usr/share/zoneminder/events - rm -rf debian/zoneminder/usr/share/zoneminder/images - rm -rf debian/zoneminder/usr/share/zoneminder/temp - # The link stuff for these folders has been moved to - # zoneminder-core.links file - # - # This is a slightly lesser kludge; moving the cgi stuff to - # /usr/share/zoneminder/cgi-bin breaks one set of behavior, - # having it just in /usr/lib/cgi-bin breaks another bit of - # behavior. - # The link stuff for /usr/share/zoneminder/cgi-bin has been moved to - # zoneminder-ui-base.links file - -override_dh_installinit: - dh_installinit --package=zoneminder-core --name=zoneminder - -override_dh_systemd_start: - dh_systemd_start --package=zoneminder-core --name=zoneminder \ - --restart-after-upgrade - -override_dh_systemd_enable: - dh_systemd_enable --package=zoneminder-core --name=zoneminder - -override_dh_fixperms: - dh_fixperms - # - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:www-data debian/zoneminder-core/etc/zm/zm.conf - chmod 640 debian/zoneminder-core/etc/zm/zm.conf - -override_dh_auto_test: - # do not run tests... - -.PHONY: override_dh_strip -override_dh_strip: - dh_strip --dbg-package=zoneminder-core-dbg - -# Inspired by https://wiki.debian.org/onlyjob/get-orig-source -.PHONY: get-orig-source -get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE)) - @ - -$(TARBALL): - $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...)) - @echo "# Cloning origin repository..."; \ - if ! git clone $(UURL) $(PKG)-$(VER); then \ - $(RM) -r $(PKG)-$(VER); \ - echo "failed to clone repository, aborting..."; \ - false; \ - fi - @if [ $(BRANCH) != "master" ]; then \ - cd $(PKG)-$(VER); \ - echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \ - git fetch origin $(BRANCH):$(BRANCH) || false; \ - echo "# Switching to branch '$(BRANCH)'..."; \ - git checkout $(BRANCH) || false; \ - fi - @echo "# Checking local source..." - @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \ - echo "even with origin, ok"; \ - true; \ - else \ - echo "not even with origin, aborting..."; \ - false; \ - fi - @echo "# Setting times..." - @cd $(PKG)-$(VER) \ - && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \ - touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \ - done - @echo "# Cleaning-up..." - cd $(PKG)-$(VER) && $(RM) -r .git - @echo "# Packing file '$(TARBALL)'..." - @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \ - | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \ - && $(RM) -r "$(PKG)-$(VER)" diff --git a/distros/ubuntu1410/source/format b/distros/ubuntu1410/source/format deleted file mode 100644 index 89ae9db8f..000000000 --- a/distros/ubuntu1410/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/distros/ubuntu1410/source/local-options b/distros/ubuntu1410/source/local-options deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1410/source/options b/distros/ubuntu1410/source/options deleted file mode 100644 index 8bd61fce6..000000000 --- a/distros/ubuntu1410/source/options +++ /dev/null @@ -1 +0,0 @@ -extend-diff-ignore = "(^|/)(config\.sub|config\.guess|Makefile|aclocal.m4|compile|config.h.in|configure|depcomp|install-sh|missing)$" diff --git a/distros/ubuntu1410/zoneminder-core.config b/distros/ubuntu1410/zoneminder-core.config deleted file mode 100644 index 2a15a599e..000000000 --- a/distros/ubuntu1410/zoneminder-core.config +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.dirs b/distros/ubuntu1410/zoneminder-core.dirs deleted file mode 100644 index 350c32aff..000000000 --- a/distros/ubuntu1410/zoneminder-core.dirs +++ /dev/null @@ -1,4 +0,0 @@ -var/log/zm -var/cache/zoneminder/events -var/cache/zoneminder/images -var/cache/zoneminder/temp diff --git a/distros/ubuntu1410/zoneminder-core.install b/distros/ubuntu1410/zoneminder-core.install deleted file mode 100644 index afd9ada95..000000000 --- a/distros/ubuntu1410/zoneminder-core.install +++ /dev/null @@ -1,4 +0,0 @@ -etc/zm -usr/bin -usr/share/polkit-1/actions -usr/share/polkit-1/rules.d diff --git a/distros/ubuntu1410/zoneminder-core.links b/distros/ubuntu1410/zoneminder-core.links deleted file mode 100644 index 5560a100a..000000000 --- a/distros/ubuntu1410/zoneminder-core.links +++ /dev/null @@ -1,3 +0,0 @@ -var/cache/zoneminder/events usr/share/zoneminder/events -var/cache/zoneminder/images usr/share/zoneminder/images -var/cache/zoneminder/temp usr/share/zoneminder/temp diff --git a/distros/ubuntu1410/zoneminder-core.postinst b/distros/ubuntu1410/zoneminder-core.postinst deleted file mode 100644 index da2b444fe..000000000 --- a/distros/ubuntu1410/zoneminder-core.postinst +++ /dev/null @@ -1,80 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Source the config file -CONFIGFILE=/etc/zm/zm.conf -. $CONFIGFILE - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Retrieve data from dbconfig (inputs from user) - . /etc/dbconfig-common/zoneminder.conf - - # ZoneMinder config file handling - # Inspired by: http://manpages.debian.org/cgi-bin/man.cgi?query=debconf-devel&sektion=7 - - # Backup the config file - cp -a -f $CONFIGFILE ${CONFIGFILE}.postinst.bak - - # Redeclare variables if missing in config file - test -z $dbc_dbserver || grep -Eq "^ *ZM_DB_HOST=" $CONFIGFILE \ - || echo "ZM_DB_HOST=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || grep -Eq "^ *ZM_DB_NAME=" $CONFIGFILE \ - || echo "ZM_DB_NAME=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || grep -Eq "^ *ZM_DB_USER=" $CONFIGFILE \ - || echo "ZM_DB_USER=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || grep -Eq "^ *ZM_DB_PASS=" $CONFIGFILE \ - || echo "ZM_DB_PASS=" >> ${CONFIGFILE}.postinst.bak - - # Prevent ZM_DB_HOST to be empty if user selected the 'unix socket' method - if test -z $dbc_dbserver; then - dbc_dbserver_override="localhost" - else - dbc_dbserver_override=$dbc_dbserver - fi - - # Update variables in config file - sed -i "s/^ *ZM_DB_HOST=.*/ZM_DB_HOST=$dbc_dbserver_override/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || sed -i "s/^ *ZM_DB_NAME=.*/ZM_DB_NAME=$dbc_dbname/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || sed -i "s/^ *ZM_DB_USER=.*/ZM_DB_USER=$dbc_dbuser/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || sed -i "s/^ *ZM_DB_PASS=.*/ZM_DB_PASS=$dbc_dbpass/" \ - ${CONFIGFILE}.postinst.bak - - # Clean-up backup file - mv -f ${CONFIGFILE}.postinst.bak $CONFIGFILE - - - # Set some file permissions - chown $ZM_WEB_USER:$ZM_WEB_GROUP /var/log/zm - if [ -z "$2" ]; then - chown $ZM_WEB_USER:$ZM_WEB_GROUP -R /var/cache/zoneminder - fi - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:${ZM_WEB_GROUP} $CONFIGFILE - chmod 640 $CONFIGFILE -fi - -# Do this every time the package is installed or upgraded -# Test for database presence to avoid failure of zmupdate.pl -if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then - - # Ensure zoneminder is stopped - deb-systemd-invoke stop zoneminder.service || exit $? - - # Run the ZoneMinder update tool - zmupdate.pl --nointeractive - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.postrm b/distros/ubuntu1410/zoneminder-core.postrm deleted file mode 100644 index d75e75e8b..000000000 --- a/distros/ubuntu1410/zoneminder-core.postrm +++ /dev/null @@ -1,37 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -if [ "$1" = "purge" ]; then - - # Ask the user if we have to remove the cache directory even if not empty - if [ -d /var/cache/zoneminder ] \ - && [ ! $(find /var/cache/zoneminder -maxdepth 0 -type d -empty 2>/dev/null) ]; then - RET="" - db_input high zoneminder/ask_delete || true - db_go || true - db_get zoneminder/ask_delete - if [ "$RET" = "true" ]; then - RET="" - db_input high zoneminder/ask_delete_again || true - db_go || true - db_get zoneminder/ask_delete_again - if [ "$RET" = "true" ]; then - rm -rf /var/cache/zoneminder - fi - fi - fi -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.preinst b/distros/ubuntu1410/zoneminder-core.preinst deleted file mode 100644 index 3ed1ef661..000000000 --- a/distros/ubuntu1410/zoneminder-core.preinst +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# preinst maintainer script for zoneminder-core package - -set -e - -abort=false -if [ -L /usr/share/zoneminder/events ]; then - l=$(readlink /usr/share/zoneminder/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -L /usr/share/zoneminder/images ]; then - l=$(readlink /usr/share/zoneminder/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.templates b/distros/ubuntu1410/zoneminder-core.templates deleted file mode 100644 index 35fdefd7a..000000000 --- a/distros/ubuntu1410/zoneminder-core.templates +++ /dev/null @@ -1,19 +0,0 @@ -Template: zoneminder/ask_delete -Type: boolean -Default: false -_Description: Delete this non empty directory? - A purge of the ZoneMinder package is performed but the directory - '/var/cache/zoneminder' is not empty so it will not be deleted. - . - Please consider that this directory is designed to contain data resulting from - event detection. Therefore, "proof of evidence" could be lost!" - . - If you are not sure of your decision, please do not delete this directory but - perform a manual checkup. - -Template: zoneminder/ask_delete_again -Type: boolean -Default: false -_Description: Deletion confirmed? - You have allowed the deletion of directory '/var/cache/zoneminder' although - it may contain critical data. diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.init b/distros/ubuntu1410/zoneminder-core.zoneminder.init deleted file mode 100644 index d3354c1d8..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.init +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: zoneminder -# Required-Start: $network $remote_fs $syslog -# Required-Stop: $network $remote_fs $syslog -# Should-Start: mysql -# Should-Stop: mysql -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Control ZoneMinder as a Service -### END INIT INFO -# description: Control ZoneMinder as a Service -# chkconfig: 2345 20 20 - -# Source function library. -#. /etc/rc.d/init.d/functions - -prog=ZoneMinder -ZM_PATH_BIN="/usr/bin" -RUNDIR=/var/run/zm -TMPDIR=/tmp/zm -command="$ZM_PATH_BIN/zmpkg.pl" - -start() { - echo -n "Starting $prog: " - mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR - mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR - $command start - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && touch /var/lock/zm - return $RETVAL -} -stop() { - echo -n "Stopping $prog: " - # - # Why is this status check being done? - # as $command stop returns 1 if zoneminder - # is stopped, which will result in - # this returning 1, which will stuff - # dpkg when it tries to stop zoneminder before - # uninstalling . . . - # - result=`$command status` - if [ ! "$result" = "running" ]; then - echo "Zoneminder already stopped" - echo - RETVAL=0 - else - $command stop - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && rm -f /var/lock/zm - fi -} -status() { - result=`$command status` - if [ "$result" = "running" ]; then - echo "ZoneMinder is running" - RETVAL=0 - else - echo "ZoneMinder is stopped" - RETVAL=1 - fi -} - -case "$1" in -'start') - start - ;; -'stop') - stop - ;; -'restart' | 'force-reload') - stop - start - ;; -'status') - status - ;; -*) - echo "Usage: $0 { start | stop | restart | status }" - RETVAL=1 - ;; -esac -exit $RETVAL diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.service b/distros/ubuntu1410/zoneminder-core.zoneminder.service deleted file mode 100644 index d82270024..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.service +++ /dev/null @@ -1,19 +0,0 @@ -# ZoneMinder systemd unit file -# This file is intended to work with debian distributions - -[Unit] -Description=ZoneMinder CCTV recording and security system -After=network.target mysql.service apache2.service -Requires=apache2.service -Wants=mysql.service - -[Service] -User=www-data -Type=forking -ExecStart=/usr/bin/zmpkg.pl start -ExecReload=/usr/bin/zmpkg.pl restart -ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid - -[Install] -WantedBy=multi-user.target diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile b/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile deleted file mode 100644 index 6ea70bf35..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile +++ /dev/null @@ -1 +0,0 @@ -d /var/run/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/zoneminder-database.config b/distros/ubuntu1410/zoneminder-database.config deleted file mode 100644 index f6a84d36d..000000000 --- a/distros/ubuntu1410/zoneminder-database.config +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Set the first version in which dbconfig-common was introduced in the package -dbc_first_version="1.28.0" - -CONFIGFILE=/etc/zm/zm.conf -if [ -e $CONFIGFILE ]; then - # Source the config file if exists - . $CONFIGFILE -elif [ -e ${CONFIGFILE}.dpkg-new ]; then - # If no config file, source the config file which is going to be installed - # by the core package - . ${CONFIGFILE}.dpkg-new -else - # If no config file is going to be installed, set some default values - ZM_DB_HOST= - ZM_DB_NAME="zm" - ZM_DB_USER="zmuser" -fi - -# Set some variables for the dbconfig-common stuff -dbc_dbserver="$ZM_DB_HOST" -dbc_dbname="$ZM_DB_NAME" -dbc_dbuser="$ZM_DB_USER" - -if [ -f /usr/share/dbconfig-common/dpkg/config ]; then - - # Default use dbconfig-common - dbc_install="true" - - # Currently we only support mysql database - dbc_dbtypes="mysql" - - # Set authentication method to password - dbc_authmethod_user="password" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/config -fi - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user if the database shall be installed locally or remotely - db_input high zoneminder/debconf_dblocation || true - db_go || true - db_get zoneminder/debconf_dblocation - - if [ "$RET" = "local" ]; then - if [ ! -e "/usr/sbin/mysqld" ]; then - # Display a message and exit if the user want a local database but - # no database server is available - db_input high zoneminder/debconf_dblocalmissingerror || true - db_go || true - exit 0 - else - # Set the database server to localhost - dbc_dbserver="localhost" - fi - else - # Source the dbconfig main configuration file - if [ -f /etc/dbconfig-common/config ]; then - . /etc/dbconfig-common/config - fi - if [ "$dbc_remote_questions_default" = "false" ]; then - # Display a message and exit if the dbconfig configuration does not - # allow installation of remote databases - # Note: To overcome this issue, we could think to override the - # default setting by using dbc_remote_questions_default='true' in - # maintainer scripts but unfortunately this does not work due to - # current dbconfig design - # More information here: - # https://bugs.launchpad.net/ubuntu/+source/dbconfig-common/+bug/1065331 - db_input high zoneminder/debconf_dbconfigerror || true - db_go || true - exit 0 - fi - fi - - # Ask the user for all database settings - dbc_go zoneminder $@ - - # Ask the user for the password of the database administrator if the user - # has not yet answered to this question. - # This situation may occur if the user skipped the database creation step - # when reconfiguring the package. - RET="" - db_get zoneminder/mysql/admin-pass - if [ -z "$RET" ]; then - db_input high zoneminder/mysql/admin-pass || true - db_go || true - fi - - # Do this only when not upgrading the package (no old version in argument) - if [ -z "$2" ]; then - # Ask for the password of 'admin' user - while :; do - RET="" - db_input high zoneminder/admin_password || true - db_go || true - db_get zoneminder/admin_password - # If password isn't empty we ask for password verification - if [ -z "$RET" ]; then - db_fset zoneminder/admin_password seen false - db_fset zoneminder/admin_password_again seen false - break - fi - ROOT_PW="$RET" - db_input high zoneminder/admin_password_again || true - db_go || true - db_get zoneminder/admin_password_again - if [ "$RET" = "$ROOT_PW" ]; then - ROOT_PW="" - break - fi - db_fset zoneminder/password_mismatch seen false - db_input critical zoneminder/password_mismatch || true - db_set zoneminder/admin_password "" - db_set zoneminder/admin_password_again "" - db_go || true - done - else - # If we are upgrading the package, set an empty password to disable - # password update in ZoneMinder database - db_set zoneminder/admin_password "" - fi - # Set the seen flag to not ask this question again if no password is - # provided - db_fset zoneminder/admin_password seen true - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.dirs b/distros/ubuntu1410/zoneminder-database.dirs deleted file mode 100644 index b37463a9e..000000000 --- a/distros/ubuntu1410/zoneminder-database.dirs +++ /dev/null @@ -1,3 +0,0 @@ -usr/share/zoneminder/db -usr/share/dbconfig-common/data/zoneminder/install -usr/share/dbconfig-common/data/zoneminder/upgrade/mysql diff --git a/distros/ubuntu1410/zoneminder-database.install b/distros/ubuntu1410/zoneminder-database.install deleted file mode 100644 index 756c5bbfa..000000000 --- a/distros/ubuntu1410/zoneminder-database.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/db diff --git a/distros/ubuntu1410/zoneminder-database.postinst b/distros/ubuntu1410/zoneminder-database.postinst deleted file mode 100644 index 41d4e5b5b..000000000 --- a/distros/ubuntu1410/zoneminder-database.postinst +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -mysql_update() { - - # Source the dbconfig stuff - . /usr/share/dbconfig-common/internal/mysql - - # Update the password of the hard-coded default 'admin' account - test -z $ADMIN_PASSWORD || dbc_mysql_exec_command "UPDATE Users SET Password = password('$ADMIN_PASSWORD') WHERE Username = 'admin';" || true - - # Update the database version - dbc_mysql_exec_command "UPDATE Config SET Value = '$DB_VERSION' WHERE Name = 'ZM_DYN_DB_VERSION';" || true -} - -if [ -f /usr/share/dbconfig-common/dpkg/postinst ]; then - - # Set the first version in which dbconfig-common was introduced in the package - dbc_first_version="1.28.0" - - # Set the database type - dbc_dbtypes="mysql" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/postinst -fi - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Install sql database create file for dbconfig - # (needed at first package installation) - if [ ! -f /usr/share/dbconfig-common/data/zoneminder/install/mysql ]; then - install -m 644 /usr/share/zoneminder/db/zm_create.sql \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - # Remove unneeded sql requests - # dbconfig will create the underlying database - sed -i "/^ *CREATE DATABASE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - sed -i "/^ *USE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - fi - - # Symlink sql update files for dbconfig (needed when upgrading the package) - for sqlfile in /usr/share/zoneminder/db/zm_update-*.sql; do - lnk=`echo $sqlfile | sed "s/^\/usr\/share\/zoneminder\/db\/zm_update-\(.*\)\.sql/\1/"` - if [ ! -L /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk ]; then - ln -sf $sqlfile \ - /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk - fi - done || true - - # Create the underlying database and populate it - # dbconfig will take care of applying any updates which are newer than the - # previously installed version - dbc_go zoneminder $@ - - # Get the password of ZoneMinder user 'admin' from debconf - db_get zoneminder/admin_password - ADMIN_PASSWORD=$RET - - # Remove the password from debconf database - test -z $ADMIN_PASSWORD || db_reset zoneminder/admin_password || true - - # Get the lastest database version from dbconfig upgrade folder - DB_VERSION=$(ls -rv /usr/share/dbconfig-common/data/zoneminder/upgrade/$dbc_dbtypes | head -1) - - # Update the default admin account and database version - mysql_update -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.postrm b/distros/ubuntu1410/zoneminder-database.postrm deleted file mode 100644 index 231f01ad7..000000000 --- a/distros/ubuntu1410/zoneminder-database.postrm +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# Source the dbconfig stuff -if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then - . /usr/share/dbconfig-common/dpkg/postrm - # Ask the user what do to with dbconfig when removing the package - dbc_go zoneminder $@ -fi - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Remove dbconfig stuff added in postinst script - rm -rf /usr/share/dbconfig-common/data/zoneminder - # No need to manually remove the zm database, dbconfig take care of this -fi - -if [ "$1" = "purge" ]; then - # Delete a potential remaining file used in postinst script - rm -f /etc/zm/zm.conf.postinst.bak -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.prerm b/distros/ubuntu1410/zoneminder-database.prerm deleted file mode 100644 index 31786116a..000000000 --- a/distros/ubuntu1410/zoneminder-database.prerm +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# prerm script for zoneminder-db package - -set -e - -# Source the debconf stuff if file exists -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# If dbconfig-common is installed and has been used by zoneminder -if [ -f /usr/share/dbconfig-common/dpkg/prerm ] \ - && [ -f /etc/dbconfig-common/zoneminder.conf ]; then - # Source the dbconfig stuff - . /usr/share/dbconfig-common/dpkg/prerm - # Ask the user what do to with dbconfig before removing the package - dbc_go zoneminder $@ -fi - -# #DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.templates b/distros/ubuntu1410/zoneminder-database.templates deleted file mode 100644 index 4de4342f6..000000000 --- a/distros/ubuntu1410/zoneminder-database.templates +++ /dev/null @@ -1,58 +0,0 @@ -Template: zoneminder/debconf_dblocation -Type: select -__Choices: local, remote -Default: local -_Description: Database location: - A database server is required to run ZoneMinder. The database can be installed - either locally or remotely on a machine of your network. - . - If you choose a remote location, you will have to select the 'tcp/ip' - connection method and enter the hostname or ip address of the remote machine - in the next configuration screens. - -Template: zoneminder/debconf_dblocalmissingerror -Type: error -_Description: No local database server is available: - Currently ZoneMinder supports mysql or mariadb database server but none of them - appears to be installed on this machine. - . - In order to complete ZoneMinder's installation, after ending of this assistant, - please install a compatible database server and then restart the assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/debconf_dbconfigerror -Type: error -_Description: Remote database servers are not allowed: - The current configuration of dbconfig-common does not allow installation of - a database on remote servers. - . - In order to reconfigure dbconfig-common, please invoke the following command - after ending of this assistant: - . - $ sudo dpkg-reconfigure dbconfig-common - . - Then, to complete ZoneMinder's installation, please restart this assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/admin_password -Type: password -_Description: New password for the ZoneMinder 'admin' user: - Please enter the password of the default administrative user. - . - While not mandatory, it is highly recommended that you set a custom password - for the administrative 'admin' user. - . - If this field is left blank, the password will not be changed. - -Template: zoneminder/admin_password_again -Type: password -_Description: Repeat password for the ZoneMinder 'admin' user: - -Template: zoneminder/password_mismatch -Type: error -_Description: Password input error - The two passwords you entered were not the same. Please try again. diff --git a/distros/ubuntu1410/zoneminder-ui-base.config b/distros/ubuntu1410/zoneminder-ui-base.config deleted file mode 100644 index 2660208a8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.config +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user for the web server(s) to configure - db_input high zoneminder/webserver || true - db_go || true -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.install b/distros/ubuntu1410/zoneminder-ui-base.install deleted file mode 100644 index f72b569be..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.install +++ /dev/null @@ -1,11 +0,0 @@ -debian/apache.conf etc/zm -usr/lib/cgi-bin -usr/share/zoneminder/ajax -usr/share/zoneminder/css -usr/share/zoneminder/graphics -usr/share/zoneminder/includes -usr/share/zoneminder/index.php -usr/share/zoneminder/js -usr/share/zoneminder/lang -usr/share/zoneminder/tools -usr/share/zoneminder/views diff --git a/distros/ubuntu1410/zoneminder-ui-base.links b/distros/ubuntu1410/zoneminder-ui-base.links deleted file mode 100644 index b00a147d6..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.links +++ /dev/null @@ -1 +0,0 @@ -usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/ubuntu1410/zoneminder-ui-base.postinst b/distros/ubuntu1410/zoneminder-ui-base.postinst deleted file mode 100644 index a5bce3c98..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postinst +++ /dev/null @@ -1,48 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -apache_install() { - - mkdir -p /etc/apache2/conf-available - ln -sf ../../zm/apache.conf /etc/apache2/conf-available/zoneminder.conf - - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke enconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - [ -d /etc/apache2/conf.d/ ] && [ ! -L /etc/apache2/conf.d/zoneminder.conf ] && ln -s ../conf-available/zoneminder.conf /etc/apache2/conf.d/zoneminder.conf - fi - - # Enable CGI script module in apache (not enabled by default on jessie) - a2enmod cgi >/dev/null 2>&1 - - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Configure the web server - db_get zoneminder/webserver - webservers="$RET" - - for webserver in $webservers; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_install $1 - fi - done -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.postrm b/distros/ubuntu1410/zoneminder-ui-base.postrm deleted file mode 100644 index 441bb5218..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postrm +++ /dev/null @@ -1,41 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -apache_remove() { - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke disconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - rm -f /etc/apache2/conf.d/zoneminder.conf - fi - rm -f /etc/apache2/conf-available/zoneminder.conf - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Deconfigure the web server - db_get zoneminder/webserver - for webserver in $RET; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_remove $1 - fi - done -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.templates b/distros/ubuntu1410/zoneminder-ui-base.templates deleted file mode 100644 index 31e70277f..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.templates +++ /dev/null @@ -1,7 +0,0 @@ -Template: zoneminder/webserver -Type: multiselect -Choices: apache2 -Default: apache2 -_Description: Web server to reconfigure automatically: - Please choose the web server that should be automatically configured for - ZoneMinder's web portal access. diff --git a/distros/ubuntu1410/zoneminder-ui-classic.install b/distros/ubuntu1410/zoneminder-ui-classic.install deleted file mode 100644 index 9532d9dc9..000000000 --- a/distros/ubuntu1410/zoneminder-ui-classic.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/classic diff --git a/distros/ubuntu1410/zoneminder-ui-mobile.install b/distros/ubuntu1410/zoneminder-ui-mobile.install deleted file mode 100644 index 464bb74eb..000000000 --- a/distros/ubuntu1410/zoneminder-ui-mobile.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/mobile diff --git a/distros/ubuntu1410/zoneminder-ui-xml.install b/distros/ubuntu1410/zoneminder-ui-xml.install deleted file mode 100644 index 6617707f8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-xml.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/xml From 4cf7c1a16e09076ba74b83a8c5f0dbbe44bc7a85 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 15:02:00 -0400 Subject: [PATCH 204/319] deprecate trusty and precise. Use new cleaned up rules for focal and buster --- utils/do_debian_package.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 6d9decc1b..f09b879fd 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -225,8 +225,8 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do fi; # Generate Changlog - if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then - cp -Rpd distros/ubuntu1204 debian + if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ]; then + cp -Rpd distros/ubuntu2004 debian else if [ "$DISTRO" == "wheezy" ]; then cp -Rpd distros/debian debian From 8e06a5698fabf3ca325d9907aa90f5424eea263b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 15:02:22 -0400 Subject: [PATCH 205/319] Add new cleaned up rules for focal and buster --- distros/ubuntu2004/NEWS | 10 + distros/ubuntu2004/README.Debian | 130 +++++++++++++ distros/ubuntu2004/TODO.Debian | 12 ++ distros/ubuntu2004/changelog | 3 + distros/ubuntu2004/clean | 3 + distros/ubuntu2004/compat | 1 + .../ubuntu2004/conf/apache2/zoneminder.conf | 57 ++++++ distros/ubuntu2004/control | 166 +++++++++++++++++ distros/ubuntu2004/copyright | 174 ++++++++++++++++++ distros/ubuntu2004/examples/nginx.conf | 32 ++++ distros/ubuntu2004/gbp.conf | 7 + distros/ubuntu2004/libzoneminder-perl.install | 2 + distros/ubuntu2004/patches/series | 0 distros/ubuntu2004/rules | 96 ++++++++++ distros/ubuntu2004/source/format | 1 + distros/ubuntu2004/source/lintian-overrides | 9 + distros/ubuntu2004/zoneminder-doc.doc-base | 8 + distros/ubuntu2004/zoneminder-doc.install | 1 + distros/ubuntu2004/zoneminder-doc.links | 2 + distros/ubuntu2004/zoneminder.apache2 | 1 + distros/ubuntu2004/zoneminder.bug-presubj | 5 + distros/ubuntu2004/zoneminder.dirs | 9 + distros/ubuntu2004/zoneminder.docs | 1 + distros/ubuntu2004/zoneminder.examples | 2 + distros/ubuntu2004/zoneminder.install | 11 ++ distros/ubuntu2004/zoneminder.links | 1 + distros/ubuntu2004/zoneminder.linktrees | 14 ++ .../ubuntu2004/zoneminder.lintian-overrides | 14 ++ distros/ubuntu2004/zoneminder.logrotate | 13 ++ distros/ubuntu2004/zoneminder.maintscript | 1 + distros/ubuntu2004/zoneminder.manpages | 1 + distros/ubuntu2004/zoneminder.postinst | 90 +++++++++ distros/ubuntu2004/zoneminder.postrm | 14 ++ distros/ubuntu2004/zoneminder.preinst | 11 ++ distros/ubuntu2004/zoneminder.service | 23 +++ distros/ubuntu2004/zoneminder.tmpfile | 4 + 36 files changed, 929 insertions(+) create mode 100644 distros/ubuntu2004/NEWS create mode 100644 distros/ubuntu2004/README.Debian create mode 100644 distros/ubuntu2004/TODO.Debian create mode 100644 distros/ubuntu2004/changelog create mode 100644 distros/ubuntu2004/clean create mode 100644 distros/ubuntu2004/compat create mode 100644 distros/ubuntu2004/conf/apache2/zoneminder.conf create mode 100644 distros/ubuntu2004/control create mode 100644 distros/ubuntu2004/copyright create mode 100644 distros/ubuntu2004/examples/nginx.conf create mode 100644 distros/ubuntu2004/gbp.conf create mode 100644 distros/ubuntu2004/libzoneminder-perl.install create mode 100644 distros/ubuntu2004/patches/series create mode 100755 distros/ubuntu2004/rules create mode 100644 distros/ubuntu2004/source/format create mode 100644 distros/ubuntu2004/source/lintian-overrides create mode 100644 distros/ubuntu2004/zoneminder-doc.doc-base create mode 100644 distros/ubuntu2004/zoneminder-doc.install create mode 100644 distros/ubuntu2004/zoneminder-doc.links create mode 100644 distros/ubuntu2004/zoneminder.apache2 create mode 100644 distros/ubuntu2004/zoneminder.bug-presubj create mode 100644 distros/ubuntu2004/zoneminder.dirs create mode 100644 distros/ubuntu2004/zoneminder.docs create mode 100644 distros/ubuntu2004/zoneminder.examples create mode 100644 distros/ubuntu2004/zoneminder.install create mode 100644 distros/ubuntu2004/zoneminder.links create mode 100644 distros/ubuntu2004/zoneminder.linktrees create mode 100644 distros/ubuntu2004/zoneminder.lintian-overrides create mode 100644 distros/ubuntu2004/zoneminder.logrotate create mode 100644 distros/ubuntu2004/zoneminder.maintscript create mode 100644 distros/ubuntu2004/zoneminder.manpages create mode 100644 distros/ubuntu2004/zoneminder.postinst create mode 100644 distros/ubuntu2004/zoneminder.postrm create mode 100644 distros/ubuntu2004/zoneminder.preinst create mode 100644 distros/ubuntu2004/zoneminder.service create mode 100644 distros/ubuntu2004/zoneminder.tmpfile diff --git a/distros/ubuntu2004/NEWS b/distros/ubuntu2004/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/ubuntu2004/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu2004/README.Debian b/distros/ubuntu2004/README.Debian new file mode 100644 index 000000000..4fe3464d2 --- /dev/null +++ b/distros/ubuntu2004/README.Debian @@ -0,0 +1,130 @@ +Zoneminder for Debian +--------------------- + +Initializing database +--------------------- + + pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf +OR + cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf + + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" +accordingly. + +The following command can help to ensure that zoneminder can read its +configuration file: + + chgrp -c www-data /etc/zm/zm.conf + + +Upgrading database +------------------ + +The database is updated automatically on installation. You should not need to take this step. + +Assuming that database is on "localhost" then the following command can be +used to upgrade "zm" database: + + zmupdate.pl + +Additional permissions may be required to perform upgrade: + + echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +The following command prints the current version of zoneminder database: + + echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm + + +Enabling service +---------------- + +By default Zoneminder service is not automatically started and needs to be +manually enabled once database is configured: + + sudo systemctl enable zoneminder.service + + +Web server set-up +----------------- + +There are few manual steps to get the web interface working: + +## Apache2 + +Apache can be configured as folder "/zm" using sample .conf: + + sudo a2enconf zoneminder + +Alternatively Apache web site configuration template can be used to setup +zoneminder as "http://zoneminder": + + sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ + sudo a2ensite zoneminder.conf + +Common configuration steps for Apache2: + + sudo a2enmod cgi + sudo service apache2 reload + + +## nginx / fcgiwrap + +Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package +for binary "cgi-bin" applications: + + sudo apt-get install php-fpm fcgiwrap + +To enable a URL alias that makes Zoneminder available from + + http://yourserver/zm + +the following line is to be added to "server" section of a web site +configuration: + + include /usr/share/doc/zoneminder/examples/nginx.conf; + +For "default" web site it would be sufficient to include the above +statement to the file + + /etc/nginx/sites-enabled/default + +To avoid problems with feeds from multiple cameras "fcgiwrap" should be +configured to start at least as many processes as there are cameras. +It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". +Systemd users may be affected by the following bug: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 + + +## Note: + +When Zoneminder web site is running it may be necessary to set +Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web +site configuration. + + +Changing the location for images and events +------------------------------------------- + +ZoneMinder is now able to be configured to use an alternative location for storing +events and images at compile time. This package makes use of that, so symlinks in +/usr/share/zoneminder/www are no longer necessary. + +Access to /dev/video* +--------------------- + +For cameras which require access to /dev/video*, zoneminder may need the +www-data user added to the video group in order to see those cameras: + + adduser www-data video + +Note that all web applications running on the zoneminder server will then have +access to all video devices on the system. + + -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu2004/TODO.Debian b/distros/ubuntu2004/TODO.Debian new file mode 100644 index 000000000..9dc59613b --- /dev/null +++ b/distros/ubuntu2004/TODO.Debian @@ -0,0 +1,12 @@ + +## Separate substantial /usr/share into its own arch-all package. + +## Decide how to handle database updates. + + * Consider possibility that database may be on another machine (#469239). + * Consider dbconfig-common? Probably not (what if database is not on localhost?). + +### Run `zmupdate.pl` from service control scripts (init.d, service) on start? + + Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). + diff --git a/distros/ubuntu2004/changelog b/distros/ubuntu2004/changelog new file mode 100644 index 000000000..616f75178 --- /dev/null +++ b/distros/ubuntu2004/changelog @@ -0,0 +1,3 @@ +zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low + * + -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 diff --git a/distros/ubuntu2004/clean b/distros/ubuntu2004/clean new file mode 100644 index 000000000..941ef2a3a --- /dev/null +++ b/distros/ubuntu2004/clean @@ -0,0 +1,3 @@ +.gitattributes +web/api/.gitattributes +web/api/.gitignore diff --git a/distros/ubuntu2004/compat b/distros/ubuntu2004/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/distros/ubuntu2004/compat @@ -0,0 +1 @@ +9 diff --git a/distros/ubuntu2004/conf/apache2/zoneminder.conf b/distros/ubuntu2004/conf/apache2/zoneminder.conf new file mode 100644 index 000000000..e3164d36c --- /dev/null +++ b/distros/ubuntu2004/conf/apache2/zoneminder.conf @@ -0,0 +1,57 @@ +# Remember to enable cgi mod (i.e. "a2enmod cgi"). +ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + + +# Order matters. This alias must come first. +Alias /zm/cache /var/cache/zoneminder/cache + + Options -Indexes +FollowSymLinks + AllowOverride None + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + +Alias /zm /usr/share/zoneminder/www + + Options -Indexes +FollowSymLinks + + DirectoryIndex index.php + + + +# For better visibility, the following directives have been migrated from the +# default .htaccess files included with the CakePHP project. +# Parameters not set here are inherited from the parent directive above. + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + RewriteBase /zm/api + diff --git a/distros/ubuntu2004/control b/distros/ubuntu2004/control new file mode 100644 index 000000000..4e7c53503 --- /dev/null +++ b/distros/ubuntu2004/control @@ -0,0 +1,166 @@ +Source: zoneminder +Section: net +Priority: optional +Maintainer: Isaac Connor +Uploaders: Isaac Connor +Build-Depends: debhelper, dh-systemd, python3-sphinx, dh-linktree, dh-systemd, dh-apache2 + ,cmake + ,libx264-dev, libmp4v2-dev + ,libavdevice-dev + ,libavcodec-dev + ,libavformat-dev + ,libavutil-dev + ,libswresample-dev + ,libswscale-dev + ,ffmpeg + ,net-tools + ,libbz2-dev + ,libgcrypt20-dev + ,libcurl4-gnutls-dev + ,libturbojpeg0-dev + ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat + ,libpcre3-dev + ,libpolkit-gobject-1-dev + ,libv4l-dev [!hurd-any] + ,libvlc-dev + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libsys-mmap-perl [!hurd-any] + ,libwww-perl + ,libdata-uuid-perl + ,libssl-dev + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +# Unbundled (dh_linktree): + ,libjs-jquery + ,libjs-mootools +Standards-Version: 3.9.8 +Homepage: http://www.zoneminder.com/ +Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git +Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git + +Package: zoneminder +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + ,javascript-common + ,libmp4v2-2, libx264-155 + ,libswscale5 + ,libswresample4 + ,ffmpeg + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libmodule-load-conditional-perl + ,libnet-sftp-foreign-perl + ,libarchive-zip-perl + ,libdbd-mysql-perl + ,libdevice-serialport-perl + ,libimage-info-perl + ,libjson-maybexs-perl + ,libsys-mmap-perl [!hurd-any] + ,liburi-encode-perl + ,libwww-perl, liburi-perl + ,libdata-dump-perl + ,libdatetime-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl + ,libnumber-bytes-human-perl + ,libfile-slurp-perl + ,mysql-client | mariadb-client | virtual-mysql-client + ,perl-modules + ,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json + ,policykit-1 + ,rsyslog | system-log-daemon + ,zip + ,libpcre3 + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +Recommends: ${misc:Recommends} + ,libapache2-mod-php | php-fpm + ,mysql-server | mariadb-server | virtual-mysql-server + ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg +Suggests: fcgiwrap, logrotate +Description: video camera security and surveillance solution + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + +#Package: libzoneminder-perl +#Section: perl +#Architecture: all +#Multi-Arch: foreign +#Depends: ${misc:Depends}, ${perl:Depends} +# ,libarchive-zip-perl +# ,libdbd-mysql-perl +# ,libdevice-serialport-perl +# ,libimage-info-perl +# ,libjson-maybexs-perl +# ,libsys-mmap-perl [!hurd-any] +# ,liburi-encode-perl +# ,libwww-perl +#Description: ZoneMinder Perl libraries +# ZoneMinder is intended for use in single or multi-camera video security +# applications, including commercial or home CCTV, theft prevention and child +# or family member or home monitoring and other care scenarios. It +# supports capture, analysis, recording, and monitoring of video data coming +# from one or more video or network cameras attached to a Linux system. +# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom +# cameras using a variety of protocols. It is suitable for use as a home +# video security system and for commercial or professional video security +# and surveillance. It can also be integrated into a home automation system +# via X.10 or other protocols. +# . +# This package provides ZoneMinder Perl libraries; it can be used to +# write custom interfaces as well. + +Package: zoneminder-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme +Suggests: www-browser +Description: ZoneMinder documentation + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides ZoneMinder documentation in HTML format. + +Package: zoneminder-dbg +Section: debug +Priority: extra +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Zoneminder -- debugging symbols + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides debugging symbols diff --git a/distros/ubuntu2004/copyright b/distros/ubuntu2004/copyright new file mode 100644 index 000000000..c48025a25 --- /dev/null +++ b/distros/ubuntu2004/copyright @@ -0,0 +1,174 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ZoneMinder +Upstream-Contact: Philip Coombes +Source: https://github.com/ZoneMinder/ZoneMinder +Comment: + This package was originally debianized by matrix + on Mon, 7 Mar 2005 02:07:57 -0500. + It was re-done for submission to the Debian project by Peter Howard + on Fri, 8 Dec 2006 10:19:43 +1100 +Files-Excluded: + web/skins/*/js/jquery-* + web/tools/mootools/*-yc.js + +Files: * +Copyright: 2001-2014 Philip Coombes + 2008 Brian Rudy + 2014 Vincent Giovannone + 2013 Tim Craig + 2003-2008 Corey DeLasaux + 2001-2010 Chris Kistner +License: GPL-2+ + +Files: distros/* +Copyright: 2001-2008 Philip Coombes + 2014 Isaac Connor + 2005 Serg Oskin +License: GPL-2+ + +Files: web/skins/*/js/jquery-* +Copyright: 2010 John Resig + 2010 The Dojo Foundation +License: GPL-2 or Expat +Comment: + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license + . + Includes Sizzle.js http://sizzlejs.com/ + Released under the MIT, BSD, and GPL Licenses. + +Files: web/tools/mootools/*.js +Copyright: 2009 Marcelo Jorge Vieira (metal) + 2006-2010 Valerio Proietti (http://mad4milk.net/) +License: Expat + +Files: web/api/* +Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) +License: Expat + +Files: + cmake/Modules/CheckPrototypeDefinition*.cmake + cmake/Modules/FindGLIB2.cmake + cmake/Modules/FindPolkit.cmake + cmake/Modules/GNUInstallDirs.cmake +Copyright: + 2005-2011 Kitware, Inc. + 2010-2011 Andreas Schneider + 2009 Dario Freddi + 2008 Laurent Montel, + 2011 Nikita Krupen'ko +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + . + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + . + * The names of Kitware, Inc., the Insight Consortium, or the names of + any consortium members, or of any contributors, may not be used to + endorse or promote products derived from this software without + specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: cmake/Modules/FindPerlModules.cmake +Copyright: 2012 Iowa State University +License: Boost-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: debian/* +Copyright: 2015 Dmitry Smirnov + 2007-2014 Peter Howard + 2010-2012 Vagrant Cascadian + 2001-2008 Philip Coombes +License: GPL-2+ + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +License: GPL-2+ + This package is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + . + You should have received a copy of the GNU General Public + License along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-2 + This package is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + . + You should have received a copy of the GNU General Public + License along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/ubuntu2004/examples/nginx.conf b/distros/ubuntu2004/examples/nginx.conf new file mode 100644 index 000000000..5636ca3e1 --- /dev/null +++ b/distros/ubuntu2004/examples/nginx.conf @@ -0,0 +1,32 @@ +location /zm/cgi-bin { + gzip off; + alias /usr/lib/zoneminder/cgi-bin; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/fcgiwrap.socket; +} + +location /zm { +# if ($scheme ~ ^http:){ +# rewrite ^(.*)$ https://$host$1 permanent; +# } + + gzip off; + alias /usr/share/zoneminder/www; + index index.php; + + location ~ \.php$ { + if (!-f $request_filename) { return 404; } + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } +} diff --git a/distros/ubuntu2004/gbp.conf b/distros/ubuntu2004/gbp.conf new file mode 100644 index 000000000..4608913d9 --- /dev/null +++ b/distros/ubuntu2004/gbp.conf @@ -0,0 +1,7 @@ + +[dch] +id-length = 0 + +[import-orig] +pristine-tar = False +merge = False diff --git a/distros/ubuntu2004/libzoneminder-perl.install b/distros/ubuntu2004/libzoneminder-perl.install new file mode 100644 index 000000000..67191d9cf --- /dev/null +++ b/distros/ubuntu2004/libzoneminder-perl.install @@ -0,0 +1,2 @@ +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu2004/patches/series b/distros/ubuntu2004/patches/series new file mode 100644 index 000000000..e69de29bb diff --git a/distros/ubuntu2004/rules b/distros/ubuntu2004/rules new file mode 100755 index 000000000..c671a1b03 --- /dev/null +++ b/distros/ubuntu2004/rules @@ -0,0 +1,96 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed + +ifeq ($(DEB_BUILD_ARCH_OS),hurd) +ARGS:= -DZM_NO_MMAP=ON +endif + +%: + dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ + --with systemd,sphinxdoc,apache2,linktree + +override_dh_auto_configure: + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_RUNDIR="/var/run/zm" \ + -DZM_SOCKDIR="/var/run/zm" \ + -DZM_TMPDIR="/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ + -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + +override_dh_clean: + dh_clean $(MANPAGES1) + $(RM) -r docs/_build + +build-indep: + #$(MAKE) -C docs text + $(MAKE) -C docs html + +MANPAGES1 = dbuild/scripts/zmupdate.pl.1 +$(MANPAGES1): + # generate man page(s): + pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@ + +## reproducible build: +LAST_CHANGE=$(shell dpkg-parsechangelog -S Date) +BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)") +override_dh_installman: $(MANPAGES1) + $(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\"" + dh_installman --language=C $(MANPAGES1) + +override_dh_auto_install: + dh_auto_install --destdir=$(CURDIR)/debian/tmp + # remove worthless files: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in + # remove empty directories: + find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n' + # remove extra-license-file: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt + +override_dh_fixperms: + dh_fixperms + # + # As requested by the Debian Webapps Policy Manual §3.2.1 + chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + +override_dh_systemd_start: + dh_systemd_start --no-start + +override_dh_systemd_enable: + dh_systemd_enable --no-enable + +override_dh_apache2: + dh_apache2 --noenable + +override_dh_strip: + [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ + && dh_strip --dbg-package=zoneminder-dbg \ + || dh_strip + +#%: +# dh $@ --parallel --buildsystem=autoconf --with autoreconf +# +#override_dh_auto_configure: +# dh_auto_configure -- \ +# --sysconfdir=/etc/zm \ +# --with-mysql=/usr \ +# --with-webdir=/usr/share/zoneminder \ +# --with-ffmpeg=/usr \ +# --with-cgidir=/usr/lib/cgi-bin \ +# --with-webuser=www-data \ +# --with-webgroup=www-data \ +# --enable-mmap=yes diff --git a/distros/ubuntu2004/source/format b/distros/ubuntu2004/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/distros/ubuntu2004/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/distros/ubuntu2004/source/lintian-overrides b/distros/ubuntu2004/source/lintian-overrides new file mode 100644 index 000000000..3669e5de8 --- /dev/null +++ b/distros/ubuntu2004/source/lintian-overrides @@ -0,0 +1,9 @@ +## Actually sources are there: "*-nc.js". +source-is-missing web/tools/mootools/mootools-*-yc.js + +## We're using "libjs-jquery" instead. +source-is-missing web/skins/*/js/jquery-1.4.2.min.js + +## Acknowledged, will repack eventually. +source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js +source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu2004/zoneminder-doc.doc-base b/distros/ubuntu2004/zoneminder-doc.doc-base new file mode 100644 index 000000000..c43dc4336 --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.doc-base @@ -0,0 +1,8 @@ +Document: zoneminder-doc +Title: Zoneminder documentation +Abstract: This document describes how to use Zoneminder. +Section: System/Administration + +Format: HTML +Index: /usr/share/doc/zoneminder-doc/html/index.html +Files: /usr/share/doc/zoneminder-doc/html/* diff --git a/distros/ubuntu2004/zoneminder-doc.install b/distros/ubuntu2004/zoneminder-doc.install new file mode 100644 index 000000000..c19bc6f3a --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.install @@ -0,0 +1 @@ +docs/_build/html usr/share/doc/zoneminder-doc/ diff --git a/distros/ubuntu2004/zoneminder-doc.links b/distros/ubuntu2004/zoneminder-doc.links new file mode 100644 index 000000000..cc09f6462 --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.links @@ -0,0 +1,2 @@ +## Convenience symlink: +/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html diff --git a/distros/ubuntu2004/zoneminder.apache2 b/distros/ubuntu2004/zoneminder.apache2 new file mode 100644 index 000000000..466144fa7 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.apache2 @@ -0,0 +1 @@ +conf debian/conf/apache2/zoneminder.conf nginx diff --git a/distros/ubuntu2004/zoneminder.bug-presubj b/distros/ubuntu2004/zoneminder.bug-presubj new file mode 100644 index 000000000..990fc1d94 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.bug-presubj @@ -0,0 +1,5 @@ +Unless bug is specific to Debian please consider reporting it directly to +upstream developer(s): + + https://github.com/ZoneMinder/ZoneMinder/issues + diff --git a/distros/ubuntu2004/zoneminder.dirs b/distros/ubuntu2004/zoneminder.dirs new file mode 100644 index 000000000..6db3d5a95 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.dirs @@ -0,0 +1,9 @@ +var/log/zm +var/lib/zm +var/cache/zoneminder/events +var/cache/zoneminder/images +var/cache/zoneminder/temp +var/cache/zoneminder/cache +usr/share/zoneminder/db +etc/zm/ +etc/zm/conf.d diff --git a/distros/ubuntu2004/zoneminder.docs b/distros/ubuntu2004/zoneminder.docs new file mode 100644 index 000000000..b43bf86b5 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.docs @@ -0,0 +1 @@ +README.md diff --git a/distros/ubuntu2004/zoneminder.examples b/distros/ubuntu2004/zoneminder.examples new file mode 100644 index 000000000..3b8befe7b --- /dev/null +++ b/distros/ubuntu2004/zoneminder.examples @@ -0,0 +1,2 @@ +debian/examples/* +dbuild/misc/apache.conf diff --git a/distros/ubuntu2004/zoneminder.install b/distros/ubuntu2004/zoneminder.install new file mode 100644 index 000000000..67b135de5 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.install @@ -0,0 +1,11 @@ +etc/zm/zm.conf +etc/zm/conf.d/* +usr/bin +usr/lib/zoneminder +usr/share/polkit-1 +usr/share/zoneminder/db +usr/share/zoneminder/www + +# libzoneminder-perl files: +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu2004/zoneminder.links b/distros/ubuntu2004/zoneminder.links new file mode 100644 index 000000000..b7258c3c4 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.links @@ -0,0 +1 @@ +/var/tmp /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu2004/zoneminder.linktrees b/distros/ubuntu2004/zoneminder.linktrees new file mode 100644 index 000000000..2e843bbf1 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.linktrees @@ -0,0 +1,14 @@ +## cakephp +#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake + +## libjs-mootools +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js + +## libjs-jquery +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu2004/zoneminder.lintian-overrides b/distros/ubuntu2004/zoneminder.lintian-overrides new file mode 100644 index 000000000..90be05a9f --- /dev/null +++ b/distros/ubuntu2004/zoneminder.lintian-overrides @@ -0,0 +1,14 @@ +# Depends: policykit-1 +unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + +# Intentionally not others-readable, #637685. +non-standard-file-perm etc/zm/zm.conf 0640 != 0644 + +# Bundled Cake PHP framework, not intended for direct execution: +script-not-executable usr/share/zoneminder/www/api/* + +# Annoying but seems to be too much troubles to fix; should be fixed upstream: +script-with-language-extension usr/bin/*.pl + +# dh-linktree: +package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/* diff --git a/distros/ubuntu2004/zoneminder.logrotate b/distros/ubuntu2004/zoneminder.logrotate new file mode 100644 index 000000000..6162e9c4d --- /dev/null +++ b/distros/ubuntu2004/zoneminder.logrotate @@ -0,0 +1,13 @@ +/var/log/zm/*.log { + missingok + notifempty + sharedscripts + delaycompress + compress + postrotate + /usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || : + endscript + daily + rotate 7 + maxage 7 +} diff --git a/distros/ubuntu2004/zoneminder.maintscript b/distros/ubuntu2004/zoneminder.maintscript new file mode 100644 index 000000000..3aa20b3a0 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.maintscript @@ -0,0 +1 @@ +rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/ubuntu2004/zoneminder.manpages b/distros/ubuntu2004/zoneminder.manpages new file mode 100644 index 000000000..d2053d688 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.manpages @@ -0,0 +1 @@ +docs/_build/man/*.1 diff --git a/distros/ubuntu2004/zoneminder.postinst b/distros/ubuntu2004/zoneminder.postinst new file mode 100644 index 000000000..d3983950b --- /dev/null +++ b/distros/ubuntu2004/zoneminder.postinst @@ -0,0 +1,90 @@ +#! /bin/sh + +set +e + +if [ "$1" = "configure" ]; then + + . /etc/zm/zm.conf + for CONFFILE in /etc/zm/conf.d/*.conf; do + . "$CONFFILE" + done + + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then + echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." + a2enmod cgi + fi + + if [ "$ZM_DB_HOST" = "localhost" ]; then + + if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? + + # + # Get mysql started if it isn't running + # + + if [ -e "/lib/systemd/system/mariadb.service" ]; then + DBSERVICE="mariadb.service" + else + DBSERVICE="mysql.service" + fi + echo "Detected db service is $DBSERVICE" + if systemctl is-failed --quiet $DBSERVICE; then + echo "$DBSERVICE is in a failed state; it will not be started." + echo "If you have already resolved the problem preventing $DBSERVICE from running," + echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder." + exit 1 + fi + + if ! systemctl is-active --quiet mysql.service mariadb.service; then + # Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service + # However, mariadb.service will not return the status of mysql.service. + deb-systemd-invoke start $DBSERVICE + fi + + # Make sure systemctl status exit code is 0; i.e. the DB is running + if systemctl is-active --quiet "$DBSERVICE"; then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + echo "Creating zm db" + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi + # This creates the user. + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + else + echo "Updating permissions" + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi + + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + + # Add any new PTZ control configurations to the database (will not overwrite) + zmcamtool.pl --import >/dev/null 2>&1 + echo "Done Updating; starting ZoneMinder." + else + echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'MySQL/MariaDB not found; assuming remote server.' + fi + + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." + fi + deb-systemd-invoke restart zoneminder.service + +fi + +#DEBHELPER# diff --git a/distros/ubuntu2004/zoneminder.postrm b/distros/ubuntu2004/zoneminder.postrm new file mode 100644 index 000000000..ba2066c8d --- /dev/null +++ b/distros/ubuntu2004/zoneminder.postrm @@ -0,0 +1,14 @@ +#! /bin/sh + +set -e + +if [ "$1" = "purge" ]; then + echo " +Reminder: to completely remove \"zoneminder\" it may be necessary + * to delete database using the following sample command: + sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + * to delete remaining data files in "/var/cache/zoneminder". +" +fi + +#DEBHELPER# diff --git a/distros/ubuntu2004/zoneminder.preinst b/distros/ubuntu2004/zoneminder.preinst new file mode 100644 index 000000000..6088c3ea9 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +#DEBHELPER# diff --git a/distros/ubuntu2004/zoneminder.service b/distros/ubuntu2004/zoneminder.service new file mode 100644 index 000000000..cb2d6791e --- /dev/null +++ b/distros/ubuntu2004/zoneminder.service @@ -0,0 +1,23 @@ +# ZoneMinder systemd unit file +# This file is intended to work with Debian distributions + +[Unit] +Description=ZoneMinder CCTV recording and surveillance system +After=network.target mysql.service +# Remarked out so that it will start ZM on machines that don't have mysql installed +#Requires=mysql.service + +[Service] +#User=www-data +Type=forking +ExecStart=/usr/bin/zmpkg.pl start +ExecReload=/usr/bin/zmpkg.pl restart +ExecStop=/usr/bin/zmpkg.pl stop +PIDFile=/run/zm/zm.pid +Restart=always +RestartSec=10 +Environment=TZ=:/etc/localtime +TimeoutSec=600 + +[Install] +WantedBy=multi-user.target diff --git a/distros/ubuntu2004/zoneminder.tmpfile b/distros/ubuntu2004/zoneminder.tmpfile new file mode 100644 index 000000000..cbfdec1de --- /dev/null +++ b/distros/ubuntu2004/zoneminder.tmpfile @@ -0,0 +1,4 @@ +d /run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data +d /var/tmp/zm 0755 www-data www-data +d /var/cache/zoneminder/cache 0755 www-data www-data From eb26c5f7981b560ff9ea7fdb30aa5cfc92c22bb1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 11:09:12 -0500 Subject: [PATCH 206/319] Fix use of array_key_exists instead of property_exists --- web/includes/Object.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index 9d037c014..e83aa1080 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -281,7 +281,7 @@ class ZM_Object { # Set defaults. Note that we only replace "" with null, not other values # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value foreach ( $this->defaults as $field => $default ) { - if ( (!array_key_exists($field, $this)) or ($this->{$field} == '') ) { + if ( (!property_exists($this, $field)) or ($this->{$field} == '') ) { if ( is_array($default) ) { $this->{$field} = $default['default']; } else if ( $default == null ) { From b05916fc50af913e080346bbc11aafc2dcc37139 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 14:39:22 -0400 Subject: [PATCH 207/319] Use === when testing for empty value and setting defaults --- web/includes/Object.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index e83aa1080..1668a7767 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -206,7 +206,8 @@ class ZM_Object { } } } # end foreach default - } + } # end if defaults + foreach ( $new_values as $field => $value ) { if ( method_exists($this, $field) ) { @@ -281,7 +282,7 @@ class ZM_Object { # Set defaults. Note that we only replace "" with null, not other values # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value foreach ( $this->defaults as $field => $default ) { - if ( (!property_exists($this, $field)) or ($this->{$field} == '') ) { + if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) { if ( is_array($default) ) { $this->{$field} = $default['default']; } else if ( $default == null ) { From f06dc3f1718aee437f04d3d0b2598f21d33d1154 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 28 Mar 2020 10:09:13 -0400 Subject: [PATCH 208/319] Update group save action, using Group object methods. Fixes errors on new MariaDB --- web/includes/actions/group.php | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php index 9aa988540..b23659d18 100644 --- a/web/includes/actions/group.php +++ b/web/includes/actions/group.php @@ -21,42 +21,31 @@ // Group edit actions # Should probably verify that each monitor id is a valid monitor, that we have access to. # However at the moment, you have to have System permissions to do this -if ( ! canEdit('Groups') ) { +if ( !canEdit('Groups') ) { ZM\Warning('Need group edit permissions to edit groups'); return; } if ( $action == 'Save' ) { - $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $group_id = null; - if ( !empty($_POST['gid']) ) { + if ( !empty($_POST['gid']) ) $group_id = $_POST['gid']; - dbQuery( - 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + $group = new ZM\Group($group_id); + $group->save( array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - $group_id, + 'Name'=> $_POST['newGroup']['Name'], + 'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), ) ); - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id)); - } else { - dbQuery( - 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - ) - ); - $group_id = dbInsertId(); - } + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id)); + $group_id = $group->Id(); if ( $group_id ) { foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); + dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid)); } } $view = 'none'; $refreshParent = true; $closePopup = true; -} +} ?> From 54995ab0d10c5a4436219e53076360745359a023 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:13:30 -0400 Subject: [PATCH 209/319] handle sort_asc not being set. Handle term['val'] not being set. --- web/includes/functions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index e8adbdb5c..e616e2c8f 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1063,7 +1063,7 @@ function parseSort($saveToSession=false, $querySep='&') { $sortColumn = 'E.StartTime'; break; } - if ( !$_REQUEST['sort_asc'] ) + if ( !isset($_REQUEST['sort_asc']) ) $_REQUEST['sort_asc'] = 0; $sortOrder = $_REQUEST['sort_asc'] ? 'asc' : 'desc'; $sortQuery = $querySep.'sort_field='.validHtmlStr($_REQUEST['sort_field']).$querySep.'sort_asc='.validHtmlStr($_REQUEST['sort_asc']); @@ -1230,6 +1230,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { break; } $valueList = array(); + if ( !isset($term['val']) ) $term['val'] = ''; foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { switch ( $term['attr'] ) { case 'MonitorName': From 17896b656a019ad19d69a22a9fcfe6b59d26966c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:14:46 -0400 Subject: [PATCH 210/319] cleanup and revert adding Event stack trace --- web/js/logger.js | 55 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/web/js/logger.js b/web/js/logger.js index 0a1195043..bf5444268 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -53,12 +53,13 @@ function logReport( level, message, file, line ) { /* eslint-enable no-caller */ if ( !debugReq ) { + debugParms = "view=request&request=log&task=create"; if ( Browser ) { - debugParms = "view=request&request=log&task=create&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); + debugParms += "&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); } else { - debugParms = "view=request&request=log&task=create&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; + debugParms += "&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; } - debugReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'} ); + debugReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'}); } var requestParms = debugParms; requestParms += "&level="+level+"&message="+encodeURIComponent(message); @@ -71,57 +72,57 @@ function logReport( level, message, file, line ) { if ( line ) { requestParms += "&line="+line; } - debugReq.send( requestParms ); + debugReq.send(requestParms); } -function Panic( message ) { - console.error( message ); - logReport( "PNC", message ); - alert( "PANIC: "+message ); +function Panic(message) { + console.error(message); + logReport("PNC", message); + alert("PANIC: "+message); } -function Fatal( message ) { - console.error( message ); +function Fatal(message) { + console.error(message); logReport( "FAT", message ); alert( "FATAL: "+message ); } -function Error( message ) { - console.error( message ); - logReport( "ERR", message ); +function Error(message) { + console.error(message); + logReport("ERR", message); } -function Warning( message ) { - console.warn( message ); - logReport( "WAR", message ); +function Warning(message) { + console.warn(message); + logReport("WAR", message); } -function Info( message ) { - console.info( message ); - logReport( "INF", message ); +function Info(message) { + console.info(message); + logReport("INF", message); } -function Debug( message ) { - console.debug( message ); - //logReport( "DBG", message ); +function Debug(message) { + console.debug(message); + //logReport("DBG", message); } -function Dump( value, label ) { +function Dump(value, label) { if ( label ) { - console.debug( label+" => " ); + console.debug(label+" => "); } - console.debug( value ); + console.debug(value); } window.onerror = function( message, url, line ) { - logReport( "ERR", message, url, line ); + logReport("ERR", message, url, line); }; window.addEventListener("securitypolicyviolation", function logCSP(evt) { var level = evt.disposition == "enforce" ? "ERR" : "DBG"; var message = evt.blockedURI + " violated CSP " + evt.violatedDirective; - if (evt.sample) { + if ( evt.sample ) { message += " (Sample: " + evt.sample + ")"; } logReport(level, message, evt.sourceFile, evt.lineNumber); From c0b7aad2c839e5e16246cf13bb6513bd3bc093d1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 5 Mar 2020 14:10:35 -0500 Subject: [PATCH 211/319] add passive to addEventListener --- web/js/overlay.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/js/overlay.js b/web/js/overlay.js index 6517d2d7d..84547c53f 100644 --- a/web/js/overlay.js +++ b/web/js/overlay.js @@ -51,8 +51,8 @@ var Overlay = new Class({ }, show: function() { this.mask.show(); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); this.element.tween( 'opacity', [0, 1.0] ); this.element.show(); this.element.position(); @@ -80,8 +80,8 @@ var Overlay = new Class({ } updateOverlayLoading(); this.loading.setStyle( 'display', 'block' ); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); }, hideAnimation: function() { if ( this.loading ) { From a7e49bd62adc7dea0a77bc8bff2d1b4516c14111 Mon Sep 17 00:00:00 2001 From: out Date: Thu, 9 Apr 2020 15:30:43 +0800 Subject: [PATCH 212/319] =?UTF-8?q?update=20chinese=20translation=20(?= =?UTF-8?q?=EF=BD=9E=EF=BF=A3=E2=96=BD=EF=BF=A3)=EF=BD=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/lang/cn_zh.php | 285 ++++++++++++++++++++++++++------------------- 1 file changed, 163 insertions(+), 122 deletions(-) diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index 0a751023e..dec44eb95 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -74,12 +74,14 @@ $SLANG = array( '24BitColour' => '24 位彩色', '32BitColour' => '32 位彩色', // Added - 2011-06-15 '8BitGrey' => '8 位灰度', + 'API' => 'API', // Added - 2020-04-09 + 'APIEnabled' => 'API已启用', // Added - 2020-04-09 'Action' => '活动动作', 'Actual' => '实际', 'AddNewControl' => '新建控制', 'AddNewMonitor' => '新建监视器', - 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 - 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 + 'AddNewServer' => '新建服务器', // Added - 2018-08-30 + 'AddNewStorage' => '新建存储', // Added - 2018-08-30 'AddNewUser' => '新建用户', 'AddNewZone' => '新建区域', 'Alarm' => '报警', @@ -90,11 +92,12 @@ $SLANG = array( 'AlarmMaximumFPS' => '报警最大帧率FPS', 'AlarmPx' => '报警像素', 'AlarmRGBUnset' => '你必须设置一个报警颜色(RGB)', - 'AlarmRefImageBlendPct'=> 'Alarm Reference Image Blend %ge', // Added - 2015-04-18 + 'AlarmRefImageBlendPct'=> '报警参考影像混合 %ge', // Added - 2015-04-18 'Alert' => '警报', 'All' => '全部', - 'AnalysisFPS' => 'Analysis FPS', // Added - 2015-07-22 - 'AnalysisUpdateDelay' => 'Analysis Update Delay', // Added - 2015-07-23 + 'AllTokensRevoked' => '已撤销所有tokens', // Added - 2020-04-09 + 'AnalysisFPS' => '分析帧率 FPS', // Added - 2015-07-22 + 'AnalysisUpdateDelay' => '分析更新延迟', // Added - 2015-07-23 'Apply' => '应用', 'ApplyingStateChange' => '状态改变生效', 'ArchArchived' => '仅限于存档', @@ -107,30 +110,31 @@ $SLANG = array( 'AttrArchiveStatus' => '存档状态', 'AttrAvgScore' => '平均分数', 'AttrCause' => '原因', - 'AttrDiskBlocks' => '磁碟区块', - 'AttrDiskPercent' => '磁碟百分比', - 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 + 'AttrDiskBlocks' => '磁盘区块', + 'AttrDiskPercent' => '磁盘百分比', + 'AttrDiskSpace' => '磁盘空间', // Added - 2018-08-30 'AttrDuration' => '过程', - 'AttrEndDate' => 'End Date', // Added - 2018-08-30 - 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 - 'AttrEndTime' => 'End Time', // Added - 2018-08-30 - 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 - 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 + 'AttrEndDate' => '结束日期', // Added - 2018-08-30 + 'AttrEndDateTime' => '结束日期/时间', // Added - 2018-08-30 + 'AttrEndTime' => '结束时间', // Added - 2018-08-30 + 'AttrEndWeekday' => '结束星期', // Added - 2018-08-30 + 'AttrFilterServer' => '过滤服务正运行在', // Added - 2018-08-30 'AttrFrames' => '帧', 'AttrId' => 'Id', 'AttrMaxScore' => '最大分数', 'AttrMonitorId' => '监视器 Id', 'AttrMonitorName' => '监视器名称', - 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 + 'AttrMonitorServer' => '监控服务正运行在', // Added - 2018-08-30 'AttrName' => '名称', 'AttrNotes' => '备注', - 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 - 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 - 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 - 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 - 'AttrStateId' => 'Run State', // Added - 2018-08-30 - 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 - 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 + 'AttrSecondaryStorageArea'=> '第二存储区域', // Added - 2020-04-09 + 'AttrStartDate' => '开始日期', // Added - 2018-08-30 + 'AttrStartDateTime' => '开始日期/时间', // Added - 2018-08-30 + 'AttrStartTime' => '开始时间', // Added - 2018-08-30 + 'AttrStartWeekday' => '开始星期', // Added - 2018-08-30 + 'AttrStateId' => '运行状态', // Added - 2018-08-30 + 'AttrStorageArea' => '存储区域', // Added - 2018-08-30 + 'AttrStorageServer' => '存储服务器', // Added - 2018-08-30 'AttrSystemLoad' => '系统负载', 'AttrTotalScore' => '总分数', 'Auto' => '自动', @@ -141,10 +145,10 @@ $SLANG = array( 'BackgroundFilter' => '在后台运行筛选器', 'BadAlarmFrameCount' => '报警帧数必须设为大于1的整数', 'BadAlarmMaxFPS' => '报警最大帧率必须是正整数或正浮点数', - 'BadAnalysisFPS' => 'Analysis FPS must be a positive integer or floating point value', // Added - 2015-07-22 - 'BadAnalysisUpdateDelay'=> 'Analysis update delay must be set to an integer of zero or more', // Added - 2015-07-23 + 'BadAnalysisFPS' => '分析帧率 FPS 必须是正整数或正浮点数', // Added - 2015-07-22 + 'BadAnalysisUpdateDelay'=> '分析更新延迟必须设为大于零的整数', // Added - 2015-07-23 'BadChannel' => '通道必须设为大于零的整数', - 'BadColours' => 'Target colour must be set to a valid value', // Added - 2011-06-15 + 'BadColours' => '颜色必须设置为有效值', // Added - 2011-06-15 'BadDevice' => '必须为器件设置有效值', 'BadFPSReportInterval' => 'FPS帧数报告间隔缓冲数必须是0以上整数', 'BadFormat' => '格式必须设为大于零的整数', @@ -155,9 +159,10 @@ $SLANG = array( 'BadLabelX' => '标签 X 坐标必须设为大于零的整数', 'BadLabelY' => '标签 Y 坐标必须设为大于零的整数', 'BadMaxFPS' => '最大帧数FPS必须设为正整数或着浮点数', - 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more', + 'BadMotionFrameSkip' => '运动跳帧数必须设为大于零的整数', 'BadNameChars' => '名称只可以包含字母,数字,波折号和下划线', - 'BadPalette' => 'Palette must be set to a valid value', // Added - 2009-03-31 + 'BadNoSaveJPEGsOrVideoWriter'=> '保存为JPEGs和保存为视频同时禁用后。不会有任何记录 ', // Added - 2020-04-09 + 'BadPalette' => '调色板必须设为有效值', // Added - 2009-03-31 'BadPath' => '路径必须设为有效值', 'BadPort' => '端口必须设为有效数字', 'BadPostEventCount' => '之后事件影像数目必须设为大于零的整数', @@ -165,11 +170,11 @@ $SLANG = array( 'BadRefBlendPerc' => '参考混合百分比必须设为一个正整数', 'BadSectionLength' => '节长度必须设为30的整数倍', 'BadSignalCheckColour' => '信号检查颜色必须设为有效的RGB颜色字符', - 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 + 'BadSourceType' => '源类型 \"网站\" 要求 功能 设置为 \"监视\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => '流重放缓冲必须为零或更多整数', 'BadWarmupCount' => '预热帪必须设为零或更多整数', 'BadWebColour' => 'Web颜色必须设为有效Web颜色字符', - 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 + 'BadWebSitePath' => '请输入一个完整的网站链接,包括http://或https://前缀。', // Added - 2018-08-30 'BadWidth' => '宽度必须设为有效值', 'Bandwidth' => '带宽', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -177,9 +182,9 @@ $SLANG = array( 'BlobSizes' => 'Blob大小', 'Blobs' => 'Blobs', 'Brightness' => '亮度', - 'Buffer' => 'Buffer', // Added - 2015-04-18 + 'Buffer' => '缓冲', // Added - 2015-04-18 'Buffers' => '缓冲器', - 'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18 + 'CSSDescription' => '改变本机默认css', // Added - 2015-04-18 'CanAutoFocus' => '可以自动对焦', 'CanAutoGain' => '可以自动增益控制', 'CanAutoIris' => '可以自动光圈', @@ -204,8 +209,8 @@ $SLANG = array( 'CanMoveMap' => '可以映射网格移动', 'CanMoveRel' => '可以相对移动', 'CanPan' => '可以平移' , + 'CanReboot' => '可以重启', 'CanReset' => '可以复位', - 'CanReboot' => 'Can Reboot', 'CanSetPresets' => '可以进行预设', 'CanSleep' => '可以休眠', 'CanTilt' => '可以倾斜', @@ -224,22 +229,23 @@ $SLANG = array( 'CaptureHeight' => '捕获高度', 'CaptureMethod' => '捕获方式', 'CapturePalette' => '捕获调色板', - 'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18 + 'CaptureResolution' => '捕获分辨率', // Added - 2015-04-18 'CaptureWidth' => '捕获宽度', 'Cause' => '原因', 'CheckMethod' => '报警检查方式', - 'ChooseDetectedCamera' => 'Choose Detected Camera', // Added - 2009-03-31 + 'ChooseDetectedCamera' => '选择检测到的摄像头', // Added - 2009-03-31 + 'ChooseDetectedProfile'=> '选择检测到的流媒体', // Added - 2020-04-09 'ChooseFilter' => '选择筛选器', - 'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17 + 'ChooseLogFormat' => '选择日志格式', // Added - 2011-06-17 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => '选择预置', - 'Clear' => 'Clear', // Added - 2011-06-16 - 'CloneMonitor' => 'Clone', // Added - 2018-08-30 + 'Clear' => '清除', // Added - 2011-06-16 + 'CloneMonitor' => '克隆', // Added - 2018-08-30 'Close' => '关闭', 'Colour' => '彩色', 'Command' => '命令', - 'Component' => 'Component', // Added - 2011-06-16 - 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 + 'Component' => '组件', // Added - 2011-06-16 + 'ConcurrentFilter' => '同时应用筛选器', // Added - 2018-08-30 'Config' => '配置', 'ConfiguredFor' => '配置标的', 'ConfirmDeleteEvents' => '确认希望删除所选事件?', @@ -257,24 +263,25 @@ $SLANG = array( 'ControlDevice' => '控制设备', 'ControlType' => '控制类型', 'Controllable' => '可控', - 'Current' => 'Current', // Added - 2015-04-18 + 'Current' => '现在', // Added - 2015-04-18 'Cycle' => '循环', 'CycleWatch' => '循环监视', - 'DateTime' => 'Date/Time', // Added - 2011-06-16 + 'DateTime' => '日期', // Added - 2011-06-16 'Day' => '日', 'Debug' => '调试', + 'DefaultCodec' => '默认即时观看方法', // Added - 2020-04-09 'DefaultRate' => '缺省速率', 'DefaultScale' => '缺省缩放', 'DefaultView' => '缺省视角', - 'Deinterlacing' => 'Deinterlacing', // Added - 2015-04-18 - 'Delay' => 'Delay', // Added - 2015-04-18 + 'Deinterlacing' => '去隔行', // Added - 2015-04-18 + 'Delay' => '延迟', // Added - 2015-04-18 'Delete' => '删除', 'DeleteAndNext' => '删除并下一个', 'DeleteAndPrev' => '删除并前一个', 'DeleteSavedFilter' => '删除存储过滤器', 'Description' => '描述', - 'DetectedCameras' => 'Detected Cameras', // Added - 2009-03-31 - 'DetectedProfiles' => 'Detected Profiles', // Added - 2015-04-18 + 'DetectedCameras' => '检测到的摄像头', // Added - 2009-03-31 + 'DetectedProfiles' => '检测到的流媒体', // Added - 2015-04-18 'Device' => '设备', 'DeviceChannel' => '设备通道', 'DeviceFormat' => '设备格式', @@ -283,10 +290,10 @@ $SLANG = array( 'Devices' => '设备', 'Dimensions' => '维度', 'DisableAlarms' => '关闭警报', - 'Disk' => '磁碟', - 'Display' => 'Display', // Added - 2011-01-30 - 'Displaying' => 'Displaying', // Added - 2011-06-16 - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', + 'Disk' => '磁盘', + 'Display' => '显示', // Added - 2011-01-30 + 'Displaying' => '正在显示', // Added - 2011-06-16 + 'DoNativeMotionDetection'=> '在本机进行运动检测', 'Donate' => '请捐款', 'DonateAlready' => '不,我已经捐赠过了', 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

    如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。

    感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', @@ -297,11 +304,11 @@ $SLANG = array( 'DonateRemindWeek' => '现在不,1星期内再次提醒我', 'DonateYes' => '好,我现在就捐款', 'Download' => '下载', - 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 + 'DownloadVideo' => '下载视频', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duration', 'Edit' => '编辑', - 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 + 'EditLayout' => '编辑布局', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '启动报警', 'Enabled' => '已启动', @@ -318,8 +325,9 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '排除', 'Execute' => '执行', - 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 + 'Exif' => '嵌入EXIF信息到图片', // Added - 2018-08-30 'Export' => '导出', + 'ExportCompress' => '使用压缩', // Added - 2020-04-09 'ExportDetails' => '导出时间详情', 'ExportFailed' => '导出失败', 'ExportFormat' => '导出文件格式', @@ -327,7 +335,8 @@ $SLANG = array( 'ExportFormatZip' => 'Zip', 'ExportFrames' => '导出帧详情', 'ExportImageFiles' => '导出影像文件', - 'ExportLog' => 'Export Log', // Added - 2011-06-17 + 'ExportLog' => '导出日志', // Added - 2011-06-17 + 'ExportMatches' => '导出匹配项', // Added - 2020-04-09 'ExportMiscFiles' => '导出其他文件 (如果存在)', 'ExportOptions' => '导出选项', 'ExportSucceeded' => '导出成功', @@ -341,29 +350,30 @@ $SLANG = array( 'Feed' => '转送源', 'Ffmpeg' => 'Ffmpeg', 'File' => '文件', - 'Filter' => 'Filter', // Added - 2015-04-18 - 'FilterArchiveEvents' => '将全部匹配项存档', - 'FilterDeleteEvents' => '将全部匹配项删除', - 'FilterEmailEvents' => '将全部匹配项详情电邮出去', + 'Filter' => '过滤器', // Added - 2015-04-18 + 'FilterArchiveEvents' => '存档全部匹配项', + 'FilterCopyEvents' => '复制全部匹配项', // Added - 2020-04-09 + 'FilterDeleteEvents' => '删除全部匹配项', + 'FilterEmailEvents' => '邮件发送全部匹配项详情', 'FilterExecuteEvents' => '执行全部匹配项命令', - 'FilterLog' => 'Filter log', // Added - 2015-04-18 + 'FilterLog' => '过滤日志', // Added - 2015-04-18 'FilterMessageEvents' => '全部匹配项的信息详情', - 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 + 'FilterMoveEvents' => '移除全部匹配项', // Added - 2018-08-30 'FilterPx' => '过滤器像素', 'FilterUnset' => '您必须指定过滤器宽度和高度', - 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 + 'FilterUpdateDiskSpace'=> '刷新磁盘空间', // Added - 2018-08-30 'FilterUploadEvents' => '上传全部匹配项', 'FilterVideoEvents' => '为全部匹配项创建视频', 'Filters' => '过滤器', 'First' => '首先', 'FlippedHori' => '水平翻转', 'FlippedVert' => '垂直翻转', - 'FnMocord' => 'Mocord', // Added 2013.08.16. - 'FnModect' => 'Modect', // Added 2013.08.16. - 'FnMonitor' => 'Monitor', // Added 2013.08.16. + 'FnMocord' => '运动侦测并录制', // Added 2013.08.16. + 'FnModect' => '运动侦测', // Added 2013.08.16. + 'FnMonitor' => '监视', // Added 2013.08.16. 'FnNodect' => 'Nodect', // Added 2013.08.16. - 'FnNone' => 'None', // Added 2013.08.16. - 'FnRecord' => 'Record', // Added 2013.08.16. + 'FnNone' => '无', // Added 2013.08.16. + 'FnRecord' => '录制', // Added 2013.08.16. 'Focus' => '聚焦', 'ForceAlarm' => '强制报警', 'Format' => '格式', @@ -376,7 +386,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => '增益', 'General' => '一般', - 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 + 'GenerateDownload' => '创建下载项', // Added - 2018-08-30 'GenerateVideo' => '创建视频', 'GeneratingVideo' => '正在创建视频', 'GoToZoneMinder' => '访问 ZoneMinder.com', @@ -397,7 +407,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高 B/W', 'Home' => '主页', - 'Hostname' => 'Hostname', // Added - 2018-08-30 + 'Hostname' => '主机名', // Added - 2018-08-30 'Hour' => '小时', 'Hue' => '色调', 'Id' => 'Id', @@ -408,6 +418,7 @@ $SLANG = array( 'Images' => '影像', 'In' => '在', 'Include' => '包含', + 'InvalidateTokens' => '使所有创建的tokens无效', // Added - 2020-04-09 'Inverted' => '反向', 'Iris' => '光圈', 'KeyString' => '密钥字符', @@ -415,19 +426,19 @@ $SLANG = array( 'Language' => '语言', 'Last' => '最后', 'Layout' => '布局', - 'Level' => 'Level', // Added - 2011-06-16 + 'Level' => '级别', // Added - 2011-06-16 'Libvlc' => 'Libvlc', 'LimitResultsPost' => '个结果', // This is used at the end of the phrase 'Limit to first N results only' 'LimitResultsPre' => '仅限于开始', // This is used at the beginning of the phrase 'Limit to first N results only' - 'Line' => 'Line', // Added - 2011-06-16 + 'Line' => '行', // Added - 2011-06-16 'LinkedMonitors' => '管理监视器', 'List' => '列表', - 'ListMatches' => 'List Matches', // Added - 2018-08-30 + 'ListMatches' => '列出匹配项', // Added - 2018-08-30 'Load' => '加载', 'Local' => '本地', - 'Log' => 'Log', // Added - 2011-06-16 + 'Log' => '日志', // Added - 2011-06-16 'LoggedInAs' => '登录为', - 'Logging' => 'Logging', // Added - 2011-06-16 + 'Logging' => '日志', // Added - 2011-06-16 'LoggingIn' => '登录', 'Login' => '登入', 'Logout' => '登出', @@ -465,7 +476,7 @@ $SLANG = array( 'MaximumFPS' => '最大帧率 FPS', 'Medium' => '中等', 'MediumBW' => '中等 B/W', - 'Message' => 'Message', // Added - 2011-06-16 + 'Message' => '消息', // Added - 2011-06-16 'MinAlarmAreaLtMax' => '最小报警区域应该小于最大区域', 'MinAlarmAreaUnset' => '您必须指定最小报警像素数量', 'MinBlobAreaLtMax' => '最小blob区必须小数最大区域', @@ -500,25 +511,25 @@ $SLANG = array( 'MinZoomSpeed' => '最小缩放速度', 'MinZoomStep' => '最小缩放步进', 'Misc' => '杂项', - 'Mode' => 'Mode', // Added - 2015-04-18 + 'Mode' => '模式', // Added - 2015-04-18 'Monitor' => '监视器', 'MonitorIds' => '监视器 Ids', 'MonitorPreset' => '监视器预设值', 'MonitorPresetIntro' => '从以下列表中选择一个合适的预设值.

    请注意该方式可能覆盖您为该监视器配置的数值.

    ', - 'MonitorProbe' => 'Monitor Probe', // Added - 2009-03-31 - 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

    Select the desired entry from the list below.

    Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

    ', // Added - 2009-03-31 + 'MonitorProbe' => '监视器探测', // Added - 2009-03-31 + 'MonitorProbeIntro' => '以下列表显示了检测到的模拟和网络摄像头,以及其可用状态

    请从列表中选择你想要的项

    请注意可能有些摄像头并没有检测到,而且选择一个摄像头可能覆盖一些你已经设置的配置。

    ', // Added - 2009-03-31 'Monitors' => '监视器', 'Montage' => '镜头组接', 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', - 'More' => 'More', // Added - 2011-06-16 - 'MotionFrameSkip' => 'Motion Frame Skip', + 'More' => '更多', // Added - 2011-06-16 + 'MotionFrameSkip' => '运动侦测跳帧', 'Move' => '移动', 'Mtg2widgrd' => '2-wide grid', // Added 2013.08.15. 'Mtg3widgrd' => '3-wide grid', // Added 2013.08.15. 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. 'Mtg4widgrd' => '4-wide grid', // Added 2013.08.15. - 'MtgDefault' => 'Default', // Added 2013.08.15. + 'MtgDefault' => '默认', // Added 2013.08.15. 'MustBeGe' => '必须大于等于', 'MustBeLe' => '必须小于等于', 'MustConfirmPassword' => '您必须确认密码', @@ -534,9 +545,10 @@ $SLANG = array( 'NewState' => '新状态', 'NewUser' => '新用户', 'Next' => '下一个', + 'NextMonitor' => '下一个监视器', // Added - 2020-04-09 'No' => '不', - 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 - 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 + 'NoDetectedCameras' => '没有检测到摄像头', // Added - 2009-03-31 + 'NoDetectedProfiles' => '没有检测到流媒体', // Added - 2018-08-30 'NoFramesRecorded' => '该事件没有相关帧的记录', 'NoGroup' => '无组', 'NoSavedFilters' => '没有保存过滤器', @@ -548,25 +560,27 @@ $SLANG = array( 'NumPresets' => '数值预置', 'Off' => '关', 'On' => '开', - 'OnvifCredentialsIntro'=> 'Please supply user name and password for the selected camera.
    If no user has been created for the camera then the user given here will be created with the given password.

    ', // Added - 2015-04-18 + 'OnvifCredentialsIntro'=> '请为所选摄像头提供用户名和密码。
    如果还没有为这台摄像头创建过用户,那么将会使用提供的用户名和密码创建用户

    ', // Added - 2015-04-18 'OnvifProbe' => 'ONVIF', // Added - 2015-04-18 - 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

    Select the desired entry from the list below.

    Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

    ', // Added - 2015-04-18 + 'OnvifProbeIntro' => '以下列表显示了检测到的ONVIF摄像头和其可用状态。

    选择一个你想要的项

    请注意可能有些摄像头并没有检测到,而且选择一个摄像头可能覆盖一些你已经设置的配置。

    ', // Added - 2015-04-18 'OpEq' => '等于', 'OpGt' => '大于', 'OpGtEq' => '大于等于', 'OpIn' => '在集', - 'OpIs' => 'is', // Added - 2018-08-30 - 'OpIsNot' => 'is not', // Added - 2018-08-30 + 'OpIs' => '是', // Added - 2018-08-30 + 'OpIsNot' => '不是', // Added - 2018-08-30 + 'OpLike' => '包含', // Added - 2020-04-09 'OpLt' => '小于', 'OpLtEq' => '小于等于', 'OpMatches' => '匹配', 'OpNe' => '不等于', 'OpNotIn' => '未在集', + 'OpNotLike' => '未包含', // Added - 2020-04-09 'OpNotMatches' => '不匹配', 'Open' => '打开', 'OptionHelp' => '选项帮助', 'OptionRestartWarning' => '这些改动在系统运行时可以不会完全生效.\n 当你设置完毕改动后\n请确认\n您重新启动 ZoneMinder.', - 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 + 'OptionalEncoderParam' => '编码参数(可选)', // Added - 2018-08-30 'Options' => '选项', 'OrEnterNewName' => '或输入新名词', 'Order' => '次序', @@ -579,8 +593,12 @@ $SLANG = array( 'PanRight' => '向右平移', 'PanTilt' => '平移/倾斜', 'Parameter' => '参数', + 'ParentGroup' => '父组', // Added - 2020-04-09 'Password' => '密码', 'PasswordsDifferent' => '新建密码和确认密码不一致', + 'PathToApi' => 'Api路径', // Added - 2020-04-09 + 'PathToIndex' => 'Index路径', // Added - 2020-04-09 + 'PathToZMS' => 'ZMS路径', // Added - 2020-04-09 'Paths' => '路径', 'Pause' => '暂停', 'Phone' => '电话', @@ -591,7 +609,7 @@ $SLANG = array( 'Play' => '播放', 'PlayAll' => '播放全部', 'PleaseWait' => '请等待', - 'Plugins' => 'Plugins', + 'Plugins' => '插件', 'Point' => '点', 'PostEventImageBuffer' => '事件之后影像数', 'PreEventImageBuffer' => '时间之前影像数', @@ -599,18 +617,31 @@ $SLANG = array( 'Preset' => '预置', 'Presets' => '预置', 'Prev' => '前', - 'Probe' => 'Probe', // Added - 2009-03-31 - 'ProfileProbe' => 'Stream Probe', // Added - 2015-04-18 - 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

    Select the desired entry from the list below.

    Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

    ', // Added - 2015-04-18 + 'PreviousMonitor' => '前一个监视器', // Added - 2020-04-09 + 'Privacy' => 'Privacy', // Added - 2020-04-09 + 'PrivacyAbout' => '关于', // Added - 2020-04-09 + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', // Added - 2020-04-09 + 'PrivacyConclusionText'=> 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', // Added - 2020-04-09 + 'PrivacyContact' => '联系', // Added - 2020-04-09 + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

    For support, there are three primary ways to engage with the community:

    Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

    ', // Added - 2020-04-09 + 'PrivacyCookies' => 'Cookies', // Added - 2020-04-09 + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', // Added - 2020-04-09 + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
    • Id
    • Name
    • Type
    • Function
    • Width
    • Height
    • Colours
    • MaxFPS
    • AlarmMaxFPS
    ', // Added - 2020-04-09 + 'PrivacyTelemetry' => 'Telemetry', // Added - 2020-04-09 + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
    • A unique identifier (UUID)
    • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
    • Current time
    • Total number of monitors
    • Total number of events
    • System architecture
    • Operating system kernel, distro, and distro version
    • Version of ZoneMinder
    • Total amount of memory
    • Number of cpu cores
    ', // Added - 2020-04-09 + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', // Added - 2020-04-09 + 'Probe' => '探测', // Added - 2009-03-31 + 'ProfileProbe' => '流媒体探测', // Added - 2015-04-18 + 'ProfileProbeIntro' => '以下列表显示了所选摄像头可用的流媒体。

    从列表中选择一个你想要的项

    请注意ZoneMinder不能设置额外的配置并且选择摄像头可能会覆盖一些你已设置的配置。

    ', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => '协议', - 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 - 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 + 'RTSPDescribe' => '使用 RTSP Response 媒体链接', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP传输协议', // Added - 2018-08-30 'Rate' => '速率', 'Real' => '实际', - 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 + 'RecaptchaWarning' => '你的reCaptcha秘匙无效。 请更正,否则reCaptcha无法工作', // Added - 2018-08-30 'Record' => '记录', - 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 + 'RecordAudio' => '记录事件时保存音频.', // Added - 2018-08-30 'RefImageBlendPct' => '参考影像混合 %ge', 'Refresh' => '刷新', 'Remote' => '远程', @@ -626,7 +657,7 @@ $SLANG = array( 'ReplayAll' => '全部事件', 'ReplayGapless' => '无间隙事件', 'ReplaySingle' => '单一事件', - 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 + 'ReportEventAudit' => '事件报表', // Added - 2018-08-30 'Reset' => '重置', 'ResetEventCounts' => '重置事件数', 'Restart' => '重启动', @@ -635,24 +666,29 @@ $SLANG = array( 'RestrictedMonitors' => '受限监视器', 'ReturnDelay' => '返回延时', 'ReturnLocation' => '返回位置', + 'RevokeAllTokens' => '撤销所有Tokens', // Added - 2020-04-09 'Rewind' => '重绕', 'RotateLeft' => '向左旋转', 'RotateRight' => '向右旋转', - 'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25 + 'RunAudit' => '审计', // Added - 2020-04-09 + 'RunEventNotification' => '事件提醒', // Added - 2020-04-09 + 'RunLocalUpdate' => '请运行zmupdate.pl来更新', // Added - 2011-05-25 'RunMode' => '运行模式', 'RunState' => '运行状态', + 'RunStats' => '状态检测', // Added - 2020-04-09 + 'RunTrigger' => '触发', // Added - 2020-04-09 'Running' => '运行', 'Save' => '保存', 'SaveAs' => '另存为', 'SaveFilter' => '存储过滤器', - 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 + 'SaveJPEGs' => '保存为JPEGs', // Added - 2018-08-30 'Scale' => '比例', 'Score' => '分数', 'Secs' => '秒', 'Sectionlength' => '段长度', 'Select' => '选择', - 'SelectFormat' => 'Select Format', // Added - 2011-06-17 - 'SelectLog' => 'Select Log', // Added - 2011-06-17 + 'SelectFormat' => '选择格式', // Added - 2011-06-17 + 'SelectLog' => '选择日志', // Added - 2011-06-17 'SelectMonitors' => '选择监视器', 'SelfIntersecting' => '多边形边线不得交叉', 'Set' => '设置', @@ -661,10 +697,11 @@ $SLANG = array( 'Settings' => '设置', 'ShowFilterWindow' => '显示过滤器视窗', 'ShowTimeline' => '显示时间轴', - 'SignalCheckColour' => '型号检查颜色', - 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 + 'Shutdown' => '关机', // Added - 2020-04-09 + 'SignalCheckColour' => '信号检查颜色', + 'SignalCheckPoints' => '信号检测点数目', // Added - 2018-08-30 'Size' => '大小', - 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 + 'SkinDescription' => '改变本机默认皮肤', // Added - 2011-01-30 'Sleep' => '睡眠', 'SortAsc' => '升序', 'SortBy' => '排序', @@ -682,10 +719,10 @@ $SLANG = array( 'State' => '状态', 'Stats' => '统计', 'Status' => '状况', - 'StatusConnected' => 'Capturing', // Added - 2018-08-30 - 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 - 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 - 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 + 'StatusConnected' => '正在捕获', // Added - 2018-08-30 + 'StatusNotRunning' => '未运行', // Added - 2018-08-30 + 'StatusRunning' => '未捕获', // Added - 2018-08-30 + 'StatusUnknown' => '未知', // Added - 2018-08-30 'Step' => '步进', 'StepBack' => '单步后退', 'StepForward' => '单步前进', @@ -696,14 +733,16 @@ $SLANG = array( 'Stills' => '静止', 'Stop' => '停止', 'Stopped' => '已停止', - 'StorageArea' => 'Storage Area', // Added - 2018-08-30 - 'StorageScheme' => 'Scheme', // Added - 2018-08-30 + 'StorageArea' => '存储区域', // Added - 2018-08-30 + 'StorageDoDelete' => '开始删除', // Added - 2020-04-09 + 'StorageScheme' => '存储方案', // Added - 2018-08-30 'Stream' => '流', 'StreamReplayBuffer' => '流重放影像缓冲', 'Submit' => '发送', 'System' => '系统', - 'SystemLog' => 'System Log', // Added - 2011-06-16 - 'TargetColorspace' => 'Target colorspace', // Added - 2015-04-18 + 'SystemLog' => '系统日志', // Added - 2011-06-16 + 'TZUnset' => '未设置 - 使用php.ini的配置', // Added - 2020-04-09 + 'TargetColorspace' => '色彩空间', // Added - 2015-04-18 'Tele' => 'Tele', 'Thumbnail' => '缩略图', 'Tilt' => '倾斜', @@ -711,18 +750,18 @@ $SLANG = array( 'TimeDelta' => '相对时间', 'TimeStamp' => '时间戳', 'Timeline' => '时间轴', - 'TimelineTip1' => 'Pass your mouse over the graph to view a snapshot image and event details.', // Added 2013.08.15. - 'TimelineTip2' => 'Click on the coloured sections of the graph, or the image, to view the event.', // Added 2013.08.15. + 'TimelineTip1' => '移动你的鼠标到图表上来查看快照图片和事件详情。', // Added 2013.08.15. + 'TimelineTip2' => '单击图形或图像的彩色部分来查看事件。', // Added 2013.08.15. 'TimelineTip3' => 'Click on the background to zoom in to a smaller time period based around your click.', // Added 2013.08.15. 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '时间戳', 'TimestampLabelFormat' => '时间戳标签格式', - 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 + 'TimestampLabelSize' => '字体大小', // Added - 2018-08-30 'TimestampLabelX' => '时间戳标签 X', 'TimestampLabelY' => '时间戳标签 Y', 'Today' => '今天', 'Tools' => '工具', - 'Total' => 'Total', // Added - 2011-06-16 + 'Total' => '总', // Added - 2011-06-16 'TotalBrScore' => '总
    分数', 'TrackDelay' => '轨迹延时', 'TrackMotion' => '轨迹运动', @@ -737,23 +776,24 @@ $SLANG = array( 'Update' => '更新', 'UpdateAvailable' => '有新版本的ZoneMinder.', 'UpdateNotNecessary' => '无须更新', - 'Updated' => 'Updated', // Added - 2011-06-16 - 'Upload' => 'Upload', // Added - 2011-08-23 + 'Updated' => '已更新', // Added - 2011-06-16 + 'Upload' => '上传', // Added - 2011-08-23 'UseFilter' => '使用筛选器', 'UseFilterExprsPost' => ' 筛选器 表达式', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => '使用 ', // This is used at the beginning of the phrase 'use N filter expressions' - 'UsedPlugins' => 'Used Plugins', + 'UsedPlugins' => '已使用的插件', 'User' => '用户', 'Username' => '用户名', 'Users' => '用户', 'V4L' => 'V4L', // Added - 2015-04-18 - 'V4LCapturesPerFrame' => 'Captures Per Frame', // Added - 2015-04-18 - 'V4LMultiBuffer' => 'Multi Buffering', // Added - 2015-04-18 + 'V4LCapturesPerFrame' => '每帧捕获', // Added - 2015-04-18 + 'V4LMultiBuffer' => '多缓冲', // Added - 2015-04-18 'Value' => '数值', 'Version' => '版本', 'VersionIgnore' => '忽略该版本', 'VersionRemindDay' => '一天内再次提醒', 'VersionRemindHour' => '一小时内再次提醒', + 'VersionRemindMonth' => '一个月内再次提醒', // Added - 2020-04-09 'VersionRemindNever' => '不再提醒新版本', 'VersionRemindWeek' => '一周内再次提醒', 'Video' => '视频', @@ -764,17 +804,18 @@ $SLANG = array( 'VideoGenParms' => '视频产生参数', 'VideoGenSucceeded' => '视频产生成功!', 'VideoSize' => '视频尺寸', - 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 + 'VideoWriter' => '保存为视频', // Added - 2018-08-30 'View' => '查看', 'ViewAll' => '查看全部', 'ViewEvent' => '查看事件', + 'ViewMatches' => '查看匹配项', // Added - 2020-04-09 'ViewPaged' => '查看分页', 'Wake' => '唤醒', 'WarmupFrames' => '预热帪', 'Watch' => '观察', 'Web' => 'Web', 'WebColour' => 'Web颜色', - 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 + 'WebSiteUrl' => '网站链接', // Added - 2018-08-30 'Week' => '周', 'White' => '白', 'WhiteBalance' => '白平衡', @@ -797,7 +838,7 @@ $SLANG = array( 'ZoneMinMaxBlobs' => '最小/最大污渍区数 Blobs', 'ZoneMinMaxFiltArea' => '最小/最大过滤区域', 'ZoneMinMaxPixelThres' => '最小/最大像素阈值(0-255)', - 'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17 + 'ZoneMinderLog' => 'ZoneMinder日志', // Added - 2011-06-17 'ZoneOverloadFrames' => '忽略过载帪数', 'Zones' => '区域', 'Zoom' => '缩放', @@ -814,7 +855,7 @@ $CLANG = array( 'MonitorCount' => '%1$s %2$s', // For example '4 Monitors' (from Vlang below) 'MonitorFunction' => '监视器 %1$s 功能', 'RunningRecentVer' => '您运行的是最新版的 ZoneMinder, v%s.', - 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25 + 'VersionMismatch' => '版本不匹配, 系统版本是 %1$s, 数据库版本是 %2$s.', // Added - 2011-05-25 ); // The next section allows you to describe a series of word ending and counts used to From 617b74b8bbabea21668ae00b3db4947ccd831524 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Apr 2020 15:28:38 -0400 Subject: [PATCH 213/319] fix botched merge --- web/lang/cn_zh.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index dec44eb95..9cdd9c4f1 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -601,6 +601,7 @@ $SLANG = array( 'PathToZMS' => 'ZMS路径', // Added - 2020-04-09 'Paths' => '路径', 'Pause' => '暂停', + 'PauseCycle' => 'Pause Cycle', // Added - 2020-04-09 'Phone' => '电话', 'PhoneBW' => '电话 B/W', 'Pid' => 'PID', // Added - 2011-06-16 @@ -608,6 +609,7 @@ $SLANG = array( 'Pixels' => '像素', 'Play' => '播放', 'PlayAll' => '播放全部', + 'PlayCycle' => 'Play Cycle', // Added - 2020-04-09 'PleaseWait' => '请等待', 'Plugins' => '插件', 'Point' => '点', From 042086b5650603bd3bb546c90ad08444502b4104 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 21 Feb 2020 09:30:31 -0500 Subject: [PATCH 214/319] Extend width of ControlDevice and Address to full width --- web/skins/classic/css/base/views/monitor.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index 217b2f374..c0f4a36d9 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -10,7 +10,9 @@ } textarea, -input[name="newMonitor[Name]"] { +input[name="newMonitor[Name]"], +input[name="newMonitor[ControlDevice]"], +input[name="newMonitor[ControlAddress]"] { width: 100%; } input[name="newMonitor[Width]"], From 96774bd03868e475069938a3366586857c853514 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:30:30 -0400 Subject: [PATCH 215/319] fix inline javascript in ptz buttons --- .../classic/includes/control_functions.php | 72 +++++++++---------- web/skins/classic/views/js/watch.js | 13 ++-- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 80cfe4aff..6f1399786 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -25,15 +25,15 @@ function controlFocus($monitor, $cmds) { ?>
    - - - + + +
    CanAutoFocus() ) { ?> - - + + @@ -48,15 +48,15 @@ function controlZoom($monitor, $cmds) { ?>
    - - - + + +
    CanAutoZoom() ) { ?> - - + + @@ -70,15 +70,15 @@ function controlIris($monitor, $cmds) { ?>
    - - - + + +
    CanAutoIris() ) { ?> - - + + @@ -93,15 +93,15 @@ function controlWhite($monitor, $cmds) { ?>
    - - - + + +
    CanAutoWhite() ) { ?> - - + + @@ -122,19 +122,19 @@ function controlPanTilt($monitor, $cmds) { $hasTilt = $control->CanTilt(); $hasDiag = $hasPan && $hasTilt && $control->CanMoveDiag(); ?> - - - - + + + + - + - - - - + + + +
    Id() ) ) as $row ) { + foreach ( dbFetchAll($sql, NULL, array($monitor->Id())) as $row ) { $labels[$row['Preset']] = $row['Label']; } @@ -162,7 +162,7 @@ function controlPresets($monitor, $cmds) { NumPresets(); $i++ ) { ?> - + @@ -171,7 +171,7 @@ function controlPresets($monitor, $cmds) { HasHomePreset() ) { ?> - + CanSetPresets() ) { @@ -196,22 +196,22 @@ function controlPower($monitor, $cmds) { CanWake() ) { ?> - + CanSleep() ) { ?> - + CanReset() ) { ?> - + CanReboot() ) { ?> - + diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 83eae3475..0ad3a5536 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -651,8 +651,13 @@ function getControlResponse( respObj, respText ) { } } -function controlCmd( control, event, xtell, ytell ) { - var locParms = ""; +function controlCmd(button) { + + control = button.getAttribute('value'); + xtell = button.getAttribute('xtell'); + ytell = button.getAttribute('ytell'); + + var locParms = ''; if ( event && (xtell || ytell) ) { console.log(event); var target = event.target; @@ -668,7 +673,7 @@ function controlCmd( control, event, xtell, ytell ) { } else if ( xtell == 2 ) { xge = 2*(50 - xge); } - locParms += "&xge="+xge; + locParms += '&xge='+xge; } if ( ytell ) { var yge = parseInt( (y*100)/coords.height ); @@ -677,7 +682,7 @@ function controlCmd( control, event, xtell, ytell ) { } else if ( ytell == 2 ) { yge = 2*(50 - yge); } - locParms += "&yge="+yge; + locParms += '&yge='+yge; } } controlReq.send( controlParms+"&control="+control+locParms ); From ced36a533365912e260c25a3f32f235aa3c72320 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 11:08:23 -0500 Subject: [PATCH 216/319] remove debug and spacing --- web/includes/Event.php | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/web/includes/Event.php b/web/includes/Event.php index 340894379..f1ba99626 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -417,31 +417,27 @@ class Event extends ZM_Object { } } // end if capture file exists } // end if analyze file exists - } + } // end if frame or snapshot $captPath = $eventPath.'/'.$captImage; if ( ! file_exists($captPath) ) { Error("Capture file does not exist at $captPath"); } - //echo "CI:$captImage, CP:$captPath, TCP:$captPath
    "; - $analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']); $analPath = $eventPath.'/'.$analImage; - //echo "AI:$analImage, AP:$analPath, TAP:$analPath
    "; - $alarmFrame = $frame['Type']=='Alarm'; $hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath); $isAnalImage = $hasAnalImage && !$captureOnly; - if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists('imagecreatefromjpeg') ) { + if ( !ZM_WEB_SCALE_THUMBS || ($scale >= SCALE_BASE) || !function_exists('imagecreatefromjpeg') ) { $imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath; $imageFile = $imagePath; $thumbFile = $thumbPath; } else { - if ( version_compare( phpversion(), '4.3.10', '>=') ) + if ( version_compare(phpversion(), '4.3.10', '>=') ) $fraction = sprintf('%.3F', $scale/SCALE_BASE); else $fraction = sprintf('%.3f', $scale/SCALE_BASE); @@ -459,19 +455,19 @@ class Event extends ZM_Object { } $thumbFile = $thumbPath; - if ( $overwrite || ! file_exists( $thumbFile ) || ! filesize( $thumbFile ) ) { + if ( $overwrite || ! file_exists($thumbFile) || ! filesize($thumbFile) ) { // Get new dimensions - list( $imageWidth, $imageHeight ) = getimagesize( $imagePath ); + list( $imageWidth, $imageHeight ) = getimagesize($imagePath); $thumbWidth = $imageWidth * $fraction; $thumbHeight = $imageHeight * $fraction; // Resample - $thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight ); - $image = imagecreatefromjpeg( $imagePath ); - imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight ); + $thumbImage = imagecreatetruecolor($thumbWidth, $thumbHeight); + $image = imagecreatefromjpeg($imagePath); + imagecopyresampled($thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight); - if ( !imagejpeg( $thumbImage, $thumbPath ) ) - Error( "Can't create thumbnail '$thumbPath'" ); + if ( !imagejpeg($thumbImage, $thumbPath) ) + Error("Can't create thumbnail '$thumbPath'"); } } # Create thumbnails @@ -555,7 +551,7 @@ class Event extends ZM_Object { $Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server(); if ( $Server->Id() != ZM_SERVER_ID ) { - $url = $Server->UrlToApi() . '/events/'.$this->{'Id'}.'.json'; + $url = $Server->UrlToApi().'/events/'.$this->{'Id'}.'.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS); From a6f5ae260129ca3f12cab1e6a10cae46be3f910d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 13:17:02 -0500 Subject: [PATCH 217/319] fix cyclePlay button not working. Move SetButtonState into skin.js --- web/skins/classic/js/skin.js | 13 +++++++++++++ web/skins/classic/views/js/event.js | 12 ------------ web/skins/classic/views/js/watch.js | 6 ------ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 2c21c0503..9e9e80764 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -557,3 +557,16 @@ function scaleToFit(baseWidth, baseHeight, scaleEl, bottomEl) { autoScale = closest; return {width: Math.floor(newWidth), height: Math.floor(newHeight), autoScale: autoScale}; } + +function setButtonState(element, butClass) { + if ( element ) { + element.className = butClass; + if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { + element.disabled = true; + } else { + element.disabled = false; + } + } else { + console.log('Element was null in setButtonState'); + } +} diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index f7da1341a..58a700bfb 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -130,18 +130,6 @@ function renderAlarmCues(containerEl) { return alarmHtml; } -function setButtonState( element, butClass ) { - if ( element ) { - element.className = butClass; - if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { - element.disabled = true; - } else { - element.disabled = false; - } - } else { - console.log('Element was null in setButtonState'); - } -} function changeCodec() { location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+$j('#codec').val()); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 0ad3a5536..7fd25d603 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1,9 +1,3 @@ -function setButtonState(element, butClass) { - if ( element ) { - element.className = butClass; - element.disabled = (butClass != 'inactive'); - } -} function showEvents() { $('ptzControls').addClass( 'hidden' ); From 91b1d7bbeb8cd726a26dea8b0ea4f7e3e9cc38f0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 11:52:32 -0500 Subject: [PATCH 218/319] alter setButtonState to take either an id or an element so that we can log the missing element --- web/skins/classic/js/skin.js | 5 +- web/skins/classic/views/js/watch.js | 136 ++++++++++++++-------------- 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 9e9e80764..dd965a39f 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -558,7 +558,8 @@ function scaleToFit(baseWidth, baseHeight, scaleEl, bottomEl) { return {width: Math.floor(newWidth), height: Math.floor(newHeight), autoScale: autoScale}; } -function setButtonState(element, butClass) { +function setButtonState(element_id, butClass) { + var element = $(element_id); if ( element ) { element.className = butClass; if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { @@ -567,6 +568,6 @@ function setButtonState(element, butClass) { element.disabled = false; } } else { - console.log('Element was null in setButtonState'); + console.log('Element was null or not found in setButtonState. id:'+element_id); } } diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 7fd25d603..d4ee2dc34 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -197,9 +197,9 @@ function getStreamCmdResponse(respObj, respText) { $('zoomValue').set('text', streamStatus.zoom); if ( streamStatus.zoom == '1.0' ) { - setButtonState($('zoomOutBtn'), 'unavail'); + setButtonState('zoomOutBtn', 'unavail'); } else { - setButtonState($('zoomOutBtn'), 'inactive'); + setButtonState('zoomOutBtn', 'inactive'); } if ( canEditMonitors ) { @@ -260,107 +260,107 @@ function getStreamCmdResponse(respObj, respText) { } function streamCmdPause( action ) { - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'active'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); if ( action ) { streamCmdReq.send( streamCmdParms+"&command="+CMD_PAUSE ); } } function streamCmdPlay( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'active' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'active'); if ( streamStatus.delayed == true ) { - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); } else { - setButtonState( $('stopBtn'), 'unavail' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'unavail' ); + setButtonState('stopBtn', 'unavail'); + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); } if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PLAY ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PLAY); } } function streamCmdStop( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'unavail' ); - setButtonState( $('stopBtn'), 'active' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'unavail' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'unavail'); + setButtonState('stopBtn', 'active'); + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_STOP ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_STOP); } - setButtonState( $('stopBtn'), 'unavail' ); - setButtonState( $('playBtn'), 'active' ); + setButtonState('stopBtn', 'unavail'); + setButtonState('playBtn', 'active'); } function streamCmdFastFwd( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_FASTFWD ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTFWD); } } function streamCmdSlowFwd( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'active'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SLOWFWD ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWFWD); } - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); + setButtonState('pauseBtn', 'active'); + setButtonState('slowFwdBtn', 'inactive'); } function streamCmdSlowRev( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'active' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'active'); + setButtonState('fastRevBtn', 'inactive'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SLOWREV ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWREV); } - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'active'); + setButtonState('slowRevBtn', 'inactive'); } function streamCmdFastRev( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_FASTREV ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTREV); } } From 8d2ce341498f4504b79a9d43d78cbbd871fbd034 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 3 Mar 2020 16:59:38 -0500 Subject: [PATCH 219/319] include id in monitors dropdown so that it is searchable --- web/skins/classic/views/_monitor_filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index a88c1d437..62a91d7f3 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -233,7 +233,7 @@ $html .= ' } } - $monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Name']; + $monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Id'].' '.$monitors[$i]['Name']; if ( count($selected_monitor_ids) and ! in_array($monitors[$i]['Id'], $selected_monitor_ids) ) { continue; From b6c1a328a80e4cd6325299a6237c82ef3243c8d3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 12:42:41 -0400 Subject: [PATCH 220/319] Fix XSS from monitor Name, group Name, storage Name, server Name --- web/skins/classic/views/console.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index aa0484997..71c60b6f0 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -280,7 +280,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ?> lens - ' : '>') . $monitor['Name'] ?>
    + ' : '>') . validHtmlStr($monitor['Name']) ?>
    ', @@ -290,7 +290,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $Groups = $Group->Parents(); array_push( $Groups, $Group ); } - return implode(' > ', array_map(function($Group){ return ''.$Group->Name().''; }, $Groups )); + return implode(' > ', array_map(function($Group){ return ''.validHtmlStr($Group->Name()).''; }, $Groups )); }, $Monitor->GroupIds() ) ); ?>
    @@ -315,13 +315,13 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
    - Name(); ?> + Name()); ?> '. makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', ''.validHtmlStr($Monitor->Source()).'', canEdit('Monitors') ).''; if ( $show_storage_areas ) { ?> - Name(); } ?> + Name()); } ?> Date: Fri, 10 Apr 2020 11:05:12 -0400 Subject: [PATCH 221/319] defend against XSS in Monitor Name --- web/skins/classic/views/event.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 947bd3972..eb4f2ad1a 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -128,16 +128,16 @@ if ( ! $Event->Id() ) { ?>
    Id() ?> - Id() . ' ' . $Monitor->Name() ?> + Id().' '.validHtmlStr($Monitor->Name()) ?> Cause()) ?> - StartTime() ) ) ?> + StartTime())) ?> Length().'s' ?> Frames() ?>/AlarmFrames() ?> TotScore() ?>/AvgScore() ?>/MaxScore() ?> DiskSpace(null)) . ' on ' . $Event->Storage()->Name(). - ( $Event->SecondaryStorageId() ? ', ' . $Event->SecondaryStorage()->Name() :'' ) + human_filesize($Event->DiskSpace(null)) . ' on ' . validHtmlStr($Event->Storage()->Name()). + ( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>
    From bab609426d4a24aa12695e48856c70d7bee78398 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 17 Apr 2020 09:46:10 -0400 Subject: [PATCH 222/319] spacing in views/event.php --- web/skins/classic/views/event.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index eb4f2ad1a..6ed41b319 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -24,7 +24,7 @@ if ( !canView('Events') ) { } $eid = validInt($_REQUEST['eid']); -$fid = !empty($_REQUEST['fid'])?validInt($_REQUEST['fid']):1; +$fid = !empty($_REQUEST['fid']) ? validInt($_REQUEST['fid']) : 1; $Event = new ZM\Event($eid); if ( $user['MonitorIds'] ) { @@ -79,7 +79,7 @@ $replayModes = array( 'gapless' => translate('ReplayGapless'), ); -if ( isset( $_REQUEST['streamMode'] ) ) +if ( isset($_REQUEST['streamMode']) ) $streamMode = validHtmlStr($_REQUEST['streamMode']); else $streamMode = 'video'; @@ -104,7 +104,16 @@ if ( $Monitor->VideoWriter() == '2' ) { $Zoom = $Event->Height()/$Event->Width(); } -// These are here to figure out the next/prev event +// These are here to figure out the next/prev event, however if there is no filter, then default to one that specifies the Monitor +if ( !isset($_REQUEST['filter']) ) { + $_REQUEST['filter'] = array( + 'Query'=>array( + 'terms'=>array( + array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$Event->MonitorId()) + ) + ) + ); +} parseSort(); parseFilter($_REQUEST['filter']); $filterQuery = $_REQUEST['filter']['query']; From b7fd4e26a5a636e36aa848540ee815dc51852dc3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:05:19 -0400 Subject: [PATCH 223/319] defend against XSS in Monitor Name --- web/skins/classic/views/groups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/groups.php b/web/skins/classic/views/groups.php index 4ffe47f7d..9840d87f7 100644 --- a/web/skins/classic/views/groups.php +++ b/web/skins/classic/views/groups.php @@ -73,7 +73,7 @@ function group_line( $Group ) { } else { $html .= validHtmlStr($Group->Name()); } - $html .= ''. monitorIdsToNames($Group->MonitorIds(), 30).''; + $html .= ''. validHtmlStr(monitorIdsToNames($Group->MonitorIds(), 30)).''; if ( canEdit('Groups') ) { $html .= ''; } From 7347ef403044082847c307c80362892614516012 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 11:18:02 -0500 Subject: [PATCH 224/319] Handle a zero value for scale --- web/skins/classic/views/js/cycle.js | 38 ++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index 4e6de5320..f9b1d19fc 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -46,7 +46,7 @@ function changeSize() { // Scale the frame monitor_frame = $j('#imageFeed'); if ( !monitor_frame ) { - console.log("Error finding frame"); + console.log('Error finding frame'); return; } if ( width ) { @@ -92,14 +92,20 @@ function changeScale() { // Scale the frame monitor_frame = $j('#imageFeed'); if ( !monitor_frame ) { - console.log("Error finding frame"); + console.log('Error finding frame'); return; } - if ( newWidth ) { - monitor_frame.css('width', newWidth+'px'); - } - if ( newHeight ) { - monitor_frame.css('height', newHeight+'px'); + + if ( scale != '0' ) { + if ( newWidth ) { + monitor_frame.css('width', newWidth+'px'); + } + if ( newHeight ) { + monitor_frame.css('height', newHeight+'px'); + } + } else { + monitor_frame.css('width', '100%'); + monitor_frame.css('height', 'auto'); } /*Stream could be an applet so can't use moo tools*/ var streamImg = $j('#liveStream'+monitorData[monIdx].id)[0]; @@ -110,12 +116,22 @@ function changeScale() { //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); - src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); - src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + if ( scale != '0' ) { + src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); + src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + } else { + src = src.replace(/width=[\.\d]+/i, 'width='+monitorData[monIdx].width); + src = src.replace(/height=[\.\d]+/i, 'height='+monitorData[monIdx].height); + } streamImg.src = src; } - streamImg.style.width = newWidth + 'px'; - streamImg.style.height = newHeight + 'px'; + if ( scale != '0' ) { + streamImg.style.width = newWidth+'px'; + streamImg.style.height = newHeight+'px'; + } else { + streamImg.style.width = '100%'; + streamImg.style.height = 'auto'; + } } else { console.log("Did not find liveStream"+monitorData[monIdx].id); } From 12252f1f377e28bb08b7f319ebd9fc22756332af Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:05:48 -0400 Subject: [PATCH 225/319] defend against XSS in Monitor Name --- web/skins/classic/views/js/event.js.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index 3dae5abfe..7ba03b621 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -34,7 +34,7 @@ var eventData = { StartTime: 'StartTime() ?>', EndTime: 'EndTime() ?>', Frames: 'Frames() ?>', - MonitorName: 'Name() ?>' + MonitorName: 'Name()) ?>' }; var monitorUrl = 'Storage()->Server()->UrlToIndex(); ?>'; @@ -46,7 +46,7 @@ var rate = ''; // really only used when setting up initial pl var scale = ""; var LabelFormat = "LabelFormat())?>"; -var canEditEvents = ; +var canEditEvents = ; var streamTimeout = ; var canStreamNative = ; @@ -55,8 +55,8 @@ var streamMode = ''; // // Strings // -var deleteString = ""; -var causeString = ""; +var deleteString = ""; +var causeString = ""; var WEB_LIST_THUMB_WIDTH = ''; var WEB_LIST_THUMB_HEIGHT = ''; var popup = ''; From 6a9a8f1d5ff6b1bc94da969fd91635a77cdf49e2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 6 Apr 2020 11:59:27 -0400 Subject: [PATCH 226/319] check length of returned tr array to prevent errors in js log --- web/skins/classic/views/js/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 811fa5f28..f0aee7c00 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -175,7 +175,7 @@ function clearLog() { var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse}); var tbody = $(logTable).getElement('tbody'); var rows = tbody.getElements('tr'); - if ( rows ) { + if ( rows && rows.length ) { var minTime = rows[0].getElement('td').get('text'); clearParms += "&minTime="+encodeURIComponent(minTime); var maxTime = rows[rows.length-1].getElement('td').get('text'); From 63aaf76f0d58105bcfe8eae1a16e63cfc4150110 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 12:13:05 -0500 Subject: [PATCH 227/319] handle a 0 value for scale as the scale to fit value --- web/skins/classic/views/js/montage.js | 31 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 68a2acf2c..b88833db0 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -334,7 +334,7 @@ function changeScale() { Cookie.write('zmMontageScale', scale, {duration: 10*365}); Cookie.write('zmMontageWidth', '', {duration: 10*365}); Cookie.write('zmMontageHeight', '', {duration: 10*365}); - if ( !scale ) { + if ( scale == '' ) { selectLayout('#zmMontageLayout'); return; } @@ -349,8 +349,12 @@ function changeScale() { console.log("Error finding frame for " + monitor.id); continue; } - if ( newWidth ) { - monitor_frame.css('width', newWidth); + if ( scale != '0' ) { + if ( newWidth ) { + monitor_frame.css('width', newWidth); + } + } else { + monitor_frame.css('width', '100%'); } // We don't set the frame height because it has the status bar as well //if ( height ) { @@ -364,13 +368,24 @@ function changeScale() { streamImg.src = ''; //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); - src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); - src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); - src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + if ( scale != '0' ) { + src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); + src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + } else { + src = src.replace(/scale=[\.\d]+/i, 'scale=100'); + src = src.replace(/width=[\.\d]+/i, 'width='+monitorData[i].width); + src = src.replace(/height=[\.\d]+/i, 'height='+monitorData[i].height); + } streamImg.src = src; } - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; + if ( scale != '0' ) { + streamImg.style.width = newWidth + "px"; + streamImg.style.height = newHeight + "px"; + } else { + streamImg.style.width = '100%'; + streamImg.style.height = 'auto'; + } } } } From 8e2c76df61653b072ea2c6c8b820f8728b15a322 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Mar 2020 16:16:32 -0400 Subject: [PATCH 228/319] minor code cleanup, adding monitor click event in a previous for loop instead of iterating again --- web/skins/classic/views/js/montagereview.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 46f9b1ab2..9c7592ae3 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -967,6 +967,7 @@ function initPage() { imagedone(this, this.monId, false); }; loadImage2Monitor(monId, monitorImageURL[monId]); + monitorCanvasObj[monId].addEventListener('click', clickMonitor, false); } } // end foreach monitor @@ -982,15 +983,6 @@ function initPage() { ctx = canvas.getContext('2d'); drawGraph(); } - for ( i=0, len=monitorPtr.length; i < len; i += 1 ) { - var monitor_id = monitorPtr[i]; - monitor_canvas = $('Monitor'+monitor_id); - if ( ! monitor_canvas ) { - console.log("No canvas found for monitor " + monitor_id); - continue; - } - monitor_canvas.addEventListener('click', clickMonitor, false); - } setSpeed(speedIndex); //setFit(fitMode); // will redraw //setLive(liveMode); // will redraw From 93a37671c2c08c3636f39929c96ed690b2fede24 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 18:22:55 -0500 Subject: [PATCH 229/319] Merge branch 'release-1.34' --- web/skins/classic/views/js/video.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/skins/classic/views/js/video.js b/web/skins/classic/views/js/video.js index 0aea4482c..24c5474b1 100644 --- a/web/skins/classic/views/js/video.js +++ b/web/skins/classic/views/js/video.js @@ -23,7 +23,11 @@ function generateVideoResponse( respObj, respText ) { window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0) ); } +<<<<<<< HEAD function generateVideo() { +======= +function generateVideo( ) { +>>>>>>> bf9ba39aa... Merge branch 'release-1.34' form = $j('#contentForm')[0]; var parms = 'view=request&request=event&action=video'; parms += '&'+$(form).toQueryString(); From 7d0421a02c97988fae63bf3bfb2b5d6c25576e12 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:06:39 -0400 Subject: [PATCH 230/319] defend against XSS in Monitor Name --- .../classic/views/js/montagereview.js.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index d8850c06b..90ba28b2a 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -3,7 +3,7 @@ var server_utc_offset = getOffset($now); -echo $offset . '; // ' . floor($offset / 3600) . ' hours '; +echo $offset.'; // '.floor($offset / 3600).' hours '; ?> var currentScale=; @@ -182,18 +182,18 @@ foreach ( $monitors as $m ) { echo " monitorLoading[" . $m->Id() . "]=false;\n"; echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n"; echo " monitorLoadingStageURL[" . $m->Id() . "] = '';\n"; - echo " monitorColour[" . $m->Id() . "]=\"" . $m->WebColour() . "\";\n"; - echo " monitorWidth[" . $m->Id() . "]=" . $m->ViewWidth() . ";\n"; - echo " monitorHeight[" . $m->Id() . "]=" . $m->ViewHeight() . ";\n"; + echo " monitorColour[" . $m->Id() . "]=\"" . validHtmlStr($m->WebColour()) . "\";\n"; + echo " monitorWidth[" . $m->Id() . "]=" . validHtmlStr($m->ViewWidth()) . ";\n"; + echo " monitorHeight[" . $m->Id() . "]=" . validHtmlStr($m->ViewHeight()) . ";\n"; echo " monitorIndex[" . $m->Id() . "]=" . $numMonitors . ";\n"; echo " monitorServerId[" . $m->Id() . "]='" .($m->ServerId() ? $m->ServerId() : '0'). "';\n"; - echo " monitorName[" . $m->Id() . "]=\"" . $m->Name() . "\";\n"; + echo " monitorName[" . $m->Id() . "]=\"" . validHtmlStr($m->Name()) . "\";\n"; echo " monitorLoadStartTimems[" . $m->Id() . "]=0;\n"; echo " monitorLoadEndTimems[" . $m->Id() . "]=0;\n"; echo " monitorNormalizeScale[" . $m->Id() . "]=" . sqrt($avgArea / ($m->Width() * $m->Height() )) . ";\n"; $zoomScale=1.0; - if(isset($_REQUEST[ 'z' . $m->Id() ]) ) - $zoomScale = floatval( validHtmlStr($_REQUEST[ 'z' . $m->Id() ]) ); + if ( isset($_REQUEST['z'.$m->Id()]) ) + $zoomScale = floatval(validHtmlStr($_REQUEST['z'.$m->Id()])); echo " monitorZoomScale[" . $m->Id() . "]=" . $zoomScale . ";\n"; echo " monitorPtr[" . $numMonitors . "]=" . $m->Id() . ";\n"; $numMonitors += 1; @@ -205,14 +205,14 @@ var maxTimeSecs=parseInt($maxTimeSecs); var minTime='$minTime'; var maxTime='$maxTime'; "; -echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n"; -if(isset($defaultCurrentTime)) - echo "var currentTimeSecs=parseInt(" . strtotime($defaultCurrentTime) . ");\n"; +echo "var rangeTimeSecs=".($maxTimeSecs - $minTimeSecs + 1).";\n"; +if ( isset($defaultCurrentTime) ) + echo "var currentTimeSecs=parseInt(".strtotime($defaultCurrentTime).");\n"; else - echo "var currentTimeSecs=parseInt(" . ($minTimeSecs + $maxTimeSecs)/2 . ");\n"; + echo "var currentTimeSecs=parseInt(".($minTimeSecs + $maxTimeSecs)/2.");\n"; echo 'var speeds=['; -for ($i=0; $i0)?', ':'') . $speeds[$i]; echo "];\n"; ?> From d3f2dd4d6ab95d6d45c50ab5486cadf84fb90429 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:09:46 -0400 Subject: [PATCH 231/319] fix --- web/skins/classic/views/js/montagereview.js.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 90ba28b2a..8ff77f317 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -205,11 +205,11 @@ var maxTimeSecs=parseInt($maxTimeSecs); var minTime='$minTime'; var maxTime='$maxTime'; "; -echo "var rangeTimeSecs=".($maxTimeSecs - $minTimeSecs + 1).";\n"; +echo 'var rangeTimeSecs='.($maxTimeSecs - $minTimeSecs + 1).";\n"; if ( isset($defaultCurrentTime) ) - echo "var currentTimeSecs=parseInt(".strtotime($defaultCurrentTime).");\n"; + echo 'var currentTimeSecs=parseInt('.strtotime($defaultCurrentTime).");\n"; else - echo "var currentTimeSecs=parseInt(".($minTimeSecs + $maxTimeSecs)/2.");\n"; + echo 'var currentTimeSecs=parseInt('.(($minTimeSecs + $maxTimeSecs)/2).");\n"; echo 'var speeds=['; for ( $i=0; $i < count($speeds); $i++ ) From 9a3f699644ab5183f8eff1a1c9f51120e7a48d7c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 16:52:45 -0500 Subject: [PATCH 232/319] Use a little less ram by passing by reference --- web/skins/classic/views/js/montagereview.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 8ff77f317..4e3db5665 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -68,7 +68,7 @@ if ( !$liveMode ) { $frame['NextFrameId'] = $next_frames[$frame['EventId']]['Id']; } $event['FramesById'] += array($frame['Id']=>$frame); - $next_frames[$frame['EventId']] = $frame; + $next_frames[$frame['EventId']] = &$event['FramesById'][$frame['Id']]; } } // end if dbQuery From 91e78410a9469a8c397afb6ee9e483393362e936 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 16:53:08 -0500 Subject: [PATCH 233/319] Stop the montagereview update while we reload the page --- web/skins/classic/views/js/montagereview.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 9c7592ae3..fe9ccd3cd 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -936,6 +936,10 @@ function changeDateTime(e) { } } + // Reloading can take a while, so stop interrupts to reduce load + clearInterval(timerObj); + timerObj = null; + var uri = "?view=" + currentView + fitStr + minStr + maxStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value]; window.location = uri; } From 81d329af2f644d92caa1bd1dcb14bca9f95392fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 29 Feb 2020 09:28:04 -0500 Subject: [PATCH 234/319] spacing, eslint --- web/skins/classic/views/js/video.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/web/skins/classic/views/js/video.js b/web/skins/classic/views/js/video.js index 0aea4482c..5f2df6ee3 100644 --- a/web/skins/classic/views/js/video.js +++ b/web/skins/classic/views/js/video.js @@ -1,11 +1,11 @@ -function deleteVideo( e ) { +function deleteVideo(e) { index = e.getAttribute('data-file-index'); - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&deleteIndex='+index ); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&deleteIndex='+index); } -function downloadVideo( e ) { +function downloadVideo(e) { index = e.getAttribute('data-file-index'); - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&downloadIndex='+index ); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&downloadIndex='+index); } var generateVideoTimer = null; @@ -13,25 +13,25 @@ var generateVideoTimer = null; function generateVideoProgress() { var tickerText = $('videoProgressTicker').get('text'); if ( tickerText.length < 1 || tickerText.length > 4 ) { - $('videoProgressTicker').set( 'text', '.' ); + $('videoProgressTicker').set('text', '.'); } else { - $('videoProgressTicker').appendText( '.' ); + $('videoProgressTicker').appendText('.'); } } function generateVideoResponse( respObj, respText ) { - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0) ); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0)); } function generateVideo() { form = $j('#contentForm')[0]; var parms = 'view=request&request=event&action=video'; parms += '&'+$(form).toQueryString(); - var query = new Request.JSON( {url: thisUrl, method: 'post', data: parms, onSuccess: generateVideoResponse} ); + var query = new Request.JSON({url: thisUrl, method: 'post', data: parms, onSuccess: generateVideoResponse}); query.send(); - $('videoProgress').removeClass( 'hidden' ); - $('videoProgress').setProperty( 'class', 'warnText' ); - $('videoProgressText').set( 'text', videoGenProgressString ); + $('videoProgress').removeClass('hidden'); + $('videoProgress').setProperty('class', 'warnText'); + $('videoProgressText').set('text', videoGenProgressString); generateVideoProgress(); - generateVideoTimer = generateVideoProgress.periodical( 500 ); + generateVideoTimer = generateVideoProgress.periodical(500); } From 908e892d4890f5b61f77b2c7573d9f76ef39d19d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 11:22:08 -0500 Subject: [PATCH 235/319] Merge branch 'fix_auth_timeouts' --- web/skins/classic/views/js/watch.js | 155 ++++++++++++++-------------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index d4ee2dc34..7d4ba5e4b 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1,33 +1,33 @@ function showEvents() { - $('ptzControls').addClass( 'hidden' ); - $('events').removeClass( 'hidden' ); + $('ptzControls').addClass('hidden'); + $('events').removeClass('hidden'); if ( $('eventsControl') ) { $('eventsControl').addClass('hidden'); } if ( $('controlControl') ) { $('controlControl').removeClass('hidden'); } - showMode = "events"; + showMode = 'events'; } function showPtzControls() { - $('events').addClass( 'hidden' ); - $('ptzControls').removeClass( 'hidden' ); + $('events').addClass('hidden'); + $('ptzControls').removeClass('hidden'); if ( $('eventsControl') ) { $('eventsControl').removeClass('hidden'); } if ( $('controlControl') ) { $('controlControl').addClass('hidden'); } - showMode = "control"; + showMode = 'control'; } function changeScale() { var scale = $('scale').get('value'); var newWidth; var newHeight; - if (scale == "auto") { + if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus')); newWidth = newSize.width; newHeight = newSize.height; @@ -81,7 +81,7 @@ function setAlarmState( currentAlarmState ) { if ( SOUND_ON_ALARM ) { // Enable the alarm sound if ( !canPlayPauseAudio ) { - $('alarmSound').removeClass( 'hidden' ); + $('alarmSound').removeClass('hidden'); } else { $('MediaPlayer').Play(); } @@ -90,22 +90,20 @@ function setAlarmState( currentAlarmState ) { window.focus(); } } - if ( SOUND_ON_ALARM ) { - if ( oldAlarm ) { + if ( oldAlarm ) { // done with an event do a refresh + if ( SOUND_ON_ALARM ) { // Disable alarm sound if ( !canPlayPauseAudio ) { - $('alarmSound').addClass( 'hidden' ); + $('alarmSound').addClass('hidden'); } else { $('MediaPlayer').Stop(); } } - } - if (oldAlarm) { // done with an event do a refresh eventCmdQuery(); } lastAlarmState = alarmState; -} +} // end function setAlarmState( currentAlarmState ) if ( monitorType != 'WebSite' ) { var streamCmdParms = 'view=request&request=stream&connkey='+connKey; @@ -149,11 +147,11 @@ function getStreamCmdResponse(respObj, respText) { $('levelValue').set('text', streamStatus.level); if ( streamStatus.level > 95 ) { - $('levelValue').className = "alarm"; + $('levelValue').className = 'alarm'; } else if ( streamStatus.level > 80 ) { - $('levelValue').className = "alert"; + $('levelValue').className = 'alert'; } else { - $('levelValue').className = "ok"; + $('levelValue').className = 'ok'; } var delayString = secsToTime(streamStatus.delay); @@ -188,7 +186,7 @@ function getStreamCmdResponse(respObj, respText) { } } // rate } else { - $('modeValue').set( 'text', "Live" ); + $('modeValue').set( 'text', 'Live' ); $('rate').addClass( 'hidden' ); $('delay').addClass( 'hidden' ); $('level').addClass( 'hidden' ); @@ -224,7 +222,6 @@ function getStreamCmdResponse(respObj, respText) { if ( streamStatus.auth ) { auth_hash = streamStatus.auth; - console.log("Have a new auth hash" + streamStatus.auth); // Try to reload the image stream. var streamImg = $('liveStream'); if ( streamImg ) { @@ -237,7 +234,7 @@ function getStreamCmdResponse(respObj, respText) { } // end if have a new auth hash } // end if respObj.status } else { - checkStreamForErrors("getStreamCmdResponse", respObj);//log them + checkStreamForErrors('getStreamCmdResponse', respObj);//log them // Try to reload the image stream. // If it's an auth error, we should reload the whole page. window.location.reload(); @@ -245,9 +242,9 @@ function getStreamCmdResponse(respObj, respText) { var streamImg = $('liveStream'+monitorId); if ( streamImg ) { streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); - console.log("Changing livestream src to " + streamImg.src); + console.log('Changing livestream src to ' + streamImg.src); } else { - console.log("Unable to find streamImg liveStream"); + console.log('Unable to find streamImg liveStream'); } } } @@ -256,7 +253,7 @@ function getStreamCmdResponse(respObj, respText) { if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { streamCmdTimeout = streamCmdTimeout/5; } - streamCmdTimer = streamCmdQuery.delay( streamCmdTimeout ); + streamCmdTimer = streamCmdQuery.delay(streamCmdTimeout); } function streamCmdPause( action ) { @@ -268,7 +265,7 @@ function streamCmdPause( action ) { setButtonState('slowRevBtn', 'inactive'); setButtonState('fastRevBtn', 'inactive'); if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PAUSE ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PAUSE); } } @@ -365,23 +362,23 @@ function streamCmdFastRev( action ) { } function streamCmdZoomIn( x, y ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y); } function streamCmdZoomOut() { - streamCmdReq.send( streamCmdParms+"&command="+CMD_ZOOMOUT ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMOUT); } function streamCmdScale( scale ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale); } function streamCmdPan( x, y ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y); } function streamCmdQuery() { - streamCmdReq.send( streamCmdParms+"&command="+CMD_QUERY ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_QUERY); } if ( monitorType != 'WebSite' ) { @@ -389,12 +386,18 @@ if ( monitorType != 'WebSite' ) { if ( auth_hash ) { statusCmdParms += '&auth='+auth_hash; } - var statusCmdReq = new Request.JSON( {url: monitorUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse} ); + var statusCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getStatusCmdResponse + } ); var statusCmdTimer = null; } function getStatusCmdResponse(respObj, respText) { - watchdogOk("status"); + watchdogOk('status'); if ( statusCmdTimer ) { statusCmdTimer = clearTimeout(statusCmdTimer); } @@ -403,22 +406,22 @@ function getStatusCmdResponse(respObj, respText) { $('fpsValue').set('text', respObj.monitor.FrameRate); setAlarmState(respObj.monitor.Status); } else { - checkStreamForErrors("getStatusCmdResponse", respObj); + checkStreamForErrors('getStatusCmdResponse', respObj); } var statusCmdTimeout = statusRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { statusCmdTimeout = statusCmdTimeout/5; } - statusCmdTimer = statusCmdQuery.delay( statusCmdTimeout ); + statusCmdTimer = statusCmdQuery.delay(statusCmdTimeout); } function statusCmdQuery() { - statusCmdReq.send(); + statusCmdReq.send(statusCmdParms); } if ( monitorType != 'WebSite' ) { - var alarmCmdParms = "view=request&request=alarm&id="+monitorId; + var alarmCmdParms = 'view=request&request=alarm&id='+monitorId; if ( auth_hash ) { alarmCmdParms += '&auth='+auth_hash; } @@ -433,8 +436,8 @@ if ( monitorType != 'WebSite' ) { var alarmCmdFirst = true; } -function getAlarmCmdResponse( respObj, respText ) { - checkStreamForErrors("getAlarmCmdResponse", respObj); +function getAlarmCmdResponse(respObj, respText) { + checkStreamForErrors('getAlarmCmdResponse', respObj); } function cmdDisableAlarms() { @@ -447,14 +450,14 @@ function cmdEnableAlarms() { function cmdForceAlarm() { alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm"); - if (window.event) { + if ( window.event ) { window.event.preventDefault(); } } function cmdCancelForcedAlarm() { alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm"); - if (window.event) { + if ( window.event ) { window.event.preventDefault(); } return false; @@ -470,8 +473,8 @@ function getActResponse( respObj, respText ) { eventCmdQuery(); } -function deleteEvent( event, eventId ) { - var actParms = "view=request&request=event&action=delete&id="+eventId; +function deleteEvent(event, eventId) { + var actParms = 'view=request&request=event&action=delete&id='+eventId; if ( auth_hash ) { actParms += '&auth='+auth_hash; } @@ -479,10 +482,9 @@ function deleteEvent( event, eventId ) { url: thisUrl, method: 'post', timeout: 3000, - data: actParms, onSuccess: getActResponse } ); - actReq.send(); + actReq.send(actParms); event.stop(); } @@ -495,7 +497,6 @@ if ( monitorType != 'WebSite' ) { url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, - data: eventCmdParms, link: 'cancel', onSuccess: getEventCmdResponse, onTimeout: eventCmdQuery @@ -509,19 +510,19 @@ function highlightRow( row ) { } function getEventCmdResponse( respObj, respText ) { - watchdogOk("event"); + watchdogOk('event'); if ( eventCmdTimer ) { - eventCmdTimer = clearTimeout( 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' ); + var eventListBody = $(eventList).getElement('tbody'); + var eventListRows = $(eventListBody).getElements('tr'); - eventListRows.each( function( row ) { - row.removeClass( 'updated' ); + eventListRows.each( function(row) { + row.removeClass('updated'); } ); for ( var i = 0; i < dbEvents.length; i++ ) { @@ -575,30 +576,30 @@ function getEventCmdResponse( respObj, respText ) { 'mouseout': highlightRow.pass(row) } }); - link.set( 'text', 'X' ); - link.inject( row.getElement( 'td.colDelete' ) ); + link.set('text', 'X'); + link.inject(row.getElement('td.colDelete')); if ( i == 0 ) { - row.inject( $(eventListBody) ); + row.inject($(eventListBody)); } else { - row.inject( $(eventListBody), 'top' ); + row.inject($(eventListBody), 'top'); if ( !eventCmdFirst ) { - row.addClass( 'recent' ); + row.addClass('recent'); } } } else { - row.getElement( 'td.colName a' ).set( 'text', event.Name ); - row.getElement( 'td.colSecs' ).set( 'text', event.Length ); - row.getElement( 'td.colFrames a' ).set( 'text', event.Frames+'/'+event.AlarmFrames ); - row.getElement( 'td.colScore a' ).set( 'text', event.AvgScore+'/'+event.MaxScore ); - row.removeClass( 'recent' ); + row.getElement('td.colName a').set('text', event.Name); + row.getElement('td.colSecs').set('text', event.Length); + row.getElement('td.colFrames a').set('text', event.Frames+'/'+event.AlarmFrames); + row.getElement('td.colScore a').set('text', event.AvgScore+'/'+event.MaxScore); + row.removeClass('recent'); } - row.addClass( 'updated' ); + row.addClass('updated'); } - var rows = $(eventListBody).getElements( 'tr' ); + var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { - if ( !rows[i].hasClass( 'updated' ) ) { + if ( !rows[i].hasClass('updated') ) { rows[i].destroy(); rows.splice( i, 1 ); i--; @@ -609,39 +610,39 @@ function getEventCmdResponse( respObj, respText ) { rows.length--; } } else { - checkStreamForErrors("getEventCmdResponse", respObj); + checkStreamForErrors('getEventCmdResponse', respObj); } var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { eventCmdTimeout = eventCmdTimeout/5; } - eventCmdTimer = eventCmdQuery.delay( eventCmdTimeout ); + eventCmdTimer = eventCmdQuery.delay(eventCmdTimeout); eventCmdFirst = false; } function eventCmdQuery() { if ( eventCmdTimer ) { // avoid firing another if we are firing one - eventCmdTimer = clearTimeout( eventCmdTimer ); + eventCmdTimer = clearTimeout(eventCmdTimer); } eventCmdReq.send(); } if ( monitorType != 'WebSite' ) { - var controlParms = "view=request&request=control&id="+monitorId; + var controlParms = 'view=request&request=control&id='+monitorId; if ( auth_hash ) { controlParms += '&auth='+auth_hash; } var controlReq = new Request.JSON( {url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse} ); } -function getControlResponse( respObj, respText ) { +function getControlResponse(respObj, respText) { if ( !respObj ) { return; } //console.log( respText ); if ( respObj.result != 'Ok' ) { - alert( "Control response was status = "+respObj.status+"\nmessage = "+respObj.message ); + alert("Control response was status = "+respObj.status+"\nmessage = "+respObj.message); } } @@ -679,19 +680,19 @@ function controlCmd(button) { locParms += '&yge='+yge; } } - controlReq.send( controlParms+"&control="+control+locParms ); - if ( streamMode == "single" ) { - fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 ); + controlReq.send(controlParms+"&control="+control+locParms); + if ( streamMode == 'single' ) { + fetchImage.pass($('imageFeed').getElement('img')).delay(1000); } } function controlCmdImage( x, y ) { var imageControlParms = controlParms; - imageControlParms += "&scale="+scale; - imageControlParms += "&control="+imageControlMode; + imageControlParms += '&scale='+scale; + imageControlParms += '&control='+imageControlMode; controlReq.send( imageControlParms+"&x="+x+"&y="+y ); - if ( streamMode == "single" ) { + if ( streamMode == 'single' ) { fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 ); } } @@ -705,7 +706,7 @@ function handleClick( event ) { var x = event.page.x - $(target).getLeft(); var y = event.page.y - $(target).getTop(); - if ( showMode == "events" || !imageControlMode ) { + if ( showMode == 'events' || !imageControlMode ) { if ( event.shift ) { streamCmdPan( x, y ); } else if ( event.event.ctrlKey ) { @@ -802,7 +803,7 @@ function initPage() { if ( refreshApplet && appletRefreshTime ) { appletRefresh.delay(appletRefreshTime*1000); } - if ( scale == "auto" ) changeScale(); + if ( scale == 'auto' ) changeScale(); if ( window.history.length == 1 ) { $j('#closeControl').html(''); } From da79f761ad619702d355dcf116d87700e05ad45f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 12:43:07 -0500 Subject: [PATCH 236/319] add monitorStreamReplayBuffer to javascript vars. Use it to determine whether to update fast forward and reverse buttons --- web/skins/classic/views/js/watch.js | 88 +++++++++++++++---------- web/skins/classic/views/js/watch.js.php | 8 +-- 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 7d4ba5e4b..3ecf6f5d6 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -260,10 +260,12 @@ function streamCmdPause( action ) { setButtonState('pauseBtn', 'active'); setButtonState('playBtn', 'inactive'); setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'inactive'); - setButtonState('slowRevBtn', 'inactive'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_PAUSE); } @@ -274,16 +276,20 @@ function streamCmdPlay( action ) { setButtonState('playBtn', 'active'); if ( streamStatus.delayed == true ) { setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'inactive'); - setButtonState('slowRevBtn', 'inactive'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } } else { setButtonState('stopBtn', 'unavail'); - setButtonState('fastFwdBtn', 'unavail'); - setButtonState('slowFwdBtn', 'unavail'); - setButtonState('slowRevBtn', 'unavail'); - setButtonState('fastRevBtn', 'unavail'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); + } } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_PLAY); @@ -294,10 +300,12 @@ function streamCmdStop( action ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'unavail'); setButtonState('stopBtn', 'active'); - setButtonState('fastFwdBtn', 'unavail'); - setButtonState('slowFwdBtn', 'unavail'); - setButtonState('slowRevBtn', 'unavail'); - setButtonState('fastRevBtn', 'unavail'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_STOP); } @@ -309,10 +317,12 @@ function streamCmdFastFwd( action ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'inactive'); - setButtonState('slowRevBtn', 'inactive'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTFWD); } @@ -322,40 +332,50 @@ function streamCmdSlowFwd( action ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'active'); - setButtonState('slowRevBtn', 'inactive'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'active'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWFWD); } setButtonState('pauseBtn', 'active'); - setButtonState('slowFwdBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('slowFwdBtn', 'inactive'); + } } function streamCmdSlowRev( action ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'inactive'); - setButtonState('slowRevBtn', 'active'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'active'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWREV); } setButtonState('pauseBtn', 'active'); - setButtonState('slowRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('slowRevBtn', 'inactive'); + } } function streamCmdFastRev( action ) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('stopBtn', 'inactive'); - setButtonState('fastFwdBtn', 'inactive'); - setButtonState('slowFwdBtn', 'inactive'); - setButtonState('slowRevBtn', 'inactive'); - setButtonState('fastRevBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTREV); } diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index 5e3e3a718..3140c65bc 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -44,13 +44,13 @@ var showMode = "'; var maxDisplayEvents = ; - var monitorId = Id() ?>; var monitorWidth = ViewWidth() ?>; var monitorHeight = ViewHeight() ?>; -var monitorUrl = 'UrlToIndex(); ?>'; -var monitorType = 'Type() ) ?>'; -var monitorRefresh = 'Refresh() ) ?>'; +var monitorUrl = 'UrlToIndex() ?>'; +var monitorType = 'Type() ?>'; +var monitorRefresh = 'Refresh() ?>'; +var monitorStreamReplayBuffer = StreamReplayBuffer() ?>; var scale = ''; From 25cd13f8ad2dc5443dedc7ea114add196d523c07 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 2 Mar 2020 14:38:36 -0500 Subject: [PATCH 237/319] When sending eventCmdReq, pass eventCmdParms since it might have been updated. --- web/skins/classic/views/js/event.js | 10 ++++------ web/skins/classic/views/js/watch.js | 11 ++++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index f786c52c4..d8b6d5c3c 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -151,7 +151,7 @@ function changeScale() { } else { eventViewer = $j(vid ? '#videoobj' : '#evtStream'); } - if ( scale == 'auto' ) { + if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; @@ -163,10 +163,10 @@ function changeScale() { } if ( !(streamMode == 'stills') ) { eventViewer.width(newWidth); - } //stills handles its own width + } // stills handles its own width eventViewer.height(newHeight); if ( !vid ) { // zms needs extra sizing - streamScale(scale == "auto" ? autoScale : scale); + streamScale((scale == '0' || scale == 'auto' ) ? autoScale : scale); drawProgressBar(); } if ( streamMode == 'stills' ) { @@ -175,7 +175,7 @@ function changeScale() { } else { alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call } - if ( scale == "auto" ) { + if ( scale = '0' || scale == 'auto' ) { Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365}); } else { Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365}); @@ -209,7 +209,6 @@ function changeRate() { } }, 500); //500ms is a compromise between smooth reverse and realistic performance } // end if vid - } else { // Forward rate if ( vid ) { vid.playbackRate(rate/100); @@ -310,7 +309,6 @@ function pauseClicked() { } function streamPause( ) { - $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 3ecf6f5d6..e8977ddd6 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -645,7 +645,7 @@ function eventCmdQuery() { if ( eventCmdTimer ) { // avoid firing another if we are firing one eventCmdTimer = clearTimeout(eventCmdTimer); } - eventCmdReq.send(); + eventCmdReq.send(eventCmdParms); } if ( monitorType != 'WebSite' ) { @@ -653,7 +653,13 @@ if ( monitorType != 'WebSite' ) { if ( auth_hash ) { controlParms += '&auth='+auth_hash; } - var controlReq = new Request.JSON( {url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse} ); + var controlReq = new Request.JSON( { + url: monitorUrl, + method: 'post', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getControlResponse + } ); } function getControlResponse(respObj, respText) { @@ -667,7 +673,6 @@ function getControlResponse(respObj, respText) { } function controlCmd(button) { - control = button.getAttribute('value'); xtell = button.getAttribute('xtell'); ytell = button.getAttribute('ytell'); From 8ef70f9080dfc120ce5bd4dc1c1455a34683f69c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:06:20 -0400 Subject: [PATCH 238/319] defend against XSS in Monitor Name --- web/skins/classic/views/js/filter.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 5144c4734..5b01d150f 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -141,6 +141,11 @@ function deleteFilter( element ) { form.submit(); } } +var escape = document.createElement('textarea'); +function escapeHTML(html) { + escape.textContent = html; + return escape.innerHTML; +} function parseRows(rows) { for ( var rowNum = 0; rowNum < rows.length; rowNum++ ) { //Each row is a term @@ -221,8 +226,8 @@ function parseRows(rows) { inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"}); } else if ( attr == 'MonitorName' ) { //Monitor names var monitorSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); - for (var key in monitors) { - monitorSelect.append(''); + for ( var monitor_id in monitors ) { + monitorSelect.append(''); } var monitorVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(monitorSelect).children().val(monitorVal); From 010b230804c62418a42e6219662140b26c786d03 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 11:14:12 -0500 Subject: [PATCH 239/319] Fix value of auto in width dropdown. Allow a 0 value for scale. --- web/skins/classic/views/cycle.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index b7281a719..6795e16a2 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -40,7 +40,7 @@ if ( empty($_REQUEST['mode']) ) { } $widths = array( - '' => translate('auto'), + 'auto' => translate('auto'), '100%' => '100%', '160px' => '160px', '320px' => '320px', @@ -62,13 +62,11 @@ session_start(); if ( isset($_REQUEST['scale']) ) { $options['scale'] = validInt($_REQUEST['scale']); - ZM\Logger::Debug('Setting scale from request to '.$options['scale']); } else if ( isset($_COOKIE['zmCycleScale']) ) { $options['scale'] = $_COOKIE['zmCycleScale']; - ZM\Logger::Debug('Setting scale from cookie to '.$options['scale']); } -if ( !(isset($options['scale']) and $options['scale']) ) +if ( !isset($options['scale']) ) $options['scale'] = 100; if ( isset($_COOKIE['zmCycleWidth']) and $_COOKIE['zmCycleWidth'] ) { From 550345887495be52c4a7bd78c12d013a5afb1446 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 18:34:43 -0400 Subject: [PATCH 240/319] correct label_size array and add translations --- web/skins/classic/views/monitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index a78c6f615..303e335ee 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -367,8 +367,8 @@ $fastblendopts_alarm = array( ); $label_size = array( - 'Default' => 1, - 'Large' => 2 + 1 => translate('Default'), + 2 => translate('Large'), ); $codecs = array( From bf99daf49431c9c5eb12a4224192e57d31bda8fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 10 Mar 2020 18:47:19 -0400 Subject: [PATCH 241/319] Merge branch 'rate_dropdown' --- web/skins/classic/views/js/event.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index d8b6d5c3c..69b34d055 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -309,6 +309,7 @@ function pauseClicked() { } function streamPause( ) { + $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); @@ -1106,7 +1107,7 @@ function initPage() { } nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues - if ( scale == 'auto' ) changeScale(); + if ( scale == '0' || scale == 'auto' ) changeScale(); document.querySelectorAll('select[name="rate"]').forEach(function(el) { el.onchange = window['changeRate']; }); From 56a43741c53822e780768f1b024edab1d96b10b3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Feb 2020 11:17:46 -0500 Subject: [PATCH 242/319] Handle a zero value for scale --- web/skins/classic/views/event.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 51dfcf640..f8bceb837 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -48,7 +48,7 @@ if ( isset($_REQUEST['scale']) ) { $scale = validInt($_REQUEST['scale']); } else if ( isset($_COOKIE['zmEventScaleAuto']) ) { // If we're using scale to fit use it on all monitors - $scale = 'auto'; + $scale = '0'; } else if ( isset($_COOKIE['zmEventScale'.$Event->MonitorId()]) ) { $scale = $_COOKIE['zmEventScale'.$Event->MonitorId()]; } else { @@ -85,9 +85,9 @@ else $streamMode = 'video'; $replayMode = ''; -if ( isset( $_REQUEST['replayMode'] ) ) +if ( isset($_REQUEST['replayMode']) ) $replayMode = validHtmlStr($_REQUEST['replayMode']); -if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) ) +if ( isset($_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) ) $replayMode = validHtmlStr($_COOKIE['replayMode']); if ( ( ! $replayMode ) or ( ! $replayModes[$replayMode] ) ) { @@ -131,7 +131,7 @@ xhtmlHeaders(__FILE__, translate('Event'));