PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/php/PEAR/Downloader/Package.php

https://bitbucket.org/adarshj/convenient_website
PHP | 1988 lines | 1509 code | 230 blank | 249 comment | 393 complexity | 2a4bfddd597fcb1d63b2a06ea4544b91 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
  1. <?php
  2. /**
  3. * PEAR_Downloader_Package
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * @category pear
  8. * @package PEAR
  9. * @author Greg Beaver <cellog@php.net>
  10. * @copyright 1997-2009 The Authors
  11. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  12. * @version CVS: $Id: Package.php 313023 2011-07-06 19:17:11Z dufuz $
  13. * @link http://pear.php.net/package/PEAR
  14. * @since File available since Release 1.4.0a1
  15. */
  16. /**
  17. * Error code when parameter initialization fails because no releases
  18. * exist within preferred_state, but releases do exist
  19. */
  20. define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
  21. /**
  22. * Error code when parameter initialization fails because no releases
  23. * exist that will work with the existing PHP version
  24. */
  25. define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
  26. /**
  27. * Coordinates download parameters and manages their dependencies
  28. * prior to downloading them.
  29. *
  30. * Input can come from three sources:
  31. *
  32. * - local files (archives or package.xml)
  33. * - remote files (downloadable urls)
  34. * - abstract package names
  35. *
  36. * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
  37. * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
  38. * format returned of dependencies is slightly different from that used in package.xml.
  39. *
  40. * This class hides the differences between these elements, and makes automatic
  41. * dependency resolution a piece of cake. It also manages conflicts when
  42. * two classes depend on incompatible dependencies, or differing versions of the same
  43. * package dependency. In addition, download will not be attempted if the php version is
  44. * not supported, PEAR installer version is not supported, or non-PECL extensions are not
  45. * installed.
  46. * @category pear
  47. * @package PEAR
  48. * @author Greg Beaver <cellog@php.net>
  49. * @copyright 1997-2009 The Authors
  50. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  51. * @version Release: 1.9.4
  52. * @link http://pear.php.net/package/PEAR
  53. * @since Class available since Release 1.4.0a1
  54. */
  55. class PEAR_Downloader_Package
  56. {
  57. /**
  58. * @var PEAR_Downloader
  59. */
  60. var $_downloader;
  61. /**
  62. * @var PEAR_Config
  63. */
  64. var $_config;
  65. /**
  66. * @var PEAR_Registry
  67. */
  68. var $_registry;
  69. /**
  70. * Used to implement packagingroot properly
  71. * @var PEAR_Registry
  72. */
  73. var $_installRegistry;
  74. /**
  75. * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
  76. */
  77. var $_packagefile;
  78. /**
  79. * @var array
  80. */
  81. var $_parsedname;
  82. /**
  83. * @var array
  84. */
  85. var $_downloadURL;
  86. /**
  87. * @var array
  88. */
  89. var $_downloadDeps = array();
  90. /**
  91. * @var boolean
  92. */
  93. var $_valid = false;
  94. /**
  95. * @var boolean
  96. */
  97. var $_analyzed = false;
  98. /**
  99. * if this or a parent package was invoked with Package-state, this is set to the
  100. * state variable.
  101. *
  102. * This allows temporary reassignment of preferred_state for a parent package and all of
  103. * its dependencies.
  104. * @var string|false
  105. */
  106. var $_explicitState = false;
  107. /**
  108. * If this package is invoked with Package#group, this variable will be true
  109. */
  110. var $_explicitGroup = false;
  111. /**
  112. * Package type local|url
  113. * @var string
  114. */
  115. var $_type;
  116. /**
  117. * Contents of package.xml, if downloaded from a remote channel
  118. * @var string|false
  119. * @access private
  120. */
  121. var $_rawpackagefile;
  122. /**
  123. * @var boolean
  124. * @access private
  125. */
  126. var $_validated = false;
  127. /**
  128. * @param PEAR_Downloader
  129. */
  130. function PEAR_Downloader_Package(&$downloader)
  131. {
  132. $this->_downloader = &$downloader;
  133. $this->_config = &$this->_downloader->config;
  134. $this->_registry = &$this->_config->getRegistry();
  135. $options = $downloader->getOptions();
  136. if (isset($options['packagingroot'])) {
  137. $this->_config->setInstallRoot($options['packagingroot']);
  138. $this->_installRegistry = &$this->_config->getRegistry();
  139. $this->_config->setInstallRoot(false);
  140. } else {
  141. $this->_installRegistry = &$this->_registry;
  142. }
  143. $this->_valid = $this->_analyzed = false;
  144. }
  145. /**
  146. * Parse the input and determine whether this is a local file, a remote uri, or an
  147. * abstract package name.
  148. *
  149. * This is the heart of the PEAR_Downloader_Package(), and is used in
  150. * {@link PEAR_Downloader::download()}
  151. * @param string
  152. * @return bool|PEAR_Error
  153. */
  154. function initialize($param)
  155. {
  156. $origErr = $this->_fromFile($param);
  157. if ($this->_valid) {
  158. return true;
  159. }
  160. $options = $this->_downloader->getOptions();
  161. if (isset($options['offline'])) {
  162. if (PEAR::isError($origErr) && !isset($options['soft'])) {
  163. foreach ($origErr->getUserInfo() as $userInfo) {
  164. if (isset($userInfo['message'])) {
  165. $this->_downloader->log(0, $userInfo['message']);
  166. }
  167. }
  168. $this->_downloader->log(0, $origErr->getMessage());
  169. }
  170. return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
  171. }
  172. $err = $this->_fromUrl($param);
  173. if (PEAR::isError($err) || !$this->_valid) {
  174. if ($this->_type == 'url') {
  175. if (PEAR::isError($err) && !isset($options['soft'])) {
  176. $this->_downloader->log(0, $err->getMessage());
  177. }
  178. return PEAR::raiseError("Invalid or missing remote package file");
  179. }
  180. $err = $this->_fromString($param);
  181. if (PEAR::isError($err) || !$this->_valid) {
  182. if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
  183. return false; // instruct the downloader to silently skip
  184. }
  185. if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
  186. if (is_array($origErr->getUserInfo())) {
  187. foreach ($origErr->getUserInfo() as $err) {
  188. if (is_array($err)) {
  189. $err = $err['message'];
  190. }
  191. if (!isset($options['soft'])) {
  192. $this->_downloader->log(0, $err);
  193. }
  194. }
  195. }
  196. if (!isset($options['soft'])) {
  197. $this->_downloader->log(0, $origErr->getMessage());
  198. }
  199. if (is_array($param)) {
  200. $param = $this->_registry->parsedPackageNameToString($param, true);
  201. }
  202. if (!isset($options['soft'])) {
  203. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  204. }
  205. // Passing no message back - already logged above
  206. return PEAR::raiseError();
  207. }
  208. if (PEAR::isError($err) && !isset($options['soft'])) {
  209. $this->_downloader->log(0, $err->getMessage());
  210. }
  211. if (is_array($param)) {
  212. $param = $this->_registry->parsedPackageNameToString($param, true);
  213. }
  214. if (!isset($options['soft'])) {
  215. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  216. }
  217. // Passing no message back - already logged above
  218. return PEAR::raiseError();
  219. }
  220. }
  221. return true;
  222. }
  223. /**
  224. * Retrieve any non-local packages
  225. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
  226. */
  227. function &download()
  228. {
  229. if (isset($this->_packagefile)) {
  230. return $this->_packagefile;
  231. }
  232. if (isset($this->_downloadURL['url'])) {
  233. $this->_isvalid = false;
  234. $info = $this->getParsedPackage();
  235. foreach ($info as $i => $p) {
  236. $info[$i] = strtolower($p);
  237. }
  238. $err = $this->_fromUrl($this->_downloadURL['url'],
  239. $this->_registry->parsedPackageNameToString($this->_parsedname, true));
  240. $newinfo = $this->getParsedPackage();
  241. foreach ($newinfo as $i => $p) {
  242. $newinfo[$i] = strtolower($p);
  243. }
  244. if ($info != $newinfo) {
  245. do {
  246. if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
  247. $info['channel'] = 'pear.php.net';
  248. if ($info == $newinfo) {
  249. // skip the channel check if a pecl package says it's a PEAR package
  250. break;
  251. }
  252. }
  253. if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
  254. $info['channel'] = 'pecl.php.net';
  255. if ($info == $newinfo) {
  256. // skip the channel check if a pecl package says it's a PEAR package
  257. break;
  258. }
  259. }
  260. return PEAR::raiseError('CRITICAL ERROR: We are ' .
  261. $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
  262. 'downloaded claims to be ' .
  263. $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
  264. } while (false);
  265. }
  266. if (PEAR::isError($err) || !$this->_valid) {
  267. return $err;
  268. }
  269. }
  270. $this->_type = 'local';
  271. return $this->_packagefile;
  272. }
  273. function &getPackageFile()
  274. {
  275. return $this->_packagefile;
  276. }
  277. function &getDownloader()
  278. {
  279. return $this->_downloader;
  280. }
  281. function getType()
  282. {
  283. return $this->_type;
  284. }
  285. /**
  286. * Like {@link initialize()}, but operates on a dependency
  287. */
  288. function fromDepURL($dep)
  289. {
  290. $this->_downloadURL = $dep;
  291. if (isset($dep['uri'])) {
  292. $options = $this->_downloader->getOptions();
  293. if (!extension_loaded("zlib") || isset($options['nocompress'])) {
  294. $ext = '.tar';
  295. } else {
  296. $ext = '.tgz';
  297. }
  298. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  299. $err = $this->_fromUrl($dep['uri'] . $ext);
  300. PEAR::popErrorHandling();
  301. if (PEAR::isError($err)) {
  302. if (!isset($options['soft'])) {
  303. $this->_downloader->log(0, $err->getMessage());
  304. }
  305. return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
  306. 'cannot download');
  307. }
  308. } else {
  309. $this->_parsedname =
  310. array(
  311. 'package' => $dep['info']->getPackage(),
  312. 'channel' => $dep['info']->getChannel(),
  313. 'version' => $dep['version']
  314. );
  315. if (!isset($dep['nodefault'])) {
  316. $this->_parsedname['group'] = 'default'; // download the default dependency group
  317. $this->_explicitGroup = false;
  318. }
  319. $this->_rawpackagefile = $dep['raw'];
  320. }
  321. }
  322. function detectDependencies($params)
  323. {
  324. $options = $this->_downloader->getOptions();
  325. if (isset($options['downloadonly'])) {
  326. return;
  327. }
  328. if (isset($options['offline'])) {
  329. $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
  330. return;
  331. }
  332. $pname = $this->getParsedPackage();
  333. if (!$pname) {
  334. return;
  335. }
  336. $deps = $this->getDeps();
  337. if (!$deps) {
  338. return;
  339. }
  340. if (isset($deps['required'])) { // package.xml 2.0
  341. return $this->_detect2($deps, $pname, $options, $params);
  342. }
  343. return $this->_detect1($deps, $pname, $options, $params);
  344. }
  345. function setValidated()
  346. {
  347. $this->_validated = true;
  348. }
  349. function alreadyValidated()
  350. {
  351. return $this->_validated;
  352. }
  353. /**
  354. * Remove packages to be downloaded that are already installed
  355. * @param array of PEAR_Downloader_Package objects
  356. * @static
  357. */
  358. function removeInstalled(&$params)
  359. {
  360. if (!isset($params[0])) {
  361. return;
  362. }
  363. $options = $params[0]->_downloader->getOptions();
  364. if (!isset($options['downloadonly'])) {
  365. foreach ($params as $i => $param) {
  366. $package = $param->getPackage();
  367. $channel = $param->getChannel();
  368. // remove self if already installed with this version
  369. // this does not need any pecl magic - we only remove exact matches
  370. if ($param->_installRegistry->packageExists($package, $channel)) {
  371. $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
  372. if (version_compare($packageVersion, $param->getVersion(), '==')) {
  373. if (!isset($options['force'])) {
  374. $info = $param->getParsedPackage();
  375. unset($info['version']);
  376. unset($info['state']);
  377. if (!isset($options['soft'])) {
  378. $param->_downloader->log(1, 'Skipping package "' .
  379. $param->getShortName() .
  380. '", already installed as version ' . $packageVersion);
  381. }
  382. $params[$i] = false;
  383. }
  384. } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
  385. !isset($options['soft'])) {
  386. $info = $param->getParsedPackage();
  387. $param->_downloader->log(1, 'Skipping package "' .
  388. $param->getShortName() .
  389. '", already installed as version ' . $packageVersion);
  390. $params[$i] = false;
  391. }
  392. }
  393. }
  394. }
  395. PEAR_Downloader_Package::removeDuplicates($params);
  396. }
  397. function _detect2($deps, $pname, $options, $params)
  398. {
  399. $this->_downloadDeps = array();
  400. $groupnotfound = false;
  401. foreach (array('package', 'subpackage') as $packagetype) {
  402. // get required dependency group
  403. if (isset($deps['required'][$packagetype])) {
  404. if (isset($deps['required'][$packagetype][0])) {
  405. foreach ($deps['required'][$packagetype] as $dep) {
  406. if (isset($dep['conflicts'])) {
  407. // skip any package that this package conflicts with
  408. continue;
  409. }
  410. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  411. if (is_array($ret)) {
  412. $this->_downloadDeps[] = $ret;
  413. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  414. $this->_downloader->log(0, $ret->getMessage());
  415. }
  416. }
  417. } else {
  418. $dep = $deps['required'][$packagetype];
  419. if (!isset($dep['conflicts'])) {
  420. // skip any package that this package conflicts with
  421. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  422. if (is_array($ret)) {
  423. $this->_downloadDeps[] = $ret;
  424. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  425. $this->_downloader->log(0, $ret->getMessage());
  426. }
  427. }
  428. }
  429. }
  430. // get optional dependency group, if any
  431. if (isset($deps['optional'][$packagetype])) {
  432. $skipnames = array();
  433. if (!isset($deps['optional'][$packagetype][0])) {
  434. $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
  435. }
  436. foreach ($deps['optional'][$packagetype] as $dep) {
  437. $skip = false;
  438. if (!isset($options['alldeps'])) {
  439. $dep['package'] = $dep['name'];
  440. if (!isset($options['soft'])) {
  441. $this->_downloader->log(3, 'Notice: package "' .
  442. $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
  443. true) . '" optional dependency "' .
  444. $this->_registry->parsedPackageNameToString(array('package' =>
  445. $dep['name'], 'channel' => 'pear.php.net'), true) .
  446. '" will not be automatically downloaded');
  447. }
  448. $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
  449. $skip = true;
  450. unset($dep['package']);
  451. }
  452. $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
  453. if (PEAR::isError($ret) && !isset($options['soft'])) {
  454. $this->_downloader->log(0, $ret->getMessage());
  455. }
  456. if (!$ret) {
  457. $dep['package'] = $dep['name'];
  458. $skip = count($skipnames) ?
  459. $skipnames[count($skipnames) - 1] : '';
  460. if ($skip ==
  461. $this->_registry->parsedPackageNameToString($dep, true)) {
  462. array_pop($skipnames);
  463. }
  464. }
  465. if (!$skip && is_array($ret)) {
  466. $this->_downloadDeps[] = $ret;
  467. }
  468. }
  469. if (count($skipnames)) {
  470. if (!isset($options['soft'])) {
  471. $this->_downloader->log(1, 'Did not download optional dependencies: ' .
  472. implode(', ', $skipnames) .
  473. ', use --alldeps to download automatically');
  474. }
  475. }
  476. }
  477. // get requested dependency group, if any
  478. $groupname = $this->getGroup();
  479. $explicit = $this->_explicitGroup;
  480. if (!$groupname) {
  481. if (!$this->canDefault()) {
  482. continue;
  483. }
  484. $groupname = 'default'; // try the default dependency group
  485. }
  486. if ($groupnotfound) {
  487. continue;
  488. }
  489. if (isset($deps['group'])) {
  490. if (isset($deps['group']['attribs'])) {
  491. if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
  492. $group = $deps['group'];
  493. } elseif ($explicit) {
  494. if (!isset($options['soft'])) {
  495. $this->_downloader->log(0, 'Warning: package "' .
  496. $this->_registry->parsedPackageNameToString($pname, true) .
  497. '" has no dependency ' . 'group named "' . $groupname . '"');
  498. }
  499. $groupnotfound = true;
  500. continue;
  501. }
  502. } else {
  503. $found = false;
  504. foreach ($deps['group'] as $group) {
  505. if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
  506. $found = true;
  507. break;
  508. }
  509. }
  510. if (!$found) {
  511. if ($explicit) {
  512. if (!isset($options['soft'])) {
  513. $this->_downloader->log(0, 'Warning: package "' .
  514. $this->_registry->parsedPackageNameToString($pname, true) .
  515. '" has no dependency ' . 'group named "' . $groupname . '"');
  516. }
  517. }
  518. $groupnotfound = true;
  519. continue;
  520. }
  521. }
  522. }
  523. if (isset($group) && isset($group[$packagetype])) {
  524. if (isset($group[$packagetype][0])) {
  525. foreach ($group[$packagetype] as $dep) {
  526. $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
  527. $group['attribs']['name'] . '"', $params);
  528. if (is_array($ret)) {
  529. $this->_downloadDeps[] = $ret;
  530. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  531. $this->_downloader->log(0, $ret->getMessage());
  532. }
  533. }
  534. } else {
  535. $ret = $this->_detect2Dep($group[$packagetype], $pname,
  536. 'dependency group "' .
  537. $group['attribs']['name'] . '"', $params);
  538. if (is_array($ret)) {
  539. $this->_downloadDeps[] = $ret;
  540. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  541. $this->_downloader->log(0, $ret->getMessage());
  542. }
  543. }
  544. }
  545. }
  546. }
  547. function _detect2Dep($dep, $pname, $group, $params)
  548. {
  549. if (isset($dep['conflicts'])) {
  550. return true;
  551. }
  552. $options = $this->_downloader->getOptions();
  553. if (isset($dep['uri'])) {
  554. return array('uri' => $dep['uri'], 'dep' => $dep);;
  555. }
  556. $testdep = $dep;
  557. $testdep['package'] = $dep['name'];
  558. if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
  559. $dep['package'] = $dep['name'];
  560. if (!isset($options['soft'])) {
  561. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
  562. ' dependency "' .
  563. $this->_registry->parsedPackageNameToString($dep, true) .
  564. '", will be installed');
  565. }
  566. return false;
  567. }
  568. $options = $this->_downloader->getOptions();
  569. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  570. if ($this->_explicitState) {
  571. $pname['state'] = $this->_explicitState;
  572. }
  573. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  574. if (PEAR::isError($url)) {
  575. PEAR::popErrorHandling();
  576. return $url;
  577. }
  578. $dep['package'] = $dep['name'];
  579. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
  580. !isset($options['alldeps']), true);
  581. PEAR::popErrorHandling();
  582. if (PEAR::isError($ret)) {
  583. if (!isset($options['soft'])) {
  584. $this->_downloader->log(0, $ret->getMessage());
  585. }
  586. return false;
  587. }
  588. // check to see if a dep is already installed and is the same or newer
  589. if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
  590. $oper = 'has';
  591. } else {
  592. $oper = 'gt';
  593. }
  594. // do not try to move this before getDepPackageDownloadURL
  595. // we can't determine whether upgrade is necessary until we know what
  596. // version would be downloaded
  597. if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
  598. $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
  599. $dep['package'] = $dep['name'];
  600. if (!isset($options['soft'])) {
  601. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  602. ' dependency "' .
  603. $this->_registry->parsedPackageNameToString($dep, true) .
  604. '" version ' . $url['version'] . ', already installed as version ' .
  605. $version);
  606. }
  607. return false;
  608. }
  609. if (isset($dep['nodefault'])) {
  610. $ret['nodefault'] = true;
  611. }
  612. return $ret;
  613. }
  614. function _detect1($deps, $pname, $options, $params)
  615. {
  616. $this->_downloadDeps = array();
  617. $skipnames = array();
  618. foreach ($deps as $dep) {
  619. $nodownload = false;
  620. if (isset ($dep['type']) && $dep['type'] === 'pkg') {
  621. $dep['channel'] = 'pear.php.net';
  622. $dep['package'] = $dep['name'];
  623. switch ($dep['rel']) {
  624. case 'not' :
  625. continue 2;
  626. case 'ge' :
  627. case 'eq' :
  628. case 'gt' :
  629. case 'has' :
  630. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  631. 'required' :
  632. 'optional';
  633. if (PEAR_Downloader_Package::willDownload($dep, $params)) {
  634. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  635. . ' dependency "' .
  636. $this->_registry->parsedPackageNameToString($dep, true) .
  637. '", will be installed');
  638. continue 2;
  639. }
  640. $fakedp = new PEAR_PackageFile_v1;
  641. $fakedp->setPackage($dep['name']);
  642. // skip internet check if we are not upgrading (bug #5810)
  643. if (!isset($options['upgrade']) && $this->isInstalled(
  644. $fakedp, $dep['rel'])) {
  645. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  646. . ' dependency "' .
  647. $this->_registry->parsedPackageNameToString($dep, true) .
  648. '", is already installed');
  649. continue 2;
  650. }
  651. }
  652. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  653. if ($this->_explicitState) {
  654. $pname['state'] = $this->_explicitState;
  655. }
  656. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  657. $chan = 'pear.php.net';
  658. if (PEAR::isError($url)) {
  659. // check to see if this is a pecl package that has jumped
  660. // from pear.php.net to pecl.php.net channel
  661. if (!class_exists('PEAR_Dependency2')) {
  662. require_once 'PEAR/Dependency2.php';
  663. }
  664. $newdep = PEAR_Dependency2::normalizeDep($dep);
  665. $newdep = $newdep[0];
  666. $newdep['channel'] = 'pecl.php.net';
  667. $chan = 'pecl.php.net';
  668. $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
  669. $obj = &$this->_installRegistry->getPackage($dep['name']);
  670. if (PEAR::isError($url)) {
  671. PEAR::popErrorHandling();
  672. if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
  673. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  674. 'required' :
  675. 'optional';
  676. $dep['package'] = $dep['name'];
  677. if (!isset($options['soft'])) {
  678. $this->_downloader->log(3, $this->getShortName() .
  679. ': Skipping ' . $group . ' dependency "' .
  680. $this->_registry->parsedPackageNameToString($dep, true) .
  681. '", already installed as version ' . $obj->getVersion());
  682. }
  683. $skip = count($skipnames) ?
  684. $skipnames[count($skipnames) - 1] : '';
  685. if ($skip ==
  686. $this->_registry->parsedPackageNameToString($dep, true)) {
  687. array_pop($skipnames);
  688. }
  689. continue;
  690. } else {
  691. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  692. $this->_downloader->log(2, $this->getShortName() .
  693. ': Skipping optional dependency "' .
  694. $this->_registry->parsedPackageNameToString($dep, true) .
  695. '", no releases exist');
  696. continue;
  697. } else {
  698. return $url;
  699. }
  700. }
  701. }
  702. }
  703. PEAR::popErrorHandling();
  704. if (!isset($options['alldeps'])) {
  705. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  706. if (!isset($options['soft'])) {
  707. $this->_downloader->log(3, 'Notice: package "' .
  708. $this->getShortName() .
  709. '" optional dependency "' .
  710. $this->_registry->parsedPackageNameToString(
  711. array('channel' => $chan, 'package' =>
  712. $dep['name']), true) .
  713. '" will not be automatically downloaded');
  714. }
  715. $skipnames[] = $this->_registry->parsedPackageNameToString(
  716. array('channel' => $chan, 'package' =>
  717. $dep['name']), true);
  718. $nodownload = true;
  719. }
  720. }
  721. if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
  722. if (!isset($dep['optional']) || $dep['optional'] == 'no') {
  723. if (!isset($options['soft'])) {
  724. $this->_downloader->log(3, 'Notice: package "' .
  725. $this->getShortName() .
  726. '" required dependency "' .
  727. $this->_registry->parsedPackageNameToString(
  728. array('channel' => $chan, 'package' =>
  729. $dep['name']), true) .
  730. '" will not be automatically downloaded');
  731. }
  732. $skipnames[] = $this->_registry->parsedPackageNameToString(
  733. array('channel' => $chan, 'package' =>
  734. $dep['name']), true);
  735. $nodownload = true;
  736. }
  737. }
  738. // check to see if a dep is already installed
  739. // do not try to move this before getDepPackageDownloadURL
  740. // we can't determine whether upgrade is necessary until we know what
  741. // version would be downloaded
  742. if (!isset($options['force']) && $this->isInstalled(
  743. $url, $dep['rel'])) {
  744. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  745. 'required' :
  746. 'optional';
  747. $dep['package'] = $dep['name'];
  748. if (isset($newdep)) {
  749. $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
  750. } else {
  751. $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
  752. }
  753. $dep['version'] = $url['version'];
  754. if (!isset($options['soft'])) {
  755. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  756. ' dependency "' .
  757. $this->_registry->parsedPackageNameToString($dep, true) .
  758. '", already installed as version ' . $version);
  759. }
  760. $skip = count($skipnames) ?
  761. $skipnames[count($skipnames) - 1] : '';
  762. if ($skip ==
  763. $this->_registry->parsedPackageNameToString($dep, true)) {
  764. array_pop($skipnames);
  765. }
  766. continue;
  767. }
  768. if ($nodownload) {
  769. continue;
  770. }
  771. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  772. if (isset($newdep)) {
  773. $dep = $newdep;
  774. }
  775. $dep['package'] = $dep['name'];
  776. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
  777. isset($dep['optional']) && $dep['optional'] == 'yes' &&
  778. !isset($options['alldeps']), true);
  779. PEAR::popErrorHandling();
  780. if (PEAR::isError($ret)) {
  781. if (!isset($options['soft'])) {
  782. $this->_downloader->log(0, $ret->getMessage());
  783. }
  784. continue;
  785. }
  786. $this->_downloadDeps[] = $ret;
  787. }
  788. }
  789. if (count($skipnames)) {
  790. if (!isset($options['soft'])) {
  791. $this->_downloader->log(1, 'Did not download dependencies: ' .
  792. implode(', ', $skipnames) .
  793. ', use --alldeps or --onlyreqdeps to download automatically');
  794. }
  795. }
  796. }
  797. function setDownloadURL($pkg)
  798. {
  799. $this->_downloadURL = $pkg;
  800. }
  801. /**
  802. * Set the package.xml object for this downloaded package
  803. *
  804. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
  805. */
  806. function setPackageFile(&$pkg)
  807. {
  808. $this->_packagefile = &$pkg;
  809. }
  810. function getShortName()
  811. {
  812. return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
  813. 'package' => $this->getPackage()), true);
  814. }
  815. function getParsedPackage()
  816. {
  817. if (isset($this->_packagefile) || isset($this->_parsedname)) {
  818. return array('channel' => $this->getChannel(),
  819. 'package' => $this->getPackage(),
  820. 'version' => $this->getVersion());
  821. }
  822. return false;
  823. }
  824. function getDownloadURL()
  825. {
  826. return $this->_downloadURL;
  827. }
  828. function canDefault()
  829. {
  830. if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
  831. return false;
  832. }
  833. return true;
  834. }
  835. function getPackage()
  836. {
  837. if (isset($this->_packagefile)) {
  838. return $this->_packagefile->getPackage();
  839. } elseif (isset($this->_downloadURL['info'])) {
  840. return $this->_downloadURL['info']->getPackage();
  841. }
  842. return false;
  843. }
  844. /**
  845. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  846. */
  847. function isSubpackage(&$pf)
  848. {
  849. if (isset($this->_packagefile)) {
  850. return $this->_packagefile->isSubpackage($pf);
  851. } elseif (isset($this->_downloadURL['info'])) {
  852. return $this->_downloadURL['info']->isSubpackage($pf);
  853. }
  854. return false;
  855. }
  856. function getPackageType()
  857. {
  858. if (isset($this->_packagefile)) {
  859. return $this->_packagefile->getPackageType();
  860. } elseif (isset($this->_downloadURL['info'])) {
  861. return $this->_downloadURL['info']->getPackageType();
  862. }
  863. return false;
  864. }
  865. function isBundle()
  866. {
  867. if (isset($this->_packagefile)) {
  868. return $this->_packagefile->getPackageType() == 'bundle';
  869. }
  870. return false;
  871. }
  872. function getPackageXmlVersion()
  873. {
  874. if (isset($this->_packagefile)) {
  875. return $this->_packagefile->getPackagexmlVersion();
  876. } elseif (isset($this->_downloadURL['info'])) {
  877. return $this->_downloadURL['info']->getPackagexmlVersion();
  878. }
  879. return '1.0';
  880. }
  881. function getChannel()
  882. {
  883. if (isset($this->_packagefile)) {
  884. return $this->_packagefile->getChannel();
  885. } elseif (isset($this->_downloadURL['info'])) {
  886. return $this->_downloadURL['info']->getChannel();
  887. }
  888. return false;
  889. }
  890. function getURI()
  891. {
  892. if (isset($this->_packagefile)) {
  893. return $this->_packagefile->getURI();
  894. } elseif (isset($this->_downloadURL['info'])) {
  895. return $this->_downloadURL['info']->getURI();
  896. }
  897. return false;
  898. }
  899. function getVersion()
  900. {
  901. if (isset($this->_packagefile)) {
  902. return $this->_packagefile->getVersion();
  903. } elseif (isset($this->_downloadURL['version'])) {
  904. return $this->_downloadURL['version'];
  905. }
  906. return false;
  907. }
  908. function isCompatible($pf)
  909. {
  910. if (isset($this->_packagefile)) {
  911. return $this->_packagefile->isCompatible($pf);
  912. } elseif (isset($this->_downloadURL['info'])) {
  913. return $this->_downloadURL['info']->isCompatible($pf);
  914. }
  915. return true;
  916. }
  917. function setGroup($group)
  918. {
  919. $this->_parsedname['group'] = $group;
  920. }
  921. function getGroup()
  922. {
  923. if (isset($this->_parsedname['group'])) {
  924. return $this->_parsedname['group'];
  925. }
  926. return '';
  927. }
  928. function isExtension($name)
  929. {
  930. if (isset($this->_packagefile)) {
  931. return $this->_packagefile->isExtension($name);
  932. } elseif (isset($this->_downloadURL['info'])) {
  933. if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
  934. return $this->_downloadURL['info']->getProvidesExtension() == $name;
  935. }
  936. return false;
  937. }
  938. return false;
  939. }
  940. function getDeps()
  941. {
  942. if (isset($this->_packagefile)) {
  943. $ver = $this->_packagefile->getPackagexmlVersion();
  944. if (version_compare($ver, '2.0', '>=')) {
  945. return $this->_packagefile->getDeps(true);
  946. }
  947. return $this->_packagefile->getDeps();
  948. } elseif (isset($this->_downloadURL['info'])) {
  949. $ver = $this->_downloadURL['info']->getPackagexmlVersion();
  950. if (version_compare($ver, '2.0', '>=')) {
  951. return $this->_downloadURL['info']->getDeps(true);
  952. }
  953. return $this->_downloadURL['info']->getDeps();
  954. }
  955. return array();
  956. }
  957. /**
  958. * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
  959. * returned from getDepDownloadURL()
  960. */
  961. function isEqual($param)
  962. {
  963. if (is_object($param)) {
  964. $channel = $param->getChannel();
  965. $package = $param->getPackage();
  966. if ($param->getURI()) {
  967. $param = array(
  968. 'channel' => $param->getChannel(),
  969. 'package' => $param->getPackage(),
  970. 'version' => $param->getVersion(),
  971. 'uri' => $param->getURI(),
  972. );
  973. } else {
  974. $param = array(
  975. 'channel' => $param->getChannel(),
  976. 'package' => $param->getPackage(),
  977. 'version' => $param->getVersion(),
  978. );
  979. }
  980. } else {
  981. if (isset($param['uri'])) {
  982. if ($this->getChannel() != '__uri') {
  983. return false;
  984. }
  985. return $param['uri'] == $this->getURI();
  986. }
  987. $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
  988. $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
  989. if (isset($param['rel'])) {
  990. if (!class_exists('PEAR_Dependency2')) {
  991. require_once 'PEAR/Dependency2.php';
  992. }
  993. $newdep = PEAR_Dependency2::normalizeDep($param);
  994. $newdep = $newdep[0];
  995. } elseif (isset($param['min'])) {
  996. $newdep = $param;
  997. }
  998. }
  999. if (isset($newdep)) {
  1000. if (!isset($newdep['min'])) {
  1001. $newdep['min'] = '0';
  1002. }
  1003. if (!isset($newdep['max'])) {
  1004. $newdep['max'] = '100000000000000000000';
  1005. }
  1006. // use magic to support pecl packages suddenly jumping to the pecl channel
  1007. // we need to support both dependency possibilities
  1008. if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
  1009. if ($package == $this->getPackage()) {
  1010. $channel = 'pecl.php.net';
  1011. }
  1012. }
  1013. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  1014. if ($package == $this->getPackage()) {
  1015. $channel = 'pear.php.net';
  1016. }
  1017. }
  1018. return (strtolower($package) == strtolower($this->getPackage()) &&
  1019. $channel == $this->getChannel() &&
  1020. version_compare($newdep['min'], $this->getVersion(), '<=') &&
  1021. version_compare($newdep['max'], $this->getVersion(), '>='));
  1022. }
  1023. // use magic to support pecl packages suddenly jumping to the pecl channel
  1024. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  1025. if (strtolower($package) == strtolower($this->getPackage())) {
  1026. $channel = 'pear.php.net';
  1027. }
  1028. }
  1029. if (isset($param['version'])) {
  1030. return (strtolower($package) == strtolower($this->getPackage()) &&
  1031. $channel == $this->getChannel() &&
  1032. $param['version'] == $this->getVersion());
  1033. }
  1034. return strtolower($package) == strtolower($this->getPackage()) &&
  1035. $channel == $this->getChannel();
  1036. }
  1037. function isInstalled($dep, $oper = '==')
  1038. {
  1039. if (!$dep) {
  1040. return false;
  1041. }
  1042. if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
  1043. return false;
  1044. }
  1045. if (is_object($dep)) {
  1046. $package = $dep->getPackage();
  1047. $channel = $dep->getChannel();
  1048. if ($dep->getURI()) {
  1049. $dep = array(
  1050. 'uri' => $dep->getURI(),
  1051. 'version' => $dep->getVersion(),
  1052. );
  1053. } else {
  1054. $dep = array(
  1055. 'version' => $dep->getVersion(),
  1056. );
  1057. }
  1058. } else {
  1059. if (isset($dep['uri'])) {
  1060. $channel = '__uri';
  1061. $package = $dep['dep']['name'];
  1062. } else {
  1063. $channel = $dep['info']->getChannel();
  1064. $package = $dep['info']->getPackage();
  1065. }
  1066. }
  1067. $options = $this->_downloader->getOptions();
  1068. $test = $this->_installRegistry->packageExists($package, $channel);
  1069. if (!$test && $channel == 'pecl.php.net') {
  1070. // do magic to allow upgrading from old pecl packages to new ones
  1071. $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
  1072. $channel = 'pear.php.net';
  1073. }
  1074. if ($test) {
  1075. if (isset($dep['uri'])) {
  1076. if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
  1077. return true;
  1078. }
  1079. }
  1080. if (isset($options['upgrade'])) {
  1081. $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
  1082. if (version_compare($packageVersion, $dep['version'], '>=')) {
  1083. return true;
  1084. }
  1085. return false;
  1086. }
  1087. return true;
  1088. }
  1089. return false;
  1090. }
  1091. /**
  1092. * Detect duplicate package names with differing versions
  1093. *
  1094. * If a user requests to install Date 1.4.6 and Date 1.4.7,
  1095. * for instance, this is a logic error. This method
  1096. * detects this situation.
  1097. *
  1098. * @param array $params array of PEAR_Downloader_Package objects
  1099. * @param array $errorparams empty array
  1100. * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
  1101. */
  1102. function detectStupidDuplicates($params, &$errorparams)
  1103. {
  1104. $existing = array();
  1105. foreach ($params as $i => $param) {
  1106. $package = $param->getPackage();
  1107. $channel = $param->getChannel();
  1108. $group = $param->getGroup();
  1109. if (!isset($existing[$channel . '/' . $package])) {
  1110. $existing[$channel . '/' . $package] = array();
  1111. }
  1112. if (!isset($existing[$channel . '/' . $package][$group])) {
  1113. $existing[$channel . '/' . $package][$group] = array();
  1114. }
  1115. $existing[$channel . '/' . $package][$group][] = $i;
  1116. }
  1117. $indices = array();
  1118. foreach ($existing as $package => $groups) {
  1119. foreach ($groups as $group => $dupes) {
  1120. if (count($dupes) > 1) {
  1121. $indices = $indices + $dupes;
  1122. }
  1123. }
  1124. }
  1125. $indices = array_unique($indices);
  1126. foreach ($indices as $index) {
  1127. $errorparams[] = $params[$index];
  1128. }
  1129. return count($errorparams);
  1130. }
  1131. /**
  1132. * @param array
  1133. * @param bool ignore install groups - for final removal of dupe packages
  1134. * @static
  1135. */
  1136. function removeDuplicates(&$params, $ignoreGroups = false)
  1137. {
  1138. $pnames = array();
  1139. foreach ($params as $i => $param) {
  1140. if (!$param) {
  1141. continue;
  1142. }
  1143. if ($param->getPackage()) {
  1144. $group = $ignoreGroups ? '' : $param->getGroup();
  1145. $pnames[$i] = $param->getChannel() . '/' .
  1146. $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
  1147. }
  1148. }
  1149. $pnames = array_unique($pnames);
  1150. $unset = array_diff(array_keys($params), array_keys($pnames));
  1151. $testp = array_flip($pnames);
  1152. foreach ($params as $i => $param) {
  1153. if (!$param) {
  1154. $unset[] = $i;
  1155. continue;
  1156. }
  1157. if (!is_a($param, 'PEAR_Downloader_Package')) {
  1158. $unset[] = $i;
  1159. continue;
  1160. }
  1161. $group = $ignoreGroups ? '' : $param->getGroup();
  1162. if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
  1163. $param->getVersion() . '#' . $group])) {
  1164. $unset[] = $i;
  1165. }
  1166. }
  1167. foreach ($unset as $i) {
  1168. unset($params[$i]);
  1169. }
  1170. $ret = array();
  1171. foreach ($params as $i => $param) {
  1172. $ret[] = &$params[$i];
  1173. }
  1174. $params = array();
  1175. foreach ($ret as $i => $param) {
  1176. $params[] = &$ret[$i];
  1177. }
  1178. }
  1179. function explicitState()
  1180. {
  1181. return $this->_explicitState;
  1182. }
  1183. function setExplicitState($s)
  1184. {
  1185. $this->_explicitState = $s;
  1186. }
  1187. /**
  1188. * @static
  1189. */
  1190. function mergeDependencies(&$params)
  1191. {
  1192. $bundles = $newparams = array();
  1193. foreach ($params as $i => $param) {
  1194. if (!$param->isBundle()) {
  1195. continue;
  1196. }
  1197. $bundles[] = $i;
  1198. $pf = &$param->getPackageFile();
  1199. $newdeps = array();
  1200. $contents = $pf->getBundledPackages();
  1201. if (!is_array($contents)) {
  1202. $contents = array($contents);
  1203. }
  1204. foreach ($contents as $file) {
  1205. $filecontents = $pf->getFileContents($file);
  1206. $dl = &$param->getDownloader();
  1207. $options = $dl->getOptions();
  1208. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  1209. return $dir;
  1210. }
  1211. $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
  1212. if (!$fp) {
  1213. continue;
  1214. }
  1215. // FIXME do symlink check
  1216. fwrite($fp, $filecontents, strlen($filecontents));
  1217. fclose($fp);
  1218. if ($s = $params[$i]->explicitState()) {
  1219. $obj->setExplicitState($s);
  1220. }
  1221. $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
  1222. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1223. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  1224. PEAR::popErrorHandling();
  1225. return $dir;
  1226. }
  1227. $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
  1228. PEAR::popErrorHandling();
  1229. if (PEAR::isError($e)) {
  1230. if (!isset($options['soft'])) {
  1231. $dl->log(0, $e->getMessage());
  1232. }
  1233. continue;
  1234. }
  1235. $j = &$obj;
  1236. if (!PEAR_Downloader_Package::willDownload($j,
  1237. array_merge($params, $newparams)) && !$param->isInstalled($j)) {
  1238. $newparams[] = &$j;
  1239. }
  1240. }
  1241. }
  1242. foreach ($bundles as $i) {
  1243. unset($params[$i]); // remove bundles - only their contents matter for installation
  1244. }
  1245. PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
  1246. if (count($newparams)) { // add in bundled packages for install
  1247. foreach ($newparams as $i => $unused) {
  1248. $params[] = &$newparams[$i];
  1249. }
  1250. $newparams = array();
  1251. }
  1252. foreach ($params as $i => $param) {
  1253. $newdeps = array();
  1254. foreach ($param->_downloadDeps as $dep) {
  1255. $merge = array_merge($params, $newparams);
  1256. if (!PEAR_Downloader_Package::willDownload($dep, $merge)
  1257. && !$param->isInstalled($dep)
  1258. ) {
  1259. $newdeps[] = $dep;
  1260. } else {
  1261. //var_dump($dep);
  1262. // detect versioning conflicts here
  1263. }
  1264. }
  1265. // convert the dependencies into PEAR_Downloader_Package objects for the next time around
  1266. $params[$i]->_downloadDeps = array();
  1267. foreach ($newdeps as $dep) {
  1268. $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
  1269. if ($s = $params[$i]->explicitState()) {
  1270. $obj->setExplicitState($s);
  1271. }
  1272. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1273. $e = $obj->fromDepURL($dep);
  1274. PEAR::popErrorHandling();
  1275. if (PEAR::isError($e)) {
  1276. if (!isset($options['soft'])) {
  1277. $obj->_downloader->log(0, $e->getMessage());
  1278. }
  1279. continue;
  1280. }
  1281. $e = $obj->detectDependencies($params);
  1282. if (PEAR::isError($e)) {
  1283. if (!isset($options['soft'])) {
  1284. $obj->_downloader->log(0, $e->getMessage());
  1285. }
  1286. }
  1287. $j = &$obj;
  1288. $newparams[] = &$j;
  1289. }
  1290. }
  1291. if (count($newparams)) {
  1292. foreach ($newparams as $i => $unused) {
  1293. $params[] = &$newparams[$i];
  1294. }
  1295. return true;
  1296. }
  1297. return false;
  1298. }
  1299. /**
  1300. * @static
  1301. */
  1302. function willDownload($param, $params)
  1303. {
  1304. if (!is_array($params)) {
  1305. return false;
  1306. }
  1307. foreach ($params as $obj) {
  1308. if ($obj->isEqual($param)) {
  1309. return true;
  1310. }
  1311. }
  1312. return false;
  1313. }
  1314. /**
  1315. * For simpler unit-testing
  1316. * @param PEAR_Config
  1317. * @param int
  1318. * @param string
  1319. */
  1320. function &getPackagefileObject(&$c, $d)
  1321. {
  1322. $a = &new PEAR_PackageFile($c, $d);
  1323. return $a;
  1324. }
  1325. /**
  1326. * This will retrieve from a local file if possible, and parse out
  1327. * a group name as well. The original parameter will be modified to reflect this.
  1328. * @param string|array can be a parsed package name as well
  1329. * @access private
  1330. */
  1331. function _fromFile(&$param)
  1332. {
  1333. $saveparam = $param;
  1334. if (is_string($param)) {
  1335. if (!@file_exists($param)) {
  1336. $test = explode('#', $param);
  1337. $group = array_pop($test);
  1338. if (@file_exists(implode('#', $test))) {
  1339. $this->setGroup($group);
  1340. $param = implode('#', $test);
  1341. $this->_explicitGroup = true;
  1342. }
  1343. }
  1344. if (@is_file($param)) {
  1345. $this->_type = 'local';
  1346. $options = $this->_downloader->getOptions();
  1347. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
  1348. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1349. $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
  1350. PEAR::popErrorHandling();
  1351. if (PEAR::isError($pf)) {
  1352. $this->_valid = false;
  1353. $param = $saveparam;
  1354. return $pf;
  1355. }
  1356. $this->_packagefile = &$pf;
  1357. if (!$this->getGroup()) {
  1358. $this->setGroup('default'); // install the default dependency group
  1359. }
  1360. return $this->_valid = true;
  1361. }
  1362. }
  1363. $param = $saveparam;
  1364. return $this->_valid = false;
  1365. }
  1366. function _fromUrl($param, $saveparam = '')
  1367. {
  1368. if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
  1369. $options = $this->_downloader->getOptions();
  1370. $this->_type = 'url';
  1371. $callback = $this->_downloader->ui ?
  1372. array(&$this->_downloader, '_downloadCallback') : null;
  1373. $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
  1374. if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
  1375. $this->_downloader->popErrorHandling();
  1376. return $dir;
  1377. }
  1378. $this->_downloader->log(3, 'Downloading "' . $param . '"');
  1379. $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
  1380. $dir, $callback, null, false, $this->getChannel());
  1381. $this->_downloader->popErrorHandling();
  1382. if (PEAR::isError($file)) {
  1383. if (!empty($saveparam)) {
  1384. $saveparam = ", cannot download \"$saveparam\"";
  1385. }
  1386. $err = PEAR::raiseError('Could not download from "' . $param .
  1387. '"' . $saveparam . ' (' . $file->getMessage() . ')');
  1388. return $err;
  1389. }
  1390. if ($this->_rawpackagefile) {
  1391. require_once 'Archive/Tar.php';
  1392. $tar = &new Archive_Tar($file);
  1393. $packagexml = $tar->extractInString('package2.xml');
  1394. if (!$packagexml) {
  1395. $packagexml = $tar->extractInString('package.xml');
  1396. }
  1397. if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
  1398. str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
  1399. if ($this->getChannel() != 'pear.php.net') {
  1400. return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
  1401. 'not match value returned from xml-rpc');
  1402. }
  1403. // be more lax for the existing PEAR packages that have not-ok
  1404. // characters in their package.xml
  1405. $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
  1406. $this->getPackage() . '" package has invalid characters in its ' .
  1407. 'package.xml. The next version of PEAR may not be able to install ' .
  1408. 'this package for security reasons. Please open a bug report at ' .
  1409. 'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
  1410. }
  1411. }
  1412. // whew, download worked!
  1413. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
  1414. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1415. $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
  1416. PEAR::popErrorHandling();
  1417. if (PEAR::isError($pf)) {
  1418. if (is_array($pf->getUserInfo())) {
  1419. foreach ($pf->getUserInfo() as $err) {
  1420. if (is_array($err)) {
  1421. $err = $err['message'];
  1422. }
  1423. if (!isset($options['soft'])) {
  1424. $this->_downloader->log(0, "Validation Error: $err");
  1425. }
  1426. }
  1427. }
  1428. if (!isset($options['soft'])) {
  1429. $this->_downloader->log(0, $pf->getMessage());
  1430. }
  1431. ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
  1432. /// At least stop all deps of this package from being installed
  1433. $out = $saveparam ? $saveparam : $param;
  1434. $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
  1435. $this->_valid = false;
  1436. return $err;
  1437. }
  1438. $this->_packagefile = &$pf;
  1439. $this->setGroup('default'); // install the default dependency group
  1440. return $this->_valid = true;
  1441. }
  1442. return $this->_valid = false;
  1443. }
  1444. /**
  1445. *
  1446. * @param string|array pass in an array of format
  1447. * array(
  1448. * 'package' => 'pname',
  1449. * ['channel' => 'channame',]
  1450. * ['version' => 'version',]
  1451. * ['state' => 'state',])
  1452. * or a string of format [channame/]pname[-version|-state]
  1453. */
  1454. function _fromString($param)
  1455. {
  1456. $options = $this->_downloader->getOptions();
  1457. $channel = $this->_config->get('default_channel');
  1458. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1459. $pname = $this->_registry->parsePackageName($param, $channel);
  1460. PEAR::popErrorHandling();
  1461. if (PEAR::isError($pname)) {
  1462. if ($pname->getCode() == 'invalid') {
  1463. $this->_valid = false;
  1464. return false;
  1465. }
  1466. if ($pname->getCode() == 'channel') {
  1467. $parsed = $pname->getUserInfo();
  1468. if ($this->_downloader->discover($parsed['channel'])) {
  1469. if ($this->_config->get('auto_discover')) {
  1470. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1471. $pname = $this->_registry->parsePackageName($param, $channel);
  1472. PEAR::popErrorHandling();
  1473. } else {
  1474. if (!isset($options['soft'])) {
  1475. $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
  1476. '" is not initialized, use ' .
  1477. '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
  1478. 'or pear config-set auto_discover 1');
  1479. }
  1480. }
  1481. }
  1482. if (PEAR::isError($pname)) {
  1483. if (!isset($options['soft'])) {
  1484. $this->_downloader->log(0, $pname->getMessage());
  1485. }
  1486. if (is_array($param)) {
  1487. $param = $this->_registry->parsedPackageNameToString($param);
  1488. }
  1489. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  1490. $this->_valid = false;
  1491. return $err;
  1492. }
  1493. } else {
  1494. if (!isset($options['soft'])) {
  1495. $this->_downloader->log(0, $pname->getMessage());
  1496. }
  1497. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  1498. $this->_valid = false;
  1499. return $err;
  1500. }
  1501. }
  1502. if (!isset($this->_type)) {
  1503. $this->_type = 'rest';
  1504. }
  1505. $this->_parsedname = $pname;
  1506. $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
  1507. $this->_explicitGroup = isset($pname['group']) ? true : false;
  1508. $info = $this->_downloader->_getPackageDownloadUrl($pname);
  1509. if (PEAR::isError($info)) {
  1510. if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
  1511. // try pecl
  1512. $pname['channel'] = 'pecl.php.net';
  1513. if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
  1514. if (!PEAR::isError($test)) {
  1515. $info = PEAR::raiseError($info->getMessage() . ' - package ' .
  1516. $this->_registry->parsedPackageNameToString($pname, true) .
  1517. ' can be installed with "pecl install ' . $pname['package'] .
  1518. '"');
  1519. } else {
  1520. $pname['channel'] = 'pear.php.net';
  1521. }
  1522. } else {
  1523. $pname['channel'] = 'pear.php.net';
  1524. }
  1525. }
  1526. return $info;
  1527. }
  1528. $this->_rawpackagefile = $info['raw'];
  1529. $ret = $this->_analyzeDownloadURL($info, $param, $pname);
  1530. if (PEAR::isError($ret)) {
  1531. return $ret;
  1532. }
  1533. if ($ret) {
  1534. $this->_downloadURL = $ret;
  1535. return $this->_valid = (bool) $ret;
  1536. }
  1537. }
  1538. /**
  1539. * @param array output of package.getDownloadURL
  1540. * @param string|array|object information for detecting packages to be downloaded, and
  1541. * for errors
  1542. * @param array name information of the package
  1543. * @param array|null packages to be downloaded
  1544. * @param bool is this an optional dependency?
  1545. * @param bool is this any kind of dependency?
  1546. * @access private
  1547. */
  1548. function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
  1549. $isdependency = false)
  1550. {
  1551. if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
  1552. return false;
  1553. }
  1554. if ($info === false) {
  1555. $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
  1556. // no releases exist
  1557. return PEAR::raiseError('No releases for package "' .
  1558. $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
  1559. }
  1560. if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
  1561. $err = false;
  1562. if ($pname['channel'] == 'pecl.php.net') {
  1563. if ($info['info']->getChannel() != 'pear.php.net') {
  1564. $err = true;
  1565. }
  1566. } elseif ($info['info']->getChannel() == 'pecl.php.net') {
  1567. if ($pname['channel'] != 'pear.php.net') {
  1568. $err = true;
  1569. }
  1570. } else {
  1571. $err = true;
  1572. }
  1573. if ($err) {
  1574. return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
  1575. '" retrieved another channel\'s name for download! ("' .
  1576. $info['info']->getChannel() . '")');
  1577. }
  1578. }
  1579. $preferred_state = $this->_config->get('preferred_state');
  1580. if (!isset($info['url'])) {
  1581. $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
  1582. 'version', $info['info']->getChannel());
  1583. if ($this->isInstalled($info)) {
  1584. if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
  1585. // ignore bogus errors of "failed to download dependency"
  1586. // if it is already installed and the one that would be
  1587. // downloaded is older or the same version (Bug #7219)
  1588. return false;
  1589. }
  1590. }
  1591. if ($info['version'] === $package_version) {
  1592. if (!isset($options['soft'])) {
  1593. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  1594. '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
  1595. ' (' . $package_version . ') is the same as the locally installed one.');
  1596. }
  1597. return false;
  1598. }
  1599. if (version_compare($info['version'], $package_version, '<=')) {
  1600. if (!isset($options['soft'])) {
  1601. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  1602. '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
  1603. ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
  1604. }
  1605. return false;
  1606. }
  1607. $instead = ', will instead download version ' . $info['version'] .
  1608. ', stability "' . $info['info']->getState() . '"';
  1609. // releases exist, but we failed to get any
  1610. if (isset($this->_downloader->_options['force'])) {
  1611. if (isset($pname['version'])) {
  1612. $vs = ', version "' . $pname['version'] . '"';
  1613. } elseif (isset($pname['state'])) {
  1614. $vs = ', stability "' . $pname['state'] . '"';
  1615. } elseif ($param == 'dependency') {
  1616. if (!class_exists('PEAR_Common')) {
  1617. require_once 'PEAR/Common.php';
  1618. }
  1619. if (!in_array($info['info']->getState(),
  1620. PEAR_Common::betterStates($preferred_state, true))) {
  1621. if ($optional) {
  1622. // don't spit out confusing error message
  1623. return $this->_downloader->_getPackageDownloadUrl(
  1624. array('package' => $pname['package'],
  1625. 'channel' => $pname['channel'],
  1626. 'version' => $info['version']));
  1627. }
  1628. $vs = ' within preferred state "' . $preferred_state .
  1629. '"';
  1630. } else {
  1631. if (!class_exists('PEAR_Dependency2')) {
  1632. require_once 'PEAR/Dependency2.php';
  1633. }
  1634. if ($optional) {
  1635. // don't spit out confusing error message
  1636. return $this->_downloader->_getPackageDownloadUrl(
  1637. array('package' => $pname['package'],
  1638. 'channel' => $pname['channel'],
  1639. 'version' => $info['version']));
  1640. }
  1641. $vs = PEAR_Dependency2::_getExtraString($pname);
  1642. $instead = '';
  1643. }
  1644. } else {
  1645. $vs = ' within preferred state "' . $preferred_state . '"';
  1646. }
  1647. if (!isset($options['soft'])) {
  1648. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  1649. '/' . $pname['package'] . $vs . $instead);
  1650. }
  1651. // download the latest release
  1652. return $this->_downloader->_getPackageDownloadUrl(
  1653. array('package' => $pname['package'],
  1654. 'channel' => $pname['channel'],
  1655. 'version' => $info['version']));
  1656. } else {
  1657. if (isset($info['php']) && $info['php']) {
  1658. $err = PEAR::raiseError('Failed to download ' .
  1659. $this->_registry->parsedPackageNameToString(
  1660. array('channel' => $pname['channel'],
  1661. 'package' => $pname['package']),
  1662. true) .
  1663. ', latest release is version ' . $info['php']['v'] .
  1664. ', but it requires PHP version "' .
  1665. $info['php']['m'] . '", use "' .
  1666. $this->_registry->parsedPackageNameToString(
  1667. array('channel' => $pname['channel'], 'package' => $pname['package'],
  1668. 'version' => $info['php']['v'])) . '" to install',
  1669. PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
  1670. return $err;
  1671. }
  1672. // construct helpful error message
  1673. if (isset($pname['version'])) {
  1674. $vs = ', version "' . $pname['version'] . '"';
  1675. } elseif (isset($pname['state'])) {
  1676. $vs = ', stability "' . $pname['state'] . '"';
  1677. } elseif ($param == 'dependency') {
  1678. if (!class_exists('PEAR_Common')) {
  1679. require_once 'PEAR/Common.php';
  1680. }
  1681. if (!in_array($info['info']->getState(),
  1682. PEAR_Common::betterStates($preferred_state, true))) {
  1683. if ($optional) {
  1684. // don't spit out confusing error message, and don't die on
  1685. // optional dep failure!
  1686. return $this->_downloader->_getPackageDownloadUrl(
  1687. array('package' => $pname['package'],
  1688. 'channel' => $pname['channel'],
  1689. 'version' => $info['version']));
  1690. }
  1691. $vs = ' within preferred state "' . $preferred_state . '"';
  1692. } else {
  1693. if (!class_exists('PEAR_Dependency2')) {
  1694. require_once 'PEAR/Dependency2.php';
  1695. }
  1696. if ($optional) {
  1697. // don't spit out confusing error message, and don't die on
  1698. // optional dep failure!
  1699. return $this->_downloader->_getPackageDownloadUrl(
  1700. array('package' => $pname['package'],
  1701. 'channel' => $pname['channel'],
  1702. 'version' => $info['version']));
  1703. }
  1704. $vs = PEAR_Dependency2::_getExtraString($pname);
  1705. }
  1706. } else {
  1707. $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
  1708. }
  1709. $options = $this->_downloader->getOptions();
  1710. // this is only set by the "download-all" command
  1711. if (isset($options['ignorepreferred_state'])) {
  1712. $err = PEAR::raiseError(
  1713. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  1714. array('channel' => $pname['channel'], 'package' => $pname['package']),
  1715. true)
  1716. . $vs .
  1717. ', latest release is version ' . $info['version'] .
  1718. ', stability "' . $info['info']->getState() . '", use "' .
  1719. $this->_registry->parsedPackageNameToString(
  1720. array('channel' => $pname['channel'], 'package' => $pname['package'],
  1721. 'version' => $info['version'])) . '" to install',
  1722. PEAR_DOWNLOADER_PACKAGE_STATE);
  1723. return $err;
  1724. }
  1725. // Checks if the user has a package installed already and checks the release against
  1726. // the state against the installed package, this allows upgrades for packages
  1727. // with lower stability than the preferred_state
  1728. $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
  1729. if (!$this->isInstalled($info)
  1730. || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
  1731. ) {
  1732. $err = PEAR::raiseError(
  1733. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  1734. array('channel' => $pname['channel'], 'package' => $pname['package']),
  1735. true)
  1736. . $vs .
  1737. ', latest release is version ' . $info['version'] .
  1738. ', stability "' . $info['info']->getState() . '", use "' .
  1739. $this->_registry->parsedPackageNameToString(
  1740. array('channel' => $pname['channel'], 'package' => $pname['package'],
  1741. 'version' => $info['version'])) . '" to install');
  1742. return $err;
  1743. }
  1744. }
  1745. }
  1746. if (isset($info['deprecated']) && $info['deprecated']) {
  1747. $this->_downloader->log(0,
  1748. 'WARNING: "' .
  1749. $this->_registry->parsedPackageNameToString(
  1750. array('channel' => $info['info']->getChannel(),
  1751. 'package' => $info['info']->getPackage()), true) .
  1752. '" is deprecated in favor of "' .
  1753. $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
  1754. '"');
  1755. }
  1756. return $info;
  1757. }
  1758. }