From 6039f5de53634469ce6c50ff12c97105b99965e8 Mon Sep 17 00:00:00 2001
From: prasad <prasad@vtiger.com>
Date: Sat, 4 May 2024 14:17:03 +0530
Subject: [PATCH] Added PhpLogHandler helper class to vtlib

---
 vtlib/Vtiger/Utils/PhpLogHandler.php | 133 +++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)
 create mode 100644 vtlib/Vtiger/Utils/PhpLogHandler.php

diff --git a/vtlib/Vtiger/Utils/PhpLogHandler.php b/vtlib/Vtiger/Utils/PhpLogHandler.php
new file mode 100644
index 000000000..cfad35f71
--- /dev/null
+++ b/vtlib/Vtiger/Utils/PhpLogHandler.php
@@ -0,0 +1,133 @@
+<?php
+/*+***********************************************************************************
+ * The contents of this file are subject to the vtiger CRM Public License Version 1.0
+ * ("License"); You may not use this file except in compliance with the License
+ * The Original Code is:  vtiger CRM Open Source
+ * The Initial Developer of the Original Code is vtiger.
+ * Portions created by vtiger are Copyright (C) vtiger.
+ * All Rights Reserved.
+ *************************************************************************************/
+
+/**
+ * USAGE: For E_ALL strict development - create (config_override.php) with code below.
+ * 
+ * ini_set("display_errors", "off");
+ * error_reporting(E_ALL);
+ * ini_set("log_errors", "on");
+ * ini_set("error_log", "logs/phperr.log");
+ * require_once "vtlib/Vtiger/Utils/PhpLogHandler.php";
+ * set_error_handler(Vtiger_PhpLogHandler::getErrorHandler(__DIR__));
+ * set_exception_handler(Vtiger_PhpLogHandler::getExceptionHandler(__DIR__));
+ */
+
+class Vtiger_PhpLogHandler {
+
+    /**
+     * Capture context of request in Log to review later.
+     */
+    public static function getRequestContextToLog() {
+        $ctx = "";        
+        if (isset($_SERVER)) {
+            $ctx = $_SERVER["REQUEST_METHOD"] . " " . str_replace("?" . $_SERVER["QUERY_STRING"], "", $_SERVER["REQUEST_URI"]);
+            $params = [];
+            foreach (["module", "view", "action", "mode"] as $key) {
+                if (isset($_REQUEST[$key])) $params[$key] = $_REQUEST[$key];
+            }
+            $ctx .= "?" . http_build_query($params);
+        }
+        return $ctx;
+    }
+    
+    /**
+     * Redacted PHP error handler.
+     * - Retains only relative reference to the source file.
+     * - Logs to file if provided always (or) displays to console only when display_errors is on. 
+     */
+    public static function getErrorHandler($basedir, $logfile = null) {
+        $display_errors = filter_var(ini_get('display_errors'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
+        $log_errors = filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
+        if (!$logfile) $logfile = ini_get('error_log');
+
+        $logctx = Vtiger_PhpLogHandler::getRequestContextToLog();
+
+        return function($errno, $errstr, $errfile, $errline) use ($display_errors, $log_errors, $basedir, $logfile, $logctx) {
+            // nothing todo return early.
+            if (!$display_errors && !$log_errors && ($log_errors && !$logfile)) {
+                return;
+            }
+
+            $errtype = $errno;
+            switch($errno){
+                case E_DEPRECATED:          $errtype = "Deprecated";             break;
+                case E_ERROR:               $errtype = "Error";                  break;
+                case E_WARNING:             $errtype = "Warning";                break;
+                case E_PARSE:               $errtype = "Parse Error";            break;
+                case E_NOTICE:              $errtype = "Notice";                 break;
+                case E_CORE_ERROR:          $errtype = "Core Error";             break;
+                case E_CORE_WARNING:        $errtype = "Core Warning";           break;
+                case E_COMPILE_ERROR:       $errtype = "Compile Error";          break;
+                case E_COMPILE_WARNING:     $errtype = "Compile Warning";        break;
+                case E_USER_ERROR:          $errtype = "User Error";             break;
+                case E_USER_WARNING:        $errtype = "User Warning";           break;
+                case E_USER_NOTICE:         $errtype = "User Notice";            break;
+                case E_STRICT:              $errtype = "Strict Notice";          break;
+                case E_RECOVERABLE_ERROR:   $errtype = "Recoverable Error";      break;
+                default:                    $errtype = "Unknown error ($errno)"; break;
+            }
+            $errfilerel = str_replace(rtrim($basedir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, "", $errfile);
+
+            // format message same as default PHP
+            $msg = sprintf("%s: %s in %s on line %s\n", $errtype, $errstr, $errfilerel, $errline);
+            if ($logfile) {
+                $tmstamp = date("[Y-m-d H:i:s]");
+                $fullmsg = $tmstamp . " ". $logctx . "\n" . $tmstamp . " " . $msg;
+                file_put_contents($logfile, $fullmsg, FILE_APPEND | LOCK_EX);
+            }
+
+            // if errors are logged then don't display even when asked for security.
+            // php does not enforce this and when mis-configured leaks info to user/attacker.
+            if (!$log_errors && $display_errors) {
+                echo "\n$msg";
+            }
+        };
+    }
+
+    /**
+     * Redacted PHP exception handler.
+     * - Retains only relative reference to the source file.
+     * - Logs to file if provided always (or) displays to console only when display_errors is on. 
+     */
+    public static function getExceptionHandler($basedir, $logfile = null) {
+        $display_errors = filter_var(ini_get('display_errors'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
+        $log_errors = filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
+        if (!$logfile) $logfile = ini_get('error_log');
+
+        $logctx = Vtiger_PhpLogHandler::getRequestContextToLog();
+
+        return function(Throwable $e) use ($display_errors, $log_errors, $basedir, $logfile, $logctx) {
+            // nothing todo return early.
+            if (!$display_errors && !$log_errors && ($log_errors && !$logfile)) {
+                return;
+            }
+
+            $basedir = rtrim($basedir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+            $errfile = str_replace($basedir, "", $e->getFile());
+            $errstack= str_replace($basedir, "", $e->getTraceAsString());
+
+            // format message same as default PHP
+            $msg = sprintf("Fatal error: %s in %s:%d\nStack trace:\n%s\n    thrown in %s on line %d\n", $e->getMessage(), $errfile, $e->getLine(), $errstack, $errfile, $e->getLine());
+            if ($logfile) {
+                $tmstamp = date("[Y-m-d H:i:s]");
+                $fullmsg = $tmstamp . " ". $logctx . "\n" . $tmstamp . " " . $msg;
+                file_put_contents($logfile, $fullmsg, FILE_APPEND | LOCK_EX);
+            } 
+            
+            // if errors are logged then don't display even when asked for security.
+            // php does not enforce this and when mis-configured leaks info to user/attacker.
+            if (!$log_errors && $display_errors) {
+                echo "\n$msg";
+            }
+        };
+    }
+
+}
\ No newline at end of file
-- 
GitLab