From 43d0df60cd33d59886db8f3fd36ec2d10d32cc2e Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Wed, 29 Apr 2020 15:15:41 +0200 Subject: [PATCH] Add CLI commands manager --- debian/conf/apache.conf | 6 +- debian/ldapsaisie.links | 1 + public_html/bin/ldapsaisie.php | 34 ++++ public_html/core.php | 5 +- public_html/includes/addons/LSaddons.ftp.php | 2 +- public_html/includes/addons/LSaddons.mail.php | 2 +- public_html/includes/addons/LSaddons.ssh.php | 3 +- .../includes/class/class.LSauthMethod_CAS.php | 2 +- public_html/includes/class/class.LScli.php | 178 ++++++++++++++++++ .../class/class.LSformElement_image.php | 8 +- public_html/includes/class/class.LSimport.php | 2 +- public_html/includes/class/class.LSlog.php | 88 +++++++-- .../includes/class/class.LSlog_console.php | 61 ++++++ .../includes/class/class.LSlog_file.php | 4 +- .../includes/class/class.LSsession.php | 91 ++++++--- .../includes/class/class.LStemplate.php | 5 +- 16 files changed, 433 insertions(+), 59 deletions(-) create mode 100755 public_html/bin/ldapsaisie.php create mode 100644 public_html/includes/class/class.LScli.php create mode 100644 public_html/includes/class/class.LSlog_console.php diff --git a/debian/conf/apache.conf b/debian/conf/apache.conf index 3ebf8205..7a78bafc 100644 --- a/debian/conf/apache.conf +++ b/debian/conf/apache.conf @@ -5,5 +5,9 @@ Alias /ldapsaisie /usr/share/ldapsaisie php_flag magic_quotes_gpc Off php_flag register_globals Off - Options -Indexes +FollowSymLinks + Options -Indexes +FollowSymLinks + + + + Require all denied diff --git a/debian/ldapsaisie.links b/debian/ldapsaisie.links index ac5ab94f..ce6d4fed 100644 --- a/debian/ldapsaisie.links +++ b/debian/ldapsaisie.links @@ -2,3 +2,4 @@ var/cache/ldapsaisie usr/share/ldapsaisie/tmp usr/local/share/ldapsaisie usr/share/ldapsaisie/local usr/local/share/ldapsaisie etc/ldapsaisie/local usr/share/ldapsaisie/lang/generate_lang_file.php usr/bin/ldapsaisie-generate-lang-file +usr/share/ldapsaisie/bin/ldapsaisie.php usr/sbin/ldapsaisie diff --git a/public_html/bin/ldapsaisie.php b/public_html/bin/ldapsaisie.php new file mode 100755 index 00000000..d6d17708 --- /dev/null +++ b/public_html/bin/ldapsaisie.php @@ -0,0 +1,34 @@ +#!/usr/bin/php +getExitStatus(); return array($exit_status, $result); } - diff --git a/public_html/includes/class/class.LSauthMethod_CAS.php b/public_html/includes/class/class.LSauthMethod_CAS.php index 0387daf3..0946300a 100644 --- a/public_html/includes/class/class.LSauthMethod_CAS.php +++ b/public_html/includes/class/class.LSauthMethod_CAS.php @@ -36,7 +36,7 @@ class LSauthMethod_CAS extends LSauthMethod { if (!parent :: __construct()) return; - if (LSsession :: includeFile(PHP_CAS_PATH)) { + if (LSsession :: includeFile(PHP_CAS_PATH, true)) { if (defined('PHP_CAS_DEBUG_FILE')) { LSlog :: debug('LSauthMethod_CAS : enable debug file '.PHP_CAS_DEBUG_FILE); phpCAS::setDebug(PHP_CAS_DEBUG_FILE); diff --git a/public_html/includes/class/class.LScli.php b/public_html/includes/class/class.LScli.php new file mode 100644 index 00000000..325f4ad7 --- /dev/null +++ b/public_html/includes/class/class.LScli.php @@ -0,0 +1,178 @@ + + */ +class LScli { + + // Configured commands + private static $commands = array(); + + /** + * Add a CLI command + * + * @param[in] $command string The CLI command name (required) + * @param[in] $handler callable The CLI command handler (must be callable, required) + * @param[in] $short_desc string|false A short description of what this command does (required) + * @param[in] $usage_args string|false A short list of commands available arguments show in usage message (optional, default: false) + * @param[in] $long_desc string|false A long description of what this command does (optional, default: false) + * @param[in] $override boolean Allow override if a command already exists with the same name (optional, default: false) + **/ + public static function add_command($command, $handler, $short_desc, $usage_args=false, $long_desc=false, $override=false) { + if (array_key_exists($command, self :: $commands) && !$override) { + LSerror :: addErrorCode('LScli_01', $command); + return False; + } + + if (!is_callable($handler)) { + LSerror :: addErrorCode('LScli_02', $command); + return False; + } + + self :: $commands[$command] = array ( + 'handler' => $handler, + 'short_desc' => $short_desc, + 'usage_args' => $usage_args, + 'long_desc' => $long_desc, + ); + return True; + } + + /** + * Show usage message + * + * @param[in] $error string|false Error message to display before usage message (optional, default: false) + * @retval void + **/ + public static function usage($error=false) { + global $argv; + + if ($error) + echo "$error\n\n"; + + echo "Usage : ".basename($argv[0])." [-h] [-qdC] command\n"; + echo " -h Show this message\n"; + echo " -q|--quiet Quiet mode\n"; + echo " -d|--debug Debug mode\n"; + echo " -C|--console Log on console\n"; + echo " command Command to run\n"; + echo "\n"; + echo "Available commands :\n"; + + foreach (self :: $commands as $command => $info) { + echo " $command : ".$info['short_desc']."\n"; + echo " ".basename($argv[0])." $command ".($info['usage_args']?$info['usage_args']:'')."\n"; + if ($info['long_desc']) { + if (is_array($info['long_desc'])) + $info['long_desc'] = implode("\n", $info['long_desc']); + echo "\n ".str_replace("\n", "\n ", wordwrap($info['long_desc']))."\n"; + } + echo "\n"; + } + if (empty(self :: $commands)) + echo " Currently no available command is declared.\n"; + + exit(($error?1:0)); + } + + /** + * Handle CLI arguments and run command (if provided) + * + * @retval void + **/ + public static function handle_args() { + global $argv; + $log_level = 'INFO'; + $command = false; + $command_args = array(); + LSlog :: debug("handle_args :\n".varDump($argv)); + for ($i=1; $i < count($argv); $i++) { + if (array_key_exists($argv[$i], self :: $commands)) { + if (!$command) + $command = $argv[$i]; + else + self :: usage(_("Only one command could be executed !")); + } + else { + switch($argv[$i]) { + case '-h': + case '--help': + self :: usage(); + break; + case '-d': + case '--debug': + $log_level = 'DEBUG'; + break; + case '-q': + case '--quiet': + $log_level = 'WARNING'; + break; + case '-C': + case '--console': + LSlog :: logOnConsole(); + break; + default: + if ($command) + $command_args[] = $argv[$i]; + else + self :: usage( + getFData(_("Invalid parameter \"%{parameter}\".\nNote: Command's parameter/argument must be place after the command."), $argv[$i]) + ); + } + } + } + + // Set log level + LSlog :: setLevel($log_level); + + if (!$command) { + LSlog :: debug("LScli :: handle_args() : no detected command => show usage"); + self :: usage(); + } + + // Run command + LSlog :: debug('Run '.basename($argv[0])." command $command with argument(s) '".implode("', '", $command_args)."'"); + try { + $result = call_user_func($cli_commands[$command]['handler'], $command_args); + + exit($result?0:1); + } + catch(Exception $e) { + LSlog :: exception($e, "An exception occured running CLI command $command"); + exit(1); + } + } + +} + +/* + * Error Codes + */ +LSerror :: defineError('LScli_01', +_("LScli : The CLI command '%{command}' already exists.") +); +LSerror :: defineError('LScli_02', +_("LScli : The CLI command '%{command}' handler is not callable.") +); diff --git a/public_html/includes/class/class.LSformElement_image.php b/public_html/includes/class/class.LSformElement_image.php index 16e70f8d..78d63ef7 100644 --- a/public_html/includes/class/class.LSformElement_image.php +++ b/public_html/includes/class/class.LSformElement_image.php @@ -61,9 +61,9 @@ class LSformElement_image extends LSformElement { } if (!empty($this -> values[0])) { - $img_path = LSsession :: getTmpFile($this -> values[0]); + $img_url = LSsession :: getTmpFileURL($this -> values[0]); LStemplate :: assign('LSformElement_image',array( - 'img' => $img_path, + 'img' => $img_url, 'id' => $id, )); if (!$this -> isFreeze()) { @@ -100,10 +100,6 @@ class LSformElement_image extends LSformElement { $fp = fopen($_FILES[$this -> name]['tmp_name'], "r"); $buf = fread($fp, filesize($_FILES[$this -> name]['tmp_name'])); fclose($fp); - $tmp_file = LS_TMP_DIR.$this -> name.'_'.rand().'.tmp'; - if (move_uploaded_file($_FILES[$this -> name]['tmp_name'],$tmp_file)) { - LSsession :: addTmpFile($buf,$tmp_file); - } $return[$this -> name][0] = $buf; } else { diff --git a/public_html/includes/class/class.LSimport.php b/public_html/includes/class/class.LSimport.php index 7f47b7cb..225b42f6 100644 --- a/public_html/includes/class/class.LSimport.php +++ b/public_html/includes/class/class.LSimport.php @@ -53,7 +53,7 @@ class LSimport { $fp = fopen($_FILES['importfile']['tmp_name'], "r"); $buf = fread($fp, filesize($_FILES['importfile']['tmp_name'])); fclose($fp); - $tmp_file = LS_TMP_DIR.'importfile'.'_'.rand().'.tmp'; + $tmp_file = LS_TMP_DIR_PATH.'importfile'.'_'.rand().'.tmp'; if (move_uploaded_file($_FILES['importfile']['tmp_name'],$tmp_file)) { LSsession :: addTmpFile($buf,$tmp_file); } diff --git a/public_html/includes/class/class.LSlog.php b/public_html/includes/class/class.LSlog.php index 93d6c2f5..d2c3e506 100644 --- a/public_html/includes/class/class.LSlog.php +++ b/public_html/includes/class/class.LSlog.php @@ -20,7 +20,7 @@ ******************************************************************************/ -/** +/** * Handle logging * * @author Benjamin Renard @@ -61,8 +61,7 @@ class LSlog { public static function start() { // Load configuration self :: $enabled = self :: getConfig('enable', false, 'bool'); - self :: $level = self :: getConfig('level', self :: $default_level, 'string'); - if (!array_key_exists(self :: $level, self :: $levels)) self :: $level = 'WARNING'; + self :: setLevel(); // Load default handlers class if (!LSsession :: loadLSclass('LSlog_handler', null, true)) { @@ -80,31 +79,83 @@ class LSlog { $handler_config = array('handler' => $handler); else $handler = (isset($handler_config['handler'])?$handler_config['handler']:'system'); - - $handler_class = "LSlog_$handler"; - // Load handler class - if (!LSsession :: loadLSclass($handler_class) || !class_exists($handler_class)) { - LSerror :: addErrorCode('LSlog_01', $handler); + if (!self :: add_handler($handler, $handler_config)) continue; - } - $handler_obj = new $handler_class($handler_config); - if ($handler_obj -> checkCompatibility()) - self :: $handlers[] = $handler_obj; - else - LSdebug("LSlog handler $handler not supported."); $debug_handlers[] = $handler; } LSdebug('LSlog enabled with level='.self :: $level.' and following handlers : '.implode(', ', $debug_handlers)); + set_exception_handler(array('LSlog', 'exception')); + return True; + } + + /** + * Add handler + * + * @param[in] $handler string The handler name + * @param[in] $handler_config array The handler configuration (optional) + * + * @retval boolean True if handler added, false otherwise + **/ + public static function add_handler($handler, $handler_config = array()) { + $handler_class = "LSlog_$handler"; + + // Load handler class + if (!LSsession :: loadLSclass($handler_class) || !class_exists($handler_class)) { + LSerror :: addErrorCode('LSlog_01', $handler); + return false; + } + + $handler_obj = new $handler_class($handler_config); + if ($handler_obj -> checkCompatibility()) { + self :: $handlers[] = $handler_obj; + return True; + } + LSdebug("LSlog handler $handler not supported."); + return false; + } + + /** + * Enable console handler (if not already enabled) + * + * @retval boolean True if log on console enabled, false otherwise + **/ + public static function logOnConsole() { + for ($i=0; $i < count(self :: $handlers); $i++) + if (is_a(self :: $handlers[$i], 'LSlog_console')) + return true; + return self :: add_handler('console'); + } + + + /** + * Set log level + * + * @param[in] $level string|null The log level (optinal, default: from configuration or 'WARNING') + * + * @retval boolean True if log level set, false otherwise + **/ + public static function setLevel($level=null) { + if (!$level) { + $level = self :: getConfig('level', self :: $default_level, 'string'); + if (!array_key_exists(self :: $level, self :: $levels)) { + self :: $level = 'WARNING'; + if ($level) + self :: warning("Invalid log level '$level' configured. Set log level to WARNING."); + $level = 'WARNING'; + } + } + else if (!array_key_exists($level, self :: $levels)) + return false; + self :: $level = $level; + // Set PHP error/exception handlers if (self :: $level == 'DEBUG') set_error_handler(array('LSlog', 'php_error'), E_ALL & ~E_STRICT); else set_error_handler(array('LSlog', 'php_error'), E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED); - set_exception_handler(array('LSlog', 'exception')); - return True; } @@ -236,7 +287,7 @@ class LSlog { $traces = debug_backtrace(); if (!is_array($traces) || count($traces) <= 2) return ""; - + $msg = array(); for ($i=2; $i < count($traces); $i++) { $trace = array("#$i"); @@ -248,7 +299,7 @@ class LSlog { $trace[] = $traces[$i]['function']. "()"; $msg[] = implode(" - ", $trace); } - + return implode("\n", $msg); } @@ -382,4 +433,3 @@ class LSlog { LSerror :: defineError('LSlog_01', _("LSlog : Fail to load logging handler %{handler}.") ); - diff --git a/public_html/includes/class/class.LSlog_console.php b/public_html/includes/class/class.LSlog_console.php new file mode 100644 index 00000000..5e81e571 --- /dev/null +++ b/public_html/includes/class/class.LSlog_console.php @@ -0,0 +1,61 @@ + + */ +class LSlog_console extends LSlog_handler { + + // File-descriptors for stdout/stderr + private $stdout; + private $stderr; + + /** + * Constructor + * + * @param[in] $config array The handler configuration + * + * @retval void + **/ + public function __construct($config) { + parent :: __construct($config); + $this -> stdout = fopen('php://stdout', 'w'); + $this -> stderr = fopen('php://stderr', 'w'); + } + + /** + * Log a message + * + * @param[in] $level string The message level + * @param[in] $message string The message + * + * @retval void + **/ + public function logging($level, $message) { + return fwrite( + ($level > 1?$this -> stderr:$this -> stdout), + date('Y/m/d H:i:s').' - '.$level.' - '.$message."\n" + ); + } +} diff --git a/public_html/includes/class/class.LSlog_file.php b/public_html/includes/class/class.LSlog_file.php index 233c9233..37f2ce62 100644 --- a/public_html/includes/class/class.LSlog_file.php +++ b/public_html/includes/class/class.LSlog_file.php @@ -20,7 +20,7 @@ ******************************************************************************/ -/** +/** * Handle logging to file (using error_log PHP function with message_type = 3) * * @author Benjamin Renard @@ -41,6 +41,8 @@ class LSlog_file extends LSlog_handler { parent :: __construct($config); // For reto-compatibilty, use LSlog.filename as default log path value $this -> path = self :: getConfig('path', LSlog :: getConfig('filename', 'tmp/LS.log')); + if (substr($this -> path, 0, 1) != '/') + $this -> path = LS_ROOT_DIR."/".$this -> path; } /** diff --git a/public_html/includes/class/class.LSsession.php b/public_html/includes/class/class.LSsession.php index 286a1f7a..ed0b8da7 100644 --- a/public_html/includes/class/class.LSsession.php +++ b/public_html/includes/class/class.LSsession.php @@ -98,14 +98,14 @@ class LSsession { * * @retval true si tout c'est bien passé, false sinon */ - public static function includeFile($file) { - if (file_exists(LS_LOCAL_DIR.'/'.$file)) { - $file=LS_LOCAL_DIR.'/'.$file; - } - elseif (!file_exists($file)) { + public static function includeFile($file, $external=false) { + $path = ($external?'':LS_ROOT_DIR."/").$file; + $local_path = ($external?'':LS_ROOT_DIR."/").LS_LOCAL_DIR.'/'.$file; + $path = (file_exists($local_path)?$local_path:$path); + if (!file_exists($path)) { return; } - return include_once($file); + return include_once($path); } /** @@ -147,24 +147,24 @@ class LSsession { * @author Benjamin Renard * * @retval true si tout c'est bien passé, false sinon - */ + */ private static function startLStemplate() { if ( self :: loadLSclass('LStemplate') ) { return LStemplate :: start( array( - 'smarty_path' => LSconfig :: get('Smarty'), - 'template_dir' => LS_TEMPLATES_DIR, - 'image_dir' => LS_IMAGES_DIR, - 'css_dir' => LS_CSS_DIR, - 'compile_dir' => LS_TMP_DIR, - 'debug' => LSdebug, - 'debug_smarty' => (isset($_REQUEST['LStemplate_debug'])), + 'smarty_path' => LSconfig :: get('Smarty'), + 'template_dir' => LS_ROOT_DIR . '/'. LS_TEMPLATES_DIR, + 'image_dir' => LS_IMAGES_DIR, + 'css_dir' => LS_CSS_DIR, + 'compile_dir' => LS_TMP_DIR_PATH, + 'debug' => LSdebug, + 'debug_smarty' => (isset($_REQUEST) && isset($_REQUEST['LStemplate_debug'])), ) ); } return False; } - + /** * Retourne le topDn de la session * @@ -320,6 +320,23 @@ class LSsession { return; } + /** + * Load LdapSaisie CLI class + * + * @author Benjamin Renard + * + * @param[in] $value La valeur du fichier + * + * @retval mixed + **/ + public static function getTmpFileURL($value) { + $path = self :: getTmpFile($value); + if (substr($path, 0, strlen(LS_ROOT_DIR)) == LS_ROOT_DIR) + return substr($path, strlen(LS_ROOT_DIR)+1); + return False; + } + /** * Supprime les fichiers temporaires * diff --git a/public_html/includes/class/class.LStemplate.php b/public_html/includes/class/class.LStemplate.php index a911c304..263639f2 100644 --- a/public_html/includes/class/class.LStemplate.php +++ b/public_html/includes/class/class.LStemplate.php @@ -86,12 +86,12 @@ class LStemplate { self :: $config[$key] = $value; } - if (LSsession :: includeFile(self :: $config['smarty_path'])) { + if (LSsession :: includeFile(self :: $config['smarty_path'], true)) { self :: $_smarty = new Smarty(); self :: $_smarty -> template_dir = self :: $config['template_dir']; if ( ! is_writable(self :: $config['compile_dir']) ) { - die(_('LStemplate : compile directory is not writable (dir : '.self :: $config['compile_dir'].')')); + LSlog :: fatal(getFData(_("LStemplate : compile directory is not writable (dir : %{dir})"), self :: $config['compile_dir'])); } self :: $_smarty -> compile_dir = self :: $config['compile_dir']; @@ -442,4 +442,3 @@ _("LStemplate : Fail to execute trigger %{callable} on event %{event} : is not c LSerror :: defineError('LStemplate_03', _("LStemplate : Error during the execution of the trigger %{callable} on event %{event}.") ); -