"web_js" ) ); $string = $_POST['message']; $file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/\w+\//', '', $_POST['file'] ) : ''; if ( !empty( $_POST['line'] ) ) $line = $_POST['line']; else $line = NULL; $levels = array_flip(Logger::$codes); if ( !isset($levels[$_POST['level']]) ) Panic( "Unexpected logger level '".$_POST['level']."'" ); $level = $levels[$_POST['level']]; Logger::fetch()->logPrint( $level, $string, $file, $line ); } ajaxResponse(); break; } case 'query' : { if ( !canView( 'System' ) ) ajaxError( 'Insufficient permissions to view log entries' ); $servers = Server::find_all(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { $servers_by_Id[$server->Id()] = $server; } $minTime = isset($_REQUEST['minTime'])?$_REQUEST['minTime']:NULL; $maxTime = isset($_REQUEST['maxTime'])?$_REQUEST['maxTime']:NULL; $limit = 100; if ( isset($_REQUEST['limit']) ) { if ( ( !is_integer( $_REQUEST['limit'] ) and !ctype_digit($_REQUEST['limit']) ) ) { Error("Invalid value for limit " . $_REQUEST['limit'] ); } else { $limit = $_REQUEST['limit']; } } $sortField = 'TimeKey'; if ( isset($_REQUEST['sortField']) ) { if ( ! in_array( $_REQUEST['sortField'], $filterFields ) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) { Error("Invalid sort field " . $_REQUEST['sortField'] ); } else { $sortField = $_REQUEST['sortField']; } } $sortOrder = (isset($_REQUEST['sortOrder']) and $_REQUEST['sortOrder']) == 'asc' ? 'asc':'desc'; $filter = isset($_REQUEST['filter'])?$_REQUEST['filter']:array(); $total = dbFetchOne( 'SELECT count(*) AS Total FROM Logs', 'Total' ); $sql = 'SELECT * FROM Logs'; $where = array(); $values = array(); if ( $minTime ) { $where[] = "TimeKey > ?"; $values[] = $minTime; } elseif ( $maxTime ) { $where[] = "TimeKey < ?"; $values[] = $maxTime; } foreach ( $filter as $field=>$value ) { if ( ! in_array( $field, $filterFields ) ) { Error("$field is not in valid filter fields"); continue; } if ( $field == 'Level' ){ $where[] = $field." <= ?"; $values[] = $value; } else { $where[] = $field." = ?"; $values[] = $value; } } if ( count($where) ) $sql.= ' WHERE '.join( ' AND ', $where ); $sql .= " order by ".$sortField." ".$sortOrder." limit ".$limit; $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['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'] ); $logs[] = $log; } $options = array(); $where = array(); $values = array(); foreach( $filter as $field=>$value ) { if ( $field == 'Level' ) { $where[$field] = $field." <= ?"; $values[$field] = $value; } else { $where[$field] = $field." = ?"; $values[$field] = $value; } } foreach( $filterFields as $field ) { $sql = "SELECT DISTINCT $field FROM Logs WHERE NOT isnull($field)"; $fieldWhere = array_diff_key( $where, array( $field=>true ) ); $fieldValues = array_diff_key( $values, array( $field=>true ) ); if ( count($fieldWhere) ) $sql.= " AND ".join( ' AND ', $fieldWhere ); $sql.= " ORDER BY $field ASC"; if ( $field == 'Level' ) { foreach( dbFetchAll( $sql, $field, array_values($fieldValues) ) as $value ) if ( $value <= Logger::INFO ) $options[$field][$value] = Logger::$codes[$value]; else $options[$field][$value] = "DB".$value; } elseif ( $field == 'ServerId' ) { foreach( dbFetchAll( $sql, $field, array_values($fieldValues) ) as $value ) $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : ''; } else { foreach( dbFetchAll( $sql, $field, array_values( $fieldValues ) ) as $value ) if ( $value != '' ) $options[$field][] = $value; } } if ( count($filter) ) { $sql = "SELECT count(*) AS Available FROM Logs WHERE ".join( ' AND ', $where ); $available = dbFetchOne( $sql, 'Available', array_values($values) ); } 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' : { 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 ) { $tempTime = $minTime; $minTime = $maxTime; $maxTime = $tempTime; } //$limit = isset($_POST['limit'])?$_POST['limit']:1000; $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = 'TimeKey'; if ( isset($_POST['sortField']) ) { if ( ! in_array( $_POST['sortField'], $filterFields ) and ( $_POST['sortField'] != 'TimeKey' ) ) { Error("Invalid sort field " . $_POST['sortField'] ); } else { $sortField = $_POST['sortField']; } } $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; $servers = Server::find_all(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { $servers_by_Id[$server->Id()] = $server; } $sql = "select * from Logs"; $where = array(); $values = array(); if ( $minTime ) { preg_match( '/(.+)(\.\d+)/', $minTime, $matches ); $minTime = strtotime($matches[1]).$matches[2]; $where[] = "TimeKey >= ?"; $values[] = $minTime; } if ( $maxTime ) { preg_match( '/(.+)(\.\d+)/', $maxTime, $matches ); $maxTime = strtotime($matches[1]).$matches[2]; $where[] = "TimeKey <= ?"; $values[] = $maxTime; } foreach ( $filter as $field=>$value ) { if ( $value != '' ) { if ( $field == 'Level' ) { $where[] = $field." <= ?"; $values[] = $value; } else { $where[] = $field." = ?'"; $values[] = $value; } } } if ( count($where) ) $sql.= " where ".join( " and ", $where ); $sql .= " order by ".$sortField." ".$sortOrder; //$sql .= " limit ".dbEscape($limit); $format = isset($_POST['format'])?$_POST['format']:'text'; switch( $format ) { case 'text' : $exportExt = "txt"; break; case 'tsv' : $exportExt = "tsv"; break; case 'html' : $exportExt = "html"; break; case 'xml' : $exportExt = "xml"; break; default : Fatal( "Unrecognised log export format '$format'" ); } $exportKey = substr(md5(rand()),0,8); $exportFile = "zm-log.$exportExt"; $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; if ( !($exportFP = fopen( $exportPath, "w" )) ) 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['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } 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'] ); else 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", translate('DateTime'), translate('Component'), translate('Server'), translate('Pid'), translate('Level'), translate('Message'), translate('File'), translate('Line') )."\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'] ); } break; } case 'html' : { fwrite( $exportFP, ' '.translate('ZoneMinderLog').'

'.translate('ZoneMinderLog').'

'.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 < Logger::FATAL ) $classLevel = Logger::FATAL; elseif ( $classLevel > Logger::DEBUG ) $classLevel = Logger::DEBUG; $logClass = 'log-'.strtolower(Logger::$codes[$classLevel]); fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); } 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
' ); break; } case 'xml' : { fwrite( $exportFP, ' '.$_POST['selector'].'' ); foreach ( $filter as $field=>$value ) if ( $value != '' ) fwrite( $exportFP, ' <'.strtolower($field).'>'.htmlspecialchars($value).' ' ); fwrite( $exportFP, ' '.translate('DateTime').''.translate('Component').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').' ' ); foreach ( $logs as $log ) { fprintf( $exportFP, " %s %s %s %d %s %s %d \n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); } fwrite( $exportFP, ' ' ); break; } $exportExt = "xml"; break; } fclose( $exportFP ); ajaxResponse( array( 'key' => $exportKey, 'format' => $format, ) ); break; } case 'download' : { if ( !canView( 'System' ) ) ajaxError( 'Insufficient permissions to download logs' ); if ( empty($_REQUEST['key']) ) Fatal( "No log export key given" ); $exportKey = $_REQUEST['key']; if ( empty($_REQUEST['format']) ) Fatal( "No log export format given" ); $format = $_REQUEST['format']; switch( $format ) { case 'text' : $exportExt = "txt"; break; case 'tsv' : $exportExt = "tsv"; break; case 'html' : $exportExt = "html"; break; case 'xml' : $exportExt = "xml"; break; default : Fatal( "Unrecognised log export format '$format'" ); } $exportFile = "zm-log.$exportExt"; $exportPath = ZM_PATH_SWAP."/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( "Content-Description: File Transfer" ); header( 'Content-Disposition: attachment; filename="'.$exportFile.'"' ); header( "Content-Transfer-Encoding: binary" ); header( "Content-Type: application/force-download" ); header( "Content-Length: ".filesize($exportPath) ); readfile( $exportPath ); exit( 0 ); break; } } ajaxError( 'Unrecognised action or insufficient permissions' ); ?>