Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'inc/PEAR/Downloader.php')
-rwxr-xr-xinc/PEAR/Downloader.php680
1 files changed, 680 insertions, 0 deletions
diff --git a/inc/PEAR/Downloader.php b/inc/PEAR/Downloader.php
new file mode 100755
index 00000000000..5e16dc5ffb1
--- /dev/null
+++ b/inc/PEAR/Downloader.php
@@ -0,0 +1,680 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | Martin Jansen <mj@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'PEAR/Remote.php';
+require_once 'System.php';
+
+
+define('PEAR_INSTALLER_OK', 1);
+define('PEAR_INSTALLER_FAILED', 0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to download PEAR packages and maintain the
+ * installed package database.
+ *
+ * @since PEAR 1.4
+ * @author Greg Beaver <cellog@php.net>
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+ /**
+ * @var PEAR_Config
+ * @access private
+ */
+ var $_config;
+
+ /**
+ * @var PEAR_Registry
+ * @access private
+ */
+ var $_registry;
+
+ /**
+ * @var PEAR_Remote
+ * @access private
+ */
+ var $_remote;
+
+ /**
+ * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+ * @var string|null
+ * @access private
+ */
+ var $_preferredState;
+
+ /**
+ * Options from command-line passed to Install.
+ *
+ * Recognized options:<br />
+ * - onlyreqdeps : install all required dependencies as well
+ * - alldeps : install all dependencies, including optional
+ * - installroot : base relative path to install files in
+ * - force : force a download even if warnings would prevent it
+ * @see PEAR_Command_Install
+ * @access private
+ * @var array
+ */
+ var $_options;
+
+ /**
+ * Downloaded Packages after a call to download().
+ *
+ * Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @access private
+ * @var array
+ */
+ var $_downloadedPackages = array();
+
+ /**
+ * Packages slated for download.
+ *
+ * This is used to prevent downloading a package more than once should it be a dependency
+ * for two packages to be installed.
+ * Format of each entry:
+ *
+ * <pre>
+ * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+ * );
+ * </pre>
+ * @access private
+ * @var array
+ */
+ var $_toDownload = array();
+
+ /**
+ * Array of every package installed, with names lower-cased.
+ *
+ * Format:
+ * <code>
+ * array('package1' => 0, 'package2' => 1, );
+ * </code>
+ * @var array
+ */
+ var $_installed = array();
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $_errorStack = array();
+
+ // {{{ PEAR_Downloader()
+
+ function PEAR_Downloader(&$ui, $options, &$config)
+ {
+ $this->_options = $options;
+ $this->_config = &$config;
+ $this->_preferredState = $this->_config->get('preferred_state');
+ $this->ui = &$ui;
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+
+ $php_dir = $this->_config->get('php_dir');
+ if (isset($this->_options['installroot'])) {
+ if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
+ }
+ $this->_registry = &new PEAR_Registry($php_dir);
+ $this->_remote = &new PEAR_Remote($config);
+
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $this->_installed = $this->_registry->listPackages();
+ array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $this->_installed = array_flip($this->_installed);
+ }
+ parent::PEAR_Common();
+ }
+
+ // }}}
+ // {{{ configSet()
+ function configSet($key, $value, $layer = 'user')
+ {
+ $this->_config->set($key, $value, $layer);
+ $this->_preferredState = $this->_config->get('preferred_state');
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+ }
+
+ // }}}
+ // {{{ setOptions()
+ function setOptions($options)
+ {
+ $this->_options = $options;
+ }
+
+ // }}}
+ // {{{ _downloadFile()
+ /**
+ * @param string filename to download
+ * @param string version/state
+ * @param string original value passed to command-line
+ * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
+ * Defaults to configuration preferred state
+ * @return null|PEAR_Error|string
+ * @access private
+ */
+ function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
+ {
+ if (is_null($state)) {
+ $state = $this->_preferredState;
+ }
+ // {{{ check the package filename, and whether it's already installed
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($this->validPackageName($pkgfile)) {
+ if ($this->_registry->packageExists($pkgfile)) {
+ if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
+ }
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ $errors[] = "Could not open the package file: $pkgfile";
+ } else {
+ $errors[] = "No package file given";
+ }
+ return;
+ }
+ }
+ // }}}
+
+ // {{{ Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $this->_config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $this->log(3, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ $this->popErrorHandling();
+ if (PEAR::isError($file)) {
+ if ($this->validPackageName($origpkgfile)) {
+ if (!PEAR::isError($info = $this->_remote->call('package.info',
+ $origpkgfile))) {
+ if (!count($info['releases'])) {
+ return $this->raiseError('Package ' . $origpkgfile .
+ ' has no releases');
+ } else {
+ return $this->raiseError('No releases of preferred state "'
+ . $state . '" exist for package ' . $origpkgfile .
+ '. Use ' . $origpkgfile . '-state to install another' .
+ ' state (like ' . $origpkgfile .'-beta)',
+ PEAR_INSTALLER_ERROR_NO_PREF_STATE);
+ }
+ } else {
+ return $pkgfile;
+ }
+ } else {
+ return $this->raiseError($file);
+ }
+ }
+ $pkgfile = $file;
+ }
+ // }}}
+ return $pkgfile;
+ }
+ // }}}
+ // {{{ getPackageDownloadUrl()
+
+ function getPackageDownloadUrl($package, $version = null)
+ {
+ if ($version) {
+ $package .= "-$version";
+ }
+ if ($this === null || $this->_config === null) {
+ $package = "http://pear.php.net/get/$package";
+ } else {
+ $package = "http://" . $this->_config->get('master_server') .
+ "/get/$package";
+ }
+ if (!extension_loaded("zlib")) {
+ $package .= '?uncompress=yes';
+ }
+ return $package;
+ }
+
+ // }}}
+ // {{{ extractDownloadFileName($pkgfile, &$version)
+
+ function extractDownloadFileName($pkgfile, &$version)
+ {
+ if (@is_file($pkgfile)) {
+ return $pkgfile;
+ }
+ // regex defined in Common.php
+ if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
+ $version = (isset($m[3])) ? $m[3] : null;
+ return $m[1];
+ }
+ $version = null;
+ return $pkgfile;
+ }
+
+ // }}}
+
+ // }}}
+ // {{{ getDownloadedPackages()
+
+ /**
+ * Retrieve a list of downloaded packages after a call to {@link download()}.
+ *
+ * Also resets the list of downloaded packages.
+ * @return array
+ */
+ function getDownloadedPackages()
+ {
+ $ret = $this->_downloadedPackages;
+ $this->_downloadedPackages = array();
+ $this->_toDownload = array();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * BC-compatible method name
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function download($packages)
+ {
+ return $this->doDownload($packages);
+ }
+
+ // }}}
+ // {{{ doDownload()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function doDownload($packages)
+ {
+ $mywillinstall = array();
+ $state = $this->_preferredState;
+
+ // {{{ download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ $need_download = false;
+ if (!is_file($pkgfile)) {
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ }
+ $pkgfile = $this->_downloadNonFile($pkgfile);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ if ($pkgfile === false) {
+ continue;
+ }
+ } // end is_file()
+
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if ($need_download) {
+ $this->_toDownload[] = $tempinfo['package'];
+ }
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ } // end foreach($packages)
+ // }}}
+
+ // {{{ extract dependencies from downloaded files and then download
+ // them if necessary
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $deppackages = array();
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ // there are no dependencies
+ continue;
+ }
+ $fail = false;
+ foreach ($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ $ret = $this->_processDependency($package, $info, $mywillinstall);
+ if ($ret === false) {
+ continue;
+ }
+ if ($ret === 0) {
+ $fail = true;
+ continue;
+ }
+ if (PEAR::isError($ret)) {
+ return $ret;
+ }
+ $deppackages[] = $ret;
+ } // foreach($alldeps
+ if ($fail) {
+ $deppackages = array();
+ }
+ }
+
+ if (count($deppackages)) {
+ $this->doDownload($deppackages);
+ }
+ } // }}} if --alldeps or --onlyreqdeps
+ }
+
+ // }}}
+ // {{{ _downloadNonFile($pkgfile)
+
+ /**
+ * @return false|PEAR_Error|string false if loop should be broken out of,
+ * string if the file was downloaded,
+ * PEAR_Error on exception
+ * @access private
+ */
+ function _downloadNonFile($pkgfile)
+ {
+ $origpkgfile = $pkgfile;
+ $state = null;
+ $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile);
+ }
+ if (!$this->validPackageName($pkgfile)) {
+ return $this->raiseError("Package name '$pkgfile' not valid");
+ }
+ // ignore packages that are installed unless we are upgrading
+ $curinfo = $this->_registry->packageInfo($pkgfile);
+ if ($this->_registry->packageExists($pkgfile)
+ && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
+ return false;
+ }
+ if (in_array($pkgfile, $this->_toDownload)) {
+ return false;
+ }
+ $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true);
+ if (!count($releases)) {
+ return $this->raiseError("No releases found for package '$pkgfile'");
+ }
+ // Want a specific version/state
+ if ($version !== null) {
+ // Passed Foo-1.2
+ if ($this->validPackageVersion($version)) {
+ if (!isset($releases[$version])) {
+ return $this->raiseError("No release with version '$version' found for '$pkgfile'");
+ }
+ // Passed Foo-alpha
+ } elseif (in_array($version, $this->getReleaseStates())) {
+ $state = $version;
+ $version = 0;
+ foreach ($releases as $ver => $inf) {
+ if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ }
+ }
+ if ($version == 0) {
+ return $this->raiseError("No release with state '$state' found for '$pkgfile'");
+ }
+ // invalid suffix passed
+ } else {
+ return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
+ "version number or release state");
+ }
+ // Guess what to download
+ } else {
+ $states = $this->betterStates($this->_preferredState, true);
+ $possible = false;
+ $version = 0;
+ $higher_version = 0;
+ $prev_hi_ver = 0;
+ foreach ($releases as $ver => $inf) {
+ if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ } else {
+ $ver = (string)$ver;
+ if (version_compare($prev_hi_ver, $ver) < 0) {
+ $prev_hi_ver = $higher_version = $ver;
+ }
+ }
+ }
+ if ($version === 0 && !isset($this->_options['force'])) {
+ return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
+ "' found for '$pkgfile'");
+ } elseif ($version === 0) {
+ $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
+ "than state '$this->_preferredState'");
+ }
+ }
+ // Check if we haven't already the version
+ if (empty($this->_options['force']) && !is_null($curinfo)) {
+ if ($curinfo['version'] == $version) {
+ $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
+ return false;
+ } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
+ $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
+ " is installed and {$curinfo['version']} is > requested '$version', skipping");
+ return false;
+ }
+ }
+ $this->_toDownload[] = $pkgfile;
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
+ }
+
+ // }}}
+ // {{{ _processDependency($package, $info, $mywillinstall)
+
+ /**
+ * Process a dependency, download if necessary
+ * @param array dependency information from PEAR_Remote call
+ * @param array packages that will be installed in this iteration
+ * @return false|string|PEAR_Error
+ * @access private
+ * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
+ * in fact lower than the required value. This will be very important for BC dependencies
+ */
+ function _processDependency($package, $info, $mywillinstall)
+ {
+ $state = $this->_preferredState;
+ if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package '$package' optional dependency '$info[name]'");
+ return false;
+ }
+ // {{{ get releases
+ $releases = $this->_remote->call('package.info', $info['name'], 'releases', true);
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ $this->pushError("Package '$package' dependency '$info[name]' ".
+ "has no releases");
+ }
+ return false;
+ }
+ $found = false;
+ $save = $releases;
+ while(count($releases) && !$found) {
+ if (!empty($state) && $state != 'any') {
+ list($release_version, $release) = each($releases);
+ if ($state != $release['state'] &&
+ !in_array($release['state'], $this->betterStates($state)))
+ {
+ // drop this release - it ain't stable enough
+ array_shift($releases);
+ } else {
+ $found = true;
+ }
+ } else {
+ $found = true;
+ }
+ }
+ if (!count($releases) && !$found) {
+ $get = array();
+ foreach($save as $release) {
+ $get = array_merge($get,
+ $this->betterStates($release['state'], true));
+ }
+ $savestate = array_shift($get);
+ $this->pushError( "Release for '$package' dependency '$info[name]' " .
+ "has state '$savestate', requires '$state'");
+ return 0;
+ }
+ if (in_array(strtolower($info['name']), $this->_toDownload) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ return false;
+ }
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ // check to see if we can install the specific version required
+ if ($info['rel'] == 'eq') {
+ return $info['name'] . '-' . $info['version'];
+ }
+ // skip upgrade check for packages we don't have installed
+ return $info['name'];
+ }
+ // }}}
+
+ // {{{ see if a dependency must be upgraded
+ $inst_version = $this->_registry->packageInfo($info['name'], 'version');
+ if (!isset($info['version'])) {
+ // this is a rel='has' dependency, check against latest
+ if (version_compare($release_version, $inst_version, 'le')) {
+ return false;
+ } else {
+ return $info['name'];
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ return false;
+ }
+ return $info['name'];
+ }
+
+ // }}}
+ // {{{ _downloadCallback()
+
+ function _downloadCallback($msg, $params = null)
+ {
+ switch ($msg) {
+ case 'saveas':
+ $this->log(1, "downloading $params ...");
+ break;
+ case 'done':
+ $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
+ break;
+ case 'bytesread':
+ static $bytes;
+ if (empty($bytes)) {
+ $bytes = 0;
+ }
+ if (!($bytes % 10240)) {
+ $this->log(1, '.', false);
+ }
+ $bytes += $params;
+ break;
+ case 'start':
+ $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
+ break;
+ }
+ if (method_exists($this->ui, '_downloadCallback'))
+ $this->ui->_downloadCallback($msg, $params);
+ }
+
+ // }}}
+ // {{{ _prependPath($path, $prepend)
+
+ function _prependPath($path, $prepend)
+ {
+ if (strlen($prepend) > 0) {
+ if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+ $path = $prepend . substr($path, 2);
+ } else {
+ $path = $prepend . $path;
+ }
+ }
+ return $path;
+ }
+ // }}}
+ // {{{ pushError($errmsg, $code)
+
+ /**
+ * @param string
+ * @param integer
+ */
+ function pushError($errmsg, $code = -1)
+ {
+ array_push($this->_errorStack, array($errmsg, $code));
+ }
+
+ // }}}
+ // {{{ getErrorMsgs()
+
+ function getErrorMsgs()
+ {
+ $msgs = array();
+ $errs = $this->_errorStack;
+ foreach ($errs as $err) {
+ $msgs[] = $err[0];
+ }
+ $this->_errorStack = array();
+ return $msgs;
+ }
+
+ // }}}
+}
+// }}}
+
+?>