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

/lib/php/PEAR/PackageFile/v2/Validator.php

https://bitbucket.org/adarshj/convenient_website
PHP | 2154 lines | 2063 code | 31 blank | 60 comment | 291 complexity | 4bb188500dcecfca3ee085572a6f88d2 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_PackageFile_v2, package.xml version 2.0, read/write version
  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: Validator.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.0a8
  15. */
  16. /**
  17. * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
  18. * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
  19. * @category pear
  20. * @package PEAR
  21. * @author Greg Beaver <cellog@php.net>
  22. * @copyright 1997-2009 The Authors
  23. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24. * @version Release: 1.9.4
  25. * @link http://pear.php.net/package/PEAR
  26. * @since Class available since Release 1.4.0a8
  27. * @access private
  28. */
  29. class PEAR_PackageFile_v2_Validator
  30. {
  31. /**
  32. * @var array
  33. */
  34. var $_packageInfo;
  35. /**
  36. * @var PEAR_PackageFile_v2
  37. */
  38. var $_pf;
  39. /**
  40. * @var PEAR_ErrorStack
  41. */
  42. var $_stack;
  43. /**
  44. * @var int
  45. */
  46. var $_isValid = 0;
  47. /**
  48. * @var int
  49. */
  50. var $_filesValid = 0;
  51. /**
  52. * @var int
  53. */
  54. var $_curState = 0;
  55. /**
  56. * @param PEAR_PackageFile_v2
  57. * @param int
  58. */
  59. function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
  60. {
  61. $this->_pf = &$pf;
  62. $this->_curState = $state;
  63. $this->_packageInfo = $this->_pf->getArray();
  64. $this->_isValid = $this->_pf->_isValid;
  65. $this->_filesValid = $this->_pf->_filesValid;
  66. $this->_stack = &$pf->_stack;
  67. $this->_stack->getErrors(true);
  68. if (($this->_isValid & $state) == $state) {
  69. return true;
  70. }
  71. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  72. return false;
  73. }
  74. if (!isset($this->_packageInfo['attribs']['version']) ||
  75. ($this->_packageInfo['attribs']['version'] != '2.0' &&
  76. $this->_packageInfo['attribs']['version'] != '2.1')
  77. ) {
  78. $this->_noPackageVersion();
  79. }
  80. $structure =
  81. array(
  82. 'name',
  83. 'channel|uri',
  84. '*extends', // can't be multiple, but this works fine
  85. 'summary',
  86. 'description',
  87. '+lead', // these all need content checks
  88. '*developer',
  89. '*contributor',
  90. '*helper',
  91. 'date',
  92. '*time',
  93. 'version',
  94. 'stability',
  95. 'license->?uri->?filesource',
  96. 'notes',
  97. 'contents', //special validation needed
  98. '*compatible',
  99. 'dependencies', //special validation needed
  100. '*usesrole',
  101. '*usestask', // reserve these for 1.4.0a1 to implement
  102. // this will allow a package.xml to gracefully say it
  103. // needs a certain package installed in order to implement a role or task
  104. '*providesextension',
  105. '*srcpackage|*srcuri',
  106. '+phprelease|+extsrcrelease|+extbinrelease|' .
  107. '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
  108. '*changelog',
  109. );
  110. $test = $this->_packageInfo;
  111. if (isset($test['dependencies']) &&
  112. isset($test['dependencies']['required']) &&
  113. isset($test['dependencies']['required']['pearinstaller']) &&
  114. isset($test['dependencies']['required']['pearinstaller']['min']) &&
  115. version_compare('1.9.4',
  116. $test['dependencies']['required']['pearinstaller']['min'], '<')
  117. ) {
  118. $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
  119. return false;
  120. }
  121. // ignore post-installation array fields
  122. if (array_key_exists('filelist', $test)) {
  123. unset($test['filelist']);
  124. }
  125. if (array_key_exists('_lastmodified', $test)) {
  126. unset($test['_lastmodified']);
  127. }
  128. if (array_key_exists('#binarypackage', $test)) {
  129. unset($test['#binarypackage']);
  130. }
  131. if (array_key_exists('old', $test)) {
  132. unset($test['old']);
  133. }
  134. if (array_key_exists('_lastversion', $test)) {
  135. unset($test['_lastversion']);
  136. }
  137. if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
  138. return false;
  139. }
  140. if (empty($this->_packageInfo['name'])) {
  141. $this->_tagCannotBeEmpty('name');
  142. }
  143. $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
  144. if (empty($this->_packageInfo[$test])) {
  145. $this->_tagCannotBeEmpty($test);
  146. }
  147. if (is_array($this->_packageInfo['license']) &&
  148. (!isset($this->_packageInfo['license']['_content']) ||
  149. empty($this->_packageInfo['license']['_content']))) {
  150. $this->_tagCannotBeEmpty('license');
  151. } elseif (empty($this->_packageInfo['license'])) {
  152. $this->_tagCannotBeEmpty('license');
  153. }
  154. if (empty($this->_packageInfo['summary'])) {
  155. $this->_tagCannotBeEmpty('summary');
  156. }
  157. if (empty($this->_packageInfo['description'])) {
  158. $this->_tagCannotBeEmpty('description');
  159. }
  160. if (empty($this->_packageInfo['date'])) {
  161. $this->_tagCannotBeEmpty('date');
  162. }
  163. if (empty($this->_packageInfo['notes'])) {
  164. $this->_tagCannotBeEmpty('notes');
  165. }
  166. if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
  167. $this->_tagCannotBeEmpty('time');
  168. }
  169. if (isset($this->_packageInfo['dependencies'])) {
  170. $this->_validateDependencies();
  171. }
  172. if (isset($this->_packageInfo['compatible'])) {
  173. $this->_validateCompatible();
  174. }
  175. if (!isset($this->_packageInfo['bundle'])) {
  176. if (empty($this->_packageInfo['contents'])) {
  177. $this->_tagCannotBeEmpty('contents');
  178. }
  179. if (!isset($this->_packageInfo['contents']['dir'])) {
  180. $this->_filelistMustContainDir('contents');
  181. return false;
  182. }
  183. if (isset($this->_packageInfo['contents']['file'])) {
  184. $this->_filelistCannotContainFile('contents');
  185. return false;
  186. }
  187. }
  188. $this->_validateMaintainers();
  189. $this->_validateStabilityVersion();
  190. $fail = false;
  191. if (array_key_exists('usesrole', $this->_packageInfo)) {
  192. $roles = $this->_packageInfo['usesrole'];
  193. if (!is_array($roles) || !isset($roles[0])) {
  194. $roles = array($roles);
  195. }
  196. foreach ($roles as $role) {
  197. if (!isset($role['role'])) {
  198. $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
  199. $fail = true;
  200. } else {
  201. if (!isset($role['channel'])) {
  202. if (!isset($role['uri'])) {
  203. $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
  204. $fail = true;
  205. }
  206. } elseif (!isset($role['package'])) {
  207. $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
  208. $fail = true;
  209. }
  210. }
  211. }
  212. }
  213. if (array_key_exists('usestask', $this->_packageInfo)) {
  214. $roles = $this->_packageInfo['usestask'];
  215. if (!is_array($roles) || !isset($roles[0])) {
  216. $roles = array($roles);
  217. }
  218. foreach ($roles as $role) {
  219. if (!isset($role['task'])) {
  220. $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
  221. $fail = true;
  222. } else {
  223. if (!isset($role['channel'])) {
  224. if (!isset($role['uri'])) {
  225. $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
  226. $fail = true;
  227. }
  228. } elseif (!isset($role['package'])) {
  229. $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
  230. $fail = true;
  231. }
  232. }
  233. }
  234. }
  235. if ($fail) {
  236. return false;
  237. }
  238. $list = $this->_packageInfo['contents'];
  239. if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
  240. $this->_multipleToplevelDirNotAllowed();
  241. return $this->_isValid = 0;
  242. }
  243. $this->_validateFilelist();
  244. $this->_validateRelease();
  245. if (!$this->_stack->hasErrors()) {
  246. $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
  247. if (PEAR::isError($chan)) {
  248. $this->_unknownChannel($this->_pf->getChannel());
  249. } else {
  250. $valpack = $chan->getValidationPackage();
  251. // for channel validator packages, always use the default PEAR validator.
  252. // otherwise, they can't be installed or packaged
  253. $validator = $chan->getValidationObject($this->_pf->getPackage());
  254. if (!$validator) {
  255. $this->_stack->push(__FUNCTION__, 'error',
  256. array('channel' => $chan->getName(),
  257. 'package' => $this->_pf->getPackage(),
  258. 'name' => $valpack['_content'],
  259. 'version' => $valpack['attribs']['version']),
  260. 'package "%channel%/%package%" cannot be properly validated without ' .
  261. 'validation package "%channel%/%name%-%version%"');
  262. return $this->_isValid = 0;
  263. }
  264. $validator->setPackageFile($this->_pf);
  265. $validator->validate($state);
  266. $failures = $validator->getFailures();
  267. foreach ($failures['errors'] as $error) {
  268. $this->_stack->push(__FUNCTION__, 'error', $error,
  269. 'Channel validator error: field "%field%" - %reason%');
  270. }
  271. foreach ($failures['warnings'] as $warning) {
  272. $this->_stack->push(__FUNCTION__, 'warning', $warning,
  273. 'Channel validator warning: field "%field%" - %reason%');
  274. }
  275. }
  276. }
  277. $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
  278. if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
  279. if ($this->_pf->getPackageType() == 'bundle') {
  280. if ($this->_analyzeBundledPackages()) {
  281. $this->_filesValid = $this->_pf->_filesValid = true;
  282. } else {
  283. $this->_pf->_isValid = $this->_isValid = 0;
  284. }
  285. } else {
  286. if (!$this->_analyzePhpFiles()) {
  287. $this->_pf->_isValid = $this->_isValid = 0;
  288. } else {
  289. $this->_filesValid = $this->_pf->_filesValid = true;
  290. }
  291. }
  292. }
  293. if ($this->_isValid) {
  294. return $this->_pf->_isValid = $this->_isValid = $state;
  295. }
  296. return $this->_pf->_isValid = $this->_isValid = 0;
  297. }
  298. function _stupidSchemaValidate($structure, $xml, $root)
  299. {
  300. if (!is_array($xml)) {
  301. $xml = array();
  302. }
  303. $keys = array_keys($xml);
  304. reset($keys);
  305. $key = current($keys);
  306. while ($key == 'attribs' || $key == '_contents') {
  307. $key = next($keys);
  308. }
  309. $unfoundtags = $optionaltags = array();
  310. $ret = true;
  311. $mismatch = false;
  312. foreach ($structure as $struc) {
  313. if ($key) {
  314. $tag = $xml[$key];
  315. }
  316. $test = $this->_processStructure($struc);
  317. if (isset($test['choices'])) {
  318. $loose = true;
  319. foreach ($test['choices'] as $choice) {
  320. if ($key == $choice['tag']) {
  321. $key = next($keys);
  322. while ($key == 'attribs' || $key == '_contents') {
  323. $key = next($keys);
  324. }
  325. $unfoundtags = $optionaltags = array();
  326. $mismatch = false;
  327. if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
  328. $unfoundtags[] = $choice['tag'];
  329. $optionaltags[] = $choice['tag'];
  330. if ($key) {
  331. $mismatch = true;
  332. }
  333. }
  334. $ret &= $this->_processAttribs($choice, $tag, $root);
  335. continue 2;
  336. } else {
  337. $unfoundtags[] = $choice['tag'];
  338. $mismatch = true;
  339. }
  340. if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
  341. $loose = false;
  342. } else {
  343. $optionaltags[] = $choice['tag'];
  344. }
  345. }
  346. if (!$loose) {
  347. $this->_invalidTagOrder($unfoundtags, $key, $root);
  348. return false;
  349. }
  350. } else {
  351. if ($key != $test['tag']) {
  352. if (isset($test['multiple']) && $test['multiple'] != '*') {
  353. $unfoundtags[] = $test['tag'];
  354. $this->_invalidTagOrder($unfoundtags, $key, $root);
  355. return false;
  356. } else {
  357. if ($key) {
  358. $mismatch = true;
  359. }
  360. $unfoundtags[] = $test['tag'];
  361. $optionaltags[] = $test['tag'];
  362. }
  363. if (!isset($test['multiple'])) {
  364. $this->_invalidTagOrder($unfoundtags, $key, $root);
  365. return false;
  366. }
  367. continue;
  368. } else {
  369. $unfoundtags = $optionaltags = array();
  370. $mismatch = false;
  371. }
  372. $key = next($keys);
  373. while ($key == 'attribs' || $key == '_contents') {
  374. $key = next($keys);
  375. }
  376. if ($key && $key != $test['tag'] && isset($test['multiple'])) {
  377. $unfoundtags[] = $test['tag'];
  378. $optionaltags[] = $test['tag'];
  379. $mismatch = true;
  380. }
  381. $ret &= $this->_processAttribs($test, $tag, $root);
  382. continue;
  383. }
  384. }
  385. if (!$mismatch && count($optionaltags)) {
  386. // don't error out on any optional tags
  387. $unfoundtags = array_diff($unfoundtags, $optionaltags);
  388. }
  389. if (count($unfoundtags)) {
  390. $this->_invalidTagOrder($unfoundtags, $key, $root);
  391. } elseif ($key) {
  392. // unknown tags
  393. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  394. while ($key = next($keys)) {
  395. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  396. }
  397. }
  398. return $ret;
  399. }
  400. function _processAttribs($choice, $tag, $context)
  401. {
  402. if (isset($choice['attribs'])) {
  403. if (!is_array($tag)) {
  404. $tag = array($tag);
  405. }
  406. $tags = $tag;
  407. if (!isset($tags[0])) {
  408. $tags = array($tags);
  409. }
  410. $ret = true;
  411. foreach ($tags as $i => $tag) {
  412. if (!is_array($tag) || !isset($tag['attribs'])) {
  413. foreach ($choice['attribs'] as $attrib) {
  414. if ($attrib{0} != '?') {
  415. $ret &= $this->_tagHasNoAttribs($choice['tag'],
  416. $context);
  417. continue 2;
  418. }
  419. }
  420. }
  421. foreach ($choice['attribs'] as $attrib) {
  422. if ($attrib{0} != '?') {
  423. if (!isset($tag['attribs'][$attrib])) {
  424. $ret &= $this->_tagMissingAttribute($choice['tag'],
  425. $attrib, $context);
  426. }
  427. }
  428. }
  429. }
  430. return $ret;
  431. }
  432. return true;
  433. }
  434. function _processStructure($key)
  435. {
  436. $ret = array();
  437. if (count($pieces = explode('|', $key)) > 1) {
  438. $ret['choices'] = array();
  439. foreach ($pieces as $piece) {
  440. $ret['choices'][] = $this->_processStructure($piece);
  441. }
  442. return $ret;
  443. }
  444. $multi = $key{0};
  445. if ($multi == '+' || $multi == '*') {
  446. $ret['multiple'] = $key{0};
  447. $key = substr($key, 1);
  448. }
  449. if (count($attrs = explode('->', $key)) > 1) {
  450. $ret['tag'] = array_shift($attrs);
  451. $ret['attribs'] = $attrs;
  452. } else {
  453. $ret['tag'] = $key;
  454. }
  455. return $ret;
  456. }
  457. function _validateStabilityVersion()
  458. {
  459. $structure = array('release', 'api');
  460. $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
  461. $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
  462. if ($a) {
  463. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  464. $this->_packageInfo['version']['release'])) {
  465. $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
  466. }
  467. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  468. $this->_packageInfo['version']['api'])) {
  469. $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
  470. }
  471. if (!in_array($this->_packageInfo['stability']['release'],
  472. array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
  473. $this->_invalidState('release', $this->_packageInfo['stability']['release']);
  474. }
  475. if (!in_array($this->_packageInfo['stability']['api'],
  476. array('devel', 'alpha', 'beta', 'stable'))) {
  477. $this->_invalidState('api', $this->_packageInfo['stability']['api']);
  478. }
  479. }
  480. }
  481. function _validateMaintainers()
  482. {
  483. $structure =
  484. array(
  485. 'name',
  486. 'user',
  487. 'email',
  488. 'active',
  489. );
  490. foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
  491. if (!isset($this->_packageInfo[$type])) {
  492. continue;
  493. }
  494. if (isset($this->_packageInfo[$type][0])) {
  495. foreach ($this->_packageInfo[$type] as $lead) {
  496. $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
  497. }
  498. } else {
  499. $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
  500. '<' . $type . '>');
  501. }
  502. }
  503. }
  504. function _validatePhpDep($dep, $installcondition = false)
  505. {
  506. $structure = array(
  507. 'min',
  508. '*max',
  509. '*exclude',
  510. );
  511. $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
  512. $this->_stupidSchemaValidate($structure, $dep, $type);
  513. if (isset($dep['min'])) {
  514. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  515. $dep['min'])) {
  516. $this->_invalidVersion($type . '<min>', $dep['min']);
  517. }
  518. }
  519. if (isset($dep['max'])) {
  520. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  521. $dep['max'])) {
  522. $this->_invalidVersion($type . '<max>', $dep['max']);
  523. }
  524. }
  525. if (isset($dep['exclude'])) {
  526. if (!is_array($dep['exclude'])) {
  527. $dep['exclude'] = array($dep['exclude']);
  528. }
  529. foreach ($dep['exclude'] as $exclude) {
  530. if (!preg_match(
  531. '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  532. $exclude)) {
  533. $this->_invalidVersion($type . '<exclude>', $exclude);
  534. }
  535. }
  536. }
  537. }
  538. function _validatePearinstallerDep($dep)
  539. {
  540. $structure = array(
  541. 'min',
  542. '*max',
  543. '*recommended',
  544. '*exclude',
  545. );
  546. $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
  547. if (isset($dep['min'])) {
  548. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  549. $dep['min'])) {
  550. $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
  551. $dep['min']);
  552. }
  553. }
  554. if (isset($dep['max'])) {
  555. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  556. $dep['max'])) {
  557. $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
  558. $dep['max']);
  559. }
  560. }
  561. if (isset($dep['recommended'])) {
  562. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  563. $dep['recommended'])) {
  564. $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
  565. $dep['recommended']);
  566. }
  567. }
  568. if (isset($dep['exclude'])) {
  569. if (!is_array($dep['exclude'])) {
  570. $dep['exclude'] = array($dep['exclude']);
  571. }
  572. foreach ($dep['exclude'] as $exclude) {
  573. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  574. $exclude)) {
  575. $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
  576. $exclude);
  577. }
  578. }
  579. }
  580. }
  581. function _validatePackageDep($dep, $group, $type = '<package>')
  582. {
  583. if (isset($dep['uri'])) {
  584. if (isset($dep['conflicts'])) {
  585. $structure = array(
  586. 'name',
  587. 'uri',
  588. 'conflicts',
  589. '*providesextension',
  590. );
  591. } else {
  592. $structure = array(
  593. 'name',
  594. 'uri',
  595. '*providesextension',
  596. );
  597. }
  598. } else {
  599. if (isset($dep['conflicts'])) {
  600. $structure = array(
  601. 'name',
  602. 'channel',
  603. '*min',
  604. '*max',
  605. '*exclude',
  606. 'conflicts',
  607. '*providesextension',
  608. );
  609. } else {
  610. $structure = array(
  611. 'name',
  612. 'channel',
  613. '*min',
  614. '*max',
  615. '*recommended',
  616. '*exclude',
  617. '*nodefault',
  618. '*providesextension',
  619. );
  620. }
  621. }
  622. if (isset($dep['name'])) {
  623. $type .= '<name>' . $dep['name'] . '</name>';
  624. }
  625. $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
  626. if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
  627. isset($dep['recommended']) || isset($dep['exclude']))) {
  628. $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
  629. }
  630. if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
  631. $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
  632. }
  633. if (isset($dep['min'])) {
  634. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  635. $dep['min'])) {
  636. $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
  637. }
  638. }
  639. if (isset($dep['max'])) {
  640. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  641. $dep['max'])) {
  642. $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
  643. }
  644. }
  645. if (isset($dep['recommended'])) {
  646. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  647. $dep['recommended'])) {
  648. $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
  649. $dep['recommended']);
  650. }
  651. }
  652. if (isset($dep['exclude'])) {
  653. if (!is_array($dep['exclude'])) {
  654. $dep['exclude'] = array($dep['exclude']);
  655. }
  656. foreach ($dep['exclude'] as $exclude) {
  657. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  658. $exclude)) {
  659. $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
  660. $exclude);
  661. }
  662. }
  663. }
  664. }
  665. function _validateSubpackageDep($dep, $group)
  666. {
  667. $this->_validatePackageDep($dep, $group, '<subpackage>');
  668. if (isset($dep['providesextension'])) {
  669. $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
  670. }
  671. if (isset($dep['conflicts'])) {
  672. $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
  673. }
  674. }
  675. function _validateExtensionDep($dep, $group = false, $installcondition = false)
  676. {
  677. if (isset($dep['conflicts'])) {
  678. $structure = array(
  679. 'name',
  680. '*min',
  681. '*max',
  682. '*exclude',
  683. 'conflicts',
  684. );
  685. } else {
  686. $structure = array(
  687. 'name',
  688. '*min',
  689. '*max',
  690. '*recommended',
  691. '*exclude',
  692. );
  693. }
  694. if ($installcondition) {
  695. $type = '<installcondition><extension>';
  696. } else {
  697. $type = '<dependencies>' . $group . '<extension>';
  698. }
  699. if (isset($dep['name'])) {
  700. $type .= '<name>' . $dep['name'] . '</name>';
  701. }
  702. $this->_stupidSchemaValidate($structure, $dep, $type);
  703. if (isset($dep['min'])) {
  704. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  705. $dep['min'])) {
  706. $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
  707. }
  708. }
  709. if (isset($dep['max'])) {
  710. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  711. $dep['max'])) {
  712. $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
  713. }
  714. }
  715. if (isset($dep['recommended'])) {
  716. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  717. $dep['recommended'])) {
  718. $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
  719. }
  720. }
  721. if (isset($dep['exclude'])) {
  722. if (!is_array($dep['exclude'])) {
  723. $dep['exclude'] = array($dep['exclude']);
  724. }
  725. foreach ($dep['exclude'] as $exclude) {
  726. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  727. $exclude)) {
  728. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  729. }
  730. }
  731. }
  732. }
  733. function _validateOsDep($dep, $installcondition = false)
  734. {
  735. $structure = array(
  736. 'name',
  737. '*conflicts',
  738. );
  739. $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
  740. if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
  741. if ($dep['name'] == '*') {
  742. if (array_key_exists('conflicts', $dep)) {
  743. $this->_cannotConflictWithAllOs($type);
  744. }
  745. }
  746. }
  747. }
  748. function _validateArchDep($dep, $installcondition = false)
  749. {
  750. $structure = array(
  751. 'pattern',
  752. '*conflicts',
  753. );
  754. $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
  755. $this->_stupidSchemaValidate($structure, $dep, $type);
  756. }
  757. function _validateInstallConditions($cond, $release)
  758. {
  759. $structure = array(
  760. '*php',
  761. '*extension',
  762. '*os',
  763. '*arch',
  764. );
  765. if (!$this->_stupidSchemaValidate($structure,
  766. $cond, $release)) {
  767. return false;
  768. }
  769. foreach (array('php', 'extension', 'os', 'arch') as $type) {
  770. if (isset($cond[$type])) {
  771. $iter = $cond[$type];
  772. if (!is_array($iter) || !isset($iter[0])) {
  773. $iter = array($iter);
  774. }
  775. foreach ($iter as $package) {
  776. if ($type == 'extension') {
  777. $this->{"_validate{$type}Dep"}($package, false, true);
  778. } else {
  779. $this->{"_validate{$type}Dep"}($package, true);
  780. }
  781. }
  782. }
  783. }
  784. }
  785. function _validateDependencies()
  786. {
  787. $structure = array(
  788. 'required',
  789. '*optional',
  790. '*group->name->hint'
  791. );
  792. if (!$this->_stupidSchemaValidate($structure,
  793. $this->_packageInfo['dependencies'], '<dependencies>')) {
  794. return false;
  795. }
  796. foreach (array('required', 'optional') as $simpledep) {
  797. if (isset($this->_packageInfo['dependencies'][$simpledep])) {
  798. if ($simpledep == 'optional') {
  799. $structure = array(
  800. '*package',
  801. '*subpackage',
  802. '*extension',
  803. );
  804. } else {
  805. $structure = array(
  806. 'php',
  807. 'pearinstaller',
  808. '*package',
  809. '*subpackage',
  810. '*extension',
  811. '*os',
  812. '*arch',
  813. );
  814. }
  815. if ($this->_stupidSchemaValidate($structure,
  816. $this->_packageInfo['dependencies'][$simpledep],
  817. "<dependencies><$simpledep>")) {
  818. foreach (array('package', 'subpackage', 'extension') as $type) {
  819. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  820. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  821. if (!isset($iter[0])) {
  822. $iter = array($iter);
  823. }
  824. foreach ($iter as $package) {
  825. if ($type != 'extension') {
  826. if (isset($package['uri'])) {
  827. if (isset($package['channel'])) {
  828. $this->_UrlOrChannel($type,
  829. $package['name']);
  830. }
  831. } else {
  832. if (!isset($package['channel'])) {
  833. $this->_NoChannel($type, $package['name']);
  834. }
  835. }
  836. }
  837. $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
  838. }
  839. }
  840. }
  841. if ($simpledep == 'optional') {
  842. continue;
  843. }
  844. foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
  845. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  846. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  847. if (!isset($iter[0])) {
  848. $iter = array($iter);
  849. }
  850. foreach ($iter as $package) {
  851. $this->{"_validate{$type}Dep"}($package);
  852. }
  853. }
  854. }
  855. }
  856. }
  857. }
  858. if (isset($this->_packageInfo['dependencies']['group'])) {
  859. $groups = $this->_packageInfo['dependencies']['group'];
  860. if (!isset($groups[0])) {
  861. $groups = array($groups);
  862. }
  863. $structure = array(
  864. '*package',
  865. '*subpackage',
  866. '*extension',
  867. );
  868. foreach ($groups as $group) {
  869. if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
  870. if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
  871. $this->_invalidDepGroupName($group['attribs']['name']);
  872. }
  873. foreach (array('package', 'subpackage', 'extension') as $type) {
  874. if (isset($group[$type])) {
  875. $iter = $group[$type];
  876. if (!isset($iter[0])) {
  877. $iter = array($iter);
  878. }
  879. foreach ($iter as $package) {
  880. if ($type != 'extension') {
  881. if (isset($package['uri'])) {
  882. if (isset($package['channel'])) {
  883. $this->_UrlOrChannelGroup($type,
  884. $package['name'],
  885. $group['name']);
  886. }
  887. } else {
  888. if (!isset($package['channel'])) {
  889. $this->_NoChannelGroup($type,
  890. $package['name'],
  891. $group['name']);
  892. }
  893. }
  894. }
  895. $this->{"_validate{$type}Dep"}($package, '<group name="' .
  896. $group['attribs']['name'] . '">');
  897. }
  898. }
  899. }
  900. }
  901. }
  902. }
  903. }
  904. function _validateCompatible()
  905. {
  906. $compat = $this->_packageInfo['compatible'];
  907. if (!isset($compat[0])) {
  908. $compat = array($compat);
  909. }
  910. $required = array('name', 'channel', 'min', 'max', '*exclude');
  911. foreach ($compat as $package) {
  912. $type = '<compatible>';
  913. if (is_array($package) && array_key_exists('name', $package)) {
  914. $type .= '<name>' . $package['name'] . '</name>';
  915. }
  916. $this->_stupidSchemaValidate($required, $package, $type);
  917. if (is_array($package) && array_key_exists('min', $package)) {
  918. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  919. $package['min'])) {
  920. $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
  921. }
  922. }
  923. if (is_array($package) && array_key_exists('max', $package)) {
  924. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  925. $package['max'])) {
  926. $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
  927. }
  928. }
  929. if (is_array($package) && array_key_exists('exclude', $package)) {
  930. if (!is_array($package['exclude'])) {
  931. $package['exclude'] = array($package['exclude']);
  932. }
  933. foreach ($package['exclude'] as $exclude) {
  934. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  935. $exclude)) {
  936. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  937. }
  938. }
  939. }
  940. }
  941. }
  942. function _validateBundle($list)
  943. {
  944. if (!is_array($list) || !isset($list['bundledpackage'])) {
  945. return $this->_NoBundledPackages();
  946. }
  947. if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
  948. return $this->_AtLeast2BundledPackages();
  949. }
  950. foreach ($list['bundledpackage'] as $package) {
  951. if (!is_string($package)) {
  952. $this->_bundledPackagesMustBeFilename();
  953. }
  954. }
  955. }
  956. function _validateFilelist($list = false, $allowignore = false, $dirs = '')
  957. {
  958. $iscontents = false;
  959. if (!$list) {
  960. $iscontents = true;
  961. $list = $this->_packageInfo['contents'];
  962. if (isset($this->_packageInfo['bundle'])) {
  963. return $this->_validateBundle($list);
  964. }
  965. }
  966. if ($allowignore) {
  967. $struc = array(
  968. '*install->name->as',
  969. '*ignore->name'
  970. );
  971. } else {
  972. $struc = array(
  973. '*dir->name->?baseinstalldir',
  974. '*file->name->role->?baseinstalldir->?md5sum'
  975. );
  976. if (isset($list['dir']) && isset($list['file'])) {
  977. // stave off validation errors without requiring a set order.
  978. $_old = $list;
  979. if (isset($list['attribs'])) {
  980. $list = array('attribs' => $_old['attribs']);
  981. }
  982. $list['dir'] = $_old['dir'];
  983. $list['file'] = $_old['file'];
  984. }
  985. }
  986. if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
  987. $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
  988. $dirname = $iscontents ? '<contents>' : $unknown;
  989. } else {
  990. $dirname = '<dir name="' . $list['attribs']['name'] . '">';
  991. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  992. str_replace('\\', '/', $list['attribs']['name']))) {
  993. // file contains .. parent directory or . cur directory
  994. $this->_invalidDirName($list['attribs']['name']);
  995. }
  996. }
  997. $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
  998. if ($allowignore && $res) {
  999. $ignored_or_installed = array();
  1000. $this->_pf->getFilelist();
  1001. $fcontents = $this->_pf->getContents();
  1002. $filelist = array();
  1003. if (!isset($fcontents['dir']['file'][0])) {
  1004. $fcontents['dir']['file'] = array($fcontents['dir']['file']);
  1005. }
  1006. foreach ($fcontents['dir']['file'] as $file) {
  1007. $filelist[$file['attribs']['name']] = true;
  1008. }
  1009. if (isset($list['install'])) {
  1010. if (!isset($list['install'][0])) {
  1011. $list['install'] = array($list['install']);
  1012. }
  1013. foreach ($list['install'] as $file) {
  1014. if (!isset($filelist[$file['attribs']['name']])) {
  1015. $this->_notInContents($file['attribs']['name'], 'install');
  1016. continue;
  1017. }
  1018. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  1019. $this->_multipleInstallAs($file['attribs']['name']);
  1020. }
  1021. if (!isset($ignored_or_installed[$file['attribs']['name']])) {
  1022. $ignored_or_installed[$file['attribs']['name']] = array();
  1023. }
  1024. $ignored_or_installed[$file['attribs']['name']][] = 1;
  1025. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  1026. str_replace('\\', '/', $file['attribs']['as']))) {
  1027. // file contains .. parent directory or . cur directory references
  1028. $this->_invalidFileInstallAs($file['attribs']['name'],
  1029. $file['attribs']['as']);
  1030. }
  1031. }
  1032. }
  1033. if (isset($list['ignore'])) {
  1034. if (!isset($list['ignore'][0])) {
  1035. $list['ignore'] = array($list['ignore']);
  1036. }
  1037. foreach ($list['ignore'] as $file) {
  1038. if (!isset($filelist[$file['attribs']['name']])) {
  1039. $this->_notInContents($file['attribs']['name'], 'ignore');
  1040. continue;
  1041. }
  1042. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  1043. $this->_ignoreAndInstallAs($file['attribs']['name']);
  1044. }
  1045. }
  1046. }
  1047. }
  1048. if (!$allowignore && isset($list['file'])) {
  1049. if (is_string($list['file'])) {
  1050. $this->_oldStyleFileNotAllowed();
  1051. return false;
  1052. }
  1053. if (!isset($list['file'][0])) {
  1054. // single file
  1055. $list['file'] = array($list['file']);
  1056. }
  1057. foreach ($list['file'] as $i => $file)
  1058. {
  1059. if (isset($file['attribs']) && isset($file['attribs']['name'])) {
  1060. if ($file['attribs']['name']{0} == '.' &&
  1061. $file['attribs']['name']{1} == '/') {
  1062. // name is something like "./doc/whatever.txt"
  1063. $this->_invalidFileName($file['attribs']['name'], $dirname);
  1064. }
  1065. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  1066. str_replace('\\', '/', $file['attribs']['name']))) {
  1067. // file contains .. parent directory or . cur directory
  1068. $this->_invalidFileName($file['attribs']['name'], $dirname);
  1069. }
  1070. }
  1071. if (isset($file['attribs']) && isset($file['attribs']['role'])) {
  1072. if (!$this->_validateRole($file['attribs']['role'])) {
  1073. if (isset($this->_packageInfo['usesrole'])) {
  1074. $roles = $this->_packageInfo['usesrole'];
  1075. if (!isset($roles[0])) {
  1076. $roles = array($roles);
  1077. }
  1078. foreach ($roles as $role) {
  1079. if ($role['role'] = $file['attribs']['role']) {
  1080. $msg = 'This package contains role "%role%" and requires ' .
  1081. 'package "%package%" to be used';
  1082. if (isset($role['uri'])) {
  1083. $params = array('role' => $role['role'],
  1084. 'package' => $role['uri']);
  1085. } else {
  1086. $params = array('role' => $role['role'],
  1087. 'package' => $this->_pf->_registry->
  1088. parsedPackageNameToString(array('package' =>
  1089. $role['package'], 'channel' => $role['channel']),
  1090. true));
  1091. }
  1092. $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
  1093. }
  1094. }
  1095. }
  1096. $this->_invalidFileRole($file['attribs']['name'],
  1097. $dirname, $file['attribs']['role']);
  1098. }
  1099. }
  1100. if (!isset($file['attribs'])) {
  1101. continue;
  1102. }
  1103. $save = $file['attribs'];
  1104. if ($dirs) {
  1105. $save['name'] = $dirs . '/' . $save['name'];
  1106. }
  1107. unset($file['attribs']);
  1108. if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
  1109. foreach ($file as $task => $value) {
  1110. if ($tagClass = $this->_pf->getTask($task)) {
  1111. if (!is_array($value) || !isset($value[0])) {
  1112. $value = array($value);
  1113. }
  1114. foreach ($value as $v) {
  1115. $ret = call_user_func(array($tagClass, 'validateXml'),
  1116. $this->_pf, $v, $this->_pf->_config, $save);
  1117. if (is_array($ret)) {
  1118. $this->_invalidTask($task, $ret, isset($save['name']) ?
  1119. $save['name'] : '');
  1120. }
  1121. }
  1122. } else {
  1123. if (isset($this->_packageInfo['usestask'])) {
  1124. $roles = $this->_packageInfo['usestask'];
  1125. if (!isset($roles[0])) {
  1126. $roles = array($roles);
  1127. }
  1128. foreach ($roles as $role) {
  1129. if ($role['task'] = $task) {
  1130. $msg = 'This package contains task "%task%" and requires ' .
  1131. 'package "%package%" to be used';
  1132. if (isset($role['uri'])) {
  1133. $params = array('task' => $role['task'],
  1134. 'package' => $role['uri']);
  1135. } else {
  1136. $params = array('task' => $role['task'],
  1137. 'package' => $this->_pf->_registry->
  1138. parsedPackageNameToString(array('package' =>
  1139. $role['package'], 'channel' => $role['channel']),
  1140. true));
  1141. }
  1142. $this->_stack->push('_mustInstallTask', 'error',
  1143. $params, $msg);
  1144. }
  1145. }
  1146. }
  1147. $this->_unknownTask($task, $save['name']);
  1148. }
  1149. }
  1150. }
  1151. }
  1152. }
  1153. if (isset($list['ignore'])) {
  1154. if (!$allowignore) {
  1155. $this->_ignoreNotAllowed('ignore');
  1156. }
  1157. }
  1158. if (isset($list['install'])) {
  1159. if (!$allowignore) {
  1160. $this->_ignoreNotAllowed('install');
  1161. }
  1162. }
  1163. if (isset($list['file'])) {
  1164. if ($allowignore) {
  1165. $this->_fileNotAllowed('file');
  1166. }
  1167. }
  1168. if (isset($list['dir'])) {
  1169. if ($allowignore) {
  1170. $this->_fileNotAllowed('dir');
  1171. } else {
  1172. if (!isset($list['dir'][0])) {
  1173. $list['dir'] = array($list['dir']);
  1174. }
  1175. foreach ($list['dir'] as $dir) {
  1176. if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
  1177. if ($dir['attribs']['name'] == '/' ||
  1178. !isset($this->_packageInfo['contents']['dir']['dir'])) {
  1179. // always use nothing if the filelist has already been flattened
  1180. $newdirs = '';
  1181. } elseif ($dirs == '') {
  1182. $newdirs = $dir['attribs']['name'];
  1183. } else {
  1184. $newdirs = $dirs . '/' . $dir['attribs']['name'];
  1185. }
  1186. } else {
  1187. $newdirs = $dirs;
  1188. }
  1189. $this->_validateFilelist($dir, $allowignore, $newdirs);
  1190. }
  1191. }
  1192. }
  1193. }
  1194. function _validateRelease()
  1195. {
  1196. if (isset($this->_packageInfo['phprelease'])) {
  1197. $release = 'phprelease';
  1198. if (isset($this->_packageInfo['providesextension'])) {

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