Skip to content

Commit

Permalink
MDL-35238 Fetch the package and store it in a temporary location
Browse files Browse the repository at this point in the history
  • Loading branch information
mudrd8mz committed Nov 8, 2012
1 parent af29dad commit 4c72f55
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/pluginlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ public function make_execution_widget(available_update_info $info) {
'type' => $plugintype,
'name' => $pluginname,
'typeroot' => $pluginrootpaths[$plugintype],
'download' => $info->download,
'package' => $info->download,
'dataroot' => $CFG->dataroot,
'dirroot' => $CFG->dirroot,
'passfile' => $passfile,
Expand Down
149 changes: 145 additions & 4 deletions mdeploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
*
* This script looks after deploying available updates to the local Moodle site.
*
* CLI usage example:
* $ sudo -u apache php mdeploy.php --upgrade \
* --package=https://moodle.org/plugins/download.php/...zip \
* --dataroot=/home/mudrd8mz/moodledata/moodle24
*
* @package core
* @copyright 2012 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
Expand All @@ -34,7 +39,9 @@

class invalid_coding_exception extends Exception {}
class missing_option_exception extends Exception {}
class invalid_option_exception extends Exception {}
class unauthorized_access_exception extends Exception {}
class download_file_exception extends Exception {}


// Various support classes /////////////////////////////////////////////////////
Expand Down Expand Up @@ -106,6 +113,7 @@ class input_manager extends singleton_pattern {
const TYPE_INT = 'int'; // Integer
const TYPE_PATH = 'path'; // Full path to a file or a directory
const TYPE_RAW = 'raw'; // Raw value, keep as is
const TYPE_URL = 'url'; // URL to a file

/** @var input_cli_provider|input_http_provider the provider of the input */
protected $inputprovider = null;
Expand Down Expand Up @@ -155,12 +163,13 @@ public function get_option($name, $default = 'provide_default_value_explicitly')
public function get_option_info($name=null) {

$supportedoptions = array(
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)'),
array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'),
array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'),
array('i', 'install', input_manager::TYPE_FLAG, 'Installation mode'),
array('p', 'package', input_manager::TYPE_URL, 'URL to the ZIP package to deploy'),
array('u', 'upgrade', input_manager::TYPE_FLAG, 'Upgrade mode'),
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)'),
);

if (is_null($name)) {
Expand Down Expand Up @@ -249,6 +258,20 @@ public function cast_value($raw, $type) {
case input_manager::TYPE_RAW:
return $raw;

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 {
return '';
}

default:
throw new invalid_coding_exception('Unknown option type.');

Expand Down Expand Up @@ -582,6 +605,15 @@ class worker extends singleton_pattern {
/** @var output_manager */
protected $output = null;

/** @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;

/**
* Main - the one that actually does something
*/
Expand All @@ -598,10 +630,22 @@ public function execute() {

if ($this->input->get_option('upgrade')) {
// Fetch the ZIP file into a temporary location.
$source = $this->input->get_option('package');
if (empty($source)) {
throw new invalid_option_exception('Not a valid package URL');
}
$target = $this->target_location($source);

// Compare MD5 checksum of the ZIP file.
if ($this->download_file($source, $target)) {
$this->log('ZIP fetched into '.$target);
} else {
$this->log('cURL error ' . $this->curlerrno . ' ' . $this->curlerror);
$this->log('Unable to download the file');
}

// Compare MD5 checksum of the ZIP file - TODO

// If the target location exists, backup it.
// If the target location exists, backup it - TODO

// Unzip the ZIP file into the target location.

Expand Down Expand Up @@ -694,6 +738,103 @@ protected function authorize() {
throw new unauthorized_access_exception('Session passphrase does not match the stored one.');
}
}

/**
* 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';
}

/**
* 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)) {
// $this->log('Unable to init cURL.');
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);

$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) {
// TODO
}
}


Expand Down
8 changes: 8 additions & 0 deletions mdeploytest.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ public function data_for_cast_value() {
array("!@#$%|/etc/qwerty\n\n\t\n\r", input_manager::TYPE_RAW, "!@#$%|/etc/qwerty\n\n\t\n\r"),

array("\nrock'n'roll.mp3\t.exe", input_manager::TYPE_FILE, 'rocknroll.mp3.exe'),

array('http://localhost/moodle/dev/plugin.zip', input_manager::TYPE_URL, 'http://localhost/moodle/dev/plugin.zip'),
array(
'https://moodle.org/plugins/download.php/1292/mod_stampcoll_moodle23_2012062201.zip',
input_manager::TYPE_URL,
'https://moodle.org/plugins/download.php/1292/mod_stampcoll_moodle23_2012062201.zip'
),
array('file:///etc/passwd', input_manager::TYPE_URL, ''),
);
}

Expand Down

0 comments on commit 4c72f55

Please sign in to comment.