PageRenderTime 216ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php/PEAR/Downloader.php

https://bitbucket.org/adarshj/convenient_website
PHP | 1766 lines | 1465 code | 123 blank | 178 comment | 237 complexity | 3f62de6899c2ca37c89810e3510b77b9 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-2-Clause, GPL-2.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * PEAR_Downloader, the PEAR Installer's download utility class
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * @category pear
  8. * @package PEAR
  9. * @author Greg Beaver <cellog@php.net>
  10. * @author Stig Bakken <ssb@php.net>
  11. * @author Tomas V. V. Cox <cox@idecnet.com>
  12. * @author Martin Jansen <mj@php.net>
  13. * @copyright 1997-2009 The Authors
  14. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  15. * @version CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $
  16. * @link http://pear.php.net/package/PEAR
  17. * @since File available since Release 1.3.0
  18. */
  19. /**
  20. * Needed for constants, extending
  21. */
  22. require_once 'PEAR/Common.php';
  23. define('PEAR_INSTALLER_OK', 1);
  24. define('PEAR_INSTALLER_FAILED', 0);
  25. define('PEAR_INSTALLER_SKIPPED', -1);
  26. define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
  27. /**
  28. * Administration class used to download anything from the internet (PEAR Packages,
  29. * static URLs, xml files)
  30. *
  31. * @category pear
  32. * @package PEAR
  33. * @author Greg Beaver <cellog@php.net>
  34. * @author Stig Bakken <ssb@php.net>
  35. * @author Tomas V. V. Cox <cox@idecnet.com>
  36. * @author Martin Jansen <mj@php.net>
  37. * @copyright 1997-2009 The Authors
  38. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  39. * @version Release: 1.9.4
  40. * @link http://pear.php.net/package/PEAR
  41. * @since Class available since Release 1.3.0
  42. */
  43. class PEAR_Downloader extends PEAR_Common
  44. {
  45. /**
  46. * @var PEAR_Registry
  47. * @access private
  48. */
  49. var $_registry;
  50. /**
  51. * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  52. * @var string|null
  53. * @access private
  54. */
  55. var $_preferredState;
  56. /**
  57. * Options from command-line passed to Install.
  58. *
  59. * Recognized options:<br />
  60. * - onlyreqdeps : install all required dependencies as well
  61. * - alldeps : install all dependencies, including optional
  62. * - installroot : base relative path to install files in
  63. * - force : force a download even if warnings would prevent it
  64. * - nocompress : download uncompressed tarballs
  65. * @see PEAR_Command_Install
  66. * @access private
  67. * @var array
  68. */
  69. var $_options;
  70. /**
  71. * Downloaded Packages after a call to download().
  72. *
  73. * Format of each entry:
  74. *
  75. * <code>
  76. * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  77. * 'info' => array() // parsed package.xml
  78. * );
  79. * </code>
  80. * @access private
  81. * @var array
  82. */
  83. var $_downloadedPackages = array();
  84. /**
  85. * Packages slated for download.
  86. *
  87. * This is used to prevent downloading a package more than once should it be a dependency
  88. * for two packages to be installed.
  89. * Format of each entry:
  90. *
  91. * <pre>
  92. * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
  93. * );
  94. * </pre>
  95. * @access private
  96. * @var array
  97. */
  98. var $_toDownload = array();
  99. /**
  100. * Array of every package installed, with names lower-cased.
  101. *
  102. * Format:
  103. * <code>
  104. * array('package1' => 0, 'package2' => 1, );
  105. * </code>
  106. * @var array
  107. */
  108. var $_installed = array();
  109. /**
  110. * @var array
  111. * @access private
  112. */
  113. var $_errorStack = array();
  114. /**
  115. * @var boolean
  116. * @access private
  117. */
  118. var $_internalDownload = false;
  119. /**
  120. * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
  121. * @var array
  122. * @access private
  123. */
  124. var $_packageSortTree;
  125. /**
  126. * Temporary directory, or configuration value where downloads will occur
  127. * @var string
  128. */
  129. var $_downloadDir;
  130. /**
  131. * @param PEAR_Frontend_*
  132. * @param array
  133. * @param PEAR_Config
  134. */
  135. function PEAR_Downloader(&$ui, $options, &$config)
  136. {
  137. parent::PEAR_Common();
  138. $this->_options = $options;
  139. $this->config = &$config;
  140. $this->_preferredState = $this->config->get('preferred_state');
  141. $this->ui = &$ui;
  142. if (!$this->_preferredState) {
  143. // don't inadvertantly use a non-set preferred_state
  144. $this->_preferredState = null;
  145. }
  146. if (isset($this->_options['installroot'])) {
  147. $this->config->setInstallRoot($this->_options['installroot']);
  148. }
  149. $this->_registry = &$config->getRegistry();
  150. if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
  151. $this->_installed = $this->_registry->listAllPackages();
  152. foreach ($this->_installed as $key => $unused) {
  153. if (!count($unused)) {
  154. continue;
  155. }
  156. $strtolower = create_function('$a','return strtolower($a);');
  157. array_walk($this->_installed[$key], $strtolower);
  158. }
  159. }
  160. }
  161. /**
  162. * Attempt to discover a channel's remote capabilities from
  163. * its server name
  164. * @param string
  165. * @return boolean
  166. */
  167. function discover($channel)
  168. {
  169. $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
  170. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  171. $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
  172. if (!class_exists('System')) {
  173. require_once 'System.php';
  174. }
  175. $tmpdir = $this->config->get('temp_dir');
  176. $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
  177. $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  178. PEAR::popErrorHandling();
  179. if (PEAR::isError($a)) {
  180. // Attempt to fallback to https automatically.
  181. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  182. $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
  183. $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  184. PEAR::popErrorHandling();
  185. if (PEAR::isError($a)) {
  186. return false;
  187. }
  188. }
  189. list($a, $lastmodified) = $a;
  190. if (!class_exists('PEAR_ChannelFile')) {
  191. require_once 'PEAR/ChannelFile.php';
  192. }
  193. $b = new PEAR_ChannelFile;
  194. if ($b->fromXmlFile($a)) {
  195. unlink($a);
  196. if ($this->config->get('auto_discover')) {
  197. $this->_registry->addChannel($b, $lastmodified);
  198. $alias = $b->getName();
  199. if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
  200. $alias = $b->getAlias();
  201. }
  202. $this->log(1, 'Auto-discovered channel "' . $channel .
  203. '", alias "' . $alias . '", adding to registry');
  204. }
  205. return true;
  206. }
  207. unlink($a);
  208. return false;
  209. }
  210. /**
  211. * For simpler unit-testing
  212. * @param PEAR_Downloader
  213. * @return PEAR_Downloader_Package
  214. */
  215. function &newDownloaderPackage(&$t)
  216. {
  217. if (!class_exists('PEAR_Downloader_Package')) {
  218. require_once 'PEAR/Downloader/Package.php';
  219. }
  220. $a = &new PEAR_Downloader_Package($t);
  221. return $a;
  222. }
  223. /**
  224. * For simpler unit-testing
  225. * @param PEAR_Config
  226. * @param array
  227. * @param array
  228. * @param int
  229. */
  230. function &getDependency2Object(&$c, $i, $p, $s)
  231. {
  232. if (!class_exists('PEAR_Dependency2')) {
  233. require_once 'PEAR/Dependency2.php';
  234. }
  235. $z = &new PEAR_Dependency2($c, $i, $p, $s);
  236. return $z;
  237. }
  238. function &download($params)
  239. {
  240. if (!count($params)) {
  241. $a = array();
  242. return $a;
  243. }
  244. if (!isset($this->_registry)) {
  245. $this->_registry = &$this->config->getRegistry();
  246. }
  247. $channelschecked = array();
  248. // convert all parameters into PEAR_Downloader_Package objects
  249. foreach ($params as $i => $param) {
  250. $params[$i] = &$this->newDownloaderPackage($this);
  251. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  252. $err = $params[$i]->initialize($param);
  253. PEAR::staticPopErrorHandling();
  254. if (!$err) {
  255. // skip parameters that were missed by preferred_state
  256. continue;
  257. }
  258. if (PEAR::isError($err)) {
  259. if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
  260. $this->log(0, $err->getMessage());
  261. }
  262. $params[$i] = false;
  263. if (is_object($param)) {
  264. $param = $param->getChannel() . '/' . $param->getPackage();
  265. }
  266. if (!isset($this->_options['soft'])) {
  267. $this->log(2, 'Package "' . $param . '" is not valid');
  268. }
  269. // Message logged above in a specific verbose mode, passing null to not show up on CLI
  270. $this->pushError(null, PEAR_INSTALLER_SKIPPED);
  271. } else {
  272. do {
  273. if ($params[$i] && $params[$i]->getType() == 'local') {
  274. // bug #7090 skip channel.xml check for local packages
  275. break;
  276. }
  277. if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
  278. !isset($this->_options['offline'])
  279. ) {
  280. $channelschecked[$params[$i]->getChannel()] = true;
  281. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  282. if (!class_exists('System')) {
  283. require_once 'System.php';
  284. }
  285. $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
  286. if (PEAR::isError($curchannel)) {
  287. PEAR::staticPopErrorHandling();
  288. return $this->raiseError($curchannel);
  289. }
  290. if (PEAR::isError($dir = $this->getDownloadDir())) {
  291. PEAR::staticPopErrorHandling();
  292. break;
  293. }
  294. $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
  295. $url = 'http://' . $mirror . '/channel.xml';
  296. $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
  297. PEAR::staticPopErrorHandling();
  298. if (PEAR::isError($a) || !$a) {
  299. // Attempt fallback to https automatically
  300. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  301. $a = $this->downloadHttp('https://' . $mirror .
  302. '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
  303. PEAR::staticPopErrorHandling();
  304. if (PEAR::isError($a) || !$a) {
  305. break;
  306. }
  307. }
  308. $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
  309. 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
  310. '" to update');
  311. }
  312. } while (false);
  313. if ($params[$i] && !isset($this->_options['downloadonly'])) {
  314. if (isset($this->_options['packagingroot'])) {
  315. $checkdir = $this->_prependPath(
  316. $this->config->get('php_dir', null, $params[$i]->getChannel()),
  317. $this->_options['packagingroot']);
  318. } else {
  319. $checkdir = $this->config->get('php_dir',
  320. null, $params[$i]->getChannel());
  321. }
  322. while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
  323. $checkdir = dirname($checkdir);
  324. }
  325. if ($checkdir == '.') {
  326. $checkdir = '/';
  327. }
  328. if (!is_writeable($checkdir)) {
  329. return PEAR::raiseError('Cannot install, php_dir for channel "' .
  330. $params[$i]->getChannel() . '" is not writeable by the current user');
  331. }
  332. }
  333. }
  334. }
  335. unset($channelschecked);
  336. PEAR_Downloader_Package::removeDuplicates($params);
  337. if (!count($params)) {
  338. $a = array();
  339. return $a;
  340. }
  341. if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
  342. $reverify = true;
  343. while ($reverify) {
  344. $reverify = false;
  345. foreach ($params as $i => $param) {
  346. //PHP Bug 40768 / PEAR Bug #10944
  347. //Nested foreaches fail in PHP 5.2.1
  348. key($params);
  349. $ret = $params[$i]->detectDependencies($params);
  350. if (PEAR::isError($ret)) {
  351. $reverify = true;
  352. $params[$i] = false;
  353. PEAR_Downloader_Package::removeDuplicates($params);
  354. if (!isset($this->_options['soft'])) {
  355. $this->log(0, $ret->getMessage());
  356. }
  357. continue 2;
  358. }
  359. }
  360. }
  361. }
  362. if (isset($this->_options['offline'])) {
  363. $this->log(3, 'Skipping dependency download check, --offline specified');
  364. }
  365. if (!count($params)) {
  366. $a = array();
  367. return $a;
  368. }
  369. while (PEAR_Downloader_Package::mergeDependencies($params));
  370. PEAR_Downloader_Package::removeDuplicates($params, true);
  371. $errorparams = array();
  372. if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
  373. if (count($errorparams)) {
  374. foreach ($errorparams as $param) {
  375. $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
  376. $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
  377. }
  378. $a = array();
  379. return $a;
  380. }
  381. }
  382. PEAR_Downloader_Package::removeInstalled($params);
  383. if (!count($params)) {
  384. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  385. $a = array();
  386. return $a;
  387. }
  388. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  389. $err = $this->analyzeDependencies($params);
  390. PEAR::popErrorHandling();
  391. if (!count($params)) {
  392. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  393. $a = array();
  394. return $a;
  395. }
  396. $ret = array();
  397. $newparams = array();
  398. if (isset($this->_options['pretend'])) {
  399. return $params;
  400. }
  401. $somefailed = false;
  402. foreach ($params as $i => $package) {
  403. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  404. $pf = &$params[$i]->download();
  405. PEAR::staticPopErrorHandling();
  406. if (PEAR::isError($pf)) {
  407. if (!isset($this->_options['soft'])) {
  408. $this->log(1, $pf->getMessage());
  409. $this->log(0, 'Error: cannot download "' .
  410. $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
  411. true) .
  412. '"');
  413. }
  414. $somefailed = true;
  415. continue;
  416. }
  417. $newparams[] = &$params[$i];
  418. $ret[] = array(
  419. 'file' => $pf->getArchiveFile(),
  420. 'info' => &$pf,
  421. 'pkg' => $pf->getPackage()
  422. );
  423. }
  424. if ($somefailed) {
  425. // remove params that did not download successfully
  426. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  427. $err = $this->analyzeDependencies($newparams, true);
  428. PEAR::popErrorHandling();
  429. if (!count($newparams)) {
  430. $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
  431. $a = array();
  432. return $a;
  433. }
  434. }
  435. $this->_downloadedPackages = $ret;
  436. return $newparams;
  437. }
  438. /**
  439. * @param array all packages to be installed
  440. */
  441. function analyzeDependencies(&$params, $force = false)
  442. {
  443. if (isset($this->_options['downloadonly'])) {
  444. return;
  445. }
  446. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  447. $redo = true;
  448. $reset = $hasfailed = $failed = false;
  449. while ($redo) {
  450. $redo = false;
  451. foreach ($params as $i => $param) {
  452. $deps = $param->getDeps();
  453. if (!$deps) {
  454. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  455. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  456. $send = $param->getPackageFile();
  457. $installcheck = $depchecker->validatePackage($send, $this, $params);
  458. if (PEAR::isError($installcheck)) {
  459. if (!isset($this->_options['soft'])) {
  460. $this->log(0, $installcheck->getMessage());
  461. }
  462. $hasfailed = true;
  463. $params[$i] = false;
  464. $reset = true;
  465. $redo = true;
  466. $failed = false;
  467. PEAR_Downloader_Package::removeDuplicates($params);
  468. continue 2;
  469. }
  470. continue;
  471. }
  472. if (!$reset && $param->alreadyValidated() && !$force) {
  473. continue;
  474. }
  475. if (count($deps)) {
  476. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  477. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  478. $send = $param->getPackageFile();
  479. if ($send === null) {
  480. $send = $param->getDownloadURL();
  481. }
  482. $installcheck = $depchecker->validatePackage($send, $this, $params);
  483. if (PEAR::isError($installcheck)) {
  484. if (!isset($this->_options['soft'])) {
  485. $this->log(0, $installcheck->getMessage());
  486. }
  487. $hasfailed = true;
  488. $params[$i] = false;
  489. $reset = true;
  490. $redo = true;
  491. $failed = false;
  492. PEAR_Downloader_Package::removeDuplicates($params);
  493. continue 2;
  494. }
  495. $failed = false;
  496. if (isset($deps['required']) && is_array($deps['required'])) {
  497. foreach ($deps['required'] as $type => $dep) {
  498. // note: Dependency2 will never return a PEAR_Error if ignore-errors
  499. // is specified, so soft is needed to turn off logging
  500. if (!isset($dep[0])) {
  501. if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
  502. true, $params))) {
  503. $failed = true;
  504. if (!isset($this->_options['soft'])) {
  505. $this->log(0, $e->getMessage());
  506. }
  507. } elseif (is_array($e) && !$param->alreadyValidated()) {
  508. if (!isset($this->_options['soft'])) {
  509. $this->log(0, $e[0]);
  510. }
  511. }
  512. } else {
  513. foreach ($dep as $d) {
  514. if (PEAR::isError($e =
  515. $depchecker->{"validate{$type}Dependency"}($d,
  516. true, $params))) {
  517. $failed = true;
  518. if (!isset($this->_options['soft'])) {
  519. $this->log(0, $e->getMessage());
  520. }
  521. } elseif (is_array($e) && !$param->alreadyValidated()) {
  522. if (!isset($this->_options['soft'])) {
  523. $this->log(0, $e[0]);
  524. }
  525. }
  526. }
  527. }
  528. }
  529. if (isset($deps['optional']) && is_array($deps['optional'])) {
  530. foreach ($deps['optional'] as $type => $dep) {
  531. if (!isset($dep[0])) {
  532. if (PEAR::isError($e =
  533. $depchecker->{"validate{$type}Dependency"}($dep,
  534. false, $params))) {
  535. $failed = true;
  536. if (!isset($this->_options['soft'])) {
  537. $this->log(0, $e->getMessage());
  538. }
  539. } elseif (is_array($e) && !$param->alreadyValidated()) {
  540. if (!isset($this->_options['soft'])) {
  541. $this->log(0, $e[0]);
  542. }
  543. }
  544. } else {
  545. foreach ($dep as $d) {
  546. if (PEAR::isError($e =
  547. $depchecker->{"validate{$type}Dependency"}($d,
  548. false, $params))) {
  549. $failed = true;
  550. if (!isset($this->_options['soft'])) {
  551. $this->log(0, $e->getMessage());
  552. }
  553. } elseif (is_array($e) && !$param->alreadyValidated()) {
  554. if (!isset($this->_options['soft'])) {
  555. $this->log(0, $e[0]);
  556. }
  557. }
  558. }
  559. }
  560. }
  561. }
  562. $groupname = $param->getGroup();
  563. if (isset($deps['group']) && $groupname) {
  564. if (!isset($deps['group'][0])) {
  565. $deps['group'] = array($deps['group']);
  566. }
  567. $found = false;
  568. foreach ($deps['group'] as $group) {
  569. if ($group['attribs']['name'] == $groupname) {
  570. $found = true;
  571. break;
  572. }
  573. }
  574. if ($found) {
  575. unset($group['attribs']);
  576. foreach ($group as $type => $dep) {
  577. if (!isset($dep[0])) {
  578. if (PEAR::isError($e =
  579. $depchecker->{"validate{$type}Dependency"}($dep,
  580. false, $params))) {
  581. $failed = true;
  582. if (!isset($this->_options['soft'])) {
  583. $this->log(0, $e->getMessage());
  584. }
  585. } elseif (is_array($e) && !$param->alreadyValidated()) {
  586. if (!isset($this->_options['soft'])) {
  587. $this->log(0, $e[0]);
  588. }
  589. }
  590. } else {
  591. foreach ($dep as $d) {
  592. if (PEAR::isError($e =
  593. $depchecker->{"validate{$type}Dependency"}($d,
  594. false, $params))) {
  595. $failed = true;
  596. if (!isset($this->_options['soft'])) {
  597. $this->log(0, $e->getMessage());
  598. }
  599. } elseif (is_array($e) && !$param->alreadyValidated()) {
  600. if (!isset($this->_options['soft'])) {
  601. $this->log(0, $e[0]);
  602. }
  603. }
  604. }
  605. }
  606. }
  607. }
  608. }
  609. } else {
  610. foreach ($deps as $dep) {
  611. if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
  612. $failed = true;
  613. if (!isset($this->_options['soft'])) {
  614. $this->log(0, $e->getMessage());
  615. }
  616. } elseif (is_array($e) && !$param->alreadyValidated()) {
  617. if (!isset($this->_options['soft'])) {
  618. $this->log(0, $e[0]);
  619. }
  620. }
  621. }
  622. }
  623. $params[$i]->setValidated();
  624. }
  625. if ($failed) {
  626. $hasfailed = true;
  627. $params[$i] = false;
  628. $reset = true;
  629. $redo = true;
  630. $failed = false;
  631. PEAR_Downloader_Package::removeDuplicates($params);
  632. continue 2;
  633. }
  634. }
  635. }
  636. PEAR::staticPopErrorHandling();
  637. if ($hasfailed && (isset($this->_options['ignore-errors']) ||
  638. isset($this->_options['nodeps']))) {
  639. // this is probably not needed, but just in case
  640. if (!isset($this->_options['soft'])) {
  641. $this->log(0, 'WARNING: dependencies failed');
  642. }
  643. }
  644. }
  645. /**
  646. * Retrieve the directory that downloads will happen in
  647. * @access private
  648. * @return string
  649. */
  650. function getDownloadDir()
  651. {
  652. if (isset($this->_downloadDir)) {
  653. return $this->_downloadDir;
  654. }
  655. $downloaddir = $this->config->get('download_dir');
  656. if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
  657. if (is_dir($downloaddir) && !is_writable($downloaddir)) {
  658. $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
  659. '" is not writeable. Change download_dir config variable to ' .
  660. 'a writeable dir to avoid this warning');
  661. }
  662. if (!class_exists('System')) {
  663. require_once 'System.php';
  664. }
  665. if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
  666. return $downloaddir;
  667. }
  668. $this->log(3, '+ tmp dir created at ' . $downloaddir);
  669. }
  670. if (!is_writable($downloaddir)) {
  671. if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
  672. !is_writable($downloaddir)) {
  673. return PEAR::raiseError('download directory "' . $downloaddir .
  674. '" is not writeable. Change download_dir config variable to ' .
  675. 'a writeable dir');
  676. }
  677. }
  678. return $this->_downloadDir = $downloaddir;
  679. }
  680. function setDownloadDir($dir)
  681. {
  682. if (!@is_writable($dir)) {
  683. if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
  684. return PEAR::raiseError('download directory "' . $dir .
  685. '" is not writeable. Change download_dir config variable to ' .
  686. 'a writeable dir');
  687. }
  688. }
  689. $this->_downloadDir = $dir;
  690. }
  691. function configSet($key, $value, $layer = 'user', $channel = false)
  692. {
  693. $this->config->set($key, $value, $layer, $channel);
  694. $this->_preferredState = $this->config->get('preferred_state', null, $channel);
  695. if (!$this->_preferredState) {
  696. // don't inadvertantly use a non-set preferred_state
  697. $this->_preferredState = null;
  698. }
  699. }
  700. function setOptions($options)
  701. {
  702. $this->_options = $options;
  703. }
  704. function getOptions()
  705. {
  706. return $this->_options;
  707. }
  708. /**
  709. * @param array output of {@link parsePackageName()}
  710. * @access private
  711. */
  712. function _getPackageDownloadUrl($parr)
  713. {
  714. $curchannel = $this->config->get('default_channel');
  715. $this->configSet('default_channel', $parr['channel']);
  716. // getDownloadURL returns an array. On error, it only contains information
  717. // on the latest release as array(version, info). On success it contains
  718. // array(version, info, download url string)
  719. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  720. if (!$this->_registry->channelExists($parr['channel'])) {
  721. do {
  722. if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
  723. break;
  724. }
  725. $this->configSet('default_channel', $curchannel);
  726. return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
  727. } while (false);
  728. }
  729. $chan = &$this->_registry->getChannel($parr['channel']);
  730. if (PEAR::isError($chan)) {
  731. return $chan;
  732. }
  733. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  734. $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
  735. $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
  736. // package is installed - use the installed release stability level
  737. if (!isset($parr['state']) && $stability !== null) {
  738. $state = $stability['release'];
  739. }
  740. PEAR::staticPopErrorHandling();
  741. $base2 = false;
  742. $preferred_mirror = $this->config->get('preferred_mirror');
  743. if (!$chan->supportsREST($preferred_mirror) ||
  744. (
  745. !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
  746. &&
  747. !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  748. )
  749. ) {
  750. return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
  751. }
  752. if ($base2) {
  753. $rest = &$this->config->getREST('1.3', $this->_options);
  754. $base = $base2;
  755. } else {
  756. $rest = &$this->config->getREST('1.0', $this->_options);
  757. }
  758. $downloadVersion = false;
  759. if (!isset($parr['version']) && !isset($parr['state']) && $version
  760. && !PEAR::isError($version)
  761. && !isset($this->_options['downloadonly'])
  762. ) {
  763. $downloadVersion = $version;
  764. }
  765. $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
  766. if (PEAR::isError($url)) {
  767. $this->configSet('default_channel', $curchannel);
  768. return $url;
  769. }
  770. if ($parr['channel'] != $curchannel) {
  771. $this->configSet('default_channel', $curchannel);
  772. }
  773. if (!is_array($url)) {
  774. return $url;
  775. }
  776. $url['raw'] = false; // no checking is necessary for REST
  777. if (!is_array($url['info'])) {
  778. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  779. 'this should never happen');
  780. }
  781. if (!isset($this->_options['force']) &&
  782. !isset($this->_options['downloadonly']) &&
  783. $version &&
  784. !PEAR::isError($version) &&
  785. !isset($parr['group'])
  786. ) {
  787. if (version_compare($version, $url['version'], '=')) {
  788. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  789. $parr, true) . ' is already installed and is the same as the ' .
  790. 'released version ' . $url['version'], -976);
  791. }
  792. if (version_compare($version, $url['version'], '>')) {
  793. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  794. $parr, true) . ' is already installed and is newer than detected ' .
  795. 'released version ' . $url['version'], -976);
  796. }
  797. }
  798. if (isset($url['info']['required']) || $url['compatible']) {
  799. require_once 'PEAR/PackageFile/v2.php';
  800. $pf = new PEAR_PackageFile_v2;
  801. $pf->setRawChannel($parr['channel']);
  802. if ($url['compatible']) {
  803. $pf->setRawCompatible($url['compatible']);
  804. }
  805. } else {
  806. require_once 'PEAR/PackageFile/v1.php';
  807. $pf = new PEAR_PackageFile_v1;
  808. }
  809. $pf->setRawPackage($url['package']);
  810. $pf->setDeps($url['info']);
  811. if ($url['compatible']) {
  812. $pf->setCompatible($url['compatible']);
  813. }
  814. $pf->setRawState($url['stability']);
  815. $url['info'] = &$pf;
  816. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  817. $ext = '.tar';
  818. } else {
  819. $ext = '.tgz';
  820. }
  821. if (is_array($url) && isset($url['url'])) {
  822. $url['url'] .= $ext;
  823. }
  824. return $url;
  825. }
  826. /**
  827. * @param array dependency array
  828. * @access private
  829. */
  830. function _getDepPackageDownloadUrl($dep, $parr)
  831. {
  832. $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
  833. $curchannel = $this->config->get('default_channel');
  834. if (isset($dep['uri'])) {
  835. $xsdversion = '2.0';
  836. $chan = &$this->_registry->getChannel('__uri');
  837. if (PEAR::isError($chan)) {
  838. return $chan;
  839. }
  840. $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
  841. $this->configSet('default_channel', '__uri');
  842. } else {
  843. if (isset($dep['channel'])) {
  844. $remotechannel = $dep['channel'];
  845. } else {
  846. $remotechannel = 'pear.php.net';
  847. }
  848. if (!$this->_registry->channelExists($remotechannel)) {
  849. do {
  850. if ($this->config->get('auto_discover')) {
  851. if ($this->discover($remotechannel)) {
  852. break;
  853. }
  854. }
  855. return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
  856. } while (false);
  857. }
  858. $chan = &$this->_registry->getChannel($remotechannel);
  859. if (PEAR::isError($chan)) {
  860. return $chan;
  861. }
  862. $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
  863. $this->configSet('default_channel', $remotechannel);
  864. }
  865. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  866. if (isset($parr['state']) && isset($parr['version'])) {
  867. unset($parr['state']);
  868. }
  869. if (isset($dep['uri'])) {
  870. $info = &$this->newDownloaderPackage($this);
  871. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  872. $err = $info->initialize($dep);
  873. PEAR::staticPopErrorHandling();
  874. if (!$err) {
  875. // skip parameters that were missed by preferred_state
  876. return PEAR::raiseError('Cannot initialize dependency');
  877. }
  878. if (PEAR::isError($err)) {
  879. if (!isset($this->_options['soft'])) {
  880. $this->log(0, $err->getMessage());
  881. }
  882. if (is_object($info)) {
  883. $param = $info->getChannel() . '/' . $info->getPackage();
  884. }
  885. return PEAR::raiseError('Package "' . $param . '" is not valid');
  886. }
  887. return $info;
  888. } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
  889. &&
  890. (
  891. ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
  892. ||
  893. ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
  894. )
  895. ) {
  896. if ($base2) {
  897. $base = $base2;
  898. $rest = &$this->config->getREST('1.3', $this->_options);
  899. } else {
  900. $rest = &$this->config->getREST('1.0', $this->_options);
  901. }
  902. $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
  903. $state, $version, $chan->getName());
  904. if (PEAR::isError($url)) {
  905. return $url;
  906. }
  907. if ($parr['channel'] != $curchannel) {
  908. $this->configSet('default_channel', $curchannel);
  909. }
  910. if (!is_array($url)) {
  911. return $url;
  912. }
  913. $url['raw'] = false; // no checking is necessary for REST
  914. if (!is_array($url['info'])) {
  915. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  916. 'this should never happen');
  917. }
  918. if (isset($url['info']['required'])) {
  919. if (!class_exists('PEAR_PackageFile_v2')) {
  920. require_once 'PEAR/PackageFile/v2.php';
  921. }
  922. $pf = new PEAR_PackageFile_v2;
  923. $pf->setRawChannel($remotechannel);
  924. } else {
  925. if (!class_exists('PEAR_PackageFile_v1')) {
  926. require_once 'PEAR/PackageFile/v1.php';
  927. }
  928. $pf = new PEAR_PackageFile_v1;
  929. }
  930. $pf->setRawPackage($url['package']);
  931. $pf->setDeps($url['info']);
  932. if ($url['compatible']) {
  933. $pf->setCompatible($url['compatible']);
  934. }
  935. $pf->setRawState($url['stability']);
  936. $url['info'] = &$pf;
  937. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  938. $ext = '.tar';
  939. } else {
  940. $ext = '.tgz';
  941. }
  942. if (is_array($url) && isset($url['url'])) {
  943. $url['url'] .= $ext;
  944. }
  945. return $url;
  946. }
  947. return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
  948. }
  949. /**
  950. * @deprecated in favor of _getPackageDownloadUrl
  951. */
  952. function getPackageDownloadUrl($package, $version = null, $channel = false)
  953. {
  954. if ($version) {
  955. $package .= "-$version";
  956. }
  957. if ($this === null || $this->_registry === null) {
  958. $package = "http://pear.php.net/get/$package";
  959. } else {
  960. $chan = $this->_registry->getChannel($channel);
  961. if (PEAR::isError($chan)) {
  962. return '';
  963. }
  964. $package = "http://" . $chan->getServer() . "/get/$package";
  965. }
  966. if (!extension_loaded("zlib")) {
  967. $package .= '?uncompress=yes';
  968. }
  969. return $package;
  970. }
  971. /**
  972. * Retrieve a list of downloaded packages after a call to {@link download()}.
  973. *
  974. * Also resets the list of downloaded packages.
  975. * @return array
  976. */
  977. function getDownloadedPackages()
  978. {
  979. $ret = $this->_downloadedPackages;
  980. $this->_downloadedPackages = array();
  981. $this->_toDownload = array();
  982. return $ret;
  983. }
  984. function _downloadCallback($msg, $params = null)
  985. {
  986. switch ($msg) {
  987. case 'saveas':
  988. $this->log(1, "downloading $params ...");
  989. break;
  990. case 'done':
  991. $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
  992. break;
  993. case 'bytesread':
  994. static $bytes;
  995. if (empty($bytes)) {
  996. $bytes = 0;
  997. }
  998. if (!($bytes % 10240)) {
  999. $this->log(1, '.', false);
  1000. }
  1001. $bytes += $params;
  1002. break;
  1003. case 'start':
  1004. if($params[1] == -1) {
  1005. $length = "Unknown size";
  1006. } else {
  1007. $length = number_format($params[1], 0, '', ',')." bytes";
  1008. }
  1009. $this->log(1, "Starting to download {$params[0]} ($length)");
  1010. break;
  1011. }
  1012. if (method_exists($this->ui, '_downloadCallback'))
  1013. $this->ui->_downloadCallback($msg, $params);
  1014. }
  1015. function _prependPath($path, $prepend)
  1016. {
  1017. if (strlen($prepend) > 0) {
  1018. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  1019. if (preg_match('/^[a-z]:/i', $prepend)) {
  1020. $prepend = substr($prepend, 2);
  1021. } elseif ($prepend{0} != '\\') {
  1022. $prepend = "\\$prepend";
  1023. }
  1024. $path = substr($path, 0, 2) . $prepend . substr($path, 2);
  1025. } else {
  1026. $path = $prepend . $path;
  1027. }
  1028. }
  1029. return $path;
  1030. }
  1031. /**
  1032. * @param string
  1033. * @param integer
  1034. */
  1035. function pushError($errmsg, $code = -1)
  1036. {
  1037. array_push($this->_errorStack, array($errmsg, $code));
  1038. }
  1039. function getErrorMsgs()
  1040. {
  1041. $msgs = array();
  1042. $errs = $this->_errorStack;
  1043. foreach ($errs as $err) {
  1044. $msgs[] = $err[0];
  1045. }
  1046. $this->_errorStack = array();
  1047. return $msgs;
  1048. }
  1049. /**
  1050. * for BC
  1051. *
  1052. * @deprecated
  1053. */
  1054. function sortPkgDeps(&$packages, $uninstall = false)
  1055. {
  1056. $uninstall ?
  1057. $this->sortPackagesForUninstall($packages) :
  1058. $this->sortPackagesForInstall($packages);
  1059. }
  1060. /**
  1061. * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  1062. *
  1063. * This uses the topological sort method from graph theory, and the
  1064. * Structures_Graph package to properly sort dependencies for installation.
  1065. * @param array an array of downloaded PEAR_Downloader_Packages
  1066. * @return array array of array(packagefilename, package.xml contents)
  1067. */
  1068. function sortPackagesForInstall(&$packages)
  1069. {
  1070. require_once 'Structures/Graph.php';
  1071. require_once 'Structures/Graph/Node.php';
  1072. require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
  1073. $depgraph = new Structures_Graph(true);
  1074. $nodes = array();
  1075. $reg = &$this->config->getRegistry();
  1076. foreach ($packages as $i => $package) {
  1077. $pname = $reg->parsedPackageNameToString(
  1078. array(
  1079. 'channel' => $package->getChannel(),
  1080. 'package' => strtolower($package->getPackage()),
  1081. ));
  1082. $nodes[$pname] = new Structures_Graph_Node;
  1083. $nodes[$pname]->setData($packages[$i]);
  1084. $depgraph->addNode($nodes[$pname]);
  1085. }
  1086. $deplinks = array();
  1087. foreach ($nodes as $package => $node) {
  1088. $pf = &$node->getData();
  1089. $pdeps = $pf->getDeps(true);
  1090. if (!$pdeps) {
  1091. continue;
  1092. }
  1093. if ($pf->getPackagexmlVersion() == '1.0') {
  1094. foreach ($pdeps as $dep) {
  1095. if ($dep['type'] != 'pkg' ||
  1096. (isset($dep['optional']) && $dep['optional'] == 'yes')) {
  1097. continue;
  1098. }
  1099. $dname = $reg->parsedPackageNameToString(
  1100. array(
  1101. 'channel' => 'pear.php.net',
  1102. 'package' => strtolower($dep['name']),
  1103. ));
  1104. if (isset($nodes[$dname])) {
  1105. if (!isset($deplinks[$dname])) {
  1106. $deplinks[$dname] = array();
  1107. }
  1108. $deplinks[$dname][$package] = 1;
  1109. // dependency is in installed packages
  1110. continue;
  1111. }
  1112. $dname = $reg->parsedPackageNameToString(
  1113. array(
  1114. 'channel' => 'pecl.php.net',
  1115. 'package' => strtolower($dep['name']),
  1116. ));
  1117. if (isset($nodes[$dname])) {
  1118. if (!isset($deplinks[$dname])) {
  1119. $deplinks[$dname] = array();
  1120. }
  1121. $deplinks[$dname][$package] = 1;
  1122. // dependency is in installed packages
  1123. continue;
  1124. }
  1125. }
  1126. } else {
  1127. // the only ordering we care about is:
  1128. // 1) subpackages must be installed before packages that depend on them
  1129. // 2) required deps must be installed before packages that depend on them
  1130. if (isset($pdeps['required']['subpackage'])) {
  1131. $t = $pdeps['required']['subpackage'];
  1132. if (!isset($t[0])) {
  1133. $t = array($t);
  1134. }
  1135. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  1136. }
  1137. if (isset($pdeps['group'])) {
  1138. if (!isset($pdeps['group'][0])) {
  1139. $pdeps['group'] = array($pdeps['group']);
  1140. }
  1141. foreach ($pdeps['group'] as $group) {
  1142. if (isset($group['subpackage'])) {
  1143. $t = $group['subpackage'];
  1144. if (!isset($t[0])) {
  1145. $t = array($t);
  1146. }
  1147. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  1148. }
  1149. }
  1150. }
  1151. if (isset($pdeps['optional']['subpackage'])) {
  1152. $t = $pdeps['optional']['subpackage'];
  1153. if (!isset($t[0])) {
  1154. $t = array($t);
  1155. }
  1156. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  1157. }
  1158. if (isset($pdeps['required']['package'])) {
  1159. $t = $pdeps['required']['package'];
  1160. if (!isset($t[0])) {
  1161. $t = array($t);
  1162. }
  1163. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  1164. }
  1165. if (isset($pdeps['group'])) {
  1166. if (!isset($pdeps['group'][0])) {
  1167. $pdeps['group'] = array($pdeps['group']);
  1168. }
  1169. foreach ($pdeps['group'] as $group) {
  1170. if (isset($group['package'])) {
  1171. $t = $group['package'];
  1172. if (!isset($t[0])) {
  1173. $t = array($t);
  1174. }
  1175. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  1176. }
  1177. }
  1178. }
  1179. }
  1180. }
  1181. $this->_detectDepCycle($deplinks);
  1182. foreach ($deplinks as $dependent => $parents) {
  1183. foreach ($parents as $parent => $unused) {
  1184. $nodes[$dependent]->connectTo($nodes[$parent]);
  1185. }
  1186. }
  1187. $installOrder = Structures_Graph_Manipulator_TopologicalSorter:

Large files files are truncated, but you can click here to view the full file