2012-09-14 16:18:31 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
// This file is part of Moodle - http://moodle.org/
|
|
|
|
//
|
|
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moodle deployment utility
|
|
|
|
*
|
2013-03-14 20:46:28 +01:00
|
|
|
* This script looks after deploying new add-ons and available updates for them
|
|
|
|
* to the local Moodle site. It can operate via both HTTP and CLI mode.
|
|
|
|
* Moodle itself calls this utility via the HTTP mode when the admin is about to
|
|
|
|
* install or update an add-on. You can use the CLI mode in your custom deployment
|
|
|
|
* shell scripts.
|
2012-09-14 16:18:31 +02:00
|
|
|
*
|
2012-10-11 07:29:44 +02:00
|
|
|
* CLI usage example:
|
2013-03-14 20:46:28 +01:00
|
|
|
*
|
|
|
|
* $ sudo -u apache php mdeploy.php --install \
|
|
|
|
* --package=https://moodle.org/plugins/download.php/...zip \
|
|
|
|
* --typeroot=/var/www/moodle/htdocs/blocks
|
|
|
|
* --name=loancalc
|
|
|
|
* --md5=...
|
|
|
|
* --dataroot=/var/www/moodle/data
|
|
|
|
*
|
2012-10-11 07:29:44 +02:00
|
|
|
* $ sudo -u apache php mdeploy.php --upgrade \
|
|
|
|
* --package=https://moodle.org/plugins/download.php/...zip \
|
2013-03-14 20:46:28 +01:00
|
|
|
* --typeroot=/var/www/moodle/htdocs/blocks
|
|
|
|
* --name=loancalc
|
|
|
|
* --md5=...
|
|
|
|
* --dataroot=/var/www/moodle/data
|
|
|
|
*
|
|
|
|
* When called via HTTP, additional parameters returnurl, passfile and password must be
|
|
|
|
* provided. Optional proxy configuration can be passed using parameters proxy, proxytype
|
|
|
|
* and proxyuserpwd.
|
|
|
|
*
|
|
|
|
* Changes
|
|
|
|
*
|
|
|
|
* 1.1 - Added support to install a new plugin from the Moodle Plugins directory.
|
|
|
|
* 1.0 - Initial version used in Moodle 2.4 to deploy available updates.
|
2012-10-11 07:29:44 +02:00
|
|
|
*
|
2012-09-14 16:18:31 +02:00
|
|
|
* @package core
|
2013-03-14 20:46:28 +01:00
|
|
|
* @subpackage mdeploy
|
|
|
|
* @version 1.1
|
2012-09-14 16:18:31 +02:00
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (defined('MOODLE_INTERNAL')) {
|
|
|
|
die('This is a standalone utility that should not be included by any other Moodle code.');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Exceptions //////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
class invalid_coding_exception extends Exception {}
|
|
|
|
class missing_option_exception extends Exception {}
|
2012-10-11 07:29:44 +02:00
|
|
|
class invalid_option_exception extends Exception {}
|
2012-09-17 18:52:49 +02:00
|
|
|
class unauthorized_access_exception extends Exception {}
|
2012-10-11 07:29:44 +02:00
|
|
|
class download_file_exception extends Exception {}
|
2012-10-11 12:37:51 +02:00
|
|
|
class backup_folder_exception extends Exception {}
|
|
|
|
class zip_exception extends Exception {}
|
|
|
|
class filesystem_exception extends Exception {}
|
2012-11-07 16:29:07 +01:00
|
|
|
class checksum_exception extends Exception {}
|
2012-09-14 16:18:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
// Various support classes /////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base class implementing the singleton pattern using late static binding feature.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
abstract class singleton_pattern {
|
|
|
|
|
|
|
|
/** @var array singleton_pattern instances */
|
|
|
|
protected static $singletoninstances = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Factory method returning the singleton instance.
|
|
|
|
*
|
|
|
|
* Subclasses may want to override the {@link self::initialize()} method that is
|
|
|
|
* called right after their instantiation.
|
|
|
|
*
|
|
|
|
* @return mixed the singleton instance
|
|
|
|
*/
|
|
|
|
final public static function instance() {
|
|
|
|
$class = get_called_class();
|
|
|
|
if (!isset(static::$singletoninstances[$class])) {
|
|
|
|
static::$singletoninstances[$class] = new static();
|
|
|
|
static::$singletoninstances[$class]->initialize();
|
|
|
|
}
|
|
|
|
return static::$singletoninstances[$class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Optional post-instantiation code.
|
|
|
|
*/
|
|
|
|
protected function initialize() {
|
|
|
|
// Do nothing in this base class.
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Direct instantiation not allowed, use the factory method {@link instance()}
|
|
|
|
*/
|
|
|
|
final protected function __construct() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sorry, this is singleton.
|
|
|
|
*/
|
|
|
|
final protected function __clone() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// User input handling /////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides access to the script options.
|
|
|
|
*
|
|
|
|
* Implements the delegate pattern by dispatching the calls to appropriate
|
|
|
|
* helper class (CLI or HTTP).
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class input_manager extends singleton_pattern {
|
|
|
|
|
2012-09-17 18:52:49 +02:00
|
|
|
const TYPE_FILE = 'file'; // File name
|
2012-09-14 16:18:31 +02:00
|
|
|
const TYPE_FLAG = 'flag'; // No value, just a flag (switch)
|
|
|
|
const TYPE_INT = 'int'; // Integer
|
2012-09-17 18:52:49 +02:00
|
|
|
const TYPE_PATH = 'path'; // Full path to a file or a directory
|
|
|
|
const TYPE_RAW = 'raw'; // Raw value, keep as is
|
2012-10-11 07:29:44 +02:00
|
|
|
const TYPE_URL = 'url'; // URL to a file
|
2012-10-11 09:36:12 +02:00
|
|
|
const TYPE_PLUGIN = 'plugin'; // Plugin name
|
2012-11-07 16:29:07 +01:00
|
|
|
const TYPE_MD5 = 'md5'; // MD5 hash
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-09-17 14:59:23 +02:00
|
|
|
/** @var input_cli_provider|input_http_provider the provider of the input */
|
2012-09-14 16:18:31 +02:00
|
|
|
protected $inputprovider = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the value of an option passed to the script.
|
|
|
|
*
|
|
|
|
* If the caller passes just the $name, the requested argument is considered
|
|
|
|
* required. The caller may specify the second argument which then
|
|
|
|
* makes the argument optional with the given default value.
|
|
|
|
*
|
|
|
|
* If the type of the $name option is TYPE_FLAG (switch), this method returns
|
|
|
|
* true if the flag has been passed or false if it was not. Specifying the
|
|
|
|
* default value makes no sense in this case and leads to invalid coding exception.
|
|
|
|
*
|
|
|
|
* The array options are not supported.
|
|
|
|
*
|
|
|
|
* @example $filename = $input->get_option('f');
|
|
|
|
* @example $filename = $input->get_option('filename');
|
|
|
|
* @example if ($input->get_option('verbose')) { ... }
|
|
|
|
* @param string $name
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function get_option($name, $default = 'provide_default_value_explicitly') {
|
|
|
|
|
|
|
|
$this->validate_option_name($name);
|
|
|
|
|
|
|
|
$info = $this->get_option_info($name);
|
|
|
|
|
|
|
|
if ($info->type === input_manager::TYPE_FLAG) {
|
|
|
|
return $this->inputprovider->has_option($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (func_num_args() == 1) {
|
|
|
|
return $this->get_required_option($name);
|
|
|
|
} else {
|
|
|
|
return $this->get_optional_option($name, $default);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the meta-information about the given option.
|
|
|
|
*
|
|
|
|
* @param string|null $name short or long option name, defaults to returning the list of all
|
|
|
|
* @return array|object|false array with all, object with the specific option meta-information or false of no such an option
|
|
|
|
*/
|
|
|
|
public function get_option_info($name=null) {
|
|
|
|
|
|
|
|
$supportedoptions = array(
|
2012-10-11 07:29:44 +02:00
|
|
|
array('', 'passfile', input_manager::TYPE_FILE, 'File name of the passphrase file (HTTP access only)'),
|
|
|
|
array('', 'password', input_manager::TYPE_RAW, 'Session passphrase (HTTP access only)'),
|
2012-11-30 16:11:52 +01:00
|
|
|
array('', 'proxy', input_manager::TYPE_RAW, 'HTTP proxy host and port (e.g. \'our.proxy.edu:8888\')'),
|
|
|
|
array('', 'proxytype', input_manager::TYPE_RAW, 'Proxy type (HTTP or SOCKS5)'),
|
|
|
|
array('', 'proxyuserpwd', input_manager::TYPE_RAW, 'Proxy username and password (e.g. \'username:password\')'),
|
2012-10-11 09:36:12 +02:00
|
|
|
array('', 'returnurl', input_manager::TYPE_URL, 'Return URL (HTTP access only)'),
|
2012-09-17 18:52:49 +02:00
|
|
|
array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'),
|
2012-09-14 16:18:31 +02:00
|
|
|
array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'),
|
|
|
|
array('i', 'install', input_manager::TYPE_FLAG, 'Installation mode'),
|
2012-11-07 16:29:07 +01:00
|
|
|
array('m', 'md5', input_manager::TYPE_MD5, 'Expected MD5 hash of the ZIP package to deploy'),
|
2012-10-11 09:36:12 +02:00
|
|
|
array('n', 'name', input_manager::TYPE_PLUGIN, 'Plugin name (the name of its folder)'),
|
2012-10-11 07:29:44 +02:00
|
|
|
array('p', 'package', input_manager::TYPE_URL, 'URL to the ZIP package to deploy'),
|
2012-10-11 09:36:12 +02:00
|
|
|
array('r', 'typeroot', input_manager::TYPE_PATH, 'Full path of the container for this plugin type'),
|
2012-09-14 16:18:31 +02:00
|
|
|
array('u', 'upgrade', input_manager::TYPE_FLAG, 'Upgrade mode'),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (is_null($name)) {
|
|
|
|
$all = array();
|
|
|
|
foreach ($supportedoptions as $optioninfo) {
|
|
|
|
$info = new stdClass();
|
|
|
|
$info->shortname = $optioninfo[0];
|
|
|
|
$info->longname = $optioninfo[1];
|
|
|
|
$info->type = $optioninfo[2];
|
|
|
|
$info->desc = $optioninfo[3];
|
|
|
|
$all[] = $info;
|
|
|
|
}
|
|
|
|
return $all;
|
|
|
|
}
|
|
|
|
|
|
|
|
$found = false;
|
|
|
|
|
|
|
|
foreach ($supportedoptions as $optioninfo) {
|
|
|
|
if (strlen($name) == 1) {
|
|
|
|
// Search by the short option name
|
|
|
|
if ($optioninfo[0] === $name) {
|
|
|
|
$found = $optioninfo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Search by the long option name
|
|
|
|
if ($optioninfo[1] === $name) {
|
|
|
|
$found = $optioninfo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$found) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$info = new stdClass();
|
|
|
|
$info->shortname = $found[0];
|
|
|
|
$info->longname = $found[1];
|
|
|
|
$info->type = $found[2];
|
|
|
|
$info->desc = $found[3];
|
|
|
|
|
|
|
|
return $info;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Casts the value to the given type.
|
|
|
|
*
|
|
|
|
* @param mixed $raw the raw value
|
|
|
|
* @param string $type the expected value type, e.g. {@link input_manager::TYPE_INT}
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function cast_value($raw, $type) {
|
|
|
|
|
|
|
|
if (is_array($raw)) {
|
|
|
|
throw new invalid_coding_exception('Unsupported array option.');
|
|
|
|
} else if (is_object($raw)) {
|
|
|
|
throw new invalid_coding_exception('Unsupported object option.');
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
|
2012-09-17 18:52:49 +02:00
|
|
|
case input_manager::TYPE_FILE:
|
|
|
|
$raw = preg_replace('~[[:cntrl:]]|[&<>"`\|\':\\\\/]~u', '', $raw);
|
|
|
|
$raw = preg_replace('~\.\.+~', '', $raw);
|
|
|
|
if ($raw === '.') {
|
|
|
|
$raw = '';
|
|
|
|
}
|
|
|
|
return $raw;
|
|
|
|
|
2012-09-14 16:18:31 +02:00
|
|
|
case input_manager::TYPE_FLAG:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case input_manager::TYPE_INT:
|
|
|
|
return (int)$raw;
|
|
|
|
|
2012-09-17 18:52:49 +02:00
|
|
|
case input_manager::TYPE_PATH:
|
2012-11-07 21:54:22 +01:00
|
|
|
if (strpos($raw, '~') !== false) {
|
|
|
|
throw new invalid_option_exception('Using the tilde (~) character in paths is not supported');
|
|
|
|
}
|
2013-03-13 12:08:38 +01:00
|
|
|
$colonpos = strpos($raw, ':');
|
|
|
|
if ($colonpos !== false) {
|
|
|
|
if ($colonpos !== 1 or strrpos($raw, ':') !== 1) {
|
|
|
|
throw new invalid_option_exception('Using the colon (:) character in paths is supported for Windows drive labels only.');
|
|
|
|
}
|
|
|
|
if (preg_match('/^[a-zA-Z]:/', $raw) !== 1) {
|
|
|
|
throw new invalid_option_exception('Using the colon (:) character in paths is supported for Windows drive labels only.');
|
|
|
|
}
|
|
|
|
}
|
2012-09-17 18:52:49 +02:00
|
|
|
$raw = str_replace('\\', '/', $raw);
|
2013-03-13 12:08:38 +01:00
|
|
|
$raw = preg_replace('~[[:cntrl:]]|[&<>"`\|\']~u', '', $raw);
|
2012-09-17 18:52:49 +02:00
|
|
|
$raw = preg_replace('~\.\.+~', '', $raw);
|
|
|
|
$raw = preg_replace('~//+~', '/', $raw);
|
|
|
|
$raw = preg_replace('~/(\./)+~', '/', $raw);
|
|
|
|
return $raw;
|
|
|
|
|
|
|
|
case input_manager::TYPE_RAW:
|
|
|
|
return $raw;
|
|
|
|
|
2012-10-11 07:29:44 +02:00
|
|
|
case input_manager::TYPE_URL:
|
|
|
|
$regex = '^(https?|ftp)\:\/\/'; // protocol
|
|
|
|
$regex .= '([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?'; // optional user and password
|
|
|
|
$regex .= '[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*'; // hostname or IP (one word like http://localhost/ allowed)
|
|
|
|
$regex .= '(\:[0-9]{2,5})?'; // port (optional)
|
|
|
|
$regex .= '(\/([a-z0-9+\$_-]\.?)+)*\/?'; // path to the file
|
|
|
|
$regex .= '(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?'; // HTTP params
|
|
|
|
|
|
|
|
if (preg_match('#'.$regex.'#i', $raw)) {
|
|
|
|
return $raw;
|
|
|
|
} else {
|
2012-10-11 09:36:12 +02:00
|
|
|
throw new invalid_option_exception('Not a valid URL');
|
2012-10-11 07:29:44 +02:00
|
|
|
}
|
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
case input_manager::TYPE_PLUGIN:
|
|
|
|
if (!preg_match('/^[a-z][a-z0-9_]*[a-z0-9]$/', $raw)) {
|
|
|
|
throw new invalid_option_exception('Invalid plugin name');
|
|
|
|
}
|
|
|
|
if (strpos($raw, '__') !== false) {
|
|
|
|
throw new invalid_option_exception('Invalid plugin name');
|
|
|
|
}
|
|
|
|
return $raw;
|
|
|
|
|
2012-11-07 16:29:07 +01:00
|
|
|
case input_manager::TYPE_MD5:
|
|
|
|
if (!preg_match('/^[a-f0-9]{32}$/', $raw)) {
|
|
|
|
throw new invalid_option_exception('Invalid MD5 hash format');
|
|
|
|
}
|
|
|
|
return $raw;
|
|
|
|
|
2012-09-14 16:18:31 +02:00
|
|
|
default:
|
|
|
|
throw new invalid_coding_exception('Unknown option type.');
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Picks the appropriate helper class to delegate calls to.
|
|
|
|
*/
|
|
|
|
protected function initialize() {
|
|
|
|
if (PHP_SAPI === 'cli') {
|
|
|
|
$this->inputprovider = input_cli_provider::instance();
|
|
|
|
} else {
|
|
|
|
$this->inputprovider = input_http_provider::instance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of external API
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates the parameter name.
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @throws invalid_coding_exception
|
|
|
|
*/
|
|
|
|
protected function validate_option_name($name) {
|
|
|
|
|
|
|
|
if (empty($name)) {
|
|
|
|
throw new invalid_coding_exception('Invalid empty option name.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$meta = $this->get_option_info($name);
|
|
|
|
if (empty($meta)) {
|
|
|
|
throw new invalid_coding_exception('Invalid option name: '.$name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns cleaned option value or throws exception.
|
|
|
|
*
|
|
|
|
* @param string $name the name of the parameter
|
|
|
|
* @param string $type the parameter type, e.g. {@link input_manager::TYPE_INT}
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2012-09-17 16:24:02 +02:00
|
|
|
protected function get_required_option($name) {
|
2012-09-14 16:18:31 +02:00
|
|
|
if ($this->inputprovider->has_option($name)) {
|
2012-09-17 16:24:02 +02:00
|
|
|
return $this->inputprovider->get_option($name);
|
2012-09-14 16:18:31 +02:00
|
|
|
} else {
|
|
|
|
throw new missing_option_exception('Missing required option: '.$name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns cleaned option value or the default value
|
|
|
|
*
|
|
|
|
* @param string $name the name of the parameter
|
|
|
|
* @param string $type the parameter type, e.g. {@link input_manager::TYPE_INT}
|
|
|
|
* @param mixed $default the default value.
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2012-09-17 16:24:02 +02:00
|
|
|
protected function get_optional_option($name, $default) {
|
2012-09-14 16:18:31 +02:00
|
|
|
if ($this->inputprovider->has_option($name)) {
|
2012-09-17 16:24:02 +02:00
|
|
|
return $this->inputprovider->get_option($name);
|
2012-09-14 16:18:31 +02:00
|
|
|
} else {
|
|
|
|
return $default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base class for input providers.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
abstract class input_provider extends singleton_pattern {
|
|
|
|
|
|
|
|
/** @var array list of all passed valid options */
|
|
|
|
protected $options = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the casted value of the option.
|
|
|
|
*
|
|
|
|
* @param string $name option name
|
|
|
|
* @throws invalid_coding_exception if the option has not been passed
|
|
|
|
* @return mixed casted value of the option
|
|
|
|
*/
|
|
|
|
public function get_option($name) {
|
|
|
|
|
|
|
|
if (!$this->has_option($name)) {
|
|
|
|
throw new invalid_coding_exception('Option not passed: '.$name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->options[$name];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Was the given option passed?
|
|
|
|
*
|
|
|
|
* @param string $name optionname
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function has_option($name) {
|
|
|
|
return array_key_exists($name, $this->options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the input provider.
|
|
|
|
*/
|
|
|
|
protected function initialize() {
|
|
|
|
$this->populate_options();
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of external API
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses and validates all supported options passed to the script.
|
|
|
|
*/
|
|
|
|
protected function populate_options() {
|
|
|
|
|
|
|
|
$input = input_manager::instance();
|
|
|
|
$raw = $this->parse_raw_options();
|
|
|
|
$cooked = array();
|
|
|
|
|
|
|
|
foreach ($raw as $k => $v) {
|
|
|
|
if (is_array($v) or is_object($v)) {
|
|
|
|
// Not supported.
|
|
|
|
}
|
|
|
|
|
|
|
|
$info = $input->get_option_info($k);
|
|
|
|
if (!$info) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$casted = $input->cast_value($v, $info->type);
|
|
|
|
|
|
|
|
if (!empty($info->shortname)) {
|
|
|
|
$cooked[$info->shortname] = $casted;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($info->longname)) {
|
|
|
|
$cooked[$info->longname] = $casted;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the options.
|
|
|
|
$this->options = $cooked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides access to the script options passed via CLI.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class input_cli_provider extends input_provider {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses raw options passed to the script.
|
|
|
|
*
|
|
|
|
* @return array as returned by getopt()
|
|
|
|
*/
|
|
|
|
protected function parse_raw_options() {
|
|
|
|
|
|
|
|
$input = input_manager::instance();
|
|
|
|
|
|
|
|
// Signatures of some in-built PHP functions are just crazy, aren't they.
|
|
|
|
$short = '';
|
|
|
|
$long = array();
|
|
|
|
|
|
|
|
foreach ($input->get_option_info() as $option) {
|
|
|
|
if ($option->type === input_manager::TYPE_FLAG) {
|
|
|
|
// No value expected for this option.
|
|
|
|
$short .= $option->shortname;
|
|
|
|
$long[] = $option->longname;
|
|
|
|
} else {
|
|
|
|
// A value expected for the option, all considered as optional.
|
|
|
|
$short .= empty($option->shortname) ? '' : $option->shortname.'::';
|
|
|
|
$long[] = empty($option->longname) ? '' : $option->longname.'::';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return getopt($short, $long);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides access to the script options passed via HTTP request.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class input_http_provider extends input_provider {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses raw options passed to the script.
|
|
|
|
*
|
|
|
|
* @return array of raw values passed via HTTP request
|
|
|
|
*/
|
|
|
|
protected function parse_raw_options() {
|
2012-10-10 00:01:49 +02:00
|
|
|
return $_POST;
|
2012-09-14 16:18:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Output handling /////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
2012-09-17 14:59:23 +02:00
|
|
|
* Provides output operations.
|
2012-09-14 16:18:31 +02:00
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class output_manager extends singleton_pattern {
|
|
|
|
|
2012-09-17 14:59:23 +02:00
|
|
|
/** @var output_cli_provider|output_http_provider the provider of the output functionality */
|
|
|
|
protected $outputprovider = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Magic method triggered when invoking an inaccessible method.
|
|
|
|
*
|
|
|
|
* @param string $name method name
|
|
|
|
* @param array $arguments method arguments
|
|
|
|
*/
|
|
|
|
public function __call($name, array $arguments = array()) {
|
|
|
|
call_user_func_array(array($this->outputprovider, $name), $arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Picks the appropriate helper class to delegate calls to.
|
|
|
|
*/
|
|
|
|
protected function initialize() {
|
|
|
|
if (PHP_SAPI === 'cli') {
|
|
|
|
$this->outputprovider = output_cli_provider::instance();
|
|
|
|
} else {
|
|
|
|
$this->outputprovider = output_http_provider::instance();
|
|
|
|
}
|
|
|
|
}
|
2012-09-14 16:18:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-17 14:59:23 +02:00
|
|
|
/**
|
|
|
|
* Base class for all output providers.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
abstract class output_provider extends singleton_pattern {
|
|
|
|
}
|
2012-09-14 16:18:31 +02:00
|
|
|
|
|
|
|
/**
|
2012-09-17 14:59:23 +02:00
|
|
|
* Provides output to the command line.
|
2012-09-14 16:18:31 +02:00
|
|
|
*
|
2012-09-17 14:59:23 +02:00
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class output_cli_provider extends output_provider {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints help information in CLI mode.
|
|
|
|
*/
|
|
|
|
public function help() {
|
|
|
|
|
|
|
|
$this->outln('mdeploy.php - Moodle (http://moodle.org) deployment utility');
|
|
|
|
$this->outln();
|
|
|
|
$this->outln('Usage: $ sudo -u apache php mdeploy.php [options]');
|
|
|
|
$this->outln();
|
|
|
|
$input = input_manager::instance();
|
|
|
|
foreach($input->get_option_info() as $info) {
|
|
|
|
$option = array();
|
|
|
|
if (!empty($info->shortname)) {
|
|
|
|
$option[] = '-'.$info->shortname;
|
|
|
|
}
|
|
|
|
if (!empty($info->longname)) {
|
|
|
|
$option[] = '--'.$info->longname;
|
|
|
|
}
|
|
|
|
$this->outln(sprintf('%-20s %s', implode(', ', $option), $info->desc));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of external API
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes a text to the STDOUT followed by a new line character.
|
|
|
|
*
|
|
|
|
* @param string $text text to print
|
|
|
|
*/
|
|
|
|
protected function outln($text='') {
|
|
|
|
fputs(STDOUT, $text.PHP_EOL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides HTML output as a part of HTTP response.
|
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class output_http_provider extends output_provider {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints help on the script usage.
|
|
|
|
*/
|
|
|
|
public function help() {
|
|
|
|
// No help available via HTTP
|
|
|
|
}
|
2012-11-07 23:20:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Display the information about uncaught exception
|
|
|
|
*
|
|
|
|
* @param Exception $e uncaught exception
|
|
|
|
*/
|
|
|
|
public function exception(Exception $e) {
|
2012-11-30 13:16:01 +01:00
|
|
|
|
|
|
|
$docslink = 'http://docs.moodle.org/en/admin/mdeploy/'.get_class($e);
|
2012-11-07 23:20:54 +01:00
|
|
|
$this->start_output();
|
|
|
|
echo('<h1>Oops! It did it again</h1>');
|
2012-11-30 13:16:01 +01:00
|
|
|
echo('<p><strong>Moodle deployment utility had a trouble with your request.
|
|
|
|
See <a href="'.$docslink.'">the docs page</a> and the debugging information for more details.</strong></p>');
|
2012-11-07 23:20:54 +01:00
|
|
|
echo('<pre>');
|
|
|
|
echo exception_handlers::format_exception_info($e);
|
|
|
|
echo('</pre>');
|
|
|
|
$this->end_output();
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of external API
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produce the HTML page header
|
|
|
|
*/
|
|
|
|
protected function start_output() {
|
|
|
|
echo '<!doctype html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<style type="text/css">
|
|
|
|
body {background-color:#666;font-family:"DejaVu Sans","Liberation Sans",Freesans,sans-serif;}
|
|
|
|
h1 {text-align:center;}
|
|
|
|
pre {white-space: pre-wrap;}
|
|
|
|
#page {background-color:#eee;width:1024px;margin:5em auto;border:3px solid #333;border-radius: 15px;padding:1em;}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="page">';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Produce the HTML page footer
|
|
|
|
*/
|
|
|
|
protected function end_output() {
|
|
|
|
echo '</div></body></html>';
|
|
|
|
}
|
2012-09-17 14:59:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The main class providing all the functionality //////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The actual worker class implementing the main functionality of the script.
|
2012-09-14 16:18:31 +02:00
|
|
|
*
|
|
|
|
* @copyright 2012 David Mudrak <david@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
class worker extends singleton_pattern {
|
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
const EXIT_OK = 0; // Success exit code.
|
|
|
|
const EXIT_HELP = 1; // Explicit help required.
|
|
|
|
const EXIT_UNKNOWN_ACTION = 127; // Neither -i nor -u provided.
|
|
|
|
|
2012-09-17 14:59:23 +02:00
|
|
|
/** @var input_manager */
|
|
|
|
protected $input = null;
|
|
|
|
|
|
|
|
/** @var output_manager */
|
|
|
|
protected $output = null;
|
|
|
|
|
2012-10-11 07:29:44 +02:00
|
|
|
/** @var int the most recent cURL error number, zero for no error */
|
|
|
|
private $curlerrno = null;
|
|
|
|
|
|
|
|
/** @var string the most recent cURL error message, empty string for no error */
|
|
|
|
private $curlerror = null;
|
|
|
|
|
|
|
|
/** @var array|false the most recent cURL request info, if it was successful */
|
|
|
|
private $curlinfo = null;
|
|
|
|
|
2012-11-07 21:12:01 +01:00
|
|
|
/** @var string the full path to the log file */
|
|
|
|
private $logfile = null;
|
|
|
|
|
2012-09-14 16:18:31 +02:00
|
|
|
/**
|
2012-09-17 14:59:23 +02:00
|
|
|
* Main - the one that actually does something
|
2012-09-14 16:18:31 +02:00
|
|
|
*/
|
2012-09-17 14:59:23 +02:00
|
|
|
public function execute() {
|
|
|
|
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('=== MDEPLOY EXECUTION START ===');
|
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
// Authorize access. None in CLI. Passphrase in HTTP.
|
|
|
|
$this->authorize();
|
|
|
|
|
|
|
|
// Asking for help in the CLI mode.
|
2012-09-17 14:59:23 +02:00
|
|
|
if ($this->input->get_option('help')) {
|
|
|
|
$this->output->help();
|
2012-10-10 00:44:39 +02:00
|
|
|
$this->done(self::EXIT_HELP);
|
2012-09-17 14:59:23 +02:00
|
|
|
}
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
if ($this->input->get_option('upgrade')) {
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Plugin upgrade requested');
|
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
// Fetch the ZIP file into a temporary location.
|
2012-10-11 07:29:44 +02:00
|
|
|
$source = $this->input->get_option('package');
|
|
|
|
$target = $this->target_location($source);
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Downloading package '.$source);
|
2012-10-10 00:44:39 +02:00
|
|
|
|
2012-10-11 07:29:44 +02:00
|
|
|
if ($this->download_file($source, $target)) {
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Package downloaded into '.$target);
|
2012-10-11 07:29:44 +02:00
|
|
|
} else {
|
|
|
|
$this->log('cURL error ' . $this->curlerrno . ' ' . $this->curlerror);
|
|
|
|
$this->log('Unable to download the file');
|
2012-11-07 21:12:01 +01:00
|
|
|
throw new download_file_exception('Unable to download the package');
|
2012-10-11 07:29:44 +02:00
|
|
|
}
|
|
|
|
|
2012-11-07 16:29:07 +01:00
|
|
|
// Compare MD5 checksum of the ZIP file
|
|
|
|
$md5remote = $this->input->get_option('md5');
|
|
|
|
$md5local = md5_file($target);
|
|
|
|
|
|
|
|
if ($md5local !== $md5remote) {
|
|
|
|
$this->log('MD5 checksum failed. Expected: '.$md5remote.' Got: '.$md5local);
|
|
|
|
throw new checksum_exception('MD5 checksum failed');
|
|
|
|
}
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('MD5 checksum ok');
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
// Backup the current version of the plugin
|
|
|
|
$plugintyperoot = $this->input->get_option('typeroot');
|
|
|
|
$pluginname = $this->input->get_option('name');
|
|
|
|
$sourcelocation = $plugintyperoot.'/'.$pluginname;
|
|
|
|
$backuplocation = $this->backup_location($sourcelocation);
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Current plugin code location: '.$sourcelocation);
|
|
|
|
$this->log('Moving the current code into archive: '.$backuplocation);
|
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
// We don't want to touch files unless we are pretty sure it would be all ok.
|
|
|
|
if (!$this->move_directory_source_precheck($sourcelocation)) {
|
|
|
|
throw new backup_folder_exception('Unable to backup the current version of the plugin (source precheck failed)');
|
|
|
|
}
|
|
|
|
if (!$this->move_directory_target_precheck($backuplocation)) {
|
|
|
|
throw new backup_folder_exception('Unable to backup the current version of the plugin (backup precheck failed)');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looking good, let's try it.
|
2012-12-06 02:37:22 +01:00
|
|
|
if (!$this->move_directory($sourcelocation, $backuplocation, true)) {
|
2012-10-11 09:36:12 +02:00
|
|
|
throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)');
|
|
|
|
}
|
|
|
|
|
2012-10-11 12:37:51 +02:00
|
|
|
// Unzip the plugin package file into the target location.
|
|
|
|
$this->unzip_plugin($target, $plugintyperoot, $sourcelocation, $backuplocation);
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Package successfully extracted');
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
// Redirect to the given URL (in HTTP) or exit (in CLI).
|
|
|
|
$this->done();
|
2012-09-14 16:18:31 +02:00
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
} else if ($this->input->get_option('install')) {
|
2013-03-14 20:44:35 +01:00
|
|
|
$this->log('Plugin installation requested');
|
|
|
|
|
|
|
|
$plugintyperoot = $this->input->get_option('typeroot');
|
|
|
|
$pluginname = $this->input->get_option('name');
|
|
|
|
$source = $this->input->get_option('package');
|
|
|
|
$md5remote = $this->input->get_option('md5');
|
|
|
|
|
|
|
|
// Check if the plugin location if available for us.
|
|
|
|
$pluginlocation = $plugintyperoot.'/'.$pluginname;
|
|
|
|
|
|
|
|
$this->log('New plugin code location: '.$pluginlocation);
|
|
|
|
|
|
|
|
if (file_exists($pluginlocation)) {
|
|
|
|
throw new filesystem_exception('Unable to prepare the plugin location (directory already exists)');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$this->create_directory_precheck($pluginlocation)) {
|
|
|
|
throw new filesystem_exception('Unable to prepare the plugin location (cannot create new directory)');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch the ZIP file into a temporary location.
|
|
|
|
$target = $this->target_location($source);
|
|
|
|
$this->log('Downloading package '.$source);
|
|
|
|
|
|
|
|
if ($this->download_file($source, $target)) {
|
|
|
|
$this->log('Package downloaded into '.$target);
|
|
|
|
} else {
|
|
|
|
$this->log('cURL error ' . $this->curlerrno . ' ' . $this->curlerror);
|
|
|
|
$this->log('Unable to download the file');
|
|
|
|
throw new download_file_exception('Unable to download the package');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare MD5 checksum of the ZIP file
|
|
|
|
$md5local = md5_file($target);
|
|
|
|
|
|
|
|
if ($md5local !== $md5remote) {
|
|
|
|
$this->log('MD5 checksum failed. Expected: '.$md5remote.' Got: '.$md5local);
|
|
|
|
throw new checksum_exception('MD5 checksum failed');
|
|
|
|
}
|
|
|
|
$this->log('MD5 checksum ok');
|
|
|
|
|
|
|
|
// Unzip the plugin package file into the plugin location.
|
|
|
|
$this->unzip_plugin($target, $plugintyperoot, $pluginlocation, false);
|
|
|
|
$this->log('Package successfully extracted');
|
|
|
|
|
|
|
|
// Redirect to the given URL (in HTTP) or exit (in CLI).
|
|
|
|
$this->done();
|
2012-10-10 00:44:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print help in CLI by default.
|
|
|
|
$this->output->help();
|
|
|
|
$this->done(self::EXIT_UNKNOWN_ACTION);
|
2012-09-14 16:18:31 +02:00
|
|
|
}
|
|
|
|
|
2012-11-07 23:20:54 +01:00
|
|
|
/**
|
|
|
|
* Attempts to log a thrown exception
|
|
|
|
*
|
|
|
|
* @param Exception $e uncaught exception
|
|
|
|
*/
|
|
|
|
public function log_exception(Exception $e) {
|
|
|
|
$this->log($e->__toString());
|
|
|
|
}
|
|
|
|
|
2012-09-17 14:59:23 +02:00
|
|
|
/**
|
|
|
|
* Initialize the worker class.
|
|
|
|
*/
|
|
|
|
protected function initialize() {
|
|
|
|
$this->input = input_manager::instance();
|
|
|
|
$this->output = output_manager::instance();
|
|
|
|
}
|
2012-09-17 18:52:49 +02:00
|
|
|
|
|
|
|
// End of external API
|
|
|
|
|
2012-10-10 00:44:39 +02:00
|
|
|
/**
|
|
|
|
* Finish this script execution.
|
|
|
|
*
|
|
|
|
* @param int $exitcode
|
|
|
|
*/
|
|
|
|
protected function done($exitcode = self::EXIT_OK) {
|
|
|
|
|
|
|
|
if (PHP_SAPI === 'cli') {
|
|
|
|
exit($exitcode);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
$returnurl = $this->input->get_option('returnurl');
|
2012-10-11 12:37:51 +02:00
|
|
|
$this->redirect($returnurl);
|
2012-10-10 00:44:39 +02:00
|
|
|
exit($exitcode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-17 18:52:49 +02:00
|
|
|
/**
|
|
|
|
* Authorize access to the script.
|
|
|
|
*
|
|
|
|
* In CLI mode, the access is automatically authorized. In HTTP mode, the
|
|
|
|
* passphrase submitted via the request params must match the contents of the
|
|
|
|
* file, the name of which is passed in another parameter.
|
|
|
|
*
|
|
|
|
* @throws unauthorized_access_exception
|
|
|
|
*/
|
|
|
|
protected function authorize() {
|
|
|
|
|
|
|
|
if (PHP_SAPI === 'cli') {
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Successfully authorized using the CLI SAPI');
|
2012-09-17 18:52:49 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$dataroot = $this->input->get_option('dataroot');
|
|
|
|
$passfile = $this->input->get_option('passfile');
|
|
|
|
$password = $this->input->get_option('password');
|
|
|
|
|
2012-10-10 00:01:49 +02:00
|
|
|
$passpath = $dataroot.'/mdeploy/auth/'.$passfile;
|
2012-09-17 18:52:49 +02:00
|
|
|
|
|
|
|
if (!is_readable($passpath)) {
|
2012-10-10 00:01:49 +02:00
|
|
|
throw new unauthorized_access_exception('Unable to read the passphrase file.');
|
2012-09-17 18:52:49 +02:00
|
|
|
}
|
|
|
|
|
2012-10-10 00:01:49 +02:00
|
|
|
$stored = file($passpath, FILE_IGNORE_NEW_LINES);
|
2012-09-17 18:52:49 +02:00
|
|
|
|
2012-10-10 00:01:49 +02:00
|
|
|
// "This message will self-destruct in five seconds." -- Mission Commander Swanbeck, Mission: Impossible II
|
|
|
|
unlink($passpath);
|
|
|
|
|
|
|
|
if (is_readable($passpath)) {
|
|
|
|
throw new unauthorized_access_exception('Unable to remove the passphrase file.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($stored) < 2) {
|
|
|
|
throw new unauthorized_access_exception('Invalid format of the passphrase file.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time() - (int)$stored[1] > 30 * 60) {
|
|
|
|
throw new unauthorized_access_exception('Passphrase timeout.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen($stored[0]) < 24) {
|
|
|
|
throw new unauthorized_access_exception('Session passphrase not long enough.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($password !== $stored[0]) {
|
2012-09-17 18:52:49 +02:00
|
|
|
throw new unauthorized_access_exception('Session passphrase does not match the stored one.');
|
|
|
|
}
|
2012-11-07 21:12:01 +01:00
|
|
|
|
|
|
|
$this->log('Successfully authorized using the passphrase file');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the full path to the log file.
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function log_location() {
|
|
|
|
|
|
|
|
if (!is_null($this->logfile)) {
|
|
|
|
return $this->logfile;
|
|
|
|
}
|
|
|
|
|
2012-11-07 23:20:54 +01:00
|
|
|
$dataroot = $this->input->get_option('dataroot', '');
|
2012-11-07 21:12:01 +01:00
|
|
|
|
2012-11-07 23:20:54 +01:00
|
|
|
if (empty($dataroot)) {
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->logfile = false;
|
|
|
|
return $this->logfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
$myroot = $dataroot.'/mdeploy';
|
|
|
|
|
|
|
|
if (!is_dir($myroot)) {
|
|
|
|
mkdir($myroot, 02777, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->logfile = $myroot.'/mdeploy.log';
|
|
|
|
return $this->logfile;
|
2012-09-17 18:52:49 +02:00
|
|
|
}
|
2012-10-11 07:29:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Choose the target location for the given ZIP's URL.
|
|
|
|
*
|
|
|
|
* @param string $source URL
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function target_location($source) {
|
|
|
|
|
|
|
|
$dataroot = $this->input->get_option('dataroot');
|
|
|
|
$pool = $dataroot.'/mdeploy/var';
|
|
|
|
|
|
|
|
if (!is_dir($pool)) {
|
|
|
|
mkdir($pool, 02777, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
$target = $pool.'/'.md5($source);
|
|
|
|
|
|
|
|
$suffix = 0;
|
|
|
|
while (file_exists($target.'.'.$suffix.'.zip')) {
|
|
|
|
$suffix++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $target.'.'.$suffix.'.zip';
|
|
|
|
}
|
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
/**
|
|
|
|
* Choose the location of the current plugin folder backup
|
|
|
|
*
|
|
|
|
* @param string $path full path to the current folder
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function backup_location($path) {
|
|
|
|
|
|
|
|
$dataroot = $this->input->get_option('dataroot');
|
|
|
|
$pool = $dataroot.'/mdeploy/archive';
|
|
|
|
|
|
|
|
if (!is_dir($pool)) {
|
|
|
|
mkdir($pool, 02777, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
$target = $pool.'/'.basename($path).'_'.time();
|
|
|
|
|
|
|
|
$suffix = 0;
|
|
|
|
while (file_exists($target.'.'.$suffix)) {
|
|
|
|
$suffix++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $target.'.'.$suffix;
|
|
|
|
}
|
|
|
|
|
2012-10-11 07:29:44 +02:00
|
|
|
/**
|
|
|
|
* Downloads the given file into the given destination.
|
|
|
|
*
|
|
|
|
* This is basically a simplified version of {@link download_file_content()} from
|
|
|
|
* Moodle itself, tuned for fetching files from moodle.org servers.
|
|
|
|
*
|
|
|
|
* @param string $source file url starting with http(s)://
|
|
|
|
* @param string $target store the downloaded content to this file (full path)
|
|
|
|
* @return bool true on success, false otherwise
|
|
|
|
* @throws download_file_exception
|
|
|
|
*/
|
|
|
|
protected function download_file($source, $target) {
|
|
|
|
|
|
|
|
$newlines = array("\r", "\n");
|
|
|
|
$source = str_replace($newlines, '', $source);
|
|
|
|
if (!preg_match('|^https?://|i', $source)) {
|
|
|
|
throw new download_file_exception('Unsupported transport protocol.');
|
|
|
|
}
|
|
|
|
if (!$ch = curl_init($source)) {
|
2012-11-07 21:12:01 +01:00
|
|
|
$this->log('Unable to init cURL.');
|
2012-10-11 07:29:44 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // verify the peer's certificate
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // check the existence of a common name and also verify that it matches the hostname provided
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return the transfer as a string
|
|
|
|
curl_setopt($ch, CURLOPT_HEADER, false); // don't include the header in the output
|
|
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 3600);
|
|
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); // nah, moodle.org is never unavailable! :-p
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $source);
|
|
|
|
|
2012-11-30 13:15:00 +01:00
|
|
|
$dataroot = $this->input->get_option('dataroot');
|
|
|
|
$cacertfile = $dataroot.'/moodleorgca.crt';
|
|
|
|
if (is_readable($cacertfile)) {
|
|
|
|
// Do not use CA certs provided by the operating system. Instead,
|
|
|
|
// use this CA cert to verify the ZIP provider.
|
|
|
|
$this->log('Using custom CA certificate '.$cacertfile);
|
|
|
|
curl_setopt($ch, CURLOPT_CAINFO, $cacertfile);
|
|
|
|
}
|
|
|
|
|
2012-11-30 16:11:52 +01:00
|
|
|
$proxy = $this->input->get_option('proxy', false);
|
|
|
|
if (!empty($proxy)) {
|
|
|
|
curl_setopt($ch, CURLOPT_PROXY, $proxy);
|
|
|
|
|
|
|
|
$proxytype = $this->input->get_option('proxytype', false);
|
|
|
|
if (strtoupper($proxytype) === 'SOCKS5') {
|
|
|
|
$this->log('Using SOCKS5 proxy');
|
|
|
|
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
|
|
|
|
} else if (!empty($proxytype)) {
|
|
|
|
$this->log('Using HTTP proxy');
|
|
|
|
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
$proxyuserpwd = $this->input->get_option('proxyuserpwd', false);
|
|
|
|
if (!empty($proxyuserpwd)) {
|
|
|
|
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuserpwd);
|
|
|
|
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-11 07:29:44 +02:00
|
|
|
$targetfile = fopen($target, 'w');
|
|
|
|
|
|
|
|
if (!$targetfile) {
|
|
|
|
throw new download_file_exception('Unable to create local file '.$target);
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_FILE, $targetfile);
|
|
|
|
|
|
|
|
$result = curl_exec($ch);
|
|
|
|
|
|
|
|
// try to detect encoding problems
|
|
|
|
if ((curl_errno($ch) == 23 or curl_errno($ch) == 61) and defined('CURLOPT_ENCODING')) {
|
|
|
|
curl_setopt($ch, CURLOPT_ENCODING, 'none');
|
|
|
|
$result = curl_exec($ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($targetfile);
|
|
|
|
|
|
|
|
$this->curlerrno = curl_errno($ch);
|
|
|
|
$this->curlerror = curl_error($ch);
|
|
|
|
$this->curlinfo = curl_getinfo($ch);
|
|
|
|
|
|
|
|
if (!$result or $this->curlerrno) {
|
|
|
|
return false;
|
|
|
|
|
|
|
|
} else if (is_array($this->curlinfo) and (empty($this->curlinfo['http_code']) or $this->curlinfo['http_code'] != 200)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a message
|
|
|
|
*
|
|
|
|
* @param string $message
|
|
|
|
*/
|
|
|
|
protected function log($message) {
|
2012-11-07 21:12:01 +01:00
|
|
|
|
|
|
|
$logpath = $this->log_location();
|
|
|
|
|
|
|
|
if (empty($logpath)) {
|
|
|
|
// no logging available
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$f = fopen($logpath, 'ab');
|
|
|
|
|
|
|
|
if ($f === false) {
|
|
|
|
throw new filesystem_exception('Unable to open the log file for appending');
|
|
|
|
}
|
|
|
|
|
|
|
|
$message = $this->format_log_message($message);
|
|
|
|
|
|
|
|
fwrite($f, $message);
|
|
|
|
|
|
|
|
fclose($f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepares the log message for writing into the file
|
|
|
|
*
|
|
|
|
* @param string $msg
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function format_log_message($msg) {
|
|
|
|
|
|
|
|
$msg = trim($msg);
|
|
|
|
$timestamp = date("Y-m-d H:i:s");
|
|
|
|
|
|
|
|
return $timestamp . ' '. $msg . PHP_EOL;
|
2012-10-11 07:29:44 +02:00
|
|
|
}
|
2012-10-11 09:36:12 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks to see if the given source could be safely moved into a new location
|
|
|
|
*
|
|
|
|
* @param string $source full path to the existing directory
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function move_directory_source_precheck($source) {
|
|
|
|
|
2012-11-09 00:20:18 +01:00
|
|
|
if (!is_writable($source)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
if (is_dir($source)) {
|
|
|
|
$handle = opendir($source);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$result = true;
|
|
|
|
|
|
|
|
while ($filename = readdir($handle)) {
|
|
|
|
$sourcepath = $source.'/'.$filename;
|
|
|
|
|
|
|
|
if ($filename === '.' or $filename === '..') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_dir($sourcepath)) {
|
|
|
|
$result = $result && $this->move_directory_source_precheck($sourcepath);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
$result = $result && is_writable($sourcepath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir($handle);
|
2012-11-09 00:20:18 +01:00
|
|
|
|
|
|
|
return $result;
|
2012-10-11 09:36:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-03-14 20:44:35 +01:00
|
|
|
* Checks to see if a source folder could be safely moved into the given new location
|
2012-10-11 09:36:12 +02:00
|
|
|
*
|
|
|
|
* @param string $destination full path to the new expected location of a folder
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function move_directory_target_precheck($target) {
|
|
|
|
|
2013-03-14 20:44:35 +01:00
|
|
|
// Check if the target folder does not exist yet, can be created
|
|
|
|
// and removed again.
|
|
|
|
$result = $this->create_directory_precheck($target);
|
|
|
|
|
|
|
|
// At the moment, it seems to be enough to check. We may want to add
|
|
|
|
// more steps in the future.
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make sure the given directory can be created (and removed)
|
|
|
|
*
|
|
|
|
* @param string $path full path to the folder
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function create_directory_precheck($path) {
|
|
|
|
|
|
|
|
if (file_exists($path)) {
|
2012-10-11 09:36:12 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-14 20:44:35 +01:00
|
|
|
$result = mkdir($path, 02777) && rmdir($path);
|
2012-10-11 09:36:12 +02:00
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves the given source into a new location recursively
|
|
|
|
*
|
2012-12-06 02:31:36 +01:00
|
|
|
* The target location can not exist.
|
|
|
|
*
|
2012-10-11 09:36:12 +02:00
|
|
|
* @param string $source full path to the existing directory
|
|
|
|
* @param string $destination full path to the new location of the folder
|
2012-12-06 02:31:36 +01:00
|
|
|
* @param bool $keepsourceroot should the root of the $source be kept or removed at the end
|
2012-10-11 09:36:12 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2012-12-06 02:31:36 +01:00
|
|
|
protected function move_directory($source, $target, $keepsourceroot = false) {
|
2012-10-11 09:36:12 +02:00
|
|
|
|
|
|
|
if (file_exists($target)) {
|
2012-10-11 12:37:51 +02:00
|
|
|
throw new filesystem_exception('Unable to move the directory - target location already exists');
|
2012-10-11 09:36:12 +02:00
|
|
|
}
|
|
|
|
|
2012-12-06 02:31:36 +01:00
|
|
|
return $this->move_directory_into($source, $target, $keepsourceroot);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves the given source into a new location recursively
|
|
|
|
*
|
|
|
|
* If the target already exists, files are moved into it. The target is created otherwise.
|
|
|
|
*
|
|
|
|
* @param string $source full path to the existing directory
|
|
|
|
* @param string $destination full path to the new location of the folder
|
|
|
|
* @param bool $keepsourceroot should the root of the $source be kept or removed at the end
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function move_directory_into($source, $target, $keepsourceroot = false) {
|
|
|
|
|
2012-10-11 09:36:12 +02:00
|
|
|
if (is_dir($source)) {
|
|
|
|
$handle = opendir($source);
|
|
|
|
} else {
|
2012-10-11 12:37:51 +02:00
|
|
|
throw new filesystem_exception('Source location is not a directory');
|
2012-10-11 09:36:12 +02:00
|
|
|
}
|
|
|
|
|
2012-12-06 02:31:36 +01:00
|
|
|
if (is_dir($target)) {
|
|
|
|
$result = true;
|
|
|
|
} else {
|
|
|
|
$result = mkdir($target, 02777);
|
|
|
|
}
|
2012-10-11 09:36:12 +02:00
|
|
|
|
|
|
|
while ($filename = readdir($handle)) {
|
|
|
|
$sourcepath = $source.'/'.$filename;
|
|
|
|
$targetpath = $target.'/'.$filename;
|
|
|
|
|
|
|
|
if ($filename === '.' or $filename === '..') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_dir($sourcepath)) {
|
2012-12-06 02:31:36 +01:00
|
|
|
$result = $result && $this->move_directory($sourcepath, $targetpath, false);
|
2012-10-11 09:36:12 +02:00
|
|
|
|
|
|
|
} else {
|
2012-12-06 02:31:36 +01:00
|
|
|
$result = $result && rename($sourcepath, $targetpath);
|
2012-10-11 09:36:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir($handle);
|
2012-12-06 02:31:36 +01:00
|
|
|
|
|
|
|
if (!$keepsourceroot) {
|
|
|
|
$result = $result && rmdir($source);
|
|
|
|
}
|
|
|
|
|
|
|
|
clearstatcache();
|
|
|
|
|
|
|
|
return $result;
|
2012-10-11 09:36:12 +02:00
|
|
|
}
|
2012-10-11 12:37:51 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes the given directory recursively
|
|
|
|
*
|
|
|
|
* @param string $path full path to the directory
|
2012-12-06 02:31:36 +01:00
|
|
|
* @param bool $keeppathroot should the root of the $path be kept (i.e. remove the content only) or removed too
|
|
|
|
* @return bool
|
2012-10-11 12:37:51 +02:00
|
|
|
*/
|
2012-12-06 02:31:36 +01:00
|
|
|
protected function remove_directory($path, $keeppathroot = false) {
|
|
|
|
|
|
|
|
$result = true;
|
2012-10-11 12:37:51 +02:00
|
|
|
|
|
|
|
if (!file_exists($path)) {
|
2012-12-06 02:31:36 +01:00
|
|
|
return $result;
|
2012-10-11 12:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_dir($path)) {
|
|
|
|
$handle = opendir($path);
|
|
|
|
} else {
|
|
|
|
throw new filesystem_exception('Given path is not a directory');
|
|
|
|
}
|
|
|
|
|
|
|
|
while ($filename = readdir($handle)) {
|
|
|
|
$filepath = $path.'/'.$filename;
|
|
|
|
|
|
|
|
if ($filename === '.' or $filename === '..') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_dir($filepath)) {
|
2012-12-06 02:31:36 +01:00
|
|
|
$result = $result && $this->remove_directory($filepath, false);
|
2012-10-11 12:37:51 +02:00
|
|
|
|
|
|
|
} else {
|
2012-12-06 02:31:36 +01:00
|
|
|
$result = $result && unlink($filepath);
|
2012-10-11 12:37:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir($handle);
|
2012-12-06 02:31:36 +01:00
|
|
|
|
|
|
|
if (!$keeppathroot) {
|
|
|
|
$result = $result && rmdir($path);
|
|
|
|
}
|
|
|
|
|
|
|
|
clearstatcache();
|
|
|
|
|
|
|
|
return $result;
|
2012-10-11 12:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unzip the file obtained from the Plugins directory to this site
|
|
|
|
*
|
|
|
|
* @param string $ziplocation full path to the ZIP file
|
|
|
|
* @param string $plugintyperoot full path to the plugin's type location
|
|
|
|
* @param string $expectedlocation expected full path to the plugin after it is extracted
|
2013-03-14 20:46:28 +01:00
|
|
|
* @param string|bool $backuplocation location of the previous version of the plugin or false for no backup
|
2012-10-11 12:37:51 +02:00
|
|
|
*/
|
|
|
|
protected function unzip_plugin($ziplocation, $plugintyperoot, $expectedlocation, $backuplocation) {
|
|
|
|
|
|
|
|
$zip = new ZipArchive();
|
|
|
|
$result = $zip->open($ziplocation);
|
|
|
|
|
|
|
|
if ($result !== true) {
|
2013-03-14 20:44:35 +01:00
|
|
|
if ($backuplocation !== false) {
|
|
|
|
$this->move_directory($backuplocation, $expectedlocation);
|
|
|
|
}
|
2012-10-11 12:37:51 +02:00
|
|
|
throw new zip_exception('Unable to open the zip package');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that the ZIP has expected structure
|
|
|
|
$pluginname = basename($expectedlocation);
|
|
|
|
for ($i = 0; $i < $zip->numFiles; $i++) {
|
|
|
|
$stat = $zip->statIndex($i);
|
|
|
|
$filename = $stat['name'];
|
|
|
|
$filename = explode('/', $filename);
|
|
|
|
if ($filename[0] !== $pluginname) {
|
|
|
|
$zip->close();
|
|
|
|
throw new zip_exception('Invalid structure of the zip package');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$zip->extractTo($plugintyperoot)) {
|
|
|
|
$zip->close();
|
2012-12-06 02:37:22 +01:00
|
|
|
$this->remove_directory($expectedlocation, true); // just in case something was created
|
2013-03-14 20:44:35 +01:00
|
|
|
if ($backuplocation !== false) {
|
|
|
|
$this->move_directory_into($backuplocation, $expectedlocation);
|
|
|
|
}
|
2012-10-11 12:37:51 +02:00
|
|
|
throw new zip_exception('Unable to extract the zip package');
|
|
|
|
}
|
|
|
|
|
|
|
|
$zip->close();
|
2012-11-07 16:30:57 +01:00
|
|
|
unlink($ziplocation);
|
2012-10-11 12:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirect the browser
|
|
|
|
*
|
|
|
|
* @todo check if there has been some output yet
|
|
|
|
* @param string $url
|
|
|
|
*/
|
|
|
|
protected function redirect($url) {
|
|
|
|
header('Location: '.$url);
|
|
|
|
}
|
2012-09-14 16:18:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-07 23:20:54 +01:00
|
|
|
/**
|
|
|
|
* Provides exception handlers for this script
|
|
|
|
*/
|
|
|
|
class exception_handlers {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the exception handler
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param string $handler name
|
|
|
|
*/
|
|
|
|
public static function set_handler($handler) {
|
|
|
|
|
|
|
|
if (PHP_SAPI === 'cli') {
|
|
|
|
// No custom handler available for CLI mode.
|
|
|
|
set_exception_handler(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_exception_handler('exception_handlers::'.$handler.'_exception_handler');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the text describing the thrown exception
|
|
|
|
*
|
|
|
|
* By default, PHP displays full path to scripts when the exception is thrown. In order to prevent
|
|
|
|
* sensitive information leak (and yes, the path to scripts at a web server _is_ sensitive information)
|
|
|
|
* the path to scripts is removed from the message.
|
|
|
|
*
|
|
|
|
* @param Exception $e thrown exception
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function format_exception_info(Exception $e) {
|
|
|
|
|
|
|
|
$mydir = dirname(__FILE__).'/';
|
|
|
|
$text = $e->__toString();
|
|
|
|
$text = str_replace($mydir, '', $text);
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Very basic exception handler
|
|
|
|
*
|
|
|
|
* @param Exception $e uncaught exception
|
|
|
|
*/
|
|
|
|
public static function bootstrap_exception_handler(Exception $e) {
|
|
|
|
echo('<h1>Oops! It did it again</h1>');
|
|
|
|
echo('<p><strong>Moodle deployment utility had a trouble with your request. See the debugging information for more details.</strong></p>');
|
|
|
|
echo('<pre>');
|
|
|
|
echo self::format_exception_info($e);
|
|
|
|
echo('</pre>');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default exception handler
|
|
|
|
*
|
|
|
|
* When this handler is used, input_manager and output_manager singleton instances already
|
|
|
|
* exist in the memory and can be used.
|
|
|
|
*
|
|
|
|
* @param Exception $e uncaught exception
|
|
|
|
*/
|
|
|
|
public static function default_exception_handler(Exception $e) {
|
|
|
|
|
|
|
|
$worker = worker::instance();
|
|
|
|
$worker->log_exception($e);
|
|
|
|
|
|
|
|
$output = output_manager::instance();
|
|
|
|
$output->exception($e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-14 16:18:31 +02:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Check if the script is actually executed or if it was just included by someone
|
|
|
|
// else - typically by the PHPUnit. This is a PHP alternative to the Python's
|
|
|
|
// if __name__ == '__main__'
|
|
|
|
if (!debug_backtrace()) {
|
2012-09-17 14:59:23 +02:00
|
|
|
// We are executed by the SAPI.
|
2012-11-07 23:20:54 +01:00
|
|
|
exception_handlers::set_handler('bootstrap');
|
2012-09-14 16:18:31 +02:00
|
|
|
// Initialize the worker class to actually make the job.
|
|
|
|
$worker = worker::instance();
|
2012-11-07 23:20:54 +01:00
|
|
|
exception_handlers::set_handler('default');
|
2012-09-14 16:18:31 +02:00
|
|
|
|
|
|
|
// Lights, Camera, Action!
|
2012-09-17 14:59:23 +02:00
|
|
|
$worker->execute();
|
2012-09-14 16:18:31 +02:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// We are included - probably by some unit testing framework. Do nothing.
|
|
|
|
}
|