# A file matching approach Order Allow,Deny Allow from All AllowOverride None AuthType Basic AuthName "Password Required" # AuthUserFile c:/password/htpasswd.txt # Windows example AuthUserFile /etc/apache2/htpassword # OpenWRT or Linux example # Require user admin # Specific user example Require valid-user # Any valid user example # If we host this script as index.php in this folder Order Allow,Deny Allow from All AllowOverride None AuthType Basic AuthName "Password Required" # AuthUserFile c:/password/htpasswd.txt # Windows example AuthUserFile /etc/apache2/htpassword # OpenWRT or Linux example # Require user admin # Specific user example Require valid-user # Any valid user example If including logview.php from index.php in a folder-based access solution, use: IP filtering rules: ------------------------------ * It is strongly recommended that access is restricted to LAN only * Use either internal $ip_mask or external INI config, not both Filters using global var $ip_mask which is also stored in INI config file Avoid the use of partial addresses and excess * wildcards where possible other than on local LAN addresses (e.g. '192.168.*') Global var: $ip_mask may have 1 or more whitespace separated IP masks An IP mask may comprise of a literal IPV4 address or be comprised of 1 to 4 octet-parameters of either NNN, * or mixed ? N placeholders < 4 octet parameters can be used for a partial (prefix) match (e.g. '192.*') * and ? placeholders may be mixed within an IP address mask but not in an octet ? matches an exact number of characters. "??" will match NN but not N or NNN Example: $ip_mask="127.0.0.1 192.* 169.254.* 243.144.3.1?? 1.2.3.??" Result: Match 127.0.0.1 exactly; any address with 192 prefix; any 3 digit 243.144.3.* address with 3 digits and '1' prefix; any 2 digit IP on subnet 1.2.3.* Note: Windows APIPA range is: 169.254.0.1 to 169.254.254.255 https://www.lifewire.com/automatic-private-internet-protocol-addressing-816437 Note: Private Class "A" subnet is 10.* Note: Private Class "B" subnet is 172.16.0.0 – 172.31.255.255 Note: Private Class "C" subnet is 192.168.* https://en.wikipedia.org/wiki/Private_network Note: CIDR has superseded much of the above https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing Attributes in /var/log (or /tmp/log) ------------------------------------ Impossible to set sufficient attributes to enable PHP script read access These attributes, if set are lost after each reboot Suggest where USB mount is used that Apache is configured to use an alternative log folder which places logs on the USB drive. Reconfigure /etc/apache2/apache2.conf accordingly ////////////////////////////////////////////////////////////////////////////////////// LogView Error log ----------------- This script can generate an error log which will output in CSV format, ready for spreadsheet import ////////////////////////////////////////////////////////////////////////////////////// Future versions --------------- I might add highlighting of individual keywords as well as matched row entries ////////////////////////////////////////////////////////////////////////////////////// INI file settings Section: [config] theme=short-theme-name ; Which theme to select ip_mask= ; Access filter lines= ; Number of lines to display refresh= ; Refresh interval in seconds ////////////////////////////////////////////////////////////////////////////////////// The script... */ // User-configurable options start ... // Configurable default parameters (overridable via INI file) // ** IP ADDRESS SECURITY RESTRICTIONS ** // Configure as "..." // Localhost, Local LAN (192.168.*) (10.*), APIPA range (168.254.*) $ip_mask="127.0.0.1 192.168.* *"; // Default internal permitted IP address //// Folder structure // //// OpenWRT Example /////////////// //* // ** Uncomment to enable for OpenWRT // Must be writeable (pick a suitable folder...) $ini_file="/usr/share/apache2/htdocs/logview.ini"; // Any suitable, secure+writeable folder // Must be writeable $log_dir="/log/apache2/"; // A path not a URL (OpenWRT example) // Must be readable $error_log="/log/apache2/"; // A Path not a URL (OpenWRT example) */ //// Windows Example /////////////// /* // ** Uncomment to enable for Windows (MicroApachce) // Must be writeable $ini_file="C:\\wwwroot\\logview.ini"; // Default internal configuration file (path not URL) // Must be writeable $log_dir="C:\\Apps\\MicroApache-2.0.64-PHP-5.2.17\\logs\\"; // A path not a URL // Must be readable $error_log="C:\\Apps\\MicroApache-2.0.64-PHP-5.2.17\\logs\\"; // Error log. A PATH (we will append filename later) */ // User configuration continued... $theme='standard'; // Default internal theme style set_theme($theme); // Set the theme (ensure we select in drop down box) $css_file='/css/logview.css'; // This is a URL not a path $refresh_secs=60; // Refresh interval in seconds (Default: 1 minute) $highlight_string="jpg png"; // Specimen highlight string $display_lines=20; // Default number of log lines to display (Default: 20 lines) $_no_css=false; // Disable CSS handling if desired (Default: enabled) $menu_font_colour='FFFFFF'; // Configuration menu screen font (fore) $menu_back_colour='8080FF'; // Configuration menu screen back $font_size=3; // Relevant HTML font sizes: 1=8pt, 2=10pt, 3=12pt, 4=14pt ////////////////////////////////////////////////////////////////////////////////////// // User configuration section ends // ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // Not user-configurable! (For future modifications, please leave) // These globals should ideally be const $_EXENAME="LogView"; // Display name only (NOT script name) $_VER='1.02'; // This version release $_YEAR='2021'; // Release year $_WEBSITE='http://www.kerys.co.uk'; // Download site $script_name="logview.php"; // Internal default (is overridden) $font_style=''; // Leave empty for now (unused) $theme_count=0; // Poss not required (unused) $rawcgi=false; // Whether or not to send raw GGI (unused) $_send_headers=false; // Whether or not to send headers for error page etc. for Xitami server $theme_name=array( // Used to build the theme menu selection list 'chalkboard' => 'Chalkboard', 'chocolate' => 'Chocolate', 'console' => 'Console', 'dark' => 'Dark', 'gothic' => 'Gothic', 'green' => 'Green Screen', 'large' => 'Large Font', 'modern' => 'Modern', 'paper' => 'Paper', 'pink' => 'Pink', 'small' => 'Small Font', 'soviet' => 'Soviet', 'standard' => 'Standard Theme', 'blues' => 'The Blues', 'times' => 'Times', ); function set_theme($theme) { ////////////////////////////////////////////////////////// // Purpose: Sets relevant display values by theme name // Customisable. Set your own themes in here // Be sure to update the menu array above if you add new ////////////////////////////////////////////////////////// global $fore_colour_hex; global $back_colour_hex; global $highlight_colour_hex; global $font_name; global $font_size; if(strcmp($theme,"blues")==0) { $fore_colour_hex='C0C0C0'; // FFFFFF $back_colour_hex='000080'; $highlight_colour_hex='8080FF'; $font_name='Arial'; $font_size=3; } else if(strcmp($theme,"chalkboard")==0) { $fore_colour_hex='FFFFFF'; $back_colour_hex='000000'; $highlight_colour_hex='FFFF00'; $font_name="Comic Sans MS"; $font_size=2; } else if(strcmp($theme,"chocolate")==0) { $fore_colour_hex='F5F5DC'; $back_colour_hex='5C3317'; $highlight_colour_hex='ED9564'; $font_name='Papyrus'; $font_size=3; } else if(strcmp($theme,"dark")==0) { $fore_colour_hex='808080'; $back_colour_hex='000000'; $highlight_colour_hex='C0C0C0'; $font_name='Arial'; $font_size=3; } else if(strcmp($theme,"gothic")==0) { $fore_colour_hex='000000'; $back_colour_hex='7A378B'; $highlight_colour_hex='FFB6C1'; //$font_name="BlackChancery"; // Custom font $font_name="Segoe Script"; // Set your own "gothic" type font here $font_size=2; } else if(strcmp($theme,"green")==0) { $fore_colour_hex='00FF00'; $back_colour_hex='000000'; $highlight_colour_hex='FF8000'; $font_name='Courier New'; $font_size=2; } else if(strcmp($theme,"modern")==0) { $fore_colour_hex='FAF0E6'; $back_colour_hex='708090'; $highlight_colour_hex='FA8072'; $font_name='Arial'; $font_size=3; } else if(strcmp($theme,"large")==0) { $fore_colour_hex='C0C0C0'; $back_colour_hex='000000'; $highlight_colour_hex='0000FF'; $font_name='Arial'; $font_size=4; } else if(strcmp($theme,"console")==0) { $fore_colour_hex='C0C0C0'; $back_colour_hex='000000'; $highlight_colour_hex='FF0000'; $font_name='Courier New'; $font_size=2; } else if(strcmp($theme,"paper")==0) { $fore_colour_hex='000000'; $back_colour_hex='FFFFFF'; $highlight_colour_hex='0000FF'; $font_name='Terminal'; $font_size=3; } else if(strcmp($theme,"pink")==0) { $fore_colour_hex='FFFFFF'; $back_colour_hex='FF00BA'; $highlight_colour_hex='FFCC00'; $font_name='Arial'; $font_size=3; } else if(strcmp($theme,"small")==0) { $fore_colour_hex='C0C0C0'; $back_colour_hex='000000'; $highlight_colour_hex='0000FF'; $font_name='Arial'; $font_size=2; } else if(strcmp($theme,"soviet")==0) { $fore_colour_hex='FFFFFF'; $back_colour_hex='FF0000'; $highlight_colour_hex='FFFF00'; $font_name="Arial"; $font_size=3; } else if(strcmp($theme,"standard")==0) { $fore_colour_hex='C0C0C0'; $back_colour_hex='000000'; $highlight_colour_hex='0000FF'; $font_name='Arial'; $font_size=3; } else if(strcmp($theme,"times")==0) { $fore_colour_hex='000000'; $back_colour_hex='FFF8DC'; $highlight_colour_hex='FF0000'; $font_name="Times New Roman"; $font_size=3; } else // Default { $fore_colour_hex='C0C0C0'; $back_colour_hex='000000'; $highlight_colour_hex='0000FF'; $font_name='Arial'; $font_size=3; } } function write_php_ini($array, $file) { // Will overwrite the whole file so all values must be set // https://www.php.net/manual/en/function.parse-ini-file.php $res = array(); foreach($array as $key => $val) { if(is_array($val)) { $res[] = "[$key]"; foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"'); } else $res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"'); } safefilerewrite($file, implode("\r\n", $res)); } // write_php_ini // function safefilerewrite($fileName, $dataToSave) { // https://www.php.net/manual/en/function.parse-ini-file.php if ($fp = @fopen($fileName, 'w')) { $startTime = microtime(TRUE); do { $canWrite = flock($fp, LOCK_EX); // If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load if(!$canWrite) usleep(round(rand(0, 100)*1000)); } while ((!$canWrite)and((microtime(TRUE)-$startTime) < 5)); //file was locked so now we can store information if ($canWrite) { fwrite($fp, $dataToSave); flock($fp, LOCK_UN); } fclose($fp); } } // safefilerewrite // function safeprint($s) { ////////////////////////////////////////////////////////// // Purpose: Single character print with filtering // We might encounter HTML angled brackets in log files // Blocks print content with < > from messing up display // or causing <> enclosed items to "disappear" ////////////////////////////////////////////////////////// if($s) { if(strstr($s,"<") || strstr($s,">")) { $ch=str_split($s); $count=count($ch); for($i=0; $i < $count; $i++) { if($ch[$i]=="<") printf("<"); else if($ch[$i]==">") printf(">"); else if($ch[$i]!="\r" && $ch[$i]!="\n") printf("%s",$ch[$i]); } } else printf("%s",$s); // NB puts() Inserts a newline char \n } } // safeprint // function fset_lines_from_eof($fp, $lines) { ///////////////////////////////////////////////////////////////////// // Purpose: Sets the file pointer "x" lines from EOF of an open file // so that we can effectively 'tail' it ///////////////////////////////////////////////////////////////////// // [in] An open FILE pointer // [in] Number of lines to read // [return] Number of lines actually read-in ///////////////////////////////////////////////////////////////////// // fseek ( resource $stream , int $offset , int $whence = SEEK_SET ) : int // ftell ( resource $stream ) : int|false // fgetc ( resource $stream ) : string|false ///////////////////////////////////////////////////////////////////// $ignoredlast=false; $fpos=0; $linecount=0; if($lines <= 0) { if(fseek($fp,0,SEEK_END)!=0) return ftell($fp); else return 0; } else if($fp) { if(fseek($fp,0,SEEK_END) != 0) return 0; $fpos=ftell($fp); //Get file ponter position absolute while($fpos > 0) { $ch=fgetc($fp); if($ch=="\n") //(Ignore first line) { if($ignoredlast) $linecount++; if($linecount==0) $ignoredlast=true; } if($linecount >= $lines) //We have wound back enough { //printf("Debug: Returning $fpos
"); return $fpos; } $fpos--; //printf("Debug: Seeking $fpos
"); fseek($fp,$fpos,SEEK_SET); // Wind back the file pointer } } return 0; } // fset_linesfrom_eof // function html_error($msg) { /////////////////////////////////////////////////////////////// // Purpose: Embed a standard error message in the current page // (does NOT create ) // Called by: html_errorpage /////////////////////////////////////////////////////////////// printf(""); printf("

%s

",$msg); printf("
Go Back    Home
"); } // html_error // function html_errorpage($msg, $css_file, $send_status=false, $htmlerror=200, $send_codes=true) { /////////////////////////////////////////////////////// // Purpose: Full error page with HTTP code prefix // Calls: html_error /////////////////////////////////////////////////////// // FIX Some args are unused and ported over from C++ // May be used later, but left here for now /////////////////////////////////////////////////////// global $_send_headers; global $_EXENAME; global $_no_css; if($send_status) // Hibachi et. al. require 200 header printf("HTTP/1.0 200 OK\n"); // Prefix else if($send_codes) // If flagged to send extended (failure) codes (e.g. non 200 codes) { if($htmlerror == 406) printf("Status: 406 Not Acceptable\n"); // 406 //"HTTP/1.1 400 Bad Request", // Alternative //"HTTP/1.1 204 No Content", // Alternative } if($_send_headers) // Globally compiled setting (not required for PHP) printf("Content-type: text/html\n\n"); printf(""); printf("%s - Error Message\n",$_EXENAME); printf("\n",$css_file); printf(""); if($_no_css) // Global printf("\n"); else printf("\n"); html_error($msg); printf(""); die; } // html_errorpage // function octet_match($o1, $o2) { /////////////////////////////////////////////////////////////// // Purpose: Check match on IP octet against wildcard with ? // ???, ??, ?, N?N, etc. // We must match ALL characters exactly across both mask and IP /////////////////////////////////////////////////////////////// // [in] Mask octet // [in] IP address octet // [return] Boolean /////////////////////////////////////////////////////////////// $count=0; $target_count=0; if($o1 && $o2) { $o1_ch=str_split($o1); // Mask $o1_count=count($o1_ch); $o2_ch=str_split($o2); // IP $o2_count=count($o2_ch); $target_count=max($o1_count,$o2_count); //printf("Debug: octet_match mask:'%s' ip:'%s' target_count:'%s'
",$o1,$o2,$target_count); for($i=0; $i < $o2_count; $i++) // For all mask chars { if($o1_ch[$i]==$o2_ch[$i]) // Direct digit match $count++; else if($o1_ch[$i]=="?") // Wildcard met $count++; if($count == $target_count) // Octet matching must be exact and satisfy mask and IP chars { //printf("Debug: Returning at count %s = %s
",$count,$target_count); return true; } } } return false; } // octet_match // function ip_match($mask, $ip) { /////////////////////////////////////////////////////////////// // Purpose: Check match on IP address against ONE IP mask // Note: Could have probably done this using regex but // I'm more used to using code and this code is ported from C /////////////////////////////////////////////////////////////// // [in] A single IPV4 mask spec // [in] IPV4 address // [return] Boolean /////////////////////////////////////////////////////////////// $count=0; if($mask && $ip) { //$mask_ch=str_split($mask); $mask_ch=explode(".",$mask); // Split mask by "." $mask_count=count($mask_ch); if($mask_count >= 1 && $mask_count <=4) // Mask may have 1 to 4 octets { $ip_ch=explode(".",$ip); // Split IP by "." $ip_count=count($ip_ch); if($ip_count == 4) // IPV4 address must have 4 octets { for($i=0; $i < $mask_count; $i++) // Iter for mask chars { //printf("Debug: ip_match. Check octet: '%s'='%s'
",$mask_ch[$i],$ip_ch[$i]); if($mask_ch[$i]==$ip_ch[$i]) // Octets match $count++; else if($mask_ch[$i]=='*') // Mask octet is wildcard $count++; else if(strstr($mask_ch[$i],"?")) // If we have a single char wildcard { if(octet_match($mask_ch[$i],$ip_ch[$i])) // Fine grained octet matches using ? $count++; } if($count == $mask_count) // Mask is satisfied return true; } } } } return false; } // ip_match // function ip_filter($mask,$ip) { //////////////////////////////////////////////////////////////// // Purpose: Check match IP address against multiple mask filter // e.g. "127.0.0.1 192.168.2.* 192.168.3.*" //////////////////////////////////////////////////////////////// // [in] IPV4 mask (with 1 or more IPV4 address specs) // [in] IPV4 address // [return] Boolean /////////////////////////////////////////////////////////////// if($mask && $ip) { $mask=str_replace("\t"," ",$mask); // Normalise whitespace: Replace \t with " " $mask_seg=explode(" ",$mask); $mask_count=count($mask_seg); for($i=0; $i < $mask_count; $i++) { //printf("Debug: ip_filter matching '%s' == '%s'
",$mask_seg[$i],$ip); if(ip_match($mask_seg[$i],$ip)) // Check each mask segment return true; } } return false; } // ip_filter // function log_append($log_file, $ip, $msg, $user_agent) { ////////////////////////////////////////////////////// // e.g. log_append($log_file,"Denied",$user_agent); ////////////////////////////////////////////////////// try { $q="\""; $qc="\",\""; // Handle errors more gracefully $fp=@fopen($log_file,"a+"); if($fp) { //printf("Debug: %s is open
",$log_file); $date= date("Ymd"); $time=date("H:i:s"); // fwrite ( resource $handle , string $string , int $length = ? ) : int if($user_agent) $ua=$user_agent; else $ua="-"; fwrite($fp,$q.$date.$qc.$time.$qc.$ip.$qc.$msg.$qc.$ua.$q."\n"); // CSV format fclose($fp); } } catch (Exception $e) { $temp="Exception in log_append. Check filename/path"; html_errorpage($temp,$css_file,$rawcgi); die; } } // log_append // function form_menu($script_name, $ini_file, $logfile_array, $css_file, $form_font_colour, $form_back_colour, $highlight_string) { ///////////////////////////////////////////////////////////////////// // Main menu form ///////////////////////////////////////////////////////////////////// // We parse (read) the INI file (if any) here and populate the menu // We re-save it on clicking 'start' to save any preferences // This way we read the file as minimally as possible // The IP filter mask is also loaded from the main menu, here ///////////////////////////////////////////////////////////////////// // FIX remove $theme arg, highlight_string global $_EXENAME; global $_VER; global $_YEAR; global $_WEBSITE; global $css_file; global $ip_mask; global $theme; global $display_lines; global $refresh_secs; global $highlight_string; //printf("Debug: checking '%s'
",$ini_file); if(file_exists($ini_file)) { $options=@parse_ini_file($ini_file,false); // We don't really need 2D array //print_r($options); } // The form is populated with these values which are then possibly altered and submitted via 'POST' callback if($options["filename"]) // Used to create persistent menu item only $filename=$options["filename"]; if($options["theme"]) $theme=$options["theme"]; if($options["lines"]) $display_lines=$options["lines"]; if($options["refresh"]) $refresh_secs=$options["refresh"]; if($options["keywords"]) $highlight_string=$options["keywords"]; //printf("
theme:'%s' ip_mask: '%s' display_lines:'%s' refresh_secs:'%s'
",$theme,$ip_mask,$display_lines,$refresh_secs); $size='2'; // menu font size (hard coded) echo "\n"; if($css_file) printf("\n",$css_file); printf("\n",$_EXENAME,$_VER); printf("%s v%s - Control Page - %s\n",$_EXENAME,$_VER,$_WEBSITE); printf("\n",$_EXENAME,$_VER); echo "\n"; // BODY... printf("\n"); printf("

\n"); // Fieldset: File Selections printf("
%s - File Selection\n",$_EXENAME); printf("
\n",$exename); printf("
\n"); printf("\n"); printf("\n"); // Filename list printf("\n"); // Lines to display printf("\n\n",$form_font_colour,$size); printf("\n",$display_lines); // 3 chars input printf("\n"); // Refresh interval printf("\n\n",$form_font_colour,$size); printf("\n",$refresh_secs); // 3 chars input printf("\n"); // Highlight keywords printf("\n\n",$highlight_string); // 40 chars input printf("\n"); // Display theme (list) printf("\n\n",$form_font_colour,$size); printf("\n"); printf("\n"); printf("\n"); printf("\n"); printf("\n"); // Close table printf("
\n",$form_back_colour); printf("Filename:
\n",$form_font_colour,$size); printf("
\n",$form_back_colour); printf("
\n"); } printf("\n"); printf("
\n",$form_back_colour); printf("Lines to Display:\n",$form_back_colour); printf("
\n",$form_back_colour); printf("Refresh Interval (Secs):\n",$form_back_colour); printf("
\n",$form_back_colour); printf("Highlight Keywords:\n",$form_font_colour,$size); printf("\n",$form_back_colour); printf("
\n",$form_back_colour); printf("Display Theme:\n",$form_back_colour); //Add method of indexing into current theme here.. printf("\n"); printf("
 
\n"); // Close off printf("%s - v%s - Copyright (C) M Shaw %s - %s\n",$_EXENAME,$_VER,$_YEAR,$_WEBSITE,$_WEBSITE); printf("
\n
\n"); printf("
\n"); echo "\n"; echo "\n"; // Write back to INI config on callback... die; } // form_menu // ///////////////////////////////////////////////////////////////////////////////////// // END function definitions etc. ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// # Resorted to this as init script doesn't work ///////////////////////////////////////////////////////////////////////////////////// /* $result=0; system("chmod 767 /var/log/apache2",$result); echo "result=".$result; system("chmod 767 /var/log/apache2/*",$result); echo "result=".$result; // */ ///////////////////////////////////////////////////////////////////////////////////// // Effectively this is C++ main() ... ///////////////////////////////////////////////////////////////////////////////////// // Retrieve server vars ///////////////////////////////// $script_name=$_SERVER["SCRIPT_NAME"]; // Retrieve the actual script name and override default value $request_uri=$_SERVER["REQUEST_URI"]; $query_string=$_SERVER["QUERY_STRING"]; $host_name=$_SERVER["HTTP_HOST"]; $url=$_SERVER["QUERY_STRING"]; // Build our error log file ".log" // This logs only IP filter violations and other errors not server login security failures $error_log.=pathinfo($script_name, PATHINFO_FILENAME)."-error.log"; // SECURITY: Retrieve the IP address ///////////////////////////////////// $client_ip=$_SERVER["REMOTE_ADDR"]; $user_agent=$_SERVER["HTTP_USER_AGENT"]; //echo "Checking path"; if(strcmp($client_ip,"")==0) { $temp="Failed to get your IP address!
Your access rights cannot be checked
"; log_append($error_log,$client_ip,"No IP",$user_agent); html_errorpage($temp,$css_file,$rawcgi); die; } // Read IP mask value from INI file on each callback first and foremost if(file_exists($ini_file)) { $options=parse_ini_file($ini_file,false); if(count($options) > 0) { if(strcmp($options["ip_mask"],"") != 0) // Never allow INI to supply a blank IP mask value $ip_mask=$options["ip_mask"]; } // Assert: $options no longer required for read past here } // Check access rights first of all... if(!ip_filter($ip_mask,$client_ip)) { $temp="IP address ".$client_ip." is not allowed access to this page
"; log_append($error_log,$client_ip,"Denied",$user_agent); html_errorpage($temp,$css_file,$rawcgi); die; } // END SECURITY // $user_agent==""; // No longer required if(strcmp($host_name,"")==0) // Xitami and other webservers { //printf("Debug: retrying host_name (was '$host_name')
\n"); $host_name=$_SERVER["REMOTE_HOST"]; } ///////////////////////////////////////////////////////////////////////////////////// // Check args and count ///////////////////////////////////////////////////////////////////////////////////// //$cgi_args=parse_url($url); // Incorrect parse_str($url,$cgi_args); $arg_count=count($cgi_args); //////////////////////////////////////////////////// // Callback saves configuration to INI (one-shot) //////////////////////////////////////////////////// if($query_string) // We MUST have a postd query string { if(strcmp($cgi_args["saved"],"")==0) // If "saved(=yes)" not present (check) { // Create an INI configuration array. New instance of $options $options=array( "config" => array( // x5 items (all) // config item => URL arg "theme" => $cgi_args["theme"], "lines" => $cgi_args["lines"], "refresh" => $cgi_args["refresh"], "keywords" => $cgi_args["highlight"], // FIX check "+" chars aren't populated into the INI file "filename" => $cgi_args["filename"], "ip_mask" => $ip_mask, // We may save the IP mask but don't pass it via arguments ), ); // Write entire INI file out write_php_ini($options, $ini_file); // function write_php_ini($array, $file) // Append saved=yes to the URL and call back again... if($query_string) header("Location: http://".$host_name.$script_name."?".$query_string."&saved=yes"); else header("Location: http://".$host_name.$script_name."?saved=yes"); die; } } /////////////////////////////////////////// // Build file list and display main menu /////////////////////////////////////////// if($arg_count < 1) { try { $logfile_array=@array_slice(scandir($log_dir),2); //print_r($logfile_array); // Filter out directories (folders) from files. PHP seems pretty rubbish at this simple task $filtered_array=array(); //echo "set? '".isset($logfile_array)."'
"; if(@isset($logfile_array)) { foreach($logfile_array as $key => $value) { //printf("Debug: check key:'%s' value:'%s'
",$key,$value); if(!@is_dir($log_dir.$value)) // Filter directories $filtered_array[$key]=$value; } } //print_r($logfile_array); // Debug if(@isset($logfile_array)) { form_menu($script_name, $ini_file, $filtered_array, $css_file, $menu_font_colour, $menu_back_colour, $highlight_string); die; } else { $temp="No log files found in folder '".$log_dir."'"; html_errorpage($temp,$css_file,$rawcgi); die; } } catch (Exception $e) { $temp="Exception while processing log folder"; html_errorpage($temp,$css_file,$rawcgi); die; } } ///////////////////////////////////////////////////////////////////////////////////// // ELSE - We continue and attempt to display the requested file... ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// // Start writing out HTML LOG Page ///////////////////////////////////////////////////////////////////////////////////// // file_exists ( string $filename ) : bool // filesize ( string $filename ) : int|false ///////////////////////////////////////////////////////////////////////////////////// $flen=0; $datestr=""; $log_filename=$cgi_args["filename"]; $log_filename=pathinfo($log_filename, PATHINFO_BASENAME); if(strcmp($log_filename,"")==0) { $msg="Failed to retrieve log filename!
Nothing to display
"; log_append($error_log,$client_ip,"No log filename argument",$user_agent); html_errorpage($msg,$css_file,$raw_cgi); die; } // Build the local log filename ($log_dir should include a trailing slash) $filename_path=$log_dir.$log_filename; // Set font colour details etc from the passed font name $theme=$cgi_args["theme"]; if($cgi_args["lines"]) $display_lines=$cgi_args["lines"]; $datestr=date(DATE_RFC2822); // date ( string $format , int|null $timestamp = null ) : string if(strcmp($theme,"")!=0) set_theme($theme); if($cgi_args["refresh"]) $refresh_secs=$cgi_args["refresh"]; // Ensure we capture 'refresh=' //printf("Debug: exists '%s'
",file_exists($filename_path)); //* if(is_file($filename_path)==false) //if(file_exists($filename_path)==false) { $msg="File: ".$log_filename." doesn't exist"; log_append($error_log,$client_ip,$msg,$user_agent); html_errorpage($msg,$css_file); die; } //*/ $file_size=@filesize($filename_path); printf("\n\n"); if($css_file) printf("\n",$css_file); printf("\n"); printf("%s - %s@%s / %s [%s] (%s bytes) - %s\n",$_EXENAME,$client_ip,$host_name,$log_filename,$display_lines,$file_size,$datestr); printf("\n",$_EXENAME,$_VER); if($refresh_secs > 0) //NO refresh for 0 secs printf("\n",$refresh_secs); printf("\n\n\n"); printf("\n"); printf("\n"); // PRE... printf("\n"); printf("
",$back_colour_hex);
printf("");

if(strcmp($request_uri,"")==0 && strcmp($query_string,"")==0)
{
	$msg="


Error: DOCUMENT_URI or QUERY_STRING not set
"; log_append($error_log,$client_ip,"DOCUMENT_URI or QUERY_STRING not set",$user_agent); html_errorpage($msg,$css_file); die; } // Read the file ////////////////////////////////////////////// $lines_read=0; $debug_limit=100; // Open our log file ... $fp=@fopen($filename_path,"r"); if(!$fp) { $msg="Failed to open ".$log_filename; // Don't include the full path (filename_path) log_append($error_log,$client_ip,$msg,$user_agent); html_errorpage($msg,$css_file); die; } // Wind file pointer to N lines from EOF fset_lines_from_eof($fp, $display_lines); // Don't test return // Parse highlighting keywords $highlight_keyword_found=false; $highlight=$cgi_args["highlight"]; $highlight_keywords=explode(" ",$highlight); $highlight_count=count($highlight_keywords); // Read the file and display it ... while(!feof($fp)) { $highlight_keyword_found=false; $buffer=fgets($fp); if($buffer) { //printf("Line: '%s'
",$highlight_count); if($highlight_count > 0) //if(strcmp($highlight_count,"")!=0) { $highlight_keyword_found=false; for($j=0; $j < $highlight_count; $j++) { //printf("check on '%s' '%s'
\n",$highlight_keywords[$j],stristr($buffer,$highlight_keywords[$j])); if(strcmp($highlight_keywords[$j],"") > 0) // No args creates an array with x1 element == "" { if(stristr($buffer,$highlight_keywords[$j])!=false) // haystack,needle { $highlight_keyword_found=true; break; } } } } //printf("Debug: highlight_keyword_found? %s '%s'
",$highlight_keyword_found,$highlight_keywords[$j]); if($highlight_keyword_found==true) { printf("
",$font_name,$font_size,$highlight_colour_hex); //printf("%s",buffer); safeprint($buffer); printf("",$font_name,$font_size,$fore_colour_hex); $highlight_keyword_found=false; } else { safeprint($buffer); } ++ $lines_read; if($lines_read >= $display_lines) break; } } fclose($fp); if($lines_read < 1) printf("\n
  ** The file is currently empty **  
\n"); ///////////////////////////////////////////////////////////////////////////////////// // Close off... ///////////////////////////////////////////////////////////////////////////////////// printf("
"); printf("

\n"); printf("

\n"); // FIX force centre or put in a table // Bottom menu printf("","grey"); printf("\n"); printf("  %s v%s",$_EXENAME,$_VER); printf("  (%s)",$log_filename); printf(""); if($refresh_secs > 0) printf("   Stop",$script_name); else printf("   Menu",$script_name); printf("   Back"); printf("   Home"); printf("   Tail Website  \n",$_WEBSITE); printf(""); // Close off the page... printf("\n\n\n"); die; ///////////////////////////////////////////////////////////////////////////////////// // EOF // ////////////////////////////////////////////////////////////////////////////////////// ?>