Редакция 69 | Только различия | Учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS
Редакция 69 | Редакция 87 | ||
---|---|---|---|
1 | <?php
|
1 | <?php
|
2 | /**
|
2 | /**
|
3 | * PEAR_Downloader, the PEAR Installer's download utility class
|
3 | * PEAR_Downloader, the PEAR Installer's download utility class
|
4 | *
|
4 | *
|
5 | * PHP versions 4 and 5
|
5 | * PHP versions 4 and 5
|
6 | *
|
6 | *
|
7 | * LICENSE: This source file is subject to version 3.0 of the PHP license
|
7 | * LICENSE: This source file is subject to version 3.0 of the PHP license
|
8 | * that is available through the world-wide-web at the following URI:
|
8 | * that is available through the world-wide-web at the following URI:
|
9 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
9 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
10 | * the PHP License and are unable to obtain it through the web, please
|
10 | * the PHP License and are unable to obtain it through the web, please
|
11 | * send a note to license@php.net so we can mail you a copy immediately.
|
11 | * send a note to license@php.net so we can mail you a copy immediately.
|
12 | *
|
12 | *
|
13 | * @category pear
|
13 | * @category pear
|
14 | * @package PEAR
|
14 | * @package PEAR
|
15 | * @author Greg Beaver <cellog@php.net>
|
15 | * @author Greg Beaver <cellog@php.net>
|
16 | * @author Stig Bakken <ssb@php.net>
|
16 | * @author Stig Bakken <ssb@php.net>
|
17 | * @author Tomas V. V. Cox <cox@idecnet.com>
|
17 | * @author Tomas V. V. Cox <cox@idecnet.com>
|
18 | * @author Martin Jansen <mj@php.net>
|
18 | * @author Martin Jansen <mj@php.net>
|
19 | * @copyright 1997-2008 The PHP Group
|
19 | * @copyright 1997-2008 The PHP Group
|
20 | * @license http://www.php.net/license/3_0.txt PHP License 3.0
|
20 | * @license http://www.php.net/license/3_0.txt PHP License 3.0
|
21 | * @version CVS: $Id: Downloader.php,v 1.138 2008/04/11 01:16:40 dufuz Exp $
|
21 | * @version CVS: $Id: Downloader.php,v 1.138 2008/04/11 01:16:40 dufuz Exp $
|
22 | * @link http://pear.php.net/package/PEAR
|
22 | * @link http://pear.php.net/package/PEAR
|
23 | * @since File available since Release 1.3.0
|
23 | * @since File available since Release 1.3.0
|
24 | */
|
24 | */
|
25 | 25 | ||
26 | /**
|
26 | /**
|
27 | * Needed for constants, extending
|
27 | * Needed for constants, extending
|
28 | */
|
28 | */
|
29 | require_once 'PEAR/Common.php'; |
29 | require_once 'PEAR/Common.php'; |
30 | 30 | ||
31 | define('PEAR_INSTALLER_OK', 1); |
31 | define('PEAR_INSTALLER_OK', 1); |
32 | define('PEAR_INSTALLER_FAILED', 0); |
32 | define('PEAR_INSTALLER_FAILED', 0); |
33 | define('PEAR_INSTALLER_SKIPPED', -1); |
33 | define('PEAR_INSTALLER_SKIPPED', -1); |
34 | define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); |
34 | define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); |
35 | 35 | ||
36 | /**
|
36 | /**
|
37 | * Administration class used to download anything from the internet (PEAR Packages,
|
37 | * Administration class used to download anything from the internet (PEAR Packages,
|
38 | * static URLs, xml files)
|
38 | * static URLs, xml files)
|
39 | *
|
39 | *
|
40 | * @category pear
|
40 | * @category pear
|
41 | * @package PEAR
|
41 | * @package PEAR
|
42 | * @author Greg Beaver <cellog@php.net>
|
42 | * @author Greg Beaver <cellog@php.net>
|
43 | * @author Stig Bakken <ssb@php.net>
|
43 | * @author Stig Bakken <ssb@php.net>
|
44 | * @author Tomas V. V. Cox <cox@idecnet.com>
|
44 | * @author Tomas V. V. Cox <cox@idecnet.com>
|
45 | * @author Martin Jansen <mj@php.net>
|
45 | * @author Martin Jansen <mj@php.net>
|
46 | * @copyright 1997-2008 The PHP Group
|
46 | * @copyright 1997-2008 The PHP Group
|
47 | * @license http://www.php.net/license/3_0.txt PHP License 3.0
|
47 | * @license http://www.php.net/license/3_0.txt PHP License 3.0
|
48 | * @version Release: 1.7.2
|
48 | * @version Release: 1.7.2
|
49 | * @link http://pear.php.net/package/PEAR
|
49 | * @link http://pear.php.net/package/PEAR
|
50 | * @since Class available since Release 1.3.0
|
50 | * @since Class available since Release 1.3.0
|
51 | */
|
51 | */
|
52 | class PEAR_Downloader extends PEAR_Common |
52 | class PEAR_Downloader extends PEAR_Common |
53 | {
|
53 | {
|
54 | /**
|
54 | /**
|
55 | * @var PEAR_Registry
|
55 | * @var PEAR_Registry
|
56 | * @access private
|
56 | * @access private
|
57 | */
|
57 | */
|
58 | var $_registry; |
58 | var $_registry; |
59 | 59 | ||
60 | /**
|
60 | /**
|
61 | * @var PEAR_Remote
|
61 | * @var PEAR_Remote
|
62 | * @access private
|
62 | * @access private
|
63 | */
|
63 | */
|
64 | var $_remote; |
64 | var $_remote; |
65 | 65 | ||
66 | /**
|
66 | /**
|
67 | * Preferred Installation State (snapshot, devel, alpha, beta, stable)
|
67 | * Preferred Installation State (snapshot, devel, alpha, beta, stable)
|
68 | * @var string|null
|
68 | * @var string|null
|
69 | * @access private
|
69 | * @access private
|
70 | */
|
70 | */
|
71 | var $_preferredState; |
71 | var $_preferredState; |
72 | 72 | ||
73 | /**
|
73 | /**
|
74 | * Options from command-line passed to Install.
|
74 | * Options from command-line passed to Install.
|
75 | *
|
75 | *
|
76 | * Recognized options:<br />
|
76 | * Recognized options:<br />
|
77 | * - onlyreqdeps : install all required dependencies as well
|
77 | * - onlyreqdeps : install all required dependencies as well
|
78 | * - alldeps : install all dependencies, including optional
|
78 | * - alldeps : install all dependencies, including optional
|
79 | * - installroot : base relative path to install files in
|
79 | * - installroot : base relative path to install files in
|
80 | * - force : force a download even if warnings would prevent it
|
80 | * - force : force a download even if warnings would prevent it
|
81 | * - nocompress : download uncompressed tarballs
|
81 | * - nocompress : download uncompressed tarballs
|
82 | * @see PEAR_Command_Install
|
82 | * @see PEAR_Command_Install
|
83 | * @access private
|
83 | * @access private
|
84 | * @var array
|
84 | * @var array
|
85 | */
|
85 | */
|
86 | var $_options; |
86 | var $_options; |
87 | 87 | ||
88 | /**
|
88 | /**
|
89 | * Downloaded Packages after a call to download().
|
89 | * Downloaded Packages after a call to download().
|
90 | *
|
90 | *
|
91 | * Format of each entry:
|
91 | * Format of each entry:
|
92 | *
|
92 | *
|
93 | * <code>
|
93 | * <code>
|
94 | * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
|
94 | * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
|
95 | * 'info' => array() // parsed package.xml
|
95 | * 'info' => array() // parsed package.xml
|
96 | * );
|
96 | * );
|
97 | * </code>
|
97 | * </code>
|
98 | * @access private
|
98 | * @access private
|
99 | * @var array
|
99 | * @var array
|
100 | */
|
100 | */
|
101 | var $_downloadedPackages = array(); |
101 | var $_downloadedPackages = array(); |
102 | 102 | ||
103 | /**
|
103 | /**
|
104 | * Packages slated for download.
|
104 | * Packages slated for download.
|
105 | *
|
105 | *
|
106 | * This is used to prevent downloading a package more than once should it be a dependency
|
106 | * This is used to prevent downloading a package more than once should it be a dependency
|
107 | * for two packages to be installed.
|
107 | * for two packages to be installed.
|
108 | * Format of each entry:
|
108 | * Format of each entry:
|
109 | *
|
109 | *
|
110 | * <pre>
|
110 | * <pre>
|
111 | * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
|
111 | * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
|
112 | * );
|
112 | * );
|
113 | * </pre>
|
113 | * </pre>
|
114 | * @access private
|
114 | * @access private
|
115 | * @var array
|
115 | * @var array
|
116 | */
|
116 | */
|
117 | var $_toDownload = array(); |
117 | var $_toDownload = array(); |
118 | 118 | ||
119 | /**
|
119 | /**
|
120 | * Array of every package installed, with names lower-cased.
|
120 | * Array of every package installed, with names lower-cased.
|
121 | *
|
121 | *
|
122 | * Format:
|
122 | * Format:
|
123 | * <code>
|
123 | * <code>
|
124 | * array('package1' => 0, 'package2' => 1, );
|
124 | * array('package1' => 0, 'package2' => 1, );
|
125 | * </code>
|
125 | * </code>
|
126 | * @var array
|
126 | * @var array
|
127 | */
|
127 | */
|
128 | var $_installed = array(); |
128 | var $_installed = array(); |
129 | 129 | ||
130 | /**
|
130 | /**
|
131 | * @var array
|
131 | * @var array
|
132 | * @access private
|
132 | * @access private
|
133 | */
|
133 | */
|
134 | var $_errorStack = array(); |
134 | var $_errorStack = array(); |
135 | 135 | ||
136 | /**
|
136 | /**
|
137 | * @var boolean
|
137 | * @var boolean
|
138 | * @access private
|
138 | * @access private
|
139 | */
|
139 | */
|
140 | var $_internalDownload = false; |
140 | var $_internalDownload = false; |
141 | 141 | ||
142 | /**
|
142 | /**
|
143 | * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
|
143 | * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
|
144 | * @var array
|
144 | * @var array
|
145 | * @access private
|
145 | * @access private
|
146 | */
|
146 | */
|
147 | var $_packageSortTree; |
147 | var $_packageSortTree; |
148 | 148 | ||
149 | /**
|
149 | /**
|
150 | * Temporary directory, or configuration value where downloads will occur
|
150 | * Temporary directory, or configuration value where downloads will occur
|
151 | * @var string
|
151 | * @var string
|
152 | */
|
152 | */
|
153 | var $_downloadDir; |
153 | var $_downloadDir; |
154 | // {{{ PEAR_Downloader()
|
154 | // {{{ PEAR_Downloader()
|
155 | 155 | ||
156 | /**
|
156 | /**
|
157 | * @param PEAR_Frontend_*
|
157 | * @param PEAR_Frontend_*
|
158 | * @param array
|
158 | * @param array
|
159 | * @param PEAR_Config
|
159 | * @param PEAR_Config
|
160 | */
|
160 | */
|
161 | function PEAR_Downloader(&$ui, $options, &$config) |
161 | function PEAR_Downloader(&$ui, $options, &$config) |
162 | {
|
162 | {
|
163 | parent::PEAR_Common(); |
163 | parent::PEAR_Common(); |
164 | $this->_options = $options; |
164 | $this->_options = $options; |
165 | $this->config = &$config; |
165 | $this->config = &$config; |
166 | $this->_preferredState = $this->config->get('preferred_state'); |
166 | $this->_preferredState = $this->config->get('preferred_state'); |
167 | $this->ui = &$ui; |
167 | $this->ui = &$ui; |
168 | if (!$this->_preferredState) { |
168 | if (!$this->_preferredState) { |
169 | // don't inadvertantly use a non-set preferred_state
|
169 | // don't inadvertantly use a non-set preferred_state
|
170 | $this->_preferredState = null; |
170 | $this->_preferredState = null; |
171 | }
|
171 | }
|
172 | 172 | ||
173 | if (isset($this->_options['installroot'])) { |
173 | if (isset($this->_options['installroot'])) { |
174 | $this->config->setInstallRoot($this->_options['installroot']); |
174 | $this->config->setInstallRoot($this->_options['installroot']); |
175 | }
|
175 | }
|
176 | $this->_registry = &$config->getRegistry(); |
176 | $this->_registry = &$config->getRegistry(); |
177 | $this->_remote = &$config->getRemote(); |
177 | $this->_remote = &$config->getRemote(); |
178 | 178 | ||
179 | if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { |
179 | if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { |
180 | $this->_installed = $this->_registry->listAllPackages(); |
180 | $this->_installed = $this->_registry->listAllPackages(); |
181 | foreach ($this->_installed as $key => $unused) { |
181 | foreach ($this->_installed as $key => $unused) { |
182 | if (!count($unused)) { |
182 | if (!count($unused)) { |
183 | continue; |
183 | continue; |
184 | }
|
184 | }
|
185 | $strtolower = create_function('$a','return strtolower($a);'); |
185 | $strtolower = create_function('$a','return strtolower($a);'); |
186 | array_walk($this->_installed[$key], $strtolower); |
186 | array_walk($this->_installed[$key], $strtolower); |
187 | }
|
187 | }
|
188 | }
|
188 | }
|
189 | }
|
189 | }
|
190 | 190 | ||
191 | /**
|
191 | /**
|
192 | * Attempt to discover a channel's remote capabilities from
|
192 | * Attempt to discover a channel's remote capabilities from
|
193 | * its server name
|
193 | * its server name
|
194 | * @param string
|
194 | * @param string
|
195 | * @return boolean
|
195 | * @return boolean
|
196 | */
|
196 | */
|
197 | function discover($channel) |
197 | function discover($channel) |
198 | {
|
198 | {
|
199 | $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); |
199 | $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); |
200 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
200 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
201 | $callback = $this->ui ? array(&$this, '_downloadCallback') : null; |
201 | $callback = $this->ui ? array(&$this, '_downloadCallback') : null; |
202 | if (!class_exists('System')) { |
202 | if (!class_exists('System')) { |
203 | require_once 'System.php'; |
203 | require_once 'System.php'; |
204 | }
|
204 | }
|
205 | $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, |
205 | $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, |
206 | System::mktemp(array('-d')), $callback, false); |
206 | System::mktemp(array('-d')), $callback, false); |
207 | PEAR::popErrorHandling(); |
207 | PEAR::popErrorHandling(); |
208 | if (PEAR::isError($a)) { |
208 | if (PEAR::isError($a)) { |
209 | return false; |
209 | return false; |
210 | }
|
210 | }
|
211 | list($a, $lastmodified) = $a; |
211 | list($a, $lastmodified) = $a; |
212 | if (!class_exists('PEAR_ChannelFile')) { |
212 | if (!class_exists('PEAR_ChannelFile')) { |
213 | require_once 'PEAR/ChannelFile.php'; |
213 | require_once 'PEAR/ChannelFile.php'; |
214 | }
|
214 | }
|
215 | $b = new PEAR_ChannelFile; |
215 | $b = new PEAR_ChannelFile; |
216 | if ($b->fromXmlFile($a)) { |
216 | if ($b->fromXmlFile($a)) { |
217 | unlink($a); |
217 | unlink($a); |
218 | if ($this->config->get('auto_discover')) { |
218 | if ($this->config->get('auto_discover')) { |
219 | $this->_registry->addChannel($b, $lastmodified); |
219 | $this->_registry->addChannel($b, $lastmodified); |
220 | $alias = $b->getName(); |
220 | $alias = $b->getName(); |
221 | if ($b->getName() == $this->_registry->channelName($b->getAlias())) { |
221 | if ($b->getName() == $this->_registry->channelName($b->getAlias())) { |
222 | $alias = $b->getAlias(); |
222 | $alias = $b->getAlias(); |
223 | }
|
223 | }
|
224 | $this->log(1, 'Auto-discovered channel "' . $channel . |
224 | $this->log(1, 'Auto-discovered channel "' . $channel . |
225 | '", alias "' . $alias . '", adding to registry'); |
225 | '", alias "' . $alias . '", adding to registry'); |
226 | }
|
226 | }
|
227 | return true; |
227 | return true; |
228 | }
|
228 | }
|
229 | unlink($a); |
229 | unlink($a); |
230 | return false; |
230 | return false; |
231 | }
|
231 | }
|
232 | 232 | ||
233 | /**
|
233 | /**
|
234 | * For simpler unit-testing
|
234 | * For simpler unit-testing
|
235 | * @param PEAR_Downloader
|
235 | * @param PEAR_Downloader
|
236 | * @return PEAR_Downloader_Package
|
236 | * @return PEAR_Downloader_Package
|
237 | */
|
237 | */
|
238 | function &newDownloaderPackage(&$t) |
238 | function &newDownloaderPackage(&$t) |
239 | {
|
239 | {
|
240 | if (!class_exists('PEAR_Downloader_Package')) { |
240 | if (!class_exists('PEAR_Downloader_Package')) { |
241 | require_once 'PEAR/Downloader/Package.php'; |
241 | require_once 'PEAR/Downloader/Package.php'; |
242 | }
|
242 | }
|
243 | $a = &new PEAR_Downloader_Package($t); |
243 | $a = &new PEAR_Downloader_Package($t); |
244 | return $a; |
244 | return $a; |
245 | }
|
245 | }
|
246 | 246 | ||
247 | /**
|
247 | /**
|
248 | * For simpler unit-testing
|
248 | * For simpler unit-testing
|
249 | * @param PEAR_Config
|
249 | * @param PEAR_Config
|
250 | * @param array
|
250 | * @param array
|
251 | * @param array
|
251 | * @param array
|
252 | * @param int
|
252 | * @param int
|
253 | */
|
253 | */
|
254 | function &getDependency2Object(&$c, $i, $p, $s) |
254 | function &getDependency2Object(&$c, $i, $p, $s) |
255 | {
|
255 | {
|
256 | if (!class_exists('PEAR_Dependency2')) { |
256 | if (!class_exists('PEAR_Dependency2')) { |
257 | require_once 'PEAR/Dependency2.php'; |
257 | require_once 'PEAR/Dependency2.php'; |
258 | }
|
258 | }
|
259 | $z = &new PEAR_Dependency2($c, $i, $p, $s); |
259 | $z = &new PEAR_Dependency2($c, $i, $p, $s); |
260 | return $z; |
260 | return $z; |
261 | }
|
261 | }
|
262 | 262 | ||
263 | function &download($params) |
263 | function &download($params) |
264 | {
|
264 | {
|
265 | if (!count($params)) { |
265 | if (!count($params)) { |
266 | $a = array(); |
266 | $a = array(); |
267 | return $a; |
267 | return $a; |
268 | }
|
268 | }
|
269 | if (!isset($this->_registry)) { |
269 | if (!isset($this->_registry)) { |
270 | $this->_registry = &$this->config->getRegistry(); |
270 | $this->_registry = &$this->config->getRegistry(); |
271 | }
|
271 | }
|
272 | if (!isset($this->_remote)) { |
272 | if (!isset($this->_remote)) { |
273 | $this->_remote = &$this->config->getRemote(); |
273 | $this->_remote = &$this->config->getRemote(); |
274 | }
|
274 | }
|
275 | $channelschecked = array(); |
275 | $channelschecked = array(); |
276 | // convert all parameters into PEAR_Downloader_Package objects
|
276 | // convert all parameters into PEAR_Downloader_Package objects
|
277 | foreach ($params as $i => $param) { |
277 | foreach ($params as $i => $param) { |
278 | $params[$i] = &$this->newDownloaderPackage($this); |
278 | $params[$i] = &$this->newDownloaderPackage($this); |
279 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
279 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
280 | $err = $params[$i]->initialize($param); |
280 | $err = $params[$i]->initialize($param); |
281 | PEAR::staticPopErrorHandling(); |
281 | PEAR::staticPopErrorHandling(); |
282 | if (!$err) { |
282 | if (!$err) { |
283 | // skip parameters that were missed by preferred_state
|
283 | // skip parameters that were missed by preferred_state
|
284 | continue; |
284 | continue; |
285 | }
|
285 | }
|
286 | if (PEAR::isError($err)) { |
286 | if (PEAR::isError($err)) { |
287 | if (!isset($this->_options['soft'])) { |
287 | if (!isset($this->_options['soft'])) { |
288 | $this->log(0, $err->getMessage()); |
288 | $this->log(0, $err->getMessage()); |
289 | }
|
289 | }
|
290 | $params[$i] = false; |
290 | $params[$i] = false; |
291 | if (is_object($param)) { |
291 | if (is_object($param)) { |
292 | $param = $param->getChannel() . '/' . $param->getPackage(); |
292 | $param = $param->getChannel() . '/' . $param->getPackage(); |
293 | }
|
293 | }
|
294 | $this->pushError('Package "' . $param . '" is not valid', |
294 | $this->pushError('Package "' . $param . '" is not valid', |
295 | PEAR_INSTALLER_SKIPPED); |
295 | PEAR_INSTALLER_SKIPPED); |
296 | } else { |
296 | } else { |
297 | do { |
297 | do { |
298 | if ($params[$i] && $params[$i]->getType() == 'local') { |
298 | if ($params[$i] && $params[$i]->getType() == 'local') { |
299 | // bug #7090
|
299 | // bug #7090
|
300 | // skip channel.xml check for local packages
|
300 | // skip channel.xml check for local packages
|
301 | break; |
301 | break; |
302 | }
|
302 | }
|
303 | if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && |
303 | if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && |
304 | !isset($this->_options['offline'])) { |
304 | !isset($this->_options['offline'])) { |
305 | $channelschecked[$params[$i]->getChannel()] = true; |
305 | $channelschecked[$params[$i]->getChannel()] = true; |
306 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
306 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
307 | if (!class_exists('System')) { |
307 | if (!class_exists('System')) { |
308 | require_once 'System.php'; |
308 | require_once 'System.php'; |
309 | }
|
309 | }
|
310 | $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); |
310 | $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); |
311 | if (PEAR::isError($curchannel)) { |
311 | if (PEAR::isError($curchannel)) { |
312 | PEAR::staticPopErrorHandling(); |
312 | PEAR::staticPopErrorHandling(); |
313 | return $this->raiseError($curchannel); |
313 | return $this->raiseError($curchannel); |
314 | }
|
314 | }
|
315 | if (PEAR::isError($dir = $this->getDownloadDir())) { |
315 | if (PEAR::isError($dir = $this->getDownloadDir())) { |
316 | PEAR::staticPopErrorHandling(); |
316 | PEAR::staticPopErrorHandling(); |
317 | break; |
317 | break; |
318 | }
|
318 | }
|
319 | $mirror = $this->config->get('preferred_mirror', null, |
319 | $mirror = $this->config->get('preferred_mirror', null, |
320 | $params[$i]->getChannel()); |
320 | $params[$i]->getChannel()); |
321 | $a = $this->downloadHttp('http://' . $mirror . |
321 | $a = $this->downloadHttp('http://' . $mirror . |
322 | '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); |
322 | '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); |
323 | 323 | ||
324 | PEAR::staticPopErrorHandling(); |
324 | PEAR::staticPopErrorHandling(); |
325 | if (PEAR::isError($a) || !$a) { |
325 | if (PEAR::isError($a) || !$a) { |
326 | break; |
326 | break; |
327 | }
|
327 | }
|
328 | $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . |
328 | $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . |
329 | 'updated its protocols, use "channel-update ' . $params[$i]->getChannel() . |
329 | 'updated its protocols, use "channel-update ' . $params[$i]->getChannel() . |
330 | '" to update'); |
330 | '" to update'); |
331 | }
|
331 | }
|
332 | } while (false); |
332 | } while (false); |
333 | if ($params[$i] && !isset($this->_options['downloadonly'])) { |
333 | if ($params[$i] && !isset($this->_options['downloadonly'])) { |
334 | if (isset($this->_options['packagingroot'])) { |
334 | if (isset($this->_options['packagingroot'])) { |
335 | $checkdir = $this->_prependPath( |
335 | $checkdir = $this->_prependPath( |
336 | $this->config->get('php_dir', null, $params[$i]->getChannel()), |
336 | $this->config->get('php_dir', null, $params[$i]->getChannel()), |
337 | $this->_options['packagingroot']); |
337 | $this->_options['packagingroot']); |
338 | } else { |
338 | } else { |
339 | $checkdir = $this->config->get('php_dir', |
339 | $checkdir = $this->config->get('php_dir', |
340 | null, $params[$i]->getChannel()); |
340 | null, $params[$i]->getChannel()); |
341 | }
|
341 | }
|
342 | while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { |
342 | while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { |
343 | $checkdir = dirname($checkdir); |
343 | $checkdir = dirname($checkdir); |
344 | }
|
344 | }
|
345 | if ($checkdir == '.') { |
345 | if ($checkdir == '.') { |
346 | $checkdir = '/'; |
346 | $checkdir = '/'; |
347 | }
|
347 | }
|
348 | if (!is_writeable($checkdir)) { |
348 | if (!is_writeable($checkdir)) { |
349 | return PEAR::raiseError('Cannot install, php_dir for channel "' . |
349 | return PEAR::raiseError('Cannot install, php_dir for channel "' . |
350 | $params[$i]->getChannel() . '" is not writeable by the current user'); |
350 | $params[$i]->getChannel() . '" is not writeable by the current user'); |
351 | }
|
351 | }
|
352 | }
|
352 | }
|
353 | }
|
353 | }
|
354 | }
|
354 | }
|
355 | unset($channelschecked); |
355 | unset($channelschecked); |
356 | PEAR_Downloader_Package::removeDuplicates($params); |
356 | PEAR_Downloader_Package::removeDuplicates($params); |
357 | if (!count($params)) { |
357 | if (!count($params)) { |
358 | $a = array(); |
358 | $a = array(); |
359 | return $a; |
359 | return $a; |
360 | }
|
360 | }
|
361 | if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { |
361 | if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { |
362 | $reverify = true; |
362 | $reverify = true; |
363 | while ($reverify) { |
363 | while ($reverify) { |
364 | $reverify = false; |
364 | $reverify = false; |
365 | foreach ($params as $i => $param) { |
365 | foreach ($params as $i => $param) { |
366 | //PHP Bug 40768 / PEAR Bug #10944
|
366 | //PHP Bug 40768 / PEAR Bug #10944
|
367 | //Nested foreaches fail in PHP 5.2.1
|
367 | //Nested foreaches fail in PHP 5.2.1
|
368 | key($params); |
368 | key($params); |
369 | $ret = $params[$i]->detectDependencies($params); |
369 | $ret = $params[$i]->detectDependencies($params); |
370 | if (PEAR::isError($ret)) { |
370 | if (PEAR::isError($ret)) { |
371 | $reverify = true; |
371 | $reverify = true; |
372 | $params[$i] = false; |
372 | $params[$i] = false; |
373 | PEAR_Downloader_Package::removeDuplicates($params); |
373 | PEAR_Downloader_Package::removeDuplicates($params); |
374 | if (!isset($this->_options['soft'])) { |
374 | if (!isset($this->_options['soft'])) { |
375 | $this->log(0, $ret->getMessage()); |
375 | $this->log(0, $ret->getMessage()); |
376 | }
|
376 | }
|
377 | continue 2; |
377 | continue 2; |
378 | }
|
378 | }
|
379 | }
|
379 | }
|
380 | }
|
380 | }
|
381 | }
|
381 | }
|
382 | if (isset($this->_options['offline'])) { |
382 | if (isset($this->_options['offline'])) { |
383 | $this->log(3, 'Skipping dependency download check, --offline specified'); |
383 | $this->log(3, 'Skipping dependency download check, --offline specified'); |
384 | }
|
384 | }
|
385 | if (!count($params)) { |
385 | if (!count($params)) { |
386 | $a = array(); |
386 | $a = array(); |
387 | return $a; |
387 | return $a; |
388 | }
|
388 | }
|
389 | while (PEAR_Downloader_Package::mergeDependencies($params)); |
389 | while (PEAR_Downloader_Package::mergeDependencies($params)); |
390 | PEAR_Downloader_Package::removeDuplicates($params, true); |
390 | PEAR_Downloader_Package::removeDuplicates($params, true); |
391 | $errorparams = array(); |
391 | $errorparams = array(); |
392 | if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) { |
392 | if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) { |
393 | if (count($errorparams)) { |
393 | if (count($errorparams)) { |
394 | foreach ($errorparams as $param) { |
394 | foreach ($errorparams as $param) { |
395 | $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage()); |
395 | $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage()); |
396 | $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED); |
396 | $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED); |
397 | }
|
397 | }
|
398 | $a = array(); |
398 | $a = array(); |
399 | return $a; |
399 | return $a; |
400 | }
|
400 | }
|
401 | }
|
401 | }
|
402 | PEAR_Downloader_Package::removeInstalled($params); |
402 | PEAR_Downloader_Package::removeInstalled($params); |
403 | if (!count($params)) { |
403 | if (!count($params)) { |
404 | $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); |
404 | $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); |
405 | $a = array(); |
405 | $a = array(); |
406 | return $a; |
406 | return $a; |
407 | }
|
407 | }
|
408 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
408 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
409 | $err = $this->analyzeDependencies($params); |
409 | $err = $this->analyzeDependencies($params); |
410 | PEAR::popErrorHandling(); |
410 | PEAR::popErrorHandling(); |
411 | if (!count($params)) { |
411 | if (!count($params)) { |
412 | $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); |
412 | $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); |
413 | $a = array(); |
413 | $a = array(); |
414 | return $a; |
414 | return $a; |
415 | }
|
415 | }
|
416 | $ret = array(); |
416 | $ret = array(); |
417 | $newparams = array(); |
417 | $newparams = array(); |
418 | if (isset($this->_options['pretend'])) { |
418 | if (isset($this->_options['pretend'])) { |
419 | return $params; |
419 | return $params; |
420 | }
|
420 | }
|
421 | $somefailed = false; |
421 | $somefailed = false; |
422 | foreach ($params as $i => $package) { |
422 | foreach ($params as $i => $package) { |
423 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
423 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
424 | $pf = &$params[$i]->download(); |
424 | $pf = &$params[$i]->download(); |
425 | PEAR::staticPopErrorHandling(); |
425 | PEAR::staticPopErrorHandling(); |
426 | if (PEAR::isError($pf)) { |
426 | if (PEAR::isError($pf)) { |
427 | if (!isset($this->_options['soft'])) { |
427 | if (!isset($this->_options['soft'])) { |
428 | $this->log(1, $pf->getMessage()); |
428 | $this->log(1, $pf->getMessage()); |
429 | $this->log(0, 'Error: cannot download "' . |
429 | $this->log(0, 'Error: cannot download "' . |
430 | $this->_registry->parsedPackageNameToString($package->getParsedPackage(), |
430 | $this->_registry->parsedPackageNameToString($package->getParsedPackage(), |
431 | true) . |
431 | true) . |
432 | '"'); |
432 | '"'); |
433 | }
|
433 | }
|
434 | $somefailed = true; |
434 | $somefailed = true; |
435 | continue; |
435 | continue; |
436 | }
|
436 | }
|
437 | $newparams[] = &$params[$i]; |
437 | $newparams[] = &$params[$i]; |
438 | $ret[] = array('file' => $pf->getArchiveFile(), |
438 | $ret[] = array('file' => $pf->getArchiveFile(), |
439 | 'info' => &$pf, |
439 | 'info' => &$pf, |
440 | 'pkg' => $pf->getPackage()); |
440 | 'pkg' => $pf->getPackage()); |
441 | }
|
441 | }
|
442 | if ($somefailed) { |
442 | if ($somefailed) { |
443 | // remove params that did not download successfully
|
443 | // remove params that did not download successfully
|
444 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
444 | PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
445 | $err = $this->analyzeDependencies($newparams, true); |
445 | $err = $this->analyzeDependencies($newparams, true); |
446 | PEAR::popErrorHandling(); |
446 | PEAR::popErrorHandling(); |
447 | if (!count($newparams)) { |
447 | if (!count($newparams)) { |
448 | $this->pushError('Download failed', PEAR_INSTALLER_FAILED); |
448 | $this->pushError('Download failed', PEAR_INSTALLER_FAILED); |
449 | $a = array(); |
449 | $a = array(); |
450 | return $a; |
450 | return $a; |
451 | }
|
451 | }
|
452 | }
|
452 | }
|
453 | $this->_downloadedPackages = $ret; |
453 | $this->_downloadedPackages = $ret; |
454 | return $newparams; |
454 | return $newparams; |
455 | }
|
455 | }
|
456 | 456 | ||
457 | /**
|
457 | /**
|
458 | * @param array all packages to be installed
|
458 | * @param array all packages to be installed
|
459 | */
|
459 | */
|
460 | function analyzeDependencies(&$params, $force = false) |
460 | function analyzeDependencies(&$params, $force = false) |
461 | {
|
461 | {
|
462 | $hasfailed = $failed = false; |
462 | $hasfailed = $failed = false; |
463 | if (isset($this->_options['downloadonly'])) { |
463 | if (isset($this->_options['downloadonly'])) { |
464 | return; |
464 | return; |
465 | }
|
465 | }
|
466 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
466 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
467 | $redo = true; |
467 | $redo = true; |
468 | $reset = false; |
468 | $reset = false; |
469 | while ($redo) { |
469 | while ($redo) { |
470 | $redo = false; |
470 | $redo = false; |
471 | foreach ($params as $i => $param) { |
471 | foreach ($params as $i => $param) { |
472 | $deps = $param->getDeps(); |
472 | $deps = $param->getDeps(); |
473 | if (!$deps) { |
473 | if (!$deps) { |
474 | $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), |
474 | $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), |
475 | $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); |
475 | $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); |
476 | if ($param->getType() == 'xmlrpc') { |
476 | if ($param->getType() == 'xmlrpc') { |
477 | $send = $param->getDownloadURL(); |
477 | $send = $param->getDownloadURL(); |
478 | } else { |
478 | } else { |
479 | $send = $param->getPackageFile(); |
479 | $send = $param->getPackageFile(); |
480 | }
|
480 | }
|
481 | $installcheck = $depchecker->validatePackage($send, $this, $params); |
481 | $installcheck = $depchecker->validatePackage($send, $this, $params); |
482 | if (PEAR::isError($installcheck)) { |
482 | if (PEAR::isError($installcheck)) { |
483 | if (!isset($this->_options['soft'])) { |
483 | if (!isset($this->_options['soft'])) { |
484 | $this->log(0, $installcheck->getMessage()); |
484 | $this->log(0, $installcheck->getMessage()); |
485 | }
|
485 | }
|
486 | $hasfailed = true; |
486 | $hasfailed = true; |
487 | $params[$i] = false; |
487 | $params[$i] = false; |
488 | $reset = true; |
488 | $reset = true; |
489 | $redo = true; |
489 | $redo = true; |
490 | $failed = false; |
490 | $failed = false; |
491 | PEAR_Downloader_Package::removeDuplicates($params); |
491 | PEAR_Downloader_Package::removeDuplicates($params); |
492 | continue 2; |
492 | continue 2; |
493 | }
|
493 | }
|
494 | continue; |
494 | continue; |
495 | }
|
495 | }
|
496 | if (!$reset && $param->alreadyValidated() && !$force) { |
496 | if (!$reset && $param->alreadyValidated() && !$force) { |
497 | continue; |
497 | continue; |
498 | }
|
498 | }
|
499 | if (count($deps)) { |
499 | if (count($deps)) { |
500 | $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), |
500 | $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), |
501 | $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); |
501 | $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); |
502 | if ($param->getType() == 'xmlrpc') { |
502 | if ($param->getType() == 'xmlrpc') { |
503 | $send = $param->getDownloadURL(); |
503 | $send = $param->getDownloadURL(); |
504 | } else { |
504 | } else { |
505 | $send = $param->getPackageFile(); |
505 | $send = $param->getPackageFile(); |
506 | }
|
506 | }
|
507 | $installcheck = $depchecker->validatePackage($send, $this, $params); |
507 | $installcheck = $depchecker->validatePackage($send, $this, $params); |
508 | if (PEAR::isError($installcheck)) { |
508 | if (PEAR::isError($installcheck)) { |
509 | if (!isset($this->_options['soft'])) { |
509 | if (!isset($this->_options['soft'])) { |
510 | $this->log(0, $installcheck->getMessage()); |
510 | $this->log(0, $installcheck->getMessage()); |
511 | }
|
511 | }
|
512 | $hasfailed = true; |
512 | $hasfailed = true; |
513 | $params[$i] = false; |
513 | $params[$i] = false; |
514 | $reset = true; |
514 | $reset = true; |
515 | $redo = true; |
515 | $redo = true; |
516 | $failed = false; |
516 | $failed = false; |
517 | PEAR_Downloader_Package::removeDuplicates($params); |
517 | PEAR_Downloader_Package::removeDuplicates($params); |
518 | continue 2; |
518 | continue 2; |
519 | }
|
519 | }
|
520 | $failed = false; |
520 | $failed = false; |
521 | if (isset($deps['required'])) { |
521 | if (isset($deps['required'])) { |
522 | foreach ($deps['required'] as $type => $dep) { |
522 | foreach ($deps['required'] as $type => $dep) { |
523 | // note: Dependency2 will never return a PEAR_Error if ignore-errors
|
523 | // note: Dependency2 will never return a PEAR_Error if ignore-errors
|
524 | // is specified, so soft is needed to turn off logging
|
524 | // is specified, so soft is needed to turn off logging
|
525 | if (!isset($dep[0])) { |
525 | if (!isset($dep[0])) { |
526 | if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, |
526 | if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, |
527 | true, $params))) { |
527 | true, $params))) { |
528 | $failed = true; |
528 | $failed = true; |
529 | if (!isset($this->_options['soft'])) { |
529 | if (!isset($this->_options['soft'])) { |
530 | $this->log(0, $e->getMessage()); |
530 | $this->log(0, $e->getMessage()); |
531 | }
|
531 | }
|
532 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
532 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
533 | if (!isset($this->_options['soft'])) { |
533 | if (!isset($this->_options['soft'])) { |
534 | $this->log(0, $e[0]); |
534 | $this->log(0, $e[0]); |
535 | }
|
535 | }
|
536 | }
|
536 | }
|
537 | } else { |
537 | } else { |
538 | foreach ($dep as $d) { |
538 | foreach ($dep as $d) { |
539 | if (PEAR::isError($e = |
539 | if (PEAR::isError($e = |
540 | $depchecker->{"validate{$type}Dependency"}($d, |
540 | $depchecker->{"validate{$type}Dependency"}($d, |
541 | true, $params))) { |
541 | true, $params))) { |
542 | $failed = true; |
542 | $failed = true; |
543 | if (!isset($this->_options['soft'])) { |
543 | if (!isset($this->_options['soft'])) { |
544 | $this->log(0, $e->getMessage()); |
544 | $this->log(0, $e->getMessage()); |
545 | }
|
545 | }
|
546 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
546 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
547 | if (!isset($this->_options['soft'])) { |
547 | if (!isset($this->_options['soft'])) { |
548 | $this->log(0, $e[0]); |
548 | $this->log(0, $e[0]); |
549 | }
|
549 | }
|
550 | }
|
550 | }
|
551 | }
|
551 | }
|
552 | }
|
552 | }
|
553 | }
|
553 | }
|
554 | if (isset($deps['optional'])) { |
554 | if (isset($deps['optional'])) { |
555 | foreach ($deps['optional'] as $type => $dep) { |
555 | foreach ($deps['optional'] as $type => $dep) { |
556 | if (!isset($dep[0])) { |
556 | if (!isset($dep[0])) { |
557 | if (PEAR::isError($e = |
557 | if (PEAR::isError($e = |
558 | $depchecker->{"validate{$type}Dependency"}($dep, |
558 | $depchecker->{"validate{$type}Dependency"}($dep, |
559 | false, $params))) { |
559 | false, $params))) { |
560 | $failed = true; |
560 | $failed = true; |
561 | if (!isset($this->_options['soft'])) { |
561 | if (!isset($this->_options['soft'])) { |
562 | $this->log(0, $e->getMessage()); |
562 | $this->log(0, $e->getMessage()); |
563 | }
|
563 | }
|
564 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
564 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
565 | if (!isset($this->_options['soft'])) { |
565 | if (!isset($this->_options['soft'])) { |
566 | $this->log(0, $e[0]); |
566 | $this->log(0, $e[0]); |
567 | }
|
567 | }
|
568 | }
|
568 | }
|
569 | } else { |
569 | } else { |
570 | foreach ($dep as $d) { |
570 | foreach ($dep as $d) { |
571 | if (PEAR::isError($e = |
571 | if (PEAR::isError($e = |
572 | $depchecker->{"validate{$type}Dependency"}($d, |
572 | $depchecker->{"validate{$type}Dependency"}($d, |
573 | false, $params))) { |
573 | false, $params))) { |
574 | $failed = true; |
574 | $failed = true; |
575 | if (!isset($this->_options['soft'])) { |
575 | if (!isset($this->_options['soft'])) { |
576 | $this->log(0, $e->getMessage()); |
576 | $this->log(0, $e->getMessage()); |
577 | }
|
577 | }
|
578 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
578 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
579 | if (!isset($this->_options['soft'])) { |
579 | if (!isset($this->_options['soft'])) { |
580 | $this->log(0, $e[0]); |
580 | $this->log(0, $e[0]); |
581 | }
|
581 | }
|
582 | }
|
582 | }
|
583 | }
|
583 | }
|
584 | }
|
584 | }
|
585 | }
|
585 | }
|
586 | }
|
586 | }
|
587 | $groupname = $param->getGroup(); |
587 | $groupname = $param->getGroup(); |
588 | if (isset($deps['group']) && $groupname) { |
588 | if (isset($deps['group']) && $groupname) { |
589 | if (!isset($deps['group'][0])) { |
589 | if (!isset($deps['group'][0])) { |
590 | $deps['group'] = array($deps['group']); |
590 | $deps['group'] = array($deps['group']); |
591 | }
|
591 | }
|
592 | $found = false; |
592 | $found = false; |
593 | foreach ($deps['group'] as $group) { |
593 | foreach ($deps['group'] as $group) { |
594 | if ($group['attribs']['name'] == $groupname) { |
594 | if ($group['attribs']['name'] == $groupname) { |
595 | $found = true; |
595 | $found = true; |
596 | break; |
596 | break; |
597 | }
|
597 | }
|
598 | }
|
598 | }
|
599 | if ($found) { |
599 | if ($found) { |
600 | unset($group['attribs']); |
600 | unset($group['attribs']); |
601 | foreach ($group as $type => $dep) { |
601 | foreach ($group as $type => $dep) { |
602 | if (!isset($dep[0])) { |
602 | if (!isset($dep[0])) { |
603 | if (PEAR::isError($e = |
603 | if (PEAR::isError($e = |
604 | $depchecker->{"validate{$type}Dependency"}($dep, |
604 | $depchecker->{"validate{$type}Dependency"}($dep, |
605 | false, $params))) { |
605 | false, $params))) { |
606 | $failed = true; |
606 | $failed = true; |
607 | if (!isset($this->_options['soft'])) { |
607 | if (!isset($this->_options['soft'])) { |
608 | $this->log(0, $e->getMessage()); |
608 | $this->log(0, $e->getMessage()); |
609 | }
|
609 | }
|
610 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
610 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
611 | if (!isset($this->_options['soft'])) { |
611 | if (!isset($this->_options['soft'])) { |
612 | $this->log(0, $e[0]); |
612 | $this->log(0, $e[0]); |
613 | }
|
613 | }
|
614 | }
|
614 | }
|
615 | } else { |
615 | } else { |
616 | foreach ($dep as $d) { |
616 | foreach ($dep as $d) { |
617 | if (PEAR::isError($e = |
617 | if (PEAR::isError($e = |
618 | $depchecker->{"validate{$type}Dependency"}($d, |
618 | $depchecker->{"validate{$type}Dependency"}($d, |
619 | false, $params))) { |
619 | false, $params))) { |
620 | $failed = true; |
620 | $failed = true; |
621 | if (!isset($this->_options['soft'])) { |
621 | if (!isset($this->_options['soft'])) { |
622 | $this->log(0, $e->getMessage()); |
622 | $this->log(0, $e->getMessage()); |
623 | }
|
623 | }
|
624 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
624 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
625 | if (!isset($this->_options['soft'])) { |
625 | if (!isset($this->_options['soft'])) { |
626 | $this->log(0, $e[0]); |
626 | $this->log(0, $e[0]); |
627 | }
|
627 | }
|
628 | }
|
628 | }
|
629 | }
|
629 | }
|
630 | }
|
630 | }
|
631 | }
|
631 | }
|
632 | }
|
632 | }
|
633 | }
|
633 | }
|
634 | } else { |
634 | } else { |
635 | foreach ($deps as $dep) { |
635 | foreach ($deps as $dep) { |
636 | if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) { |
636 | if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) { |
637 | $failed = true; |
637 | $failed = true; |
638 | if (!isset($this->_options['soft'])) { |
638 | if (!isset($this->_options['soft'])) { |
639 | $this->log(0, $e->getMessage()); |
639 | $this->log(0, $e->getMessage()); |
640 | }
|
640 | }
|
641 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
641 | } elseif (is_array($e) && !$param->alreadyValidated()) { |
642 | if (!isset($this->_options['soft'])) { |
642 | if (!isset($this->_options['soft'])) { |
643 | $this->log(0, $e[0]); |
643 | $this->log(0, $e[0]); |
644 | }
|
644 | }
|
645 | }
|
645 | }
|
646 | }
|
646 | }
|
647 | }
|
647 | }
|
648 | $params[$i]->setValidated(); |
648 | $params[$i]->setValidated(); |
649 | }
|
649 | }
|
650 | if ($failed) { |
650 | if ($failed) { |
651 | $hasfailed = true; |
651 | $hasfailed = true; |
652 | $params[$i] = false; |
652 | $params[$i] = false; |
653 | $reset = true; |
653 | $reset = true; |
654 | $redo = true; |
654 | $redo = true; |
655 | $failed = false; |
655 | $failed = false; |
656 | PEAR_Downloader_Package::removeDuplicates($params); |
656 | PEAR_Downloader_Package::removeDuplicates($params); |
657 | continue 2; |
657 | continue 2; |
658 | }
|
658 | }
|
659 | }
|
659 | }
|
660 | }
|
660 | }
|
661 | PEAR::staticPopErrorHandling(); |
661 | PEAR::staticPopErrorHandling(); |
662 | if ($hasfailed && (isset($this->_options['ignore-errors']) || |
662 | if ($hasfailed && (isset($this->_options['ignore-errors']) || |
663 | isset($this->_options['nodeps']))) { |
663 | isset($this->_options['nodeps']))) { |
664 | // this is probably not needed, but just in case
|
664 | // this is probably not needed, but just in case
|
665 | if (!isset($this->_options['soft'])) { |
665 | if (!isset($this->_options['soft'])) { |
666 | $this->log(0, 'WARNING: dependencies failed'); |
666 | $this->log(0, 'WARNING: dependencies failed'); |
667 | }
|
667 | }
|
668 | }
|
668 | }
|
669 | }
|
669 | }
|
670 | 670 | ||
671 | /**
|
671 | /**
|
672 | * Retrieve the directory that downloads will happen in
|
672 | * Retrieve the directory that downloads will happen in
|
673 | * @access private
|
673 | * @access private
|
674 | * @return string
|
674 | * @return string
|
675 | */
|
675 | */
|
676 | function getDownloadDir() |
676 | function getDownloadDir() |
677 | {
|
677 | {
|
678 | if (isset($this->_downloadDir)) { |
678 | if (isset($this->_downloadDir)) { |
679 | return $this->_downloadDir; |
679 | return $this->_downloadDir; |
680 | }
|
680 | }
|
681 | $downloaddir = $this->config->get('download_dir'); |
681 | $downloaddir = $this->config->get('download_dir'); |
682 | if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { |
682 | if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { |
683 | if (is_dir($downloaddir) && !is_writable($downloaddir)) { |
683 | if (is_dir($downloaddir) && !is_writable($downloaddir)) { |
684 | $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . |
684 | $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . |
685 | '" is not writeable. Change download_dir config variable to ' . |
685 | '" is not writeable. Change download_dir config variable to ' . |
686 | 'a writeable dir to avoid this warning'); |
686 | 'a writeable dir to avoid this warning'); |
687 | }
|
687 | }
|
688 | if (!class_exists('System')) { |
688 | if (!class_exists('System')) { |
689 | require_once 'System.php'; |
689 | require_once 'System.php'; |
690 | }
|
690 | }
|
691 | if (PEAR::isError($downloaddir = System::mktemp('-d'))) { |
691 | if (PEAR::isError($downloaddir = System::mktemp('-d'))) { |
692 | return $downloaddir; |
692 | return $downloaddir; |
693 | }
|
693 | }
|
694 | $this->log(3, '+ tmp dir created at ' . $downloaddir); |
694 | $this->log(3, '+ tmp dir created at ' . $downloaddir); |
695 | }
|
695 | }
|
696 | if (!is_writable($downloaddir)) { |
696 | if (!is_writable($downloaddir)) { |
697 | if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || |
697 | if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || |
698 | !is_writable($downloaddir)) { |
698 | !is_writable($downloaddir)) { |
699 | return PEAR::raiseError('download directory "' . $downloaddir . |
699 | return PEAR::raiseError('download directory "' . $downloaddir . |
700 | '" is not writeable. Change download_dir config variable to ' . |
700 | '" is not writeable. Change download_dir config variable to ' . |
701 | 'a writeable dir'); |
701 | 'a writeable dir'); |
702 | }
|
702 | }
|
703 | }
|
703 | }
|
704 | return $this->_downloadDir = $downloaddir; |
704 | return $this->_downloadDir = $downloaddir; |
705 | }
|
705 | }
|
706 | 706 | ||
707 | function setDownloadDir($dir) |
707 | function setDownloadDir($dir) |
708 | {
|
708 | {
|
709 | if (!@is_writable($dir)) { |
709 | if (!@is_writable($dir)) { |
710 | if (PEAR::isError(System::mkdir(array('-p', $dir)))) { |
710 | if (PEAR::isError(System::mkdir(array('-p', $dir)))) { |
711 | return PEAR::raiseError('download directory "' . $dir . |
711 | return PEAR::raiseError('download directory "' . $dir . |
712 | '" is not writeable. Change download_dir config variable to ' . |
712 | '" is not writeable. Change download_dir config variable to ' . |
713 | 'a writeable dir'); |
713 | 'a writeable dir'); |
714 | }
|
714 | }
|
715 | }
|
715 | }
|
716 | $this->_downloadDir = $dir; |
716 | $this->_downloadDir = $dir; |
717 | }
|
717 | }
|
718 | 718 | ||
719 | // }}}
|
719 | // }}}
|
720 | // {{{ configSet()
|
720 | // {{{ configSet()
|
721 | function configSet($key, $value, $layer = 'user', $channel = false) |
721 | function configSet($key, $value, $layer = 'user', $channel = false) |
722 | {
|
722 | {
|
723 | $this->config->set($key, $value, $layer, $channel); |
723 | $this->config->set($key, $value, $layer, $channel); |
724 | $this->_preferredState = $this->config->get('preferred_state', null, $channel); |
724 | $this->_preferredState = $this->config->get('preferred_state', null, $channel); |
725 | if (!$this->_preferredState) { |
725 | if (!$this->_preferredState) { |
726 | // don't inadvertantly use a non-set preferred_state
|
726 | // don't inadvertantly use a non-set preferred_state
|
727 | $this->_preferredState = null; |
727 | $this->_preferredState = null; |
728 | }
|
728 | }
|
729 | }
|
729 | }
|
730 | 730 | ||
731 | // }}}
|
731 | // }}}
|
732 | // {{{ setOptions()
|
732 | // {{{ setOptions()
|
733 | function setOptions($options) |
733 | function setOptions($options) |
734 | {
|
734 | {
|
735 | $this->_options = $options; |
735 | $this->_options = $options; |
736 | }
|
736 | }
|
737 | 737 | ||
738 | // }}}
|
738 | // }}}
|
739 | // {{{ setOptions()
|
739 | // {{{ setOptions()
|
740 | function getOptions() |
740 | function getOptions() |
741 | {
|
741 | {
|
742 | return $this->_options; |
742 | return $this->_options; |
743 | }
|
743 | }
|
744 | 744 | ||
745 | // }}}
|
745 | // }}}
|
746 | 746 | ||
747 | /**
|
747 | /**
|
748 | * For simpler unit-testing
|
748 | * For simpler unit-testing
|
749 | * @param PEAR_Config
|
749 | * @param PEAR_Config
|
750 | * @param int
|
750 | * @param int
|
751 | * @param string
|
751 | * @param string
|
752 | */
|
752 | */
|
753 | function &getPackagefileObject(&$c, $d, $t = false) |
753 | function &getPackagefileObject(&$c, $d, $t = false) |
754 | {
|
754 | {
|
755 | if (!class_exists('PEAR_PackageFile')) { |
755 | if (!class_exists('PEAR_PackageFile')) { |
756 | require_once 'PEAR/PackageFile.php'; |
756 | require_once 'PEAR/PackageFile.php'; |
757 | }
|
757 | }
|
758 | $a = &new PEAR_PackageFile($c, $d, $t); |
758 | $a = &new PEAR_PackageFile($c, $d, $t); |
759 | return $a; |
759 | return $a; |
760 | }
|
760 | }
|
761 | 761 | ||
762 | // {{{ _getPackageDownloadUrl()
|
762 | // {{{ _getPackageDownloadUrl()
|
763 | 763 | ||
764 | /**
|
764 | /**
|
765 | * @param array output of {@link parsePackageName()}
|
765 | * @param array output of {@link parsePackageName()}
|
766 | * @access private
|
766 | * @access private
|
767 | */
|
767 | */
|
768 | function _getPackageDownloadUrl($parr) |
768 | function _getPackageDownloadUrl($parr) |
769 | {
|
769 | {
|
770 | $curchannel = $this->config->get('default_channel'); |
770 | $curchannel = $this->config->get('default_channel'); |
771 | $this->configSet('default_channel', $parr['channel']); |
771 | $this->configSet('default_channel', $parr['channel']); |
772 | // getDownloadURL returns an array. On error, it only contains information
|
772 | // getDownloadURL returns an array. On error, it only contains information
|
773 | // on the latest release as array(version, info). On success it contains
|
773 | // on the latest release as array(version, info). On success it contains
|
774 | // array(version, info, download url string)
|
774 | // array(version, info, download url string)
|
775 | $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); |
775 | $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); |
776 | if (!$this->_registry->channelExists($parr['channel'])) { |
776 | if (!$this->_registry->channelExists($parr['channel'])) { |
777 | do { |
777 | do { |
778 | if ($this->config->get('auto_discover')) { |
778 | if ($this->config->get('auto_discover')) { |
779 | if ($this->discover($parr['channel'])) { |
779 | if ($this->discover($parr['channel'])) { |
780 | break; |
780 | break; |
781 | }
|
781 | }
|
782 | }
|
782 | }
|
783 | $this->configSet('default_channel', $curchannel); |
783 | $this->configSet('default_channel', $curchannel); |
784 | return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); |
784 | return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); |
785 | } while (false); |
785 | } while (false); |
786 | }
|
786 | }
|
787 | $chan = &$this->_registry->getChannel($parr['channel']); |
787 | $chan = &$this->_registry->getChannel($parr['channel']); |
788 | if (PEAR::isError($chan)) { |
788 | if (PEAR::isError($chan)) { |
789 | return $chan; |
789 | return $chan; |
790 | }
|
790 | }
|
791 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
791 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
792 | $version = $this->_registry->packageInfo($parr['package'], 'version', |
792 | $version = $this->_registry->packageInfo($parr['package'], 'version', |
793 | $parr['channel']); |
793 | $parr['channel']); |
794 | PEAR::staticPopErrorHandling(); |
794 | PEAR::staticPopErrorHandling(); |
795 | $base2 = false; |
795 | $base2 = false; |
796 | if ($chan->supportsREST($this->config->get('preferred_mirror')) && |
796 | if ($chan->supportsREST($this->config->get('preferred_mirror')) && |
797 | (($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) || |
797 | (($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) || |
798 | ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))))) { |
798 | ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))))) { |
799 | if ($base2) { |
799 | if ($base2) { |
800 | $rest = &$this->config->getREST('1.3', $this->_options); |
800 | $rest = &$this->config->getREST('1.3', $this->_options); |
801 | $base = $base2; |
801 | $base = $base2; |
802 | } else { |
802 | } else { |
803 | $rest = &$this->config->getREST('1.0', $this->_options); |
803 | $rest = &$this->config->getREST('1.0', $this->_options); |
804 | }
|
804 | }
|
805 | if (!isset($parr['version']) && !isset($parr['state']) && $version |
805 | if (!isset($parr['version']) && !isset($parr['state']) && $version |
806 | && !PEAR::isError($version) |
806 | && !PEAR::isError($version) |
807 | && !isset($this->_options['downloadonly'])) { |
807 | && !isset($this->_options['downloadonly'])) { |
808 | $url = $rest->getDownloadURL($base, $parr, $state, $version, $chan->getName()); |
808 | $url = $rest->getDownloadURL($base, $parr, $state, $version, $chan->getName()); |
809 | } else { |
809 | } else { |
810 | $url = $rest->getDownloadURL($base, $parr, $state, false, $chan->getName()); |
810 | $url = $rest->getDownloadURL($base, $parr, $state, false, $chan->getName()); |
811 | }
|
811 | }
|
812 | if (PEAR::isError($url)) { |
812 | if (PEAR::isError($url)) { |
813 | $this->configSet('default_channel', $curchannel); |
813 | $this->configSet('default_channel', $curchannel); |
814 | return $url; |
814 | return $url; |
815 | }
|
815 | }
|
816 | if ($parr['channel'] != $curchannel) { |
816 | if ($parr['channel'] != $curchannel) { |
817 | $this->configSet('default_channel', $curchannel); |
817 | $this->configSet('default_channel', $curchannel); |
818 | }
|
818 | }
|
819 | if (!is_array($url)) { |
819 | if (!is_array($url)) { |
820 | return $url; |
820 | return $url; |
821 | }
|
821 | }
|
822 | $url['raw'] = false; // no checking is necessary for REST |
822 | $url['raw'] = false; // no checking is necessary for REST |
823 | if (!is_array($url['info'])) { |
823 | if (!is_array($url['info'])) { |
824 | return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . |
824 | return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . |
825 | 'this should never happen'); |
825 | 'this should never happen'); |
826 | }
|
826 | }
|
827 | if (!isset($this->_options['force']) && |
827 | if (!isset($this->_options['force']) && |
828 | !isset($this->_options['downloadonly']) && |
828 | !isset($this->_options['downloadonly']) && |
829 | $version && |
829 | $version && |
830 | !PEAR::isError($version) && |
830 | !PEAR::isError($version) && |
831 | !isset($parr['group'])) { |
831 | !isset($parr['group'])) { |
832 | if (version_compare($version, $url['version'], '>=')) { |
832 | if (version_compare($version, $url['version'], '>=')) { |
833 | return PEAR::raiseError($this->_registry->parsedPackageNameToString( |
833 | return PEAR::raiseError($this->_registry->parsedPackageNameToString( |
834 | $parr, true) . ' is already installed and is newer than detected ' . |
834 | $parr, true) . ' is already installed and is newer than detected ' . |
835 | 'release version ' . $url['version'], -976); |
835 | 'release version ' . $url['version'], -976); |
836 | }
|
836 | }
|
837 | }
|
837 | }
|
838 | if (isset($url['info']['required']) || $url['compatible']) { |
838 | if (isset($url['info']['required']) || $url['compatible']) { |
839 | require_once 'PEAR/PackageFile/v2.php'; |
839 | require_once 'PEAR/PackageFile/v2.php'; |
840 | $pf = new PEAR_PackageFile_v2; |
840 | $pf = new PEAR_PackageFile_v2; |
841 | $pf->setRawChannel($parr['channel']); |
841 | $pf->setRawChannel($parr['channel']); |
842 | if ($url['compatible']) { |
842 | if ($url['compatible']) { |
843 | $pf->setRawCompatible($url['compatible']); |
843 | $pf->setRawCompatible($url['compatible']); |
844 | }
|
844 | }
|
845 | } else { |
845 | } else { |
846 | require_once 'PEAR/PackageFile/v1.php'; |
846 | require_once 'PEAR/PackageFile/v1.php'; |
847 | $pf = new PEAR_PackageFile_v1; |
847 | $pf = new PEAR_PackageFile_v1; |
848 | }
|
848 | }
|
849 | $pf->setRawPackage($url['package']); |
849 | $pf->setRawPackage($url['package']); |
850 | $pf->setDeps($url['info']); |
850 | $pf->setDeps($url['info']); |
851 | if ($url['compatible']) { |
851 | if ($url['compatible']) { |
852 | $pf->setCompatible($url['compatible']); |
852 | $pf->setCompatible($url['compatible']); |
853 | }
|
853 | }
|
854 | $pf->setRawState($url['stability']); |
854 | $pf->setRawState($url['stability']); |
855 | $url['info'] = &$pf; |
855 | $url['info'] = &$pf; |
856 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
856 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
857 | $ext = '.tar'; |
857 | $ext = '.tar'; |
858 | } else { |
858 | } else { |
859 | $ext = '.tgz'; |
859 | $ext = '.tgz'; |
860 | }
|
860 | }
|
861 | if (is_array($url)) { |
861 | if (is_array($url)) { |
862 | if (isset($url['url'])) { |
862 | if (isset($url['url'])) { |
863 | $url['url'] .= $ext; |
863 | $url['url'] .= $ext; |
864 | }
|
864 | }
|
865 | }
|
865 | }
|
866 | return $url; |
866 | return $url; |
867 | } elseif ($chan->supports('xmlrpc', 'package.getDownloadURL', false, '1.1')) { |
867 | } elseif ($chan->supports('xmlrpc', 'package.getDownloadURL', false, '1.1')) { |
868 | // don't install with the old version information unless we're doing a plain
|
868 | // don't install with the old version information unless we're doing a plain
|
869 | // vanilla simple installation. If the user says to install a particular
|
869 | // vanilla simple installation. If the user says to install a particular
|
870 | // version or state, ignore the current installed version
|
870 | // version or state, ignore the current installed version
|
871 | if (!isset($parr['version']) && !isset($parr['state']) && $version |
871 | if (!isset($parr['version']) && !isset($parr['state']) && $version |
872 | && !isset($this->_options['downloadonly'])) { |
872 | && !isset($this->_options['downloadonly'])) { |
873 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state, $version); |
873 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state, $version); |
874 | } else { |
874 | } else { |
875 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state); |
875 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state); |
876 | }
|
876 | }
|
877 | } else { |
877 | } else { |
878 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state); |
878 | $url = $this->_remote->call('package.getDownloadURL', $parr, $state); |
879 | }
|
879 | }
|
880 | if (PEAR::isError($url)) { |
880 | if (PEAR::isError($url)) { |
881 | return $url; |
881 | return $url; |
882 | }
|
882 | }
|
883 | if ($parr['channel'] != $curchannel) { |
883 | if ($parr['channel'] != $curchannel) { |
884 | $this->configSet('default_channel', $curchannel); |
884 | $this->configSet('default_channel', $curchannel); |
885 | }
|
885 | }
|
886 | if (isset($url['__PEAR_ERROR_CLASS__'])) { |
886 | if (isset($url['__PEAR_ERROR_CLASS__'])) { |
887 | return PEAR::raiseError($url['message']); |
887 | return PEAR::raiseError($url['message']); |
888 | }
|
888 | }
|
889 | if (!is_array($url)) { |
889 | if (!is_array($url)) { |
890 | return $url; |
890 | return $url; |
891 | }
|
891 | }
|
892 | $url['raw'] = $url['info']; |
892 | $url['raw'] = $url['info']; |
893 | if (isset($this->_options['downloadonly'])) { |
893 | if (isset($this->_options['downloadonly'])) { |
894 | $pkg = &$this->getPackagefileObject($this->config, $this->debug); |
894 | $pkg = &$this->getPackagefileObject($this->config, $this->debug); |
895 | } else { |
895 | } else { |
896 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
896 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
897 | if (PEAR::isError($dir = $this->getDownloadDir())) { |
897 | if (PEAR::isError($dir = $this->getDownloadDir())) { |
898 | PEAR::staticPopErrorHandling(); |
898 | PEAR::staticPopErrorHandling(); |
899 | return $dir; |
899 | return $dir; |
900 | }
|
900 | }
|
901 | PEAR::staticPopErrorHandling(); |
901 | PEAR::staticPopErrorHandling(); |
902 | $pkg = &$this->getPackagefileObject($this->config, $this->debug, $dir); |
902 | $pkg = &$this->getPackagefileObject($this->config, $this->debug, $dir); |
903 | }
|
903 | }
|
904 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
904 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
905 | $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); |
905 | $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); |
906 | PEAR::staticPopErrorHandling(); |
906 | PEAR::staticPopErrorHandling(); |
907 | if (PEAR::isError($pinfo)) { |
907 | if (PEAR::isError($pinfo)) { |
908 | if (!isset($this->_options['soft'])) { |
908 | if (!isset($this->_options['soft'])) { |
909 | $this->log(0, $pinfo->getMessage()); |
909 | $this->log(0, $pinfo->getMessage()); |
910 | }
|
910 | }
|
911 | return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); |
911 | return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); |
912 | }
|
912 | }
|
913 | $url['info'] = &$pinfo; |
913 | $url['info'] = &$pinfo; |
914 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
914 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
915 | $ext = '.tar'; |
915 | $ext = '.tar'; |
916 | } else { |
916 | } else { |
917 | $ext = '.tgz'; |
917 | $ext = '.tgz'; |
918 | }
|
918 | }
|
919 | if (is_array($url)) { |
919 | if (is_array($url)) { |
920 | if (isset($url['url'])) { |
920 | if (isset($url['url'])) { |
921 | $url['url'] .= $ext; |
921 | $url['url'] .= $ext; |
922 | }
|
922 | }
|
923 | }
|
923 | }
|
924 | return $url; |
924 | return $url; |
925 | }
|
925 | }
|
926 | // }}}
|
926 | // }}}
|
927 | // {{{ getDepPackageDownloadUrl()
|
927 | // {{{ getDepPackageDownloadUrl()
|
928 | 928 | ||
929 | /**
|
929 | /**
|
930 | * @param array dependency array
|
930 | * @param array dependency array
|
931 | * @access private
|
931 | * @access private
|
932 | */
|
932 | */
|
933 | function _getDepPackageDownloadUrl($dep, $parr) |
933 | function _getDepPackageDownloadUrl($dep, $parr) |
934 | {
|
934 | {
|
935 | $xsdversion = isset($dep['rel']) ? '1.0' : '2.0'; |
935 | $xsdversion = isset($dep['rel']) ? '1.0' : '2.0'; |
936 | $curchannel = $this->config->get('default_channel'); |
936 | $curchannel = $this->config->get('default_channel'); |
937 | if (isset($dep['uri'])) { |
937 | if (isset($dep['uri'])) { |
938 | $xsdversion = '2.0'; |
938 | $xsdversion = '2.0'; |
939 | $chan = &$this->_registry->getChannel('__uri'); |
939 | $chan = &$this->_registry->getChannel('__uri'); |
940 | if (PEAR::isError($chan)) { |
940 | if (PEAR::isError($chan)) { |
941 | return $chan; |
941 | return $chan; |
942 | }
|
942 | }
|
943 | $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); |
943 | $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); |
944 | $this->configSet('default_channel', '__uri'); |
944 | $this->configSet('default_channel', '__uri'); |
945 | } else { |
945 | } else { |
946 | if (isset($dep['channel'])) { |
946 | if (isset($dep['channel'])) { |
947 | $remotechannel = $dep['channel']; |
947 | $remotechannel = $dep['channel']; |
948 | } else { |
948 | } else { |
949 | $remotechannel = 'pear.php.net'; |
949 | $remotechannel = 'pear.php.net'; |
950 | }
|
950 | }
|
951 | if (!$this->_registry->channelExists($remotechannel)) { |
951 | if (!$this->_registry->channelExists($remotechannel)) { |
952 | do { |
952 | do { |
953 | if ($this->config->get('auto_discover')) { |
953 | if ($this->config->get('auto_discover')) { |
954 | if ($this->discover($remotechannel)) { |
954 | if ($this->discover($remotechannel)) { |
955 | break; |
955 | break; |
956 | }
|
956 | }
|
957 | }
|
957 | }
|
958 | return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); |
958 | return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); |
959 | } while (false); |
959 | } while (false); |
960 | }
|
960 | }
|
961 | $chan = &$this->_registry->getChannel($remotechannel); |
961 | $chan = &$this->_registry->getChannel($remotechannel); |
962 | if (PEAR::isError($chan)) { |
962 | if (PEAR::isError($chan)) { |
963 | return $chan; |
963 | return $chan; |
964 | }
|
964 | }
|
965 | $version = $this->_registry->packageInfo($dep['name'], 'version', |
965 | $version = $this->_registry->packageInfo($dep['name'], 'version', |
966 | $remotechannel); |
966 | $remotechannel); |
967 | $this->configSet('default_channel', $remotechannel); |
967 | $this->configSet('default_channel', $remotechannel); |
968 | }
|
968 | }
|
969 | $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); |
969 | $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); |
970 | if (isset($parr['state']) && isset($parr['version'])) { |
970 | if (isset($parr['state']) && isset($parr['version'])) { |
971 | unset($parr['state']); |
971 | unset($parr['state']); |
972 | }
|
972 | }
|
973 | if (isset($dep['uri'])) { |
973 | if (isset($dep['uri'])) { |
974 | $info = &$this->newDownloaderPackage($this); |
974 | $info = &$this->newDownloaderPackage($this); |
975 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
975 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
976 | $err = $info->initialize($dep); |
976 | $err = $info->initialize($dep); |
977 | PEAR::staticPopErrorHandling(); |
977 | PEAR::staticPopErrorHandling(); |
978 | if (!$err) { |
978 | if (!$err) { |
979 | // skip parameters that were missed by preferred_state
|
979 | // skip parameters that were missed by preferred_state
|
980 | return PEAR::raiseError('Cannot initialize dependency'); |
980 | return PEAR::raiseError('Cannot initialize dependency'); |
981 | }
|
981 | }
|
982 | if (PEAR::isError($err)) { |
982 | if (PEAR::isError($err)) { |
983 | if (!isset($this->_options['soft'])) { |
983 | if (!isset($this->_options['soft'])) { |
984 | $this->log(0, $err->getMessage()); |
984 | $this->log(0, $err->getMessage()); |
985 | }
|
985 | }
|
986 | if (is_object($info)) { |
986 | if (is_object($info)) { |
987 | $param = $info->getChannel() . '/' . $info->getPackage(); |
987 | $param = $info->getChannel() . '/' . $info->getPackage(); |
988 | }
|
988 | }
|
989 | return PEAR::raiseError('Package "' . $param . '" is not valid'); |
989 | return PEAR::raiseError('Package "' . $param . '" is not valid'); |
990 | }
|
990 | }
|
991 | return $info; |
991 | return $info; |
992 | } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && |
992 | } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && |
993 | $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { |
993 | $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { |
994 | $rest = &$this->config->getREST('1.0', $this->_options); |
994 | $rest = &$this->config->getREST('1.0', $this->_options); |
995 | $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, |
995 | $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, |
996 | $state, $version, $chan->getName()); |
996 | $state, $version, $chan->getName()); |
997 | if (PEAR::isError($url)) { |
997 | if (PEAR::isError($url)) { |
998 | return $url; |
998 | return $url; |
999 | }
|
999 | }
|
1000 | if ($parr['channel'] != $curchannel) { |
1000 | if ($parr['channel'] != $curchannel) { |
1001 | $this->configSet('default_channel', $curchannel); |
1001 | $this->configSet('default_channel', $curchannel); |
1002 | }
|
1002 | }
|
1003 | if (!is_array($url)) { |
1003 | if (!is_array($url)) { |
1004 | return $url; |
1004 | return $url; |
1005 | }
|
1005 | }
|
1006 | $url['raw'] = false; // no checking is necessary for REST |
1006 | $url['raw'] = false; // no checking is necessary for REST |
1007 | if (!is_array($url['info'])) { |
1007 | if (!is_array($url['info'])) { |
1008 | return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . |
1008 | return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . |
1009 | 'this should never happen'); |
1009 | 'this should never happen'); |
1010 | }
|
1010 | }
|
1011 | if (isset($url['info']['required'])) { |
1011 | if (isset($url['info']['required'])) { |
1012 | if (!class_exists('PEAR_PackageFile_v2')) { |
1012 | if (!class_exists('PEAR_PackageFile_v2')) { |
1013 | require_once 'PEAR/PackageFile/v2.php'; |
1013 | require_once 'PEAR/PackageFile/v2.php'; |
1014 | }
|
1014 | }
|
1015 | $pf = new PEAR_PackageFile_v2; |
1015 | $pf = new PEAR_PackageFile_v2; |
1016 | $pf->setRawChannel($remotechannel); |
1016 | $pf->setRawChannel($remotechannel); |
1017 | } else { |
1017 | } else { |
1018 | if (!class_exists('PEAR_PackageFile_v1')) { |
1018 | if (!class_exists('PEAR_PackageFile_v1')) { |
1019 | require_once 'PEAR/PackageFile/v1.php'; |
1019 | require_once 'PEAR/PackageFile/v1.php'; |
1020 | }
|
1020 | }
|
1021 | $pf = new PEAR_PackageFile_v1; |
1021 | $pf = new PEAR_PackageFile_v1; |
1022 | }
|
1022 | }
|
1023 | $pf->setRawPackage($url['package']); |
1023 | $pf->setRawPackage($url['package']); |
1024 | $pf->setDeps($url['info']); |
1024 | $pf->setDeps($url['info']); |
1025 | if ($url['compatible']) { |
1025 | if ($url['compatible']) { |
1026 | $pf->setCompatible($url['compatible']); |
1026 | $pf->setCompatible($url['compatible']); |
1027 | }
|
1027 | }
|
1028 | $pf->setRawState($url['stability']); |
1028 | $pf->setRawState($url['stability']); |
1029 | $url['info'] = &$pf; |
1029 | $url['info'] = &$pf; |
1030 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
1030 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
1031 | $ext = '.tar'; |
1031 | $ext = '.tar'; |
1032 | } else { |
1032 | } else { |
1033 | $ext = '.tgz'; |
1033 | $ext = '.tgz'; |
1034 | }
|
1034 | }
|
1035 | if (is_array($url)) { |
1035 | if (is_array($url)) { |
1036 | if (isset($url['url'])) { |
1036 | if (isset($url['url'])) { |
1037 | $url['url'] .= $ext; |
1037 | $url['url'] .= $ext; |
1038 | }
|
1038 | }
|
1039 | }
|
1039 | }
|
1040 | return $url; |
1040 | return $url; |
1041 | } elseif ($chan->supports('xmlrpc', 'package.getDepDownloadURL', false, '1.1')) { |
1041 | } elseif ($chan->supports('xmlrpc', 'package.getDepDownloadURL', false, '1.1')) { |
1042 | if ($version) { |
1042 | if ($version) { |
1043 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, |
1043 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, |
1044 | $state, $version); |
1044 | $state, $version); |
1045 | } else { |
1045 | } else { |
1046 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, |
1046 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, |
1047 | $state); |
1047 | $state); |
1048 | }
|
1048 | }
|
1049 | } else { |
1049 | } else { |
1050 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, $state); |
1050 | $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, $state); |
1051 | }
|
1051 | }
|
1052 | if ($this->config->get('default_channel') != $curchannel) { |
1052 | if ($this->config->get('default_channel') != $curchannel) { |
1053 | $this->configSet('default_channel', $curchannel); |
1053 | $this->configSet('default_channel', $curchannel); |
1054 | }
|
1054 | }
|
1055 | if (!is_array($url)) { |
1055 | if (!is_array($url)) { |
1056 | return $url; |
1056 | return $url; |
1057 | }
|
1057 | }
|
1058 | if (isset($url['__PEAR_ERROR_CLASS__'])) { |
1058 | if (isset($url['__PEAR_ERROR_CLASS__'])) { |
1059 | return PEAR::raiseError($url['message']); |
1059 | return PEAR::raiseError($url['message']); |
1060 | }
|
1060 | }
|
1061 | $url['raw'] = $url['info']; |
1061 | $url['raw'] = $url['info']; |
1062 | $pkg = &$this->getPackagefileObject($this->config, $this->debug); |
1062 | $pkg = &$this->getPackagefileObject($this->config, $this->debug); |
1063 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
1063 | PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); |
1064 | $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); |
1064 | $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); |
1065 | PEAR::staticPopErrorHandling(); |
1065 | PEAR::staticPopErrorHandling(); |
1066 | if (PEAR::isError($pinfo)) { |
1066 | if (PEAR::isError($pinfo)) { |
1067 | if (!isset($this->_options['soft'])) { |
1067 | if (!isset($this->_options['soft'])) { |
1068 | $this->log(0, $pinfo->getMessage()); |
1068 | $this->log(0, $pinfo->getMessage()); |
1069 | }
|
1069 | }
|
1070 | return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); |
1070 | return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); |
1071 | }
|
1071 | }
|
1072 | $url['info'] = &$pinfo; |
1072 | $url['info'] = &$pinfo; |
1073 | if (is_array($url)) { |
1073 | if (is_array($url)) { |
1074 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
1074 | if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { |
1075 | $ext = '.tar'; |
1075 | $ext = '.tar'; |
1076 | } else { |
1076 | } else { |
1077 | $ext = '.tgz'; |
1077 | $ext = '.tgz'; |
1078 | }
|
1078 | }
|
1079 | if (isset($url['url'])) { |
1079 | if (isset($url['url'])) { |
1080 | $url['url'] .= $ext; |
1080 | $url['url'] .= $ext; |
1081 | }
|
1081 | }
|
1082 | }
|
1082 | }
|
1083 | return $url; |
1083 | return $url; |
1084 | }
|
1084 | }
|
1085 | // }}}
|
1085 | // }}}
|
1086 | // {{{ getPackageDownloadUrl()
|
1086 | // {{{ getPackageDownloadUrl()
|
1087 | 1087 | ||
1088 | /**
|
1088 | /**
|
1089 | * @deprecated in favor of _getPackageDownloadUrl
|
1089 | * @deprecated in favor of _getPackageDownloadUrl
|
1090 | */
|
1090 | */
|
1091 | function getPackageDownloadUrl($package, $version = null, $channel = false) |
1091 | function getPackageDownloadUrl($package, $version = null, $channel = false) |
1092 | {
|
1092 | {
|
1093 | if ($version) { |
1093 | if ($version) { |
1094 | $package .= "-$version"; |
1094 | $package .= "-$version"; |
1095 | }
|
1095 | }
|
1096 | if ($this === null || $this->_registry === null) { |
1096 | if ($this === null || $this->_registry === null) { |
1097 | $package = "http://pear.php.net/get/$package"; |
1097 | $package = "http://pear.php.net/get/$package"; |
1098 | } else { |
1098 | } else { |
1099 | $chan = $this->_registry->getChannel($channel); |
1099 | $chan = $this->_registry->getChannel($channel); |
1100 | if (PEAR::isError($chan)) { |
1100 | if (PEAR::isError($chan)) { |
1101 | return ''; |
1101 | return ''; |
1102 | }
|
1102 | }
|
1103 | $package = "http://" . $chan->getServer() . "/get/$package"; |
1103 | $package = "http://" . $chan->getServer() . "/get/$package"; |
1104 | }
|
1104 | }
|
1105 | if (!extension_loaded("zlib")) { |
1105 | if (!extension_loaded("zlib")) { |
1106 | $package .= '?uncompress=yes'; |
1106 | $package .= '?uncompress=yes'; |
1107 | }
|
1107 | }
|
1108 | return $package; |
1108 | return $package; |
1109 | }
|
1109 | }
|
1110 | 1110 | ||
1111 | // }}}
|
1111 | // }}}
|
1112 | // {{{ getDownloadedPackages()
|
1112 | // {{{ getDownloadedPackages()
|
1113 | 1113 | ||
1114 | /**
|
1114 | /**
|
1115 | * Retrieve a list of downloaded packages after a call to {@link download()}.
|
1115 | * Retrieve a list of downloaded packages after a call to {@link download()}.
|
1116 | *
|
1116 | *
|
1117 | * Also resets the list of downloaded packages.
|
1117 | * Also resets the list of downloaded packages.
|
1118 | * @return array
|
1118 | * @return array
|
1119 | */
|
1119 | */
|
1120 | function getDownloadedPackages() |
1120 | function getDownloadedPackages() |
1121 | {
|
1121 | {
|
1122 | $ret = $this->_downloadedPackages; |
1122 | $ret = $this->_downloadedPackages; |
1123 | $this->_downloadedPackages = array(); |
1123 | $this->_downloadedPackages = array(); |
1124 | $this->_toDownload = array(); |
1124 | $this->_toDownload = array(); |
1125 | return $ret; |
1125 | return $ret; |
1126 | }
|
1126 | }
|
1127 | 1127 | ||
1128 | // }}}
|
1128 | // }}}
|
1129 | // {{{ _downloadCallback()
|
1129 | // {{{ _downloadCallback()
|
1130 | 1130 | ||
1131 | function _downloadCallback($msg, $params = null) |
1131 | function _downloadCallback($msg, $params = null) |
1132 | {
|
1132 | {
|
1133 | switch ($msg) { |
1133 | switch ($msg) { |
1134 | case 'saveas': |
1134 | case 'saveas': |
1135 | $this->log(1, "downloading $params ..."); |
1135 | $this->log(1, "downloading $params ..."); |
1136 | break; |
1136 | break; |
1137 | case 'done': |
1137 | case 'done': |
1138 | $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); |
1138 | $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); |
1139 | break; |
1139 | break; |
1140 | case 'bytesread': |
1140 | case 'bytesread': |
1141 | static $bytes; |
1141 | static $bytes; |
1142 | if (empty($bytes)) { |
1142 | if (empty($bytes)) { |
1143 | $bytes = 0; |
1143 | $bytes = 0; |
1144 | }
|
1144 | }
|
1145 | if (!($bytes % 10240)) { |
1145 | if (!($bytes % 10240)) { |
1146 | $this->log(1, '.', false); |
1146 | $this->log(1, '.', false); |
1147 | }
|
1147 | }
|
1148 | $bytes += $params; |
1148 | $bytes += $params; |
1149 | break; |
1149 | break; |
1150 | case 'start': |
1150 | case 'start': |
1151 | if($params[1] == -1) { |
1151 | if($params[1] == -1) { |
1152 | $length = "Unknown size"; |
1152 | $length = "Unknown size"; |
1153 | } else { |
1153 | } else { |
1154 | $length = number_format($params[1], 0, '', ',')." bytes"; |
1154 | $length = number_format($params[1], 0, '', ',')." bytes"; |
1155 | }
|
1155 | }
|
1156 | $this->log(1, "Starting to download {$params[0]} ($length)"); |
1156 | $this->log(1, "Starting to download {$params[0]} ($length)"); |
1157 | break; |
1157 | break; |
1158 | }
|
1158 | }
|
1159 | if (method_exists($this->ui, '_downloadCallback')) |
1159 | if (method_exists($this->ui, '_downloadCallback')) |
1160 | $this->ui->_downloadCallback($msg, $params); |
1160 | $this->ui->_downloadCallback($msg, $params); |
1161 | }
|
1161 | }
|
1162 | 1162 | ||
1163 | // }}}
|
1163 | // }}}
|
1164 | // {{{ _prependPath($path, $prepend)
|
1164 | // {{{ _prependPath($path, $prepend)
|
1165 | 1165 | ||
1166 | function _prependPath($path, $prepend) |
1166 | function _prependPath($path, $prepend) |
1167 | {
|
1167 | {
|
1168 | if (strlen($prepend) > 0) { |
1168 | if (strlen($prepend) > 0) { |
1169 | if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { |
1169 | if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { |
1170 | if (preg_match('/^[a-z]:/i', $prepend)) { |
1170 | if (preg_match('/^[a-z]:/i', $prepend)) { |
1171 | $prepend = substr($prepend, 2); |
1171 | $prepend = substr($prepend, 2); |
1172 | } elseif ($prepend{0} != '\\') { |
1172 | } elseif ($prepend{0} != '\\') { |
1173 | $prepend = "\\$prepend"; |
1173 | $prepend = "\\$prepend"; |
1174 | }
|
1174 | }
|
1175 | $path = substr($path, 0, 2) . $prepend . substr($path, 2); |
1175 | $path = substr($path, 0, 2) . $prepend . substr($path, 2); |
1176 | } else { |
1176 | } else { |
1177 | $path = $prepend . $path; |
1177 | $path = $prepend . $path; |
1178 | }
|
1178 | }
|
1179 | }
|
1179 | }
|
1180 | return $path; |
1180 | return $path; |
1181 | }
|
1181 | }
|
1182 | // }}}
|
1182 | // }}}
|
1183 | // {{{ pushError($errmsg, $code)
|
1183 | // {{{ pushError($errmsg, $code)
|
1184 | 1184 | ||
1185 | /**
|
1185 | /**
|
1186 | * @param string
|
1186 | * @param string
|
1187 | * @param integer
|
1187 | * @param integer
|
1188 | */
|
1188 | */
|
1189 | function pushError($errmsg, $code = -1) |
1189 | function pushError($errmsg, $code = -1) |
1190 | {
|
1190 | {
|
1191 | array_push($this->_errorStack, array($errmsg, $code)); |
1191 | array_push($this->_errorStack, array($errmsg, $code)); |
1192 | }
|
1192 | }
|
1193 | 1193 | ||
1194 | // }}}
|
1194 | // }}}
|
1195 | // {{{ getErrorMsgs()
|
1195 | // {{{ getErrorMsgs()
|
1196 | 1196 | ||
1197 | function getErrorMsgs() |
1197 | function getErrorMsgs() |
1198 | {
|
1198 | {
|
1199 | $msgs = array(); |
1199 | $msgs = array(); |
1200 | $errs = $this->_errorStack; |
1200 | $errs = $this->_errorStack; |
1201 | foreach ($errs as $err) { |
1201 | foreach ($errs as $err) { |
1202 | $msgs[] = $err[0]; |
1202 | $msgs[] = $err[0]; |
1203 | }
|
1203 | }
|
1204 | $this->_errorStack = array(); |
1204 | $this->_errorStack = array(); |
1205 | return $msgs; |
1205 | return $msgs; |
1206 | }
|
1206 | }
|
1207 | 1207 | ||
1208 | // }}}
|
1208 | // }}}
|
1209 | 1209 | ||
1210 | /**
|
1210 | /**
|
1211 | * for BC
|
1211 | * for BC
|
1212 | */
|
1212 | */
|
1213 | function sortPkgDeps(&$packages, $uninstall = false) |
1213 | function sortPkgDeps(&$packages, $uninstall = false) |
1214 | {
|
1214 | {
|
1215 | $uninstall ?
|
1215 | $uninstall ?
|
1216 | $this->sortPackagesForUninstall($packages) : |
1216 | $this->sortPackagesForUninstall($packages) : |
1217 | $this->sortPackagesForInstall($packages); |
1217 | $this->sortPackagesForInstall($packages); |
1218 | }
|
1218 | }
|
1219 | 1219 | ||
1220 | /**
|
1220 | /**
|
1221 | * Sort a list of arrays of array(downloaded packagefilename) by dependency.
|
1221 | * Sort a list of arrays of array(downloaded packagefilename) by dependency.
|
1222 | *
|
1222 | *
|
1223 | * This uses the topological sort method from graph theory, and the
|
1223 | * This uses the topological sort method from graph theory, and the
|
1224 | * Structures_Graph package to properly sort dependencies for installation.
|
1224 | * Structures_Graph package to properly sort dependencies for installation.
|
1225 | * @param array an array of downloaded PEAR_Downloader_Packages
|
1225 | * @param array an array of downloaded PEAR_Downloader_Packages
|
1226 | * @return array array of array(packagefilename, package.xml contents)
|
1226 | * @return array array of array(packagefilename, package.xml contents)
|
1227 | */
|
1227 | */
|
1228 | function sortPackagesForInstall(&$packages) |
1228 | function sortPackagesForInstall(&$packages) |
1229 | {
|
1229 | {
|
1230 | require_once 'Structures/Graph.php'; |
1230 | require_once 'Structures/Graph.php'; |
1231 | require_once 'Structures/Graph/Node.php'; |
1231 | require_once 'Structures/Graph/Node.php'; |
1232 | require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; |
1232 | require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; |
1233 | $depgraph = new Structures_Graph(true); |
1233 | $depgraph = new Structures_Graph(true); |
1234 | $nodes = array(); |
1234 | $nodes = array(); |
1235 | $reg = &$this->config->getRegistry(); |
1235 | $reg = &$this->config->getRegistry(); |
1236 | foreach ($packages as $i => $package) { |
1236 | foreach ($packages as $i => $package) { |
1237 | $pname = $reg->parsedPackageNameToString( |
1237 | $pname = $reg->parsedPackageNameToString( |
1238 | array( |
1238 | array( |
1239 | 'channel' => $package->getChannel(), |
1239 | 'channel' => $package->getChannel(), |
1240 | 'package' => strtolower($package->getPackage()), |
1240 | 'package' => strtolower($package->getPackage()), |
1241 | )); |
1241 | )); |
1242 | $nodes[$pname] = new Structures_Graph_Node; |
1242 | $nodes[$pname] = new Structures_Graph_Node; |
1243 | $nodes[$pname]->setData($packages[$i]); |
1243 | $nodes[$pname]->setData($packages[$i]); |
1244 | $depgraph->addNode($nodes[$pname]); |
1244 | $depgraph->addNode($nodes[$pname]); |
1245 | }
|
1245 | }
|
1246 | $deplinks = array(); |
1246 | $deplinks = array(); |
1247 | foreach ($nodes as $package => $node) { |
1247 | foreach ($nodes as $package => $node) { |
1248 | $pf = &$node->getData(); |
1248 | $pf = &$node->getData(); |
1249 | $pdeps = $pf->getDeps(true); |
1249 | $pdeps = $pf->getDeps(true); |
1250 | if (!$pdeps) { |
1250 | if (!$pdeps) { |
1251 | continue; |
1251 | continue; |
1252 | }
|
1252 | }
|
1253 | if ($pf->getPackagexmlVersion() == '1.0') { |
1253 | if ($pf->getPackagexmlVersion() == '1.0') { |
1254 | foreach ($pdeps as $dep) { |
1254 | foreach ($pdeps as $dep) { |
1255 | if ($dep['type'] != 'pkg' || |
1255 | if ($dep['type'] != 'pkg' || |
1256 | (isset($dep['optional']) && $dep['optional'] == 'yes')) { |
1256 | (isset($dep['optional']) && $dep['optional'] == 'yes')) { |
1257 | continue; |
1257 | continue; |
1258 | }
|
1258 | }
|
1259 | $dname = $reg->parsedPackageNameToString( |
1259 | $dname = $reg->parsedPackageNameToString( |
1260 | array( |
1260 | array( |
1261 | 'channel' => 'pear.php.net', |
1261 | 'channel' => 'pear.php.net', |
1262 | 'package' => strtolower($dep['name']), |
1262 | 'package' => strtolower($dep['name']), |
1263 | )); |
1263 | )); |
1264 | if (isset($nodes[$dname])) |
1264 | if (isset($nodes[$dname])) |
1265 | {
|
1265 | {
|
1266 | if (!isset($deplinks[$dname])) { |
1266 | if (!isset($deplinks[$dname])) { |
1267 | $deplinks[$dname] = array(); |
1267 | $deplinks[$dname] = array(); |
1268 | }
|
1268 | }
|
1269 | $deplinks[$dname][$package] = 1; |
1269 | $deplinks[$dname][$package] = 1; |
1270 | // dependency is in installed packages
|
1270 | // dependency is in installed packages
|
1271 | continue; |
1271 | continue; |
1272 | }
|
1272 | }
|
1273 | $dname = $reg->parsedPackageNameToString( |
1273 | $dname = $reg->parsedPackageNameToString( |
1274 | array( |
1274 | array( |
1275 | 'channel' => 'pecl.php.net', |
1275 | 'channel' => 'pecl.php.net', |
1276 | 'package' => strtolower($dep['name']), |
1276 | 'package' => strtolower($dep['name']), |
1277 | )); |
1277 | )); |
1278 | if (isset($nodes[$dname])) |
1278 | if (isset($nodes[$dname])) |
1279 | {
|
1279 | {
|
1280 | if (!isset($deplinks[$dname])) { |
1280 | if (!isset($deplinks[$dname])) { |
1281 | $deplinks[$dname] = array(); |
1281 | $deplinks[$dname] = array(); |
1282 | }
|
1282 | }
|
1283 | $deplinks[$dname][$package] = 1; |
1283 | $deplinks[$dname][$package] = 1; |
1284 | // dependency is in installed packages
|
1284 | // dependency is in installed packages
|
1285 | continue; |
1285 | continue; |
1286 | }
|
1286 | }
|
1287 | }
|
1287 | }
|
1288 | } else { |
1288 | } else { |
1289 | // the only ordering we care about is:
|
1289 | // the only ordering we care about is:
|
1290 | // 1) subpackages must be installed before packages that depend on them
|
1290 | // 1) subpackages must be installed before packages that depend on them
|
1291 | // 2) required deps must be installed before packages that depend on them
|
1291 | // 2) required deps must be installed before packages that depend on them
|
1292 | if (isset($pdeps['required']['subpackage'])) { |
1292 | if (isset($pdeps['required']['subpackage'])) { |
1293 | $t = $pdeps['required']['subpackage']; |
1293 | $t = $pdeps['required']['subpackage']; |
1294 | if (!isset($t[0])) { |
1294 | if (!isset($t[0])) { |
1295 | $t = array($t); |
1295 | $t = array($t); |
1296 | }
|
1296 | }
|
1297 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1297 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1298 | }
|
1298 | }
|
1299 | if (isset($pdeps['group'])) { |
1299 | if (isset($pdeps['group'])) { |
1300 | if (!isset($pdeps['group'][0])) { |
1300 | if (!isset($pdeps['group'][0])) { |
1301 | $pdeps['group'] = array($pdeps['group']); |
1301 | $pdeps['group'] = array($pdeps['group']); |
1302 | }
|
1302 | }
|
1303 | foreach ($pdeps['group'] as $group) { |
1303 | foreach ($pdeps['group'] as $group) { |
1304 | if (isset($group['subpackage'])) { |
1304 | if (isset($group['subpackage'])) { |
1305 | $t = $group['subpackage']; |
1305 | $t = $group['subpackage']; |
1306 | if (!isset($t[0])) { |
1306 | if (!isset($t[0])) { |
1307 | $t = array($t); |
1307 | $t = array($t); |
1308 | }
|
1308 | }
|
1309 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1309 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1310 | }
|
1310 | }
|
1311 | }
|
1311 | }
|
1312 | }
|
1312 | }
|
1313 | if (isset($pdeps['optional']['subpackage'])) { |
1313 | if (isset($pdeps['optional']['subpackage'])) { |
1314 | $t = $pdeps['optional']['subpackage']; |
1314 | $t = $pdeps['optional']['subpackage']; |
1315 | if (!isset($t[0])) { |
1315 | if (!isset($t[0])) { |
1316 | $t = array($t); |
1316 | $t = array($t); |
1317 | }
|
1317 | }
|
1318 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1318 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1319 | }
|
1319 | }
|
1320 | if (isset($pdeps['required']['package'])) { |
1320 | if (isset($pdeps['required']['package'])) { |
1321 | $t = $pdeps['required']['package']; |
1321 | $t = $pdeps['required']['package']; |
1322 | if (!isset($t[0])) { |
1322 | if (!isset($t[0])) { |
1323 | $t = array($t); |
1323 | $t = array($t); |
1324 | }
|
1324 | }
|
1325 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1325 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1326 | }
|
1326 | }
|
1327 | if (isset($pdeps['group'])) { |
1327 | if (isset($pdeps['group'])) { |
1328 | if (!isset($pdeps['group'][0])) { |
1328 | if (!isset($pdeps['group'][0])) { |
1329 | $pdeps['group'] = array($pdeps['group']); |
1329 | $pdeps['group'] = array($pdeps['group']); |
1330 | }
|
1330 | }
|
1331 | foreach ($pdeps['group'] as $group) { |
1331 | foreach ($pdeps['group'] as $group) { |
1332 | if (isset($group['package'])) { |
1332 | if (isset($group['package'])) { |
1333 | $t = $group['package']; |
1333 | $t = $group['package']; |
1334 | if (!isset($t[0])) { |
1334 | if (!isset($t[0])) { |
1335 | $t = array($t); |
1335 | $t = array($t); |
1336 | }
|
1336 | }
|
1337 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1337 | $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); |
1338 | }
|
1338 | }
|
1339 | }
|
1339 | }
|
1340 | }
|
1340 | }
|
1341 | }
|
1341 | }
|
1342 | }
|
1342 | }
|
1343 | $this->_detectDepCycle($deplinks); |
1343 | $this->_detectDepCycle($deplinks); |
1344 | foreach ($deplinks as $dependent => $parents) { |
1344 | foreach ($deplinks as $dependent => $parents) { |
1345 | foreach ($parents as $parent => $unused) { |
1345 | foreach ($parents as $parent => $unused) { |
1346 | $nodes[$dependent]->connectTo($nodes[$parent]); |
1346 | $nodes[$dependent]->connectTo($nodes[$parent]); |
1347 | }
|
1347 | }
|
1348 | }
|
1348 | }
|
1349 | $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); |
1349 | $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); |
1350 | $ret = array(); |
1350 | $ret = array(); |
1351 | for ($i = 0; $i < count($installOrder); $i++) { |
1351 | for ($i = 0; $i < count($installOrder); $i++) { |
1352 | foreach ($installOrder[$i] as $index => $sortedpackage) { |
1352 | foreach ($installOrder[$i] as $index => $sortedpackage) { |
1353 | $data = &$installOrder[$i][$index]->getData(); |
1353 | $data = &$installOrder[$i][$index]->getData(); |
1354 | $ret[] = &$nodes[$reg->parsedPackageNameToString( |
1354 | $ret[] = &$nodes[$reg->parsedPackageNameToString( |
1355 | array( |
1355 | array( |
1356 | 'channel' => $data->getChannel(), |
1356 | 'channel' => $data->getChannel(), |
1357 | 'package' => strtolower($data->getPackage()), |
1357 | 'package' => strtolower($data->getPackage()), |
1358 | ))]->getData(); |
1358 | ))]->getData(); |
1359 | }
|
1359 | }
|
1360 | }
|
1360 | }
|
1361 | $packages = $ret; |
1361 | $packages = $ret; |
1362 | return; |
1362 | return; |
1363 | }
|
1363 | }
|
1364 | 1364 | ||
1365 | /**
|
1365 | /**
|
1366 | * Detect recursive links between dependencies and break the cycles
|
1366 | * Detect recursive links between dependencies and break the cycles
|
1367 | *
|
1367 | *
|
1368 | * @param array
|
1368 | * @param array
|
1369 | * @access private
|
1369 | * @access private
|
1370 | */
|
1370 | */
|
1371 | function _detectDepCycle(&$deplinks) |
1371 | function _detectDepCycle(&$deplinks) |
1372 | {
|
1372 | {
|
1373 | do { |
1373 | do { |
1374 | $keepgoing = false; |
1374 | $keepgoing = false; |
1375 | foreach ($deplinks as $dep => $parents) { |
1375 | foreach ($deplinks as $dep => $parents) { |
1376 | foreach ($parents as $parent => $unused) { |
1376 | foreach ($parents as $parent => $unused) { |
1377 | // reset the parent cycle detector
|
1377 | // reset the parent cycle detector
|
1378 | $this->_testCycle(null, null, null); |
1378 | $this->_testCycle(null, null, null); |
1379 | if ($this->_testCycle($dep, $deplinks, $parent)) { |
1379 | if ($this->_testCycle($dep, $deplinks, $parent)) { |
1380 | $keepgoing = true; |
1380 | $keepgoing = true; |
1381 | unset($deplinks[$dep][$parent]); |
1381 | unset($deplinks[$dep][$parent]); |
1382 | if (count($deplinks[$dep]) == 0) { |
1382 | if (count($deplinks[$dep]) == 0) { |
1383 | unset($deplinks[$dep]); |
1383 | unset($deplinks[$dep]); |
1384 | }
|
1384 | }
|
1385 | continue 3; |
1385 | continue 3; |
1386 | }
|
1386 | }
|
1387 | }
|
1387 | }
|
1388 | }
|
1388 | }
|
1389 | } while ($keepgoing); |
1389 | } while ($keepgoing); |
1390 | }
|
1390 | }
|
1391 | 1391 | ||
1392 | function _testCycle($test, $deplinks, $dep) |
1392 | function _testCycle($test, $deplinks, $dep) |
1393 | {
|
1393 | {
|
1394 | static $visited = array(); |
1394 | static $visited = array(); |
1395 | if ($test === null) { |
1395 | if ($test === null) { |
1396 | $visited = array(); |
1396 | $visited = array(); |
1397 | return; |
1397 | return; |
1398 | }
|
1398 | }
|
1399 | // this happens when a parent has a dep cycle on another dependency
|
1399 | // this happens when a parent has a dep cycle on another dependency
|
1400 | // but the child is not part of the cycle
|
1400 | // but the child is not part of the cycle
|
1401 | if (isset($visited[$dep])) { |
1401 | if (isset($visited[$dep])) { |
1402 | return false; |
1402 | return false; |
1403 | }
|
1403 | }
|
1404 | $visited[$dep] = 1; |
1404 | $visited[$dep] = 1; |
1405 | if ($test == $dep) { |
1405 | if ($test == $dep) { |
1406 | return true; |
1406 | return true; |
1407 | }
|
1407 | }
|
1408 | if (isset($deplinks[$dep])) { |
1408 | if (isset($deplinks[$dep])) { |
1409 | if (in_array($test, array_keys($deplinks[$dep]), true)) { |
1409 | if (in_array($test, array_keys($deplinks[$dep]), true)) { |
1410 | return true; |
1410 | return true; |
1411 | }
|
1411 | }
|
1412 | foreach ($deplinks[$dep] as $parent => $unused) { |
1412 | foreach ($deplinks[$dep] as $parent => $unused) { |
1413 | if ($this->_testCycle($test, $deplinks, $parent)) { |
1413 | if ($this->_testCycle($test, $deplinks, $parent)) { |
1414 | return true; |
1414 | return true; |
1415 | }
|
1415 | }
|
1416 | }
|
1416 | }
|
1417 | }
|
1417 | }
|
1418 | return false; |
1418 | return false; |
1419 | }
|
1419 | }
|
1420 | 1420 | ||
1421 | /**
|
1421 | /**
|
1422 | * Set up the dependency for installation parsing
|
1422 | * Set up the dependency for installation parsing
|
1423 | *
|
1423 | *
|
1424 | * @param array $t dependency information
|
1424 | * @param array $t dependency information
|
1425 | * @param PEAR_Registry $reg
|
1425 | * @param PEAR_Registry $reg
|
1426 | * @param array $deplinks list of dependency links already established
|
1426 | * @param array $deplinks list of dependency links already established
|
1427 | * @param array $nodes all existing package nodes
|
1427 | * @param array $nodes all existing package nodes
|
1428 | * @param string $package parent package name
|
1428 | * @param string $package parent package name
|
1429 | * @access private
|
1429 | * @access private
|
1430 | */
|
1430 | */
|
1431 | function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) |
1431 | function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) |
1432 | {
|
1432 | {
|
1433 | foreach ($t as $dep) { |
1433 | foreach ($t as $dep) { |
1434 | $depchannel = !isset($dep['channel']) ? |
1434 | $depchannel = !isset($dep['channel']) ? |
1435 | '__uri': $dep['channel']; |
1435 | '__uri': $dep['channel']; |
1436 | $dname = $reg->parsedPackageNameToString( |
1436 | $dname = $reg->parsedPackageNameToString( |
1437 | array( |
1437 | array( |
1438 | 'channel' => $depchannel, |
1438 | 'channel' => $depchannel, |
1439 | 'package' => strtolower($dep['name']), |
1439 | 'package' => strtolower($dep['name']), |
1440 | )); |
1440 | )); |
1441 | if (isset($nodes[$dname])) |
1441 | if (isset($nodes[$dname])) |
1442 | {
|
1442 | {
|
1443 | if (!isset($deplinks[$dname])) { |
1443 | if (!isset($deplinks[$dname])) { |
1444 | $deplinks[$dname] = array(); |
1444 | $deplinks[$dname] = array(); |
1445 | }
|
1445 | }
|
1446 | $deplinks[$dname][$package] = 1; |
1446 | $deplinks[$dname][$package] = 1; |
1447 | }
|
1447 | }
|
1448 | }
|
1448 | }
|
1449 | }
|
1449 | }
|
1450 | 1450 | ||
1451 | function _dependsOn($a, $b) |
1451 | function _dependsOn($a, $b) |
1452 | {
|
1452 | {
|
1453 | return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), |
1453 | return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), |
1454 | $b); |
1454 | $b); |
1455 | }
|
1455 | }
|
1456 | 1456 | ||
1457 | function _checkDepTree($channel, $package, $b, $checked = array()) |
1457 | function _checkDepTree($channel, $package, $b, $checked = array()) |
1458 | {
|
1458 | {
|
1459 | $checked[$channel][$package] = true; |
1459 | $checked[$channel][$package] = true; |
1460 | if (!isset($this->_depTree[$channel][$package])) { |
1460 | if (!isset($this->_depTree[$channel][$package])) { |
1461 | return false; |
1461 | return false; |
1462 | }
|
1462 | }
|
1463 | if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] |
1463 | if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] |
1464 | [strtolower($b->getPackage())])) { |
1464 | [strtolower($b->getPackage())])) { |
1465 | return true; |
1465 | return true; |
1466 | }
|
1466 | }
|
1467 | foreach ($this->_depTree[$channel][$package] as $ch => $packages) { |
1467 | foreach ($this->_depTree[$channel][$package] as $ch => $packages) { |
1468 | foreach ($packages as $pa => $true) { |
1468 | foreach ($packages as $pa => $true) { |
1469 | if ($this->_checkDepTree($ch, $pa, $b, $checked)) { |
1469 | if ($this->_checkDepTree($ch, $pa, $b, $checked)) { |
1470 | return true; |
1470 | return true; |
1471 | }
|
1471 | }
|
1472 | }
|
1472 | }
|
1473 | }
|
1473 | }
|
1474 | return false; |
1474 | return false; |
1475 | }
|
1475 | }
|
1476 | 1476 | ||
1477 | function _sortInstall($a, $b) |
1477 | function _sortInstall($a, $b) |
1478 | {
|
1478 | {
|
1479 | if (!$a->getDeps() && !$b->getDeps()) { |
1479 | if (!$a->getDeps() && !$b->getDeps()) { |
1480 | return 0; // neither package has dependencies, order is insignificant |
1480 | return 0; // neither package has dependencies, order is insignificant |
1481 | }
|
1481 | }
|
1482 | if ($a->getDeps() && !$b->getDeps()) { |
1482 | if ($a->getDeps() && !$b->getDeps()) { |
1483 | return 1; // $a must be installed after $b because $a has dependencies |
1483 | return 1; // $a must be installed after $b because $a has dependencies |
1484 | }
|
1484 | }
|
1485 | if (!$a->getDeps() && $b->getDeps()) { |
1485 | if (!$a->getDeps() && $b->getDeps()) { |
1486 | return -1; // $b must be installed after $a because $b has dependencies |
1486 | return -1; // $b must be installed after $a because $b has dependencies |
1487 | }
|
1487 | }
|
1488 | // both packages have dependencies
|
1488 | // both packages have dependencies
|
1489 | if ($this->_dependsOn($a, $b)) { |
1489 | if ($this->_dependsOn($a, $b)) { |
1490 | return 1; |
1490 | return 1; |
1491 | }
|
1491 | }
|
1492 | if ($this->_dependsOn($b, $a)) { |
1492 | if ($this->_dependsOn($b, $a)) { |
1493 | return -1; |
1493 | return -1; |
1494 | }
|
1494 | }
|
1495 | return 0; |
1495 | return 0; |
1496 | }
|
1496 | }
|
1497 | 1497 | ||
1498 | /**
|
1498 | /**
|
1499 | * Download a file through HTTP. Considers suggested file name in
|
1499 | * Download a file through HTTP. Considers suggested file name in
|
1500 | * Content-disposition: header and can run a callback function for
|
1500 | * Content-disposition: header and can run a callback function for
|
1501 | * different events. The callback will be called with two
|
1501 | * different events. The callback will be called with two
|
1502 | * parameters: the callback type, and parameters. The implemented
|
1502 | * parameters: the callback type, and parameters. The implemented
|
1503 | * callback types are:
|
1503 | * callback types are:
|
1504 | *
|
1504 | *
|
1505 | * 'setup' called at the very beginning, parameter is a UI object
|
1505 | * 'setup' called at the very beginning, parameter is a UI object
|
1506 | * that should be used for all output
|
1506 | * that should be used for all output
|
1507 | * 'message' the parameter is a string with an informational message
|
1507 | * 'message' the parameter is a string with an informational message
|
1508 | * 'saveas' may be used to save with a different file name, the
|
1508 | * 'saveas' may be used to save with a different file name, the
|
1509 | * parameter is the filename that is about to be used.
|
1509 | * parameter is the filename that is about to be used.
|
1510 | * If a 'saveas' callback returns a non-empty string,
|
1510 | * If a 'saveas' callback returns a non-empty string,
|
1511 | * that file name will be used as the filename instead.
|
1511 | * that file name will be used as the filename instead.
|
1512 | * Note that $save_dir will not be affected by this, only
|
1512 | * Note that $save_dir will not be affected by this, only
|
1513 | * the basename of the file.
|
1513 | * the basename of the file.
|
1514 | * 'start' download is starting, parameter is number of bytes
|
1514 | * 'start' download is starting, parameter is number of bytes
|
1515 | * that are expected, or -1 if unknown
|
1515 | * that are expected, or -1 if unknown
|
1516 | * 'bytesread' parameter is the number of bytes read so far
|
1516 | * 'bytesread' parameter is the number of bytes read so far
|
1517 | * 'done' download is complete, parameter is the total number
|
1517 | * 'done' download is complete, parameter is the total number
|
1518 | * of bytes read
|
1518 | * of bytes read
|
1519 | * 'connfailed' if the TCP/SSL connection fails, this callback is called
|
1519 | * 'connfailed' if the TCP/SSL connection fails, this callback is called
|
1520 | * with array(host,port,errno,errmsg)
|
1520 | * with array(host,port,errno,errmsg)
|
1521 | * 'writefailed' if writing to disk fails, this callback is called
|
1521 | * 'writefailed' if writing to disk fails, this callback is called
|
1522 | * with array(destfile,errmsg)
|
1522 | * with array(destfile,errmsg)
|
1523 | *
|
1523 | *
|
1524 | * If an HTTP proxy has been configured (http_proxy PEAR_Config
|
1524 | * If an HTTP proxy has been configured (http_proxy PEAR_Config
|
1525 | * setting), the proxy will be used.
|
1525 | * setting), the proxy will be used.
|
1526 | *
|
1526 | *
|
1527 | * @param string $url the URL to download
|
1527 | * @param string $url the URL to download
|
1528 | * @param object $ui PEAR_Frontend_* instance
|
1528 | * @param object $ui PEAR_Frontend_* instance
|
1529 | * @param object $config PEAR_Config instance
|
1529 | * @param object $config PEAR_Config instance
|
1530 | * @param string $save_dir directory to save file in
|
1530 | * @param string $save_dir directory to save file in
|
1531 | * @param mixed $callback function/method to call for status
|
1531 | * @param mixed $callback function/method to call for status
|
1532 | * updates
|
1532 | * updates
|
1533 | * @param false|string|array $lastmodified header values to check against for caching
|
1533 | * @param false|string|array $lastmodified header values to check against for caching
|
1534 | * use false to return the header values from this download
|
1534 | * use false to return the header values from this download
|
1535 | * @param false|array $accept Accept headers to send
|
1535 | * @param false|array $accept Accept headers to send
|
1536 | * @param false|string $channel Channel to use for retrieving authentication
|
1536 | * @param false|string $channel Channel to use for retrieving authentication
|
1537 | * @return string|array Returns the full path of the downloaded file or a PEAR
|
1537 | * @return string|array Returns the full path of the downloaded file or a PEAR
|
1538 | * error on failure. If the error is caused by
|
1538 | * error on failure. If the error is caused by
|
1539 | * socket-related errors, the error object will
|
1539 | * socket-related errors, the error object will
|
1540 | * have the fsockopen error code available through
|
1540 | * have the fsockopen error code available through
|
1541 | * getCode(). If caching is requested, then return the header
|
1541 | * getCode(). If caching is requested, then return the header
|
1542 | * values.
|
1542 | * values.
|
1543 | *
|
1543 | *
|
1544 | * @access public
|
1544 | * @access public
|
1545 | */
|
1545 | */
|
1546 | function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, |
1546 | function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, |
1547 | $accept = false, $channel = false) |
1547 | $accept = false, $channel = false) |
1548 | {
|
1548 | {
|
1549 | static $redirect = 0; |
1549 | static $redirect = 0; |
1550 | // allways reset , so we are clean case of error
|
1550 | // allways reset , so we are clean case of error
|
1551 | $wasredirect = $redirect; |
1551 | $wasredirect = $redirect; |
1552 | $redirect = 0; |
1552 | $redirect = 0; |
1553 | if ($callback) { |
1553 | if ($callback) { |
1554 | call_user_func($callback, 'setup', array(&$ui)); |
1554 | call_user_func($callback, 'setup', array(&$ui)); |
1555 | }
|
1555 | }
|
1556 | $info = parse_url($url); |
1556 | $info = parse_url($url); |
1557 | if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { |
1557 | if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { |
1558 | return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); |
1558 | return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); |
1559 | }
|
1559 | }
|
1560 | if (!isset($info['host'])) { |
1560 | if (!isset($info['host'])) { |
1561 | return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); |
1561 | return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); |
1562 | } else { |
1562 | } else { |
1563 | $host = isset($info['host']) ? $info['host'] : null; |
1563 | $host = isset($info['host']) ? $info['host'] : null; |
1564 | $port = isset($info['port']) ? $info['port'] : null; |
1564 | $port = isset($info['port']) ? $info['port'] : null; |
1565 | $path = isset($info['path']) ? $info['path'] : null; |
1565 | $path = isset($info['path']) ? $info['path'] : null; |
1566 | }
|
1566 | }
|
1567 | if (isset($this)) { |
1567 | if (isset($this)) { |
1568 | $config = &$this->config; |
1568 | $config = &$this->config; |
1569 | } else { |
1569 | } else { |
1570 | $config = &PEAR_Config::singleton(); |
1570 | $config = &PEAR_Config::singleton(); |
1571 | }
|
1571 | }
|
1572 | $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
1572 | $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
1573 | if ($config->get('http_proxy') && |
1573 | if ($config->get('http_proxy') && |
1574 | $proxy = parse_url($config->get('http_proxy'))) { |
1574 | $proxy = parse_url($config->get('http_proxy'))) { |
1575 | $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; |
1575 | $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; |
1576 | if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { |
1576 | if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { |
1577 | $proxy_host = 'ssl://' . $proxy_host; |
1577 | $proxy_host = 'ssl://' . $proxy_host; |
1578 | }
|
1578 | }
|
1579 | $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; |
1579 | $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; |
1580 | $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
1580 | $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
1581 | $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
1581 | $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
1582 | 1582 | ||
1583 | if ($callback) { |
1583 | if ($callback) { |
1584 | call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); |
1584 | call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); |
1585 | }
|
1585 | }
|
1586 | }
|
1586 | }
|
1587 | if (empty($port)) { |
1587 | if (empty($port)) { |
1588 | if (isset($info['scheme']) && $info['scheme'] == 'https') { |
1588 | if (isset($info['scheme']) && $info['scheme'] == 'https') { |
1589 | $port = 443; |
1589 | $port = 443; |
1590 | } else { |
1590 | } else { |
1591 | $port = 80; |
1591 | $port = 80; |
1592 | }
|
1592 | }
|
1593 | }
|
1593 | }
|
1594 | if ($proxy_host != '') { |
1594 | if ($proxy_host != '') { |
1595 | $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); |
1595 | $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); |
1596 | if (!$fp) { |
1596 | if (!$fp) { |
1597 | if ($callback) { |
1597 | if ($callback) { |
1598 | call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port, |
1598 | call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port, |
1599 | $errno, $errstr)); |
1599 | $errno, $errstr)); |
1600 | }
|
1600 | }
|
1601 | return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); |
1601 | return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); |
1602 | }
|
1602 | }
|
1603 | if ($lastmodified === false || $lastmodified) { |
1603 | if ($lastmodified === false || $lastmodified) { |
1604 | $request = "GET $url HTTP/1.1\r\n"; |
1604 | $request = "GET $url HTTP/1.1\r\n"; |
1605 | } else { |
1605 | } else { |
1606 | $request = "GET $url HTTP/1.0\r\n"; |
1606 | $request = "GET $url HTTP/1.0\r\n"; |
1607 | }
|
1607 | }
|
1608 | } else { |
1608 | } else { |
1609 | if (isset($info['scheme']) && $info['scheme'] == 'https') { |
1609 | if (isset($info['scheme']) && $info['scheme'] == 'https') { |
1610 | $host = 'ssl://' . $host; |
1610 | $host = 'ssl://' . $host; |
1611 | }
|
1611 | }
|
1612 | $fp = @fsockopen($host, $port, $errno, $errstr); |
1612 | $fp = @fsockopen($host, $port, $errno, $errstr); |
1613 | if (!$fp) { |
1613 | if (!$fp) { |
1614 | if ($callback) { |
1614 | if ($callback) { |
1615 | call_user_func($callback, 'connfailed', array($host, $port, |
1615 | call_user_func($callback, 'connfailed', array($host, $port, |
1616 | $errno, $errstr)); |
1616 | $errno, $errstr)); |
1617 | }
|
1617 | }
|
1618 | return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); |
1618 | return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); |
1619 | }
|
1619 | }
|
1620 | if ($lastmodified === false || $lastmodified) { |
1620 | if ($lastmodified === false || $lastmodified) { |
1621 | $request = "GET $path HTTP/1.1\r\n"; |
1621 | $request = "GET $path HTTP/1.1\r\n"; |
1622 | $request .= "Host: $host:$port\r\n"; |
1622 | $request .= "Host: $host:$port\r\n"; |
1623 | } else { |
1623 | } else { |
1624 | $request = "GET $path HTTP/1.0\r\n"; |
1624 | $request = "GET $path HTTP/1.0\r\n"; |
1625 | $request .= "Host: $host\r\n"; |
1625 | $request .= "Host: $host\r\n"; |
1626 | }
|
1626 | }
|
1627 | }
|
1627 | }
|
1628 | $ifmodifiedsince = ''; |
1628 | $ifmodifiedsince = ''; |
1629 | if (is_array($lastmodified)) { |
1629 | if (is_array($lastmodified)) { |
1630 | if (isset($lastmodified['Last-Modified'])) { |
1630 | if (isset($lastmodified['Last-Modified'])) { |
1631 | $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; |
1631 | $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; |
1632 | }
|
1632 | }
|
1633 | if (isset($lastmodified['ETag'])) { |
1633 | if (isset($lastmodified['ETag'])) { |
1634 | $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; |
1634 | $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; |
1635 | }
|
1635 | }
|
1636 | } else { |
1636 | } else { |
1637 | $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); |
1637 | $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); |
1638 | }
|
1638 | }
|
1639 | $request .= $ifmodifiedsince . "User-Agent: PEAR/1.7.2/PHP/" . |
1639 | $request .= $ifmodifiedsince . "User-Agent: PEAR/1.7.2/PHP/" . |
1640 | PHP_VERSION . "\r\n"; |
1640 | PHP_VERSION . "\r\n"; |
1641 | if (isset($this)) { // only pass in authentication for non-static calls |
1641 | if (isset($this)) { // only pass in authentication for non-static calls |
1642 | $username = $config->get('username', null, $channel); |
1642 | $username = $config->get('username', null, $channel); |
1643 | $password = $config->get('password', null, $channel); |
1643 | $password = $config->get('password', null, $channel); |
1644 | if ($username && $password) { |
1644 | if ($username && $password) { |
1645 | $tmp = base64_encode("$username:$password"); |
1645 | $tmp = base64_encode("$username:$password"); |
1646 | $request .= "Authorization: Basic $tmp\r\n"; |
1646 | $request .= "Authorization: Basic $tmp\r\n"; |
1647 | }
|
1647 | }
|
1648 | }
|
1648 | }
|
1649 | if ($proxy_host != '' && $proxy_user != '') { |
1649 | if ($proxy_host != '' && $proxy_user != '') { |
1650 | $request .= 'Proxy-Authorization: Basic ' . |
1650 | $request .= 'Proxy-Authorization: Basic ' . |
1651 | base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; |
1651 | base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; |
1652 | }
|
1652 | }
|
1653 | if ($accept) { |
1653 | if ($accept) { |
1654 | $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; |
1654 | $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; |
1655 | }
|
1655 | }
|
1656 | $request .= "Connection: close\r\n"; |
1656 | $request .= "Connection: close\r\n"; |
1657 | $request .= "\r\n"; |
1657 | $request .= "\r\n"; |
1658 | fwrite($fp, $request); |
1658 | fwrite($fp, $request); |
1659 | $headers = array(); |
1659 | $headers = array(); |
1660 | $reply = 0; |
1660 | $reply = 0; |
1661 | while (trim($line = fgets($fp, 1024))) { |
1661 | while (trim($line = fgets($fp, 1024))) { |
1662 | if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { |
1662 | if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { |
1663 | $headers[strtolower($matches[1])] = trim($matches[2]); |
1663 | $headers[strtolower($matches[1])] = trim($matches[2]); |
1664 | } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { |
1664 | } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { |
1665 | $reply = (int) $matches[1]; |
1665 | $reply = (int) $matches[1]; |
1666 | if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { |
1666 | if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { |
1667 | return false; |
1667 | return false; |
1668 | }
|
1668 | }
|
1669 | if (! in_array($reply, array(200, 301, 302, 303, 305, 307))) { |
1669 | if (! in_array($reply, array(200, 301, 302, 303, 305, 307))) { |
1670 | return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)"); |
1670 | return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)"); |
1671 | }
|
1671 | }
|
1672 | }
|
1672 | }
|
1673 | }
|
1673 | }
|
1674 | if ($reply != 200) { |
1674 | if ($reply != 200) { |
1675 | if (isset($headers['location'])) { |
1675 | if (isset($headers['location'])) { |
1676 | if ($wasredirect < 5) { |
1676 | if ($wasredirect < 5) { |
1677 | $redirect = $wasredirect + 1; |
1677 | $redirect = $wasredirect + 1; |
1678 | return $this->downloadHttp($headers['location'], |
1678 | return $this->downloadHttp($headers['location'], |
1679 | $ui, $save_dir, $callback, $lastmodified, $accept); |
1679 | $ui, $save_dir, $callback, $lastmodified, $accept); |
1680 | } else { |
1680 | } else { |
1681 | return PEAR::raiseError("File http://$host:$port$path not valid (redirection looped more than 5 times)"); |
1681 | return PEAR::raiseError("File http://$host:$port$path not valid (redirection looped more than 5 times)"); |
1682 | }
|
1682 | }
|
1683 | } else { |
1683 | } else { |
1684 | return PEAR::raiseError("File http://$host:$port$path not valid (redirected but no location)"); |
1684 | return PEAR::raiseError("File http://$host:$port$path not valid (redirected but no location)"); |
1685 | }
|
1685 | }
|
1686 | }
|
1686 | }
|
1687 | if (isset($headers['content-disposition']) && |
1687 | if (isset($headers['content-disposition']) && |
1688 | preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { |
1688 | preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { |
1689 | $save_as = basename($matches[1]); |
1689 | $save_as = basename($matches[1]); |
1690 | } else { |
1690 | } else { |
1691 | $save_as = basename($url); |
1691 | $save_as = basename($url); |
1692 | }
|
1692 | }
|
1693 | if ($callback) { |
1693 | if ($callback) { |
1694 | $tmp = call_user_func($callback, 'saveas', $save_as); |
1694 | $tmp = call_user_func($callback, 'saveas', $save_as); |
1695 | if ($tmp) { |
1695 | if ($tmp) { |
1696 | $save_as = $tmp; |
1696 | $save_as = $tmp; |
1697 | }
|
1697 | }
|
1698 | }
|
1698 | }
|
1699 | $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; |
1699 | $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; |
1700 | if (!$wp = @fopen($dest_file, 'wb')) { |
1700 | if (!$wp = @fopen($dest_file, 'wb')) { |
1701 | fclose($fp); |
1701 | fclose($fp); |
1702 | if ($callback) { |
1702 | if ($callback) { |
1703 | call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); |
1703 | call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); |
1704 | }
|
1704 | }
|
1705 | return PEAR::raiseError("could not open $dest_file for writing"); |
1705 | return PEAR::raiseError("could not open $dest_file for writing"); |
1706 | }
|
1706 | }
|
1707 | if (isset($headers['content-length'])) { |
1707 | if (isset($headers['content-length'])) { |
1708 | $length = $headers['content-length']; |
1708 | $length = $headers['content-length']; |
1709 | } else { |
1709 | } else { |
1710 | $length = -1; |
1710 | $length = -1; |
1711 | }
|
1711 | }
|
1712 | $bytes = 0; |
1712 | $bytes = 0; |
1713 | if ($callback) { |
1713 | if ($callback) { |
1714 | call_user_func($callback, 'start', array(basename($dest_file), $length)); |
1714 | call_user_func($callback, 'start', array(basename($dest_file), $length)); |
1715 | }
|
1715 | }
|
1716 | while ($data = fread($fp, 1024)) { |
1716 | while ($data = fread($fp, 1024)) { |
1717 | $bytes += strlen($data); |
1717 | $bytes += strlen($data); |
1718 | if ($callback) { |
1718 | if ($callback) { |
1719 | call_user_func($callback, 'bytesread', $bytes); |
1719 | call_user_func($callback, 'bytesread', $bytes); |
1720 | }
|
1720 | }
|
1721 | if (!@fwrite($wp, $data)) { |
1721 | if (!@fwrite($wp, $data)) { |
1722 | fclose($fp); |
1722 | fclose($fp); |
1723 | if ($callback) { |
1723 | if ($callback) { |
1724 | call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); |
1724 | call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); |
1725 | }
|
1725 | }
|
1726 | return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); |
1726 | return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); |
1727 | }
|
1727 | }
|
1728 | }
|
1728 | }
|
1729 | fclose($fp); |
1729 | fclose($fp); |
1730 | fclose($wp); |
1730 | fclose($wp); |
1731 | if ($callback) { |
1731 | if ($callback) { |
1732 | call_user_func($callback, 'done', $bytes); |
1732 | call_user_func($callback, 'done', $bytes); |
1733 | }
|
1733 | }
|
1734 | if ($lastmodified === false || $lastmodified) { |
1734 | if ($lastmodified === false || $lastmodified) { |
1735 | if (isset($headers['etag'])) { |
1735 | if (isset($headers['etag'])) { |
1736 | $lastmodified = array('ETag' => $headers['etag']); |
1736 | $lastmodified = array('ETag' => $headers['etag']); |
1737 | }
|
1737 | }
|
1738 | if (isset($headers['last-modified'])) { |
1738 | if (isset($headers['last-modified'])) { |
1739 | if (is_array($lastmodified)) { |
1739 | if (is_array($lastmodified)) { |
1740 | $lastmodified['Last-Modified'] = $headers['last-modified']; |
1740 | $lastmodified['Last-Modified'] = $headers['last-modified']; |
1741 | } else { |
1741 | } else { |
1742 | $lastmodified = $headers['last-modified']; |
1742 | $lastmodified = $headers['last-modified']; |
1743 | }
|
1743 | }
|
1744 | }
|
1744 | }
|
1745 | return array($dest_file, $lastmodified, $headers); |
1745 | return array($dest_file, $lastmodified, $headers); |
1746 | }
|
1746 | }
|
1747 | return $dest_file; |
1747 | return $dest_file; |
1748 | }
|
1748 | }
|
1749 | }
|
1749 | }
|
1750 | // }}}
|
1750 | // }}}
|
1751 | 1751 | ||
1752 | ?>
|
1752 | ?>
|
1753 | 1753 |