diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index 0d4167b8f..96907eee7 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -130,9 +130,9 @@ CTRL+x to exit **Step 12:** Please check the configuration Zoneminder 1.32.x - 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcuston.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms + 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms :: - cat /etc/zm/conf.d/zmcuston.conf + cat /etc/zm/conf.d/zmcustom.conf 2. Check config of /etc/apache2/conf-enabled/zoneminder.conf has the same ScriptAlias /zm/cgi-bin that is configured in ZM_PATH. The part /nph-zms has to be left out of the ScriptAlias diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 23edf3463..1346167ca 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -54,13 +54,13 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { Error("Can't use query result: %s", mysql_error(&dbconn)); - exit( mysql_errno( &dbconn ) ); + exit(mysql_errno(&dbconn)); } MYSQL_ROW dbrow = mysql_fetch_row(result); if ( mysql_errno(&dbconn) ) { Error("Can't fetch row: %s", mysql_error(&dbconn)); - exit( mysql_errno(&dbconn)); + exit(mysql_errno(&dbconn)); } uint64_t init_event_id = atoll(dbrow[0]); @@ -77,12 +77,11 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { //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; - Debug( 3, "Set cst:%.2f", curr_stream_time ); - Debug( 3, "Set cfid:%d", curr_frame_id ); + Debug(3, "Set curr_stream_time:%.2f, curr_frame_id:%d", curr_stream_time, curr_frame_id); break; } } - Debug( 3, "Skipping %ld frames", event_data->frame_count ); + Debug(3, "Skipping %ld frames", event_data->frame_count); } } return true; @@ -93,7 +92,7 @@ bool EventStream::loadInitialEventData( uint64_t init_event_id, unsigned int ini if ( init_frame_id ) { if ( init_frame_id >= event_data->frame_count ) { - Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count ); + Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count); curr_stream_time = event_data->start_time; } else { curr_stream_time = event_data->frames[init_frame_id-1].timestamp; @@ -140,12 +139,12 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data = new EventData; event_data->event_id = event_id; - event_data->monitor_id = atoi( dbrow[0] ); - event_data->storage_id = dbrow[1] ? atoi( dbrow[1] ) : 0; + event_data->monitor_id = atoi(dbrow[0]); + event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0; event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); event_data->start_time = atoi(dbrow[3]); - event_data->duration = atof(dbrow[4]); - strncpy( event_data->video_file, dbrow[5], sizeof(event_data->video_file)-1 ); + event_data->duration = dbrow[4] ? atof(dbrow[4]) : 0.0; + strncpy(event_data->video_file, dbrow[5], sizeof(event_data->video_file)-1); std::string scheme_str = std::string(dbrow[6]); if ( scheme_str == "Deep" ) { event_data->scheme = Storage::DEEP; @@ -164,34 +163,46 @@ bool EventStream::loadEventData(uint64_t event_id) { struct tm *event_time = localtime(&event_data->start_time); if ( storage_path[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", - storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + snprintf(event_data->path, sizeof(event_data->path), + "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", + storage_path, event_data->monitor_id, + event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, + event_time->tm_hour, event_time->tm_min, event_time->tm_sec); else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", - staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + snprintf(event_data->path, sizeof(event_data->path), + "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", + staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, + event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, + event_time->tm_hour, event_time->tm_min, event_time->tm_sec); } else if ( event_data->scheme == Storage::MEDIUM ) { - struct tm *event_time = localtime( &event_data->start_time ); + struct tm *event_time = localtime(&event_data->start_time); if ( storage_path[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%04d-%02d-%02d/%" PRIu64, - storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id ); + snprintf(event_data->path, sizeof(event_data->path), + "%s/%ld/%04d-%02d-%02d/%" PRIu64, + storage_path, event_data->monitor_id, + event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, + event_data->event_id); else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%04d-%02d-%02d/%" PRIu64, - staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, - event_data->event_id ); + snprintf(event_data->path, sizeof(event_data->path), + "%s/%s/%ld/%04d-%02d-%02d/%" PRIu64, + staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, + event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, + event_data->event_id); } else { if ( storage_path[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64, - storage_path, event_data->monitor_id, event_data->event_id ); + snprintf(event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64, + storage_path, event_data->monitor_id, event_data->event_id); else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64, - staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id ); + snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64, + staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, + event_data->event_id); } delete storage; storage = NULL; - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate((double)event_data->frame_count/event_data->duration); - snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames where EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id); + snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames WHERE EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); exit(mysql_errno(&dbconn)); @@ -270,15 +281,15 @@ void EventStream::processCommand(const CmdMsg *msg) { // Check for incoming command switch( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : - Debug( 1, "Got PAUSE command" ); + Debug(1, "Got PAUSE command"); // Set paused flag paused = true; replay_rate = ZM_RATE_BASE; - last_frame_sent = TV_2_FLOAT( now ); + last_frame_sent = TV_2_FLOAT(now); break; case CMD_PLAY : - Debug( 1, "Got PLAY command" ); + Debug(1, "Got PLAY command"); if ( paused ) { paused = false; } @@ -294,18 +305,18 @@ void EventStream::processCommand(const CmdMsg *msg) { replay_rate = ZM_RATE_BASE; break; case CMD_VARPLAY : - Debug( 1, "Got VARPLAY command" ); + Debug(1, "Got VARPLAY command"); if ( paused ) { paused = false; } replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; break; case CMD_STOP : - Debug( 1, "Got STOP command" ); + Debug(1, "Got STOP command"); paused = false; break; case CMD_FASTFWD : - Debug( 1, "Got FAST FWD command" ); + Debug(1, "Got FAST FWD command"); if ( paused ) { paused = false; } @@ -330,19 +341,19 @@ void EventStream::processCommand(const CmdMsg *msg) { } break; case CMD_SLOWFWD : - Debug( 1, "Got SLOW FWD command" ); + Debug(1, "Got SLOW FWD command"); paused = true; replay_rate = ZM_RATE_BASE; step = 1; break; case CMD_SLOWREV : - Debug( 1, "Got SLOW REV command" ); + Debug(1, "Got SLOW REV command"); paused = true; replay_rate = ZM_RATE_BASE; step = -1; break; case CMD_FASTREV : - Debug( 1, "Got FAST REV command" ); + Debug(1, "Got FAST REV command"); paused = false; // Set play rate switch ( replay_rate ) { @@ -367,7 +378,7 @@ void EventStream::processCommand(const CmdMsg *msg) { case CMD_ZOOMIN : x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + Debug(1, "Got ZOOM IN command, to %d,%d", x, y); switch ( zoom ) { case 100: zoom = 150; @@ -389,7 +400,7 @@ void EventStream::processCommand(const CmdMsg *msg) { send_frame = true; break; case CMD_ZOOMOUT : - Debug( 1, "Got ZOOM OUT command" ); + Debug(1, "Got ZOOM OUT command"); switch ( zoom ) { case 500: zoom = 400; @@ -413,14 +424,14 @@ void EventStream::processCommand(const CmdMsg *msg) { case CMD_PAN : x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); + Debug(1, "Got PAN command, to %d,%d", x, y); break; case CMD_SCALE : scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); + Debug(1, "Got SCALE command, to %d", scale); break; case CMD_PREV : - Debug( 1, "Got PREV command" ); + Debug(1, "Got PREV command"); if ( replay_rate >= 0 ) curr_frame_id = 0; else @@ -429,7 +440,7 @@ void EventStream::processCommand(const CmdMsg *msg) { forceEventChange = true; break; case CMD_NEXT : - Debug( 1, "Got NEXT command" ); + Debug(1, "Got NEXT command"); if ( replay_rate >= 0 ) curr_frame_id = event_data->frame_count+1; else @@ -441,12 +452,12 @@ void EventStream::processCommand(const CmdMsg *msg) { { int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); - Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); + Debug(1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id); send_frame = true; break; } case CMD_QUERY : - Debug( 1, "Got QUERY command, sending STATUS" ); + Debug(1, "Got QUERY command, sending STATUS"); break; case CMD_QUIT : Info("User initiated exit - CMD_QUIT"); @@ -468,7 +479,7 @@ void EventStream::processCommand(const CmdMsg *msg) { status_data.rate = replay_rate; status_data.zoom = zoom; status_data.paused = paused; - Debug( 2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d", + Debug(2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d", status_data.event_id, status_data.paused, status_data.progress, @@ -494,80 +505,78 @@ void EventStream::processCommand(const CmdMsg *msg) { } void EventStream::checkEventLoaded() { - bool reload_event = false; static char sql[ZM_SQL_SML_BUFSIZ]; if ( curr_frame_id <= 0 ) { - snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id < %" PRIu64 " ORDER BY Id DESC LIMIT 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; + snprintf(sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id < %" PRIu64 " ORDER BY Id DESC LIMIT 1", event_data->monitor_id, event_data->event_id); } else if ( (unsigned int)curr_frame_id > event_data->frame_count ) { - snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; + snprintf(sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id); + } else { + // No event change required + return; } - if ( reload_event ) { - if ( forceEventChange || ( mode != MODE_SINGLE && mode != MODE_NONE ) ) { - //Info( "SQL:%s", sql ); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + // Event change required. + if ( forceEventChange || ( mode != MODE_SINGLE && mode != MODE_NONE ) ) { + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_RES *result = mysql_store_result(&dbconn); + if ( !result ) { + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + MYSQL_ROW dbrow = mysql_fetch_row(result); - if ( mysql_errno( &dbconn ) ) { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno(&dbconn)) { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } - if ( dbrow ) { - uint64_t event_id = atoll(dbrow[0]); - Debug( 1, "Loading new event %" PRIu64, event_id ); + if ( dbrow ) { + uint64_t event_id = atoll(dbrow[0]); + Debug(1, "Loading new event %" PRIu64, event_id); - loadEventData(event_id); + loadEventData(event_id); - Debug( 2, "Current frame id = %d", curr_frame_id ); - if ( replay_rate < 0 ) - curr_frame_id = event_data->frame_count; - else - curr_frame_id = 1; - Debug( 2, "New frame id = %d", curr_frame_id ); - } else { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } - mysql_free_result( result ); - forceEventChange = false; + Debug(2, "Current frame id = %d", curr_frame_id); + if ( replay_rate < 0 ) //rewind + curr_frame_id = event_data->frame_count; + else + curr_frame_id = 1; + Debug(2, "New frame id = %d", curr_frame_id); } else { if ( curr_frame_id <= 0 ) curr_frame_id = 1; else curr_frame_id = event_data->frame_count; paused = true; - } + } // end if found a new event or not + mysql_free_result(result); + forceEventChange = false; + } else { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; } -} +} // void EventStream::checkEventLoaded() Image * EventStream::getImage( ) { static char filepath[PATH_MAX]; Debug(2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id); - snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id); + snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id); Debug(2, "EventStream::getImage path(%s) ", filepath, curr_frame_id); Image *image = new Image(filepath); return image; } bool EventStream::sendFrame( int delta_us ) { - Debug( 2, "Sending frame %d", curr_frame_id ); + Debug(2, "Sending frame %d", curr_frame_id); static char filepath[PATH_MAX]; static struct stat filestat; @@ -576,27 +585,27 @@ bool EventStream::sendFrame( int delta_us ) { // This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that. // // This is also wrong, need to have this info stored in the event! FIXME if ( event_data->SaveJPEGs & 1 ) { - snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id ); + snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id); } else if ( event_data->SaveJPEGs & 2 ) { - snprintf( filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id ); - if ( stat( filepath, &filestat ) < 0 ) { + snprintf(filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id); + if ( stat(filepath, &filestat ) < 0 ) { Debug(1, "analyze file %s not found will try to stream from other", filepath); - snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id ); - if ( stat( filepath, &filestat ) < 0 ) { + snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id); + if ( stat(filepath, &filestat ) < 0 ) { Debug(1, "capture file %s not found either", filepath); filepath[0] = 0; } } - } else if ( ! ffmpeg_input ) { - Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet"); + } else if ( !ffmpeg_input ) { + Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet"); return false; } #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) { Debug(2,"Streaming MPEG"); - Image image( filepath ); + Image image(filepath); Image *send_image = prepareImage(&image); @@ -605,7 +614,7 @@ Debug(2,"Streaming MPEG"); fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType()); vid_stream->OpenStream(); } - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + /* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000); } else #endif // HAVE_LIBAVCODEC { @@ -616,7 +625,7 @@ Debug(2,"Streaming MPEG"); bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0]; - fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf(stdout, "--ZoneMinderFrame\r\n"); if ( (type != STREAM_JPEG) || (!filepath[0]) ) send_raw = false; @@ -628,8 +637,8 @@ Debug(2,"Streaming MPEG"); return false; } #if HAVE_SENDFILE - if( fstat(fileno(fdj),&filestat) < 0 ) { - Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); + if ( fstat(fileno(fdj),&filestat) < 0 ) { + Error("Failed getting information about file %s: %s", filepath, strerror(errno)); return false; } #else @@ -644,7 +653,7 @@ Debug(1, "Loading image"); } else if ( ffmpeg_input ) { // Get the frame from the mp4 input Debug(1,"Getting frame from ffmpeg"); - AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() ); + AVFrame *frame = ffmpeg_input->get_frame(ffmpeg_input->get_video_stream_id()); if ( frame ) { image = new Image(frame); av_frame_free(&frame); @@ -659,7 +668,7 @@ Debug(1, "Loading image"); Image *send_image = prepareImage(image); - switch( type ) { + switch ( type ) { case STREAM_JPEG : send_image->EncodeJpeg(img_buffer, &img_buffer_size); break; @@ -678,22 +687,22 @@ Debug(1, "Loading image"); img_buffer_size = send_image->Size(); break; default: - Fatal( "Unexpected frame type %d", type ); + Fatal("Unexpected frame type %d", type); break; } delete image; image = NULL; - } + } // end if send_raw or not - switch( type ) { + switch ( type ) { case STREAM_JPEG : - fputs( "Content-Type: image/jpeg\r\n", stdout ); + fputs("Content-Type: image/jpeg\r\n", stdout); break; case STREAM_RAW : - fputs( "Content-Type: image/x-rgb\r\n", stdout ); + fputs("Content-Type: image/x-rgb\r\n", stdout); break; case STREAM_ZIP : - fputs( "Content-Type: image/x-rgbz\r\n", stdout ); + fputs("Content-Type: image/x-rgbz\r\n", stdout); break; default : Fatal("Unexpected frame type %d", type); @@ -702,40 +711,40 @@ Debug(1, "Loading image"); if ( send_raw ) { #if HAVE_SENDFILE - fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); + fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size); if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) { /* sendfile() failed, use standard way instead */ img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { fclose(fdj); /* Close the file handle */ Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); + return false; } } #else - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); + if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { fclose(fdj); /* Close the file handle */ Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); + return false; } #endif fclose(fdj); /* Close the file handle */ } else { - Debug(3, "Content length: %d", img_buffer_size ); - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); + Debug(3, "Content length: %d", img_buffer_size); + fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); + if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { + Error("Unable to send stream frame: %s", strerror(errno)); + return false; } - } + } // end if send_raw or not fputs("\r\n\r\n", stdout); fflush(stdout); - } + } // end if stream MPEG or other last_frame_sent = TV_2_FLOAT(now); return true; -} +} // bool EventStream::sendFrame( int delta_us ) void EventStream::runStream() { openComms(); @@ -754,7 +763,7 @@ void EventStream::runStream() { Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration); updateFrameRate((double)event_data->frame_count/event_data->duration); - while( !zm_terminate ) { + while ( !zm_terminate ) { gettimeofday(&now, NULL); unsigned int delta_us = 0; @@ -795,6 +804,7 @@ void EventStream::runStream() { } if ( !in_event ) { double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; + // > 1 second if ( actual_delta_time > 1 ) { static char frame_text[64]; snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event); @@ -803,12 +813,12 @@ void EventStream::runStream() { } //else //{ - usleep( STREAM_PAUSE_WAIT ); + usleep(STREAM_PAUSE_WAIT); //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); 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 @@ -833,7 +843,7 @@ void EventStream::runStream() { Debug(2, "Sending keepalive frame"); send_frame = true; } - } + } // end if streaming stepping or doing nothing if ( send_frame ) if ( !sendFrame(delta_us) ) @@ -848,12 +858,12 @@ void EventStream::runStream() { curr_frame_id = 1; } if ( send_frame && type != STREAM_MPEG ) { - Debug( 3, "dUs: %d", delta_us ); + Debug(3, "dUs: %d", delta_us); if ( delta_us ) - usleep( delta_us ); + usleep(delta_us); } } else { - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + usleep((unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)))); } } // end while ! zm_terminate #if HAVE_LIBAVCODEC @@ -862,15 +872,17 @@ void EventStream::runStream() { #endif // HAVE_LIBAVCODEC closeComms(); -} +} // void EventStream::runStream() + void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) { - loadInitialEventData( init_event_id, init_frame_id ); - if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { - Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id ); + loadInitialEventData(init_event_id, init_frame_id); + if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { + Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); return; } } -void EventStream::setStreamStart( int monitor_id, time_t event_time ) { + +void EventStream::setStreamStart(int monitor_id, time_t event_time) { loadInitialEventData(monitor_id, event_time); if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { Fatal("Unable to load monitor id %d for streaming", monitor_id); diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 86138b6ef..ab0aa2953 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -88,7 +88,7 @@ bool User::canAccess( int monitor_id ) { // Function to load a user from username and password // Please note that in auth relay mode = none, password is NULL User *zmLoadUser( const char *username, const char *password ) { - char sql[ZM_SQL_SML_BUFSIZ] = ""; + char sql[ZM_SQL_MED_BUFSIZ] = ""; char safer_username[65]; // current db username size is 32 // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. @@ -97,35 +97,40 @@ User *zmLoadUser( const char *username, const char *password ) { if ( password ) { char safer_password[129]; // current db password size is 64 mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) ); - snprintf( sql, sizeof(sql), "select Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password ); + snprintf(sql, sizeof(sql), + "SELECT Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds" + " FROM Users WHERE Username = '%s' AND Password = password('%s') AND Enabled = 1", + safer_username, safer_password ); } else { - snprintf( sql, sizeof(sql), "select Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username ); + snprintf(sql, sizeof(sql), + "SELECT Id, Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds" + " FROM Users where Username = '%s' and Enabled = 1", safer_username ); } - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - int n_users = mysql_num_rows( result ); + int n_users = mysql_num_rows(result); if ( n_users != 1 ) { - mysql_free_result( result ); - Warning( "Unable to authenticate user %s", username ); - return( 0 ); + mysql_free_result(result); + Warning("Unable to authenticate user %s", username); + return NULL; } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row(result); - User *user = new User( dbrow ); - Info( "Authenticated user '%s'", user->getUsername() ); + User *user = new User(dbrow); + Info("Authenticated user '%s'", user->getUsername()); - mysql_free_result( result ); + mysql_free_result(result); return user; } diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index adc10cf8e..49b4d5ffc 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -422,7 +422,7 @@ void touch(const char *pathname) { 0666); if ( fd < 0 ) { // Couldn't open that path. - Error("Couldn't open() path \"%s in touch", pathname); + Error("Couldn't open() path %s in touch", pathname); return; } int rc = utimensat(AT_FDCWD, diff --git a/web/includes/Group.php b/web/includes/Group.php index 82c1daba9..89849309b 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -188,7 +188,7 @@ class Group { session_write_close(); return htmlSelect( 'Group[]', Group::get_dropdown_options(), isset($_SESSION['Group'])?$_SESSION['Group']:null, array( - 'onchange' => 'this.form.submit();', + 'data-on-change' => 'submitThisForm', 'class'=>'chosen', 'multiple'=>'multiple', 'data-placeholder'=>'All', diff --git a/web/includes/Server.php b/web/includes/Server.php index 7424aafdf..65721214d 100644 --- a/web/includes/Server.php +++ b/web/includes/Server.php @@ -9,8 +9,8 @@ class Server { 'Name' => '', 'Protocol' => '', 'Hostname' => '', - 'Port' => null, - 'PathToIndex' => '/zm/index.php', + 'Port' => null, + 'PathToIndex' => null, 'PathToZMS' => ZM_PATH_ZMS, 'PathToApi' => '/zm/api', 'zmaudit' => 1, @@ -214,5 +214,19 @@ class Server { return $results[0]; } + public function to_json() { + $json = array(); + foreach ($this->defaults as $key => $value) { + if ( is_callable(array($this, $key)) ) { + $json[$key] = $this->$key(); + } else if ( array_key_exists($key, $this) ) { + $json[$key] = $this->{$key}; + } else { + $json[$key] = $this->defaults{$key}; + } + } + return json_encode($json); + } + } # end class Server ?> diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php index 55819329c..692015e70 100644 --- a/web/includes/csrf/csrf-magic.php +++ b/web/includes/csrf/csrf-magic.php @@ -150,24 +150,25 @@ function csrf_ob_handler($buffer, $flags) { return $buffer; } } + global $cspNonce; $tokens = csrf_get_tokens(); $name = $GLOBALS['csrf']['input-name']; $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; $input = ""; $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); if ($GLOBALS['csrf']['frame-breaker']) { - $buffer = str_ireplace('', '', $buffer); + $buffer = str_ireplace('', '', $buffer); } if ($js = $GLOBALS['csrf']['rewrite-js']) { $buffer = str_ireplace( '', - ''. - '', + '', $buffer ); - $script = ''; + $script = ''; $buffer = str_ireplace('', $script . '', $buffer, $count); if (!$count) { $buffer .= $script; @@ -183,6 +184,7 @@ function csrf_ob_handler($buffer, $flags) { */ function csrf_check($fatal = true) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + global $cspNonce; csrf_start(); $name = $GLOBALS['csrf']['input-name']; $ok = false; diff --git a/web/includes/functions.php b/web/includes/functions.php index 4b18e3237..206036643 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -35,6 +35,28 @@ function noCacheHeaders() { header('Pragma: no-cache'); // HTTP/1.0 } +function CSPHeaders($view, $nonce) { + switch ($view) { + case 'bandwidth': + case 'function': + case 'log': + case 'logout': + case 'options': + case 'version': { + // Enforce script-src on pages where inline scripts and event handlers have been fixed. + // 'unsafe-inline' is only for backwards compatibility with browsers which + // only support CSP 1 (with no nonce-* support). + header("Content-Security-Policy: script-src 'unsafe-inline' 'self' 'nonce-$nonce'"); + break; + } + default: { + // Use Report-Only mode on all other pages. + header("Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self' 'nonce-$nonce'"); + break; + } + } +} + function CORSHeaders() { if ( isset($_SERVER['HTTP_ORIGIN']) ) { @@ -44,6 +66,11 @@ function CORSHeaders() { if ( sizeof($Servers) < 1 ) { # Only need CORSHeaders in the event that there are multiple servers in use. # ICON: Might not be true. multi-port? + if ( ZM_MIN_STREAMING_PORT ) { + Logger::Debug("Setting default Access-Control-Allow-Origin from " . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Headers: x-requested-with,x-request'); + } return; } foreach( $Servers as $Server ) { @@ -400,14 +427,19 @@ function makeLink( $url, $label, $condition=1, $options='' ) { } function makePopupLink( $url, $winName, $winSize, $label, $condition=1, $options='' ) { - $string = ''; + // Avoid double-encoding since some consumers incorrectly pass a pre-escaped URL. + $string = ''; + $string .= ($options ? (' ' . $options ) : '') . '>'; } else { $string .= ''; } @@ -417,11 +449,20 @@ function makePopupLink( $url, $winName, $winSize, $label, $condition=1, $options } function makePopupButton( $url, $winName, $winSize, $buttonValue, $condition=1, $options='' ) { - if ( is_array( $winSize ) ) - $popupParms = "'".$url."', '".$winName."', '".$winSize[0]."', ".$winSize[1].", ".$winSize[2]; - else - $popupParms = "'".$url."', '".$winName."', '".$winSize."'"; - $string = ''; + $string = ''; return( $string ); } @@ -2258,6 +2299,29 @@ function csrf_startup() { csrf_conf('rewrite-js', 'includes/csrf/csrf-magic.js'); } +function check_timezone() { + $now = new DateTime(); + + $sys_tzoffset = trim(shell_exec('date "+%z"')); + $php_tzoffset = trim($now->format('O')); + $mysql_tzoffset = trim(dbFetchOne("SELECT TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),'%H%i');",'TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),\'%H%i\')')); + + #Logger::Debug("System timezone offset determine to be: $sys_tzoffset,\x20 + #PHP timezone offset determine to be: $php_tzoffset,\x20 + #Mysql timezone offset determine to be: $mysql_tzoffset + #"); + + if ( $sys_tzoffset != $php_tzoffset ) + Fatal("ZoneMinder is not installed properly: php's date.timezone does not match the system timezone!"); + + if ( $sys_tzoffset != $mysql_tzoffset ) + Error("ZoneMinder is not installed properly: mysql's timezone does not match the system timezone! Event lists will display incorrect times."); + + if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) + Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); + +} + function unparse_url($parsed_url, $substitutions = array() ) { $fields = array('scheme','host','port','user','pass','path','query','fragment'); diff --git a/web/index.php b/web/index.php index ef55a81d6..29e67d628 100644 --- a/web/index.php +++ b/web/index.php @@ -69,11 +69,9 @@ define('ZM_BASE_PROTOCOL', $protocol); // Use relative URL's instead define('ZM_BASE_URL', ''); -// Check time zone is set -if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) { - date_default_timezone_set('UTC'); - Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); -} +// Verify the system, php, and mysql timezones all match +require_once('includes/functions.php'); +check_timezone(); if ( isset($_GET['skin']) ) { $skin = $_GET['skin']; @@ -155,7 +153,6 @@ if ( ZM_OPT_USE_AUTH ) { session_write_close(); require_once('includes/lang.php'); -require_once('includes/functions.php'); # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -175,6 +172,10 @@ $view = null; if ( isset($_REQUEST['view']) ) $view = detaintPath($_REQUEST['view']); +# Add CSP Headers +$cspNonce = bin2hex(openssl_random_pseudo_bytes(16)); +CSPHeaders($view, $cspNonce); + $request = null; if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 7d4b7950e..6274afae5 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -287,7 +287,7 @@ function controlPresets( $monitor, $cmds ) { } if ( canEdit('Monitors') && $monitor->CanSetPresets() ) { ?> - + diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 02789249b..eefc58ab1 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -270,12 +270,12 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {

- - - - - - + + + + + +
 
diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 196c9fe89..535d8ba31 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -120,7 +120,7 @@ echo output_link_if_exists( array( - "; + echo ""; } # end if tab == skins ?> @@ -185,7 +185,7 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI - disabled="disabled"/> + disabled="disabled"/>
- +
@@ -252,13 +252,13 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI Id(), 'zmServer', 'server', $Server->zmaudit() ? 'yes' : 'no', $canEdit) ?> Id(), 'zmServer', 'server', $Server->zmtrigger() ? 'yes' : 'no', $canEdit) ?> - disabled="disabled"/> + disabled="disabled"/>
- +
@@ -293,13 +293,13 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?> disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> - disabled="disabled"/> + disabled="disabled"/>
- +
diff --git a/web/skins/classic/views/plugin.php b/web/skins/classic/views/plugin.php index dd7de246a..7b4cf85ca 100644 --- a/web/skins/classic/views/plugin.php +++ b/web/skins/classic/views/plugin.php @@ -158,7 +158,8 @@ foreach($pluginOptions as $name => $popt) ?> - disabled="disabled"/> + disabled="disabled"/> +
diff --git a/web/skins/classic/views/privacy.php b/web/skins/classic/views/privacy.php index 1480c2973..9bd283b4a 100644 --- a/web/skins/classic/views/privacy.php +++ b/web/skins/classic/views/privacy.php @@ -65,7 +65,7 @@ xhtmlHeaders(__FILE__, translate('Privacy') );

- +
diff --git a/web/skins/classic/views/server.php b/web/skins/classic/views/server.php index 42dd5fc6f..a4e60c3f2 100644 --- a/web/skins/classic/views/server.php +++ b/web/skins/classic/views/server.php @@ -99,7 +99,7 @@ xhtmlHeaders(__FILE__, translate('Server').' - '.$Server->Name());
- +
diff --git a/web/skins/classic/views/settings.php b/web/skins/classic/views/settings.php index e5ac9707b..2ed9e7c5d 100644 --- a/web/skins/classic/views/settings.php +++ b/web/skins/classic/views/settings.php @@ -69,7 +69,7 @@ xhtmlHeaders(__FILE__, validHtmlStr($monitor['Name'])." - ".translate('Settings'
- disabled="disabled"/> + disabled="disabled"/>
diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index 3aecdb042..49c8d4dec 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -104,7 +104,7 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
- +
diff --git a/web/skins/classic/views/user.php b/web/skins/classic/views/user.php index 028ce439d..ee0c7428c 100644 --- a/web/skins/classic/views/user.php +++ b/web/skins/classic/views/user.php @@ -153,7 +153,7 @@ if ( canEdit( 'System' ) )
- +
diff --git a/web/skins/classic/views/version.php b/web/skins/classic/views/version.php index e2db1de68..70590eb2b 100644 --- a/web/skins/classic/views/version.php +++ b/web/skins/classic/views/version.php @@ -55,7 +55,7 @@ if ( ZM_DYN_DB_VERSION && (ZM_DYN_DB_VERSION != ZM_VERSION) )

- +

-

+

- +

- - + +
diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 850f0c63d..225fb83fe 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -93,8 +93,8 @@ if ( canView('Control') && $monitor->Type() == 'Local' ) { if ( canEdit('Monitors') ) { ?>
- - + +
StreamReplayBuffer() != 0 ) { ?> - - + + - - - + + + StreamReplayBuffer() != 0 ) { ?> - - + + - + diff --git a/web/skins/classic/views/zone.php b/web/skins/classic/views/zone.php index a2170eda3..cbcbc74aa 100644 --- a/web/skins/classic/views/zone.php +++ b/web/skins/classic/views/zone.php @@ -267,7 +267,7 @@ for ( $i = 0; $i < $pointCols; $i++ ) { - + disabled="disabled"/> diff --git a/web/skins/classic/views/zones.php b/web/skins/classic/views/zones.php index e9e5f292c..5446676b4 100644 --- a/web/skins/classic/views/zones.php +++ b/web/skins/classic/views/zones.php @@ -48,7 +48,7 @@ xhtmlHeaders(__FILE__, translate('Zones') );
@@ -57,7 +57,7 @@ xhtmlHeaders(__FILE__, translate('Zones') );
- disabled="disabled"/> + Width(), $monitor->Height()), translate('AddNewZone'), canEdit('Monitors')); ?>
@@ -74,10 +74,10 @@ xhtmlHeaders(__FILE__, translate('Zones') ); foreach( $zones as $zone ) { ?> - + - + - +
Width(), $monitor->Height()), $zone['Name'], true, 'onclick="streamCmdQuit( true ); return( false );"'); ?>  / Width()*$monitor->Height()) ) ?> disabled="disabled"/> disabled="disabled"/>