diff --git a/setup/scripts/manage.php b/setup/scripts/manage.php new file mode 100755 index 0000000000000000000000000000000000000000..c738318291c9fb861bad7139f7fd75403da6d876 --- /dev/null +++ b/setup/scripts/manage.php @@ -0,0 +1,60 @@ +#!/usr/bin/env php +<?php + +require_once "modules/class.module.php"; + +class Manager extends Module { + var $prologue = + "Manage one or more osTicket installations"; + + var $arguments = array( + 'action' => "Action to be managed" + ); + + var $usage = '$script action [options] [arguments]'; + + var $autohelp = false; + + function showHelp() { + foreach (glob(dirname(__file__).'/modules/*.php') as $script) + include_once $script; + + global $registered_modules; + $this->epilog = + "Currently available modules follow. Use 'manage.php <module> + --help' for usage regarding each respective module:"; + + parent::showHelp(); + + echo "\n"; + foreach ($registered_modules as $name=>$mod) + echo str_pad($name, 20) . $mod->prologue . "\n"; + } + + function run() { + if ($this->getOption('help') && !$this->getArgument('action')) + $this->showHelp(); + + else { + $action = $this->getArgument('action'); + + global $argv; + foreach ($argv as $idx=>$val) + if ($val == $action) + unset($argv[$idx]); + + include_once dirname(__file__) . '/modules/' . $action . '.php'; + $module = Module::getInstance($action); + $module->run(); + } + } +} + +if (php_sapi_name() != "cli") + die("Management only supported from command-line\n"); + +$manager = new Manager(); +$manager->parseOptions(); +$manager->run(); + +?> diff --git a/setup/scripts/modules/class.module.php b/setup/scripts/modules/class.module.php new file mode 100644 index 0000000000000000000000000000000000000000..de30f16321457bec2bb0c8762ed790d583e3ca6e --- /dev/null +++ b/setup/scripts/modules/class.module.php @@ -0,0 +1,214 @@ +<?php + +class Option { + + var $default = false; + + function Option() { + call_user_func_array(array($this, "__construct"), func_get_args()); + } + + function __construct($options=false) { + list($this->short, $this->long) = array_slice($options, 0, 2); + $this->help = (isset($options['help'])) ? $options['help'] : ""; + $this->action = (isset($options['action'])) ? $options['action'] + : "store"; + $this->dest = (isset($options['dest'])) ? $options['dest'] + : substr($this->long, 2); + $this->type = (isset($options['type'])) ? $options['type'] + : 'string'; + $this->const = (isset($options['const'])) ? $options['const'] + : null; + $this->default = (isset($options['default'])) ? $options['default'] + : null; + $this->metavar = (isset($options['metavar'])) ? $options['metavar'] + : 'var'; + } + + function hasArg() { + return $this->action != 'store_true' + && $this->action != 'store_false'; + } + + function handleValue(&$destination, $args) { + $nargs = 0; + $value = array_shift($args); + if ($value[0] == '-') + $value = null; + elseif ($value) + $nargs = 1; + switch ($this->action) { + case 'store_true': + $value = true; + break; + case 'store_false': + $value = false; + break; + case 'store_const': + $value = $this->const; + break; + case 'store': + default: + if ($this->type == 'int') + $value = (int)$value; + break; + } + $destination[$this->dest] = $value; + return $nargs; + } + + function toString() { + $short = explode(':', $this->short); + $long = explode(':', $this->long); + if ($this->nargs == '?') + $switches = sprintf(' %s [%3$s], %s[=%3$s]', $short[0], + $long[0], $this->metavar); + elseif ($this->hasArg()) + $switches = sprintf(' %s %3$s, %s=%3$s', $short[0], $long[0], + $this->metavar); + else + $switches = sprintf(" %s, %s", $short[0], $long[0]); + $help = preg_replace('/\s+/', ' ', $this->help); + if (strlen($switches) > 24) + $help = "\n" . str_repeat(" ", 24) . $help; + else + $switches = str_pad($switches, 24); + $help = wordwrap($help, 54, "\n" . str_repeat(" ", 24)); + return $switches . $help; + } +} + +class Module { + + var $options = array(); + var $arguments = array(); + var $prologue = ""; + var $epilog = ""; + var $usage = '$script [options] $args [arguments]'; + var $autohelp = true; + + function Module() { + call_user_func_array(array($this, '__construct'), func_get_args()); + } + + function __construct() { + $this->options['help'] = array("-h","--help", + 'action'=>'store_true', + 'help'=>"Display this help message"); + foreach ($this->options as &$opt) + $opt = new Option($opt); + } + + function showHelp() { + if ($this->prologue) + echo $this->prologue . "\n\n"; + + echo "Usage:\n"; + global $argv; + echo " " . str_replace( + array('$script', '$args'), + array($argv[0], implode(' ', array_keys($this->arguments))), + $this->usage) . "\n"; + + ksort($this->options); + if ($this->options) { + echo "\nOptions:\n"; + foreach ($this->options as $name=>$opt) + echo $opt->toString() . "\n"; + } + + if ($this->arguments) { + echo "\nArguments:\n"; + foreach ($this->arguments as $name=>$help) + echo $name . "\n " . wordwrap( + preg_replace('/\s+/', ' ', $help), 76, "\n "); + } + + if ($this->epilog) { + echo "\n\n"; + $epilog = preg_replace('/\s+/', ' ', $this->epilog); + echo wordwrap($epilog, 76, "\n"); + } + + echo "\n"; + } + + function getOption($name, $default=false) { + $this->parseOptions(); + if (isset($this->_options[$name])) + return $this->_options[$name]; + else + return $default; + } + + function getArgument($name, $default=false) { + $this->parseOptions(); + foreach (array_keys($this->arguments) as $idx=>$arg) + if ($arg == $name && isset($this->_args[$idx])) + return $this->_args[$idx]; + return $default; + } + + function parseOptions() { + if (is_array($this->_options)) + return; + + global $argv; + list($this->_options, $this->_args) = + $this->parseArgs(array_slice($argv, 1)); + + foreach (array_keys($this->arguments) as $idx=>$name) + if (!isset($this->_args[$idx])) + $this->optionError($name . " is a required argument"); + + if ($this->autohelp && $this->getOption('help')) { + $this->showHelp(); + die(); + } + } + + function optionError($error) { + echo "Error: " . $error . "\n\n"; + $this->showHelp(); + die(); + } + + /* abstract */ function run() { + } + + /* static */ function register($action, $class) { + global $registered_modules; + $registered_modules[$action] = new $class(); + } + + /* static */ function getInstance($action) { + global $registered_modules; + return $registered_modules[$action]; + } + + function parseArgs($argv) { + $options = $args = array(); + $argv = array_slice($argv, 0); + while ($arg = array_shift($argv)) { + if (strpos($arg, '=') !== false) { + list($arg, $value) = explode('=', $arg, 2); + array_unshift($argv, $value); + } + $found = false; + foreach ($this->options as $opt) { + if ($opt->short == $arg || $opt->long == $arg) { + if ($opt->handleValue($options, $argv)) + array_shift($argv); + $found = true; + } + } + if (!$found && $arg[0] != '-') + $args[] = $arg; + } + return array($options, $args); + } +} + +$registered_modules = array(); + +?> diff --git a/setup/scripts/modules/unpack.php b/setup/scripts/modules/unpack.php new file mode 100644 index 0000000000000000000000000000000000000000..0f6aed8ab9cc6ac862e09389393c57f3e62a7fb8 --- /dev/null +++ b/setup/scripts/modules/unpack.php @@ -0,0 +1,158 @@ +<?php + +require_once dirname(__file__) . "/class.module.php"; + +class Unpacker extends Module { + + var $prologue = "Unpacks osTicket into target install path"; + + var $epilog = + "Copies an unpacked osticket tarball or zipfile into a production + location, optionally placing the include/ folder in a separate + location if requested"; + + var $options = array( + 'include' => array('-i','--include', 'metavar'=>'path', 'help'=> + "The include/ folder, which contains the bulk of osTicket's source + code can be located outside of the install path. This is recommended + for better security. If you would like to install the include/ + folder somewhere else, give the path here. Note that the full + path is assumed, so give path/to/include/ to unpack the source + code in that folder. The folder will be automatically created if + it doesn't already exist." + ), + ); + + var $arguments = array( + 'install-path' => + "The destination for osTicket to reside. Use the --include + option to specify destination of the include/ folder, if the + administrator should chose to locate it separate from the + main installation path.", + ); + + function find_upload_folder() { + # Hop up to the root folder + $start = dirname(__file__); + for (;;) { + if (is_dir($start . '/upload')) break; + $start .= '/..'; + } + return realpath($start.'/upload'); + } + + function change_include_dir($include_path) { + # Read the main.inc.php script + $main_inc_php = $this->destination . '/main.inc.php'; + $lines = explode("\n", file_get_contents($main_inc_php)); + # Try and use ROOT_PATH + if (strpos($include_path, $this->destination) === 0) + $include_path = "ROOT_PATH . '" . + str_replace($this->destination, '', $include_path) . "'"; + else + $include_path = "'$include_path'"; + # Find the line that defines INCLUDE_DIR + foreach ($lines as &$line) { + if (preg_match("/(\s*)define\s*\(\s*'INCLUDE_DIR'/", $line, $match)) { + # Replace the definition with the new locatin + $line = $match[1] . "define('INCLUDE_DIR', " + . $include_path + . "); // Set by installer"; + break; + } + } + if (!file_put_contents($main_inc_php, implode("\n", $lines))) + die("Unable to configure location of INCLUDE_DIR in main.inc.php\n"); + } + + function exclude($pattern, $match) { + if (!$pattern) { + return false; + } elseif (is_array($pattern)) { + foreach ($pattern as $p) + if (fnmatch($p, $match)) + return true; + } else { + return fnmatch($pattern, $match); + } + return false; + } + + function unpackage($folder, $destination, $recurse=true, $exclude=false) { + foreach (glob($folder, GLOB_BRACE|GLOB_NOSORT) as $file) { + if ($this->exclude($exclude, $file)) + continue; + if (is_file($file)) { + if (!is_dir($destination)) + mkdir($destination, 0751, true); + copy($file, $destination . '/' . basename($file)); + } + } + if ($recurse) { + foreach (glob(dirname($folder).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { + if ($this->exclude($exclude, $dir)) + continue; + $this->unpackage( + dirname($folder).'/'.basename($dir).'/'.basename($folder), + $destination.'/'.basename($dir), + $recurse - 1, $exclude); + } + } + } + + function get_include_dir() { + $main_inc_php = $this->destination . '/main.inc.php'; + $lines = preg_grep("/define\s*\(\s*'INCLUDE_DIR'/", + explode("\n", file_get_contents($main_inc_php))); + + // NOTE: that this won't work for crafty folks who have a define or some + // variable in the value of their include path + if (!defined('ROOT_DIR')) define('ROOT_DIR', $this->destination . '/'); + foreach ($lines as $line) + eval($line); + + return INCLUDE_DIR; + } + + function run() { + $this->destination = $this->getArgument('install-path'); + if (!is_dir($this->destination)) + if (!mkdir($this->destination, 0751, true)) + $this->die("Destination path does not exist and cannot be created"); + + # Determine if this is an upgrade, and if so, where the include/ + # folder is currently located + $upgrade = file_exists("{$this->destination}/main.inc.php"); + + # Locate the upload folder + $upload = $this->find_upload_folder(); + + # Unpack the upload folder to the destination, except the include folder + if ($upgrade) + # Get the current value of the INCLUDE_DIR before overwriting + # main.inc.php + $include = $this->get_include_dir(); + $this->unpackage("$upload/*", $this->destination, -1, "*include"); + + if (!$upgrade) { + if ($this->getOption('include')) { + $location = $this->getOption('include'); + if (!is_dir("$location/")) + if (!mkdir("$location/", 0751, true)) + die("Unable to create folder for include/ files\n"); + $this->unpackage("$upload/include/*", $location, -1); + $this->change_include_dir($location); + } + else + $this->unpackage("$upload/include/*", "{$this->destination}/include", -1); + } + else { + $this->unpackage("$upload/include/*", $include, -1); + # Change the new main.inc.php to reflect the location of the + # include/ directory + $this->change_include_dir($include); + } + } +} + +Module::register('unpack', 'Unpacker');