diff --git a/.gitignore b/.gitignore index 81d5108cae397b9151249495acd54897a8f1fe45..c49a12edc170f56db2006f682b46e8ab5401beef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,39 @@ -.DS_Store .idea /nbproject/private/ .phplint-cache *.swp + +# macOS-specific things to exclude + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +Icon? + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Configuration for the Nova editor +.nova \ No newline at end of file diff --git a/server/lib/app.inc.php b/server/lib/app.inc.php index e0e8c85db2882eff0178e19995aa093f5d9d43a3..f8f4a3081156860fe934162c5293b393b8e68194 100644 --- a/server/lib/app.inc.php +++ b/server/lib/app.inc.php @@ -1,7 +1,6 @@ load('db_'.$conf['db_type']); + $this->load('db_' . $conf['db_type']); try { $this->db = new db; - } catch (Exception $e) { + } catch(Exception $e) { $this->db = false; } /* - Initialize the connection to the master DB, - if we are in a multiserver setup - */ + Initialize the connection to the master DB, + if we are in a multiserver setup + */ if($conf['dbmaster_host'] != '' && ($conf['dbmaster_host'] != $conf['db_host'] || ($conf['dbmaster_host'] == $conf['db_host'] && $conf['dbmaster_database'] != $conf['db_database']))) { try { @@ -67,14 +83,19 @@ class app { } else { $this->dbmaster = $this->db; } - - } + } // end constructor - } - + /** + * Getter method for some of the (valid) proprieties. + * + * @param string $name A valid property name to get. Will be checked for validity first! + * + * @return mixed + */ public function __get($name) { - $valid_names = array('functions', 'getconf', 'letsencrypt', 'modules', 'plugins', 'services', 'system'); + /** @var array List of all possible proprieties that are valid to get. */ + $valid_names = ['functions', 'getconf', 'letsencrypt', 'modules', 'plugins', 'services', 'system']; if(!in_array($name, $valid_names)) { trigger_error('Undefined property ' . $name . ' of class app', E_USER_WARNING); } @@ -89,14 +110,37 @@ class app { } } + /** + * Sets the calling script. + * + * @param callable $caller Calling script function. + * + * @return void + */ function setCaller($caller) { $this->_calling_script = $caller; } + /** + * Gets the calling script. + * + * Note that there is no error checking! + * + * @param void + * + * @return callable|null + */ function getCaller() { return $this->_calling_script; } + /** + * Emergency exit funcion. + * + * @param string $errmsg Error message to be displayedby the die() command on exit. + * + * @return void + */ function forceErrorExit($errmsg = 'undefined') { global $conf; @@ -106,16 +150,27 @@ class app { die('Exiting because of error: ' . $errmsg); } + /** + * Dynamic plugin loader and instantiator. + * + * This will include PHP scripts on demand, each representing a class to be loaded, + * and if the process succeeds, it will retrieve an instance for the class. + * + * @param string $classes A list of plugin classes to be loaded (e.g. their files will be included) + * and subsequently instantiated; it's a comma-separated string. + * + * @return void + */ function uses($classes) { - global $conf; + /** @var array|null List of classes to be used, as an array, after successful 'explosion' */ $cl = explode(',', $classes); if(is_array($cl)) { foreach($cl as $classname) { if(!@is_object($this->$classname)) { - if(is_file($conf['classpath'].'/'.$classname.'.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'].'/'.$classname.'.inc.php'))) { - include_once $conf['classpath'].'/'.$classname.'.inc.php'; + if(is_file($conf['classpath'] . '/' . $classname . '.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'] . '/' . $classname . '.inc.php'))) { + include_once $conf['classpath'] . '/' . $classname . '.inc.php'; $this->$classname = new $classname; } } @@ -123,64 +178,119 @@ class app { } } + /** + * Dynamic plugin loader (no instantation). + * + * Similar to uses() but does _not_ instantate a new class; files are merely included. + * die() is called on a failure to include the file for a class. + * + * @param string $classes A list of plugin classes to be loaded (e.g. their files will be included); + * it's a comma-separated string. + * + * @return void + */ function load($classes) { - global $conf; + /** @var array|null List of classes to be loaded, as an array, after successful 'explosion' */ $cl = explode(',', $classes); if(is_array($cl)) { foreach($cl as $classname) { - if(is_file($conf['classpath'].'/'.$classname.'.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'].'/'.$classname.'.inc.php'))) { - include_once $conf['classpath'].'/'.$classname.'.inc.php'; + if(is_file($conf['classpath'] . '/' . $classname . '.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'] . '/' . $classname . '.inc.php'))) { + include_once $conf['classpath'] . '/' . $classname . '.inc.php'; } else { - die('Unable to load: '.$conf['classpath'].'/'.$classname.'.inc.php'); + die('Unable to load: ' . $conf['classpath'] . '/' . $classname . '.inc.php'); } } } } - /* - 0 = DEBUG - 1 = WARNING - 2 = ERROR - */ - + /** + * Logs a message with a certain priority to the different log backends. + * + * This method will check if the priority is equal or larger than what the user has + * defined as the minimum logging level, and will output to several logging facilities: + * - At the very least, the message will _usually_ go to stdout; + * - It may optionally also go to the file log (usually `/var/log/ispconfig/ispconfig.log`) + * which will be created if it doesn't exist; + * - When the $dblog parameter is set to true (the default), the message will also be logged + * to the database; + * - If the system is configured to send email messages to the administrator, + * this method will also handle those (assuming, again, that the priority matches). + * + * Debugging messages will also have the name of the calling module/script as well as a line number + * to assist error tracking (gwyneth 20220315). This incurs in a slight performance hit. + * + * @param string $msg The message to be logged. + * @param int $priority Should be set to 0 = DEBUG, 1 = WARNING or 2 = ERROR; anything else + * will skip setting the priority textual variable. + * @param bool $dblog Should the message also be logged to the database? (Default is _true_) + * + * @return void + * + * @note The error() method below seems to write to an invalid priority (3), which will cause + * no message priority text to be emitted, and will _force_ a database write and/or sending + * an email to the administrator. + */ function log($msg, $priority = 0, $dblog = true) { - global $conf; + /** + * @var string $file_line_caller + * + * For debugging, deal with retrieving caller information from the stack. (gwyneth 20220315) + * See https://stackoverflow.com/q/1252529/1035977 (including the precious comments!) for an explanation + * of how this works. + **/ + $file_line_caller = ""; + /** @var string Defined here because recent versions of PHP are stricter with scoping issues. (gwyneth 20220315) */ + $priority_txt = ''; + switch ($priority) { case 0: - $priority_txt = 'DEBUG'; - break; + $priority_txt = 'DEBUG'; + $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); // we don't need _all_ data, so we save some processing time here (gwyneth 20220315) + $caller = array_shift($bt); + if(!empty($caller['file']) && !empty($caller['line'])) { + $file_line_caller = '[' . strtr(basename($caller['file'], '.php'), '_', ' ') . ':' . $caller['line'] . '] '; + } + break; case 1: - $priority_txt = 'WARNING'; - break; + $priority_txt = 'WARNING'; + break; case 2: - $priority_txt = 'ERROR'; - break; + $priority_txt = 'ERROR'; + break; + // Note: $this->error() seems to use case 3 to deliberately skip setting a priority text. + // It will also *force* a write to the logs and/or send emails. (gwyneth 20220315) } - $log_msg = @date('d.m.Y-H:i').' - '.$priority_txt.' - '. $msg; + /** @var string Formatted message to be sent to the logging subsystems. */ + $log_msg = @date('d.m.Y-H:i') . ' - ' . $priority_txt .' ' . $file_line_caller . '- '. $msg; + + // Check if the user-set priority defines that this message should be output at all. if($priority >= $conf['log_priority']) { - //if (is_writable($conf["log_file"])) { - if (!$fp = fopen($conf['log_file'], 'a')) { + // Prepare to add a line on the logfile, or to create the logfile in + // append mode if it doesn't exist yet. Failure means that die() is called. + + //if(is_writable($conf["log_file"])) { + if(!$fp = fopen($conf['log_file'], 'a')) { die('Unable to open logfile.'); } - if (!fwrite($fp, $log_msg."\r\n")) { + if(!fwrite($fp, $log_msg . '\r\n')) { die('Unable to write to logfile.'); } - echo $log_msg."\n"; + echo $log_msg . "\n"; fclose($fp); - // Log to database + // Log to database. if($dblog === true && isset($this->dbmaster)) { $server_id = $conf['server_id']; $loglevel = $priority; $message = $msg; - $datalog_id = (isset($this->modules->current_datalog_id) && $this->modules->current_datalog_id > 0)?$this->modules->current_datalog_id:0; + $datalog_id = (isset($this->modules->current_datalog_id) && $this->modules->current_datalog_id > 0)? $this->modules->current_datalog_id : 0; if($datalog_id > 0) { $tmp_rec = $this->dbmaster->queryOneRecord("SELECT count(syslog_id) as number FROM sys_log WHERE datalog_id = ? AND loglevel = ?", $datalog_id, LOGLEVEL_ERROR); //* Do not insert duplicate errors into the web log. @@ -198,18 +308,18 @@ class app { // die("Unable to write to logfile."); //} - } // if + // Send an email to the administrator if the current priority demands it. if(isset($conf['admin_notify_priority']) && $priority >= $conf['admin_notify_priority'] && $conf['admin_mail'] != '') { if($conf['hostname'] != 'localhost' && $conf['hostname'] != '') { $hostname = $conf['hostname']; } else { $hostname = exec('hostname -f'); } - // send notification to admin + // Send notification to admin. $mailBody = $hostname . " - " . $log_msg; - $mailSubject = substr("[" . $hostname . "]" . " " . $log_msg, 0, 70).'...'; + $mailSubject = substr("[" . $hostname . "]" . " " . $log_msg, 0, 70) . '...'; $mailHeaders = "MIME-Version: 1.0" . "\n"; $mailHeaders .= "Content-type: text/plain; charset=utf-8" . "\n"; $mailHeaders .= "Content-Transfer-Encoding: 8bit" . "\n"; @@ -218,26 +328,30 @@ class app { mail($conf['admin_mail'], $mailSubject, $mailBody, $mailHeaders); } - } // func - - - /* - 0 = DEBUG - 1 = WARNING - 2 = ERROR - */ + } // func log + /** + * Logs a message with an undefined priority (3) and dies. + * + * This method writes to an invalid/undefined priority level (3), which will cause + * no message priority text to be emitted, but will _force_ a database write and/or sending + * an email to the administrator. + * + * @param string $msg The message to be logged. + * + * @return void + */ function error($msg) { - $this->log($msg, 3); + $this->log($msg, 3); // isn't this supposed to be error code 2? (gwyneth 20220315) die($msg); } - } -/* - Initialize application (app) object -*/ - +/** + * @var \app $app + * + * Initialize application object. + */ $app = new app; ?>