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