ChannelFile.php 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. <?php
  2. /**
  3. * PEAR_ChannelFile, the channel handling class
  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. * @link http://pear.php.net/package/PEAR
  13. * @since File available since Release 1.4.0a1
  14. */
  15. /**
  16. * Needed for error handling
  17. */
  18. require_once 'PEAR/ErrorStack.php';
  19. require_once 'PEAR/XMLParser.php';
  20. require_once 'PEAR/Common.php';
  21. /**
  22. * Error code if the channel.xml <channel> tag does not contain a valid version
  23. */
  24. define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
  25. /**
  26. * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
  27. * currently
  28. */
  29. define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
  30. /**
  31. * Error code if parsing is attempted with no xml extension
  32. */
  33. define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
  34. /**
  35. * Error code if creating the xml parser resource fails
  36. */
  37. define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
  38. /**
  39. * Error code used for all sax xml parsing errors
  40. */
  41. define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
  42. /**#@+
  43. * Validation errors
  44. */
  45. /**
  46. * Error code when channel name is missing
  47. */
  48. define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
  49. /**
  50. * Error code when channel name is invalid
  51. */
  52. define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
  53. /**
  54. * Error code when channel summary is missing
  55. */
  56. define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
  57. /**
  58. * Error code when channel summary is multi-line
  59. */
  60. define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
  61. /**
  62. * Error code when channel server is missing for protocol
  63. */
  64. define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
  65. /**
  66. * Error code when channel server is invalid for protocol
  67. */
  68. define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
  69. /**
  70. * Error code when a mirror name is invalid
  71. */
  72. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
  73. /**
  74. * Error code when a mirror type is invalid
  75. */
  76. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
  77. /**
  78. * Error code when an attempt is made to generate xml, but the parsed content is invalid
  79. */
  80. define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
  81. /**
  82. * Error code when an empty package name validate regex is passed in
  83. */
  84. define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
  85. /**
  86. * Error code when a <function> tag has no version
  87. */
  88. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
  89. /**
  90. * Error code when a <function> tag has no name
  91. */
  92. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
  93. /**
  94. * Error code when a <validatepackage> tag has no name
  95. */
  96. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
  97. /**
  98. * Error code when a <validatepackage> tag has no version attribute
  99. */
  100. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
  101. /**
  102. * Error code when a mirror does not exist but is called for in one of the set*
  103. * methods.
  104. */
  105. define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
  106. /**
  107. * Error code when a server port is not numeric
  108. */
  109. define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
  110. /**
  111. * Error code when <static> contains no version attribute
  112. */
  113. define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
  114. /**
  115. * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
  116. */
  117. define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
  118. /**
  119. * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
  120. */
  121. define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
  122. /**
  123. * Error code when ssl attribute is present and is not "yes"
  124. */
  125. define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
  126. /**#@-*/
  127. /**
  128. * Mirror types allowed. Currently only internet servers are recognized.
  129. */
  130. $GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server');
  131. /**
  132. * The Channel handling class
  133. *
  134. * @category pear
  135. * @package PEAR
  136. * @author Greg Beaver <cellog@php.net>
  137. * @copyright 1997-2009 The Authors
  138. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  139. * @version Release: 1.10.10
  140. * @link http://pear.php.net/package/PEAR
  141. * @since Class available since Release 1.4.0a1
  142. */
  143. class PEAR_ChannelFile
  144. {
  145. /**
  146. * @access private
  147. * @var PEAR_ErrorStack
  148. * @access private
  149. */
  150. var $_stack;
  151. /**
  152. * Supported channel.xml versions, for parsing
  153. * @var array
  154. * @access private
  155. */
  156. var $_supportedVersions = array('1.0');
  157. /**
  158. * Parsed channel information
  159. * @var array
  160. * @access private
  161. */
  162. var $_channelInfo;
  163. /**
  164. * index into the subchannels array, used for parsing xml
  165. * @var int
  166. * @access private
  167. */
  168. var $_subchannelIndex;
  169. /**
  170. * index into the mirrors array, used for parsing xml
  171. * @var int
  172. * @access private
  173. */
  174. var $_mirrorIndex;
  175. /**
  176. * Flag used to determine the validity of parsed content
  177. * @var boolean
  178. * @access private
  179. */
  180. var $_isValid = false;
  181. function __construct()
  182. {
  183. $this->_stack = new PEAR_ErrorStack('PEAR_ChannelFile');
  184. $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
  185. $this->_isValid = false;
  186. }
  187. /**
  188. * @return array
  189. * @access protected
  190. */
  191. function _getErrorMessage()
  192. {
  193. return
  194. array(
  195. PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
  196. 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
  197. PEAR_CHANNELFILE_ERROR_NO_VERSION =>
  198. 'No version number found in <channel> tag',
  199. PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
  200. '%error%',
  201. PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
  202. 'Unable to create XML parser',
  203. PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
  204. '%error%',
  205. PEAR_CHANNELFILE_ERROR_NO_NAME =>
  206. 'Missing channel name',
  207. PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
  208. 'Invalid channel %tag% "%name%"',
  209. PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
  210. 'Missing channel summary',
  211. PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
  212. 'Channel summary should be on one line, but is multi-line',
  213. PEAR_CHANNELFILE_ERROR_NO_HOST =>
  214. 'Missing channel server for %type% server',
  215. PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
  216. 'Server name "%server%" is invalid for %type% server',
  217. PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
  218. 'Invalid mirror name "%name%", mirror type %type%',
  219. PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
  220. 'Invalid mirror type "%type%"',
  221. PEAR_CHANNELFILE_ERROR_INVALID =>
  222. 'Cannot generate xml, contents are invalid',
  223. PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
  224. 'packagenameregex cannot be empty',
  225. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
  226. '%parent% %protocol% function has no version',
  227. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
  228. '%parent% %protocol% function has no name',
  229. PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
  230. '%parent% rest baseurl has no type',
  231. PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
  232. 'Validation package has no name in <validatepackage> tag',
  233. PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
  234. 'Validation package "%package%" has no version',
  235. PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
  236. 'Mirror "%mirror%" does not exist',
  237. PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
  238. 'Port "%port%" must be numeric',
  239. PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
  240. '<static> tag must contain version attribute',
  241. PEAR_CHANNELFILE_URI_CANT_MIRROR =>
  242. 'The __uri pseudo-channel cannot have mirrors',
  243. PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
  244. '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
  245. );
  246. }
  247. /**
  248. * @param string contents of package.xml file
  249. * @return bool success of parsing
  250. */
  251. function fromXmlString($data)
  252. {
  253. if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
  254. if (!in_array($channelversion[1], $this->_supportedVersions)) {
  255. $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
  256. array('version' => $channelversion[1]));
  257. return false;
  258. }
  259. $parser = new PEAR_XMLParser;
  260. $result = $parser->parse($data);
  261. if ($result !== true) {
  262. if ($result->getCode() == 1) {
  263. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
  264. array('error' => $result->getMessage()));
  265. } else {
  266. $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
  267. }
  268. return false;
  269. }
  270. $this->_channelInfo = $parser->getData();
  271. return true;
  272. } else {
  273. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
  274. return false;
  275. }
  276. }
  277. /**
  278. * @return array
  279. */
  280. function toArray()
  281. {
  282. if (!$this->_isValid && !$this->validate()) {
  283. return false;
  284. }
  285. return $this->_channelInfo;
  286. }
  287. /**
  288. * @param array
  289. *
  290. * @return PEAR_ChannelFile|false false if invalid
  291. */
  292. public static function &fromArray(
  293. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  294. ) {
  295. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  296. $a->_fromArray($data);
  297. if (!$a->validate()) {
  298. $a = false;
  299. return $a;
  300. }
  301. return $a;
  302. }
  303. /**
  304. * Unlike {@link fromArray()} this does not do any validation
  305. *
  306. * @param array
  307. *
  308. * @return PEAR_ChannelFile
  309. */
  310. public static function &fromArrayWithErrors(
  311. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  312. ) {
  313. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  314. $a->_fromArray($data);
  315. return $a;
  316. }
  317. /**
  318. * @param array
  319. * @access private
  320. */
  321. function _fromArray($data)
  322. {
  323. $this->_channelInfo = $data;
  324. }
  325. /**
  326. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  327. * @param boolean determines whether to purge the error stack after retrieving
  328. * @return array
  329. */
  330. function getErrors($purge = false)
  331. {
  332. return $this->_stack->getErrors($purge);
  333. }
  334. /**
  335. * Unindent given string (?)
  336. *
  337. * @param string $str The string that has to be unindented.
  338. * @return string
  339. * @access private
  340. */
  341. function _unIndent($str)
  342. {
  343. // remove leading newlines
  344. $str = preg_replace('/^[\r\n]+/', '', $str);
  345. // find whitespace at the beginning of the first line
  346. $indent_len = strspn($str, " \t");
  347. $indent = substr($str, 0, $indent_len);
  348. $data = '';
  349. // remove the same amount of whitespace from following lines
  350. foreach (explode("\n", $str) as $line) {
  351. if (substr($line, 0, $indent_len) == $indent) {
  352. $data .= substr($line, $indent_len) . "\n";
  353. }
  354. }
  355. return $data;
  356. }
  357. /**
  358. * Parse a channel.xml file. Expects the name of
  359. * a channel xml file as input.
  360. *
  361. * @param string $descfile name of channel xml file
  362. * @return bool success of parsing
  363. */
  364. function fromXmlFile($descfile)
  365. {
  366. if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
  367. (!$fp = fopen($descfile, 'r'))) {
  368. require_once 'PEAR.php';
  369. return PEAR::raiseError("Unable to open $descfile");
  370. }
  371. // read the whole thing so we only get one cdata callback
  372. // for each block of cdata
  373. fclose($fp);
  374. $data = file_get_contents($descfile);
  375. return $this->fromXmlString($data);
  376. }
  377. /**
  378. * Parse channel information from different sources
  379. *
  380. * This method is able to extract information about a channel
  381. * from an .xml file or a string
  382. *
  383. * @access public
  384. * @param string Filename of the source or the source itself
  385. * @return bool
  386. */
  387. function fromAny($info)
  388. {
  389. if (is_string($info) && file_exists($info) && strlen($info) < 255) {
  390. $tmp = substr($info, -4);
  391. if ($tmp == '.xml') {
  392. $info = $this->fromXmlFile($info);
  393. } else {
  394. $fp = fopen($info, "r");
  395. $test = fread($fp, 5);
  396. fclose($fp);
  397. if ($test == "<?xml") {
  398. $info = $this->fromXmlFile($info);
  399. }
  400. }
  401. if (PEAR::isError($info)) {
  402. require_once 'PEAR.php';
  403. return PEAR::raiseError($info);
  404. }
  405. }
  406. if (is_string($info)) {
  407. $info = $this->fromXmlString($info);
  408. }
  409. return $info;
  410. }
  411. /**
  412. * Return an XML document based on previous parsing and modifications
  413. *
  414. * @return string XML data
  415. *
  416. * @access public
  417. */
  418. function toXml()
  419. {
  420. if (!$this->_isValid && !$this->validate()) {
  421. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
  422. return false;
  423. }
  424. if (!isset($this->_channelInfo['attribs']['version'])) {
  425. $this->_channelInfo['attribs']['version'] = '1.0';
  426. }
  427. $channelInfo = $this->_channelInfo;
  428. $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  429. $ret .= "<channel version=\"" .
  430. $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  431. xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  432. xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
  433. . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
  434. $channelInfo['attribs']['version'] . ".xsd\">
  435. <name>$channelInfo[name]</name>
  436. <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
  437. ";
  438. if (isset($channelInfo['suggestedalias'])) {
  439. $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
  440. }
  441. if (isset($channelInfo['validatepackage'])) {
  442. $ret .= ' <validatepackage version="' .
  443. $channelInfo['validatepackage']['attribs']['version']. '">' .
  444. htmlspecialchars($channelInfo['validatepackage']['_content']) .
  445. "</validatepackage>\n";
  446. }
  447. $ret .= " <servers>\n";
  448. $ret .= ' <primary';
  449. if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
  450. $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
  451. }
  452. if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
  453. $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
  454. }
  455. $ret .= ">\n";
  456. if (isset($channelInfo['servers']['primary']['rest'])) {
  457. $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], ' ');
  458. }
  459. $ret .= " </primary>\n";
  460. if (isset($channelInfo['servers']['mirror'])) {
  461. $ret .= $this->_makeMirrorsXml($channelInfo);
  462. }
  463. $ret .= " </servers>\n";
  464. $ret .= "</channel>";
  465. return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
  466. }
  467. /**
  468. * Generate the <rest> tag
  469. * @access private
  470. */
  471. function _makeRestXml($info, $indent)
  472. {
  473. $ret = $indent . "<rest>\n";
  474. if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
  475. $info['baseurl'] = array($info['baseurl']);
  476. }
  477. if (isset($info['baseurl'])) {
  478. foreach ($info['baseurl'] as $url) {
  479. $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
  480. $ret .= ">" . $url['_content'] . "</baseurl>\n";
  481. }
  482. }
  483. $ret .= $indent . "</rest>\n";
  484. return $ret;
  485. }
  486. /**
  487. * Generate the <mirrors> tag
  488. * @access private
  489. */
  490. function _makeMirrorsXml($channelInfo)
  491. {
  492. $ret = "";
  493. if (!isset($channelInfo['servers']['mirror'][0])) {
  494. $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
  495. }
  496. foreach ($channelInfo['servers']['mirror'] as $mirror) {
  497. $ret .= ' <mirror host="' . $mirror['attribs']['host'] . '"';
  498. if (isset($mirror['attribs']['port'])) {
  499. $ret .= ' port="' . $mirror['attribs']['port'] . '"';
  500. }
  501. if (isset($mirror['attribs']['ssl'])) {
  502. $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
  503. }
  504. $ret .= ">\n";
  505. if (isset($mirror['rest'])) {
  506. if (isset($mirror['rest'])) {
  507. $ret .= $this->_makeRestXml($mirror['rest'], ' ');
  508. }
  509. $ret .= " </mirror>\n";
  510. } else {
  511. $ret .= "/>\n";
  512. }
  513. }
  514. return $ret;
  515. }
  516. /**
  517. * Generate the <functions> tag
  518. * @access private
  519. */
  520. function _makeFunctionsXml($functions, $indent, $rest = false)
  521. {
  522. $ret = '';
  523. if (!isset($functions[0])) {
  524. $functions = array($functions);
  525. }
  526. foreach ($functions as $function) {
  527. $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
  528. if ($rest) {
  529. $ret .= ' uri="' . $function['attribs']['uri'] . '"';
  530. }
  531. $ret .= ">" . $function['_content'] . "</function>\n";
  532. }
  533. return $ret;
  534. }
  535. /**
  536. * Validation error. Also marks the object contents as invalid
  537. * @param error code
  538. * @param array error information
  539. * @access private
  540. */
  541. function _validateError($code, $params = array())
  542. {
  543. $this->_stack->push($code, 'error', $params);
  544. $this->_isValid = false;
  545. }
  546. /**
  547. * Validation warning. Does not mark the object contents invalid.
  548. * @param error code
  549. * @param array error information
  550. * @access private
  551. */
  552. function _validateWarning($code, $params = array())
  553. {
  554. $this->_stack->push($code, 'warning', $params);
  555. }
  556. /**
  557. * Validate parsed file.
  558. *
  559. * @access public
  560. * @return boolean
  561. */
  562. function validate()
  563. {
  564. $this->_isValid = true;
  565. $info = $this->_channelInfo;
  566. if (empty($info['name'])) {
  567. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
  568. } elseif (!$this->validChannelServer($info['name'])) {
  569. if ($info['name'] != '__uri') {
  570. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
  571. 'name' => $info['name']));
  572. }
  573. }
  574. if (empty($info['summary'])) {
  575. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  576. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  577. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  578. array('summary' => $info['summary']));
  579. }
  580. if (isset($info['suggestedalias'])) {
  581. if (!$this->validChannelServer($info['suggestedalias'])) {
  582. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  583. array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
  584. }
  585. }
  586. if (isset($info['localalias'])) {
  587. if (!$this->validChannelServer($info['localalias'])) {
  588. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  589. array('tag' => 'localalias', 'name' =>$info['localalias']));
  590. }
  591. }
  592. if (isset($info['validatepackage'])) {
  593. if (!isset($info['validatepackage']['_content'])) {
  594. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
  595. }
  596. if (!isset($info['validatepackage']['attribs']['version'])) {
  597. $content = isset($info['validatepackage']['_content']) ?
  598. $info['validatepackage']['_content'] :
  599. null;
  600. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
  601. array('package' => $content));
  602. }
  603. }
  604. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
  605. !is_numeric($info['servers']['primary']['attribs']['port'])) {
  606. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
  607. array('port' => $info['servers']['primary']['attribs']['port']));
  608. }
  609. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
  610. $info['servers']['primary']['attribs']['ssl'] != 'yes') {
  611. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  612. array('ssl' => $info['servers']['primary']['attribs']['ssl'],
  613. 'server' => $info['name']));
  614. }
  615. if (isset($info['servers']['primary']['rest']) &&
  616. isset($info['servers']['primary']['rest']['baseurl'])) {
  617. $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
  618. }
  619. if (isset($info['servers']['mirror'])) {
  620. if ($this->_channelInfo['name'] == '__uri') {
  621. $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
  622. }
  623. if (!isset($info['servers']['mirror'][0])) {
  624. $info['servers']['mirror'] = array($info['servers']['mirror']);
  625. }
  626. foreach ($info['servers']['mirror'] as $mirror) {
  627. if (!isset($mirror['attribs']['host'])) {
  628. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
  629. array('type' => 'mirror'));
  630. } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
  631. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
  632. array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
  633. }
  634. if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
  635. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  636. array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
  637. }
  638. if (isset($mirror['rest'])) {
  639. $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
  640. $mirror['attribs']['host']);
  641. }
  642. }
  643. }
  644. return $this->_isValid;
  645. }
  646. /**
  647. * @param string rest - protocol name this function applies to
  648. * @param array the functions
  649. * @param string the name of the parent element (mirror name, for instance)
  650. */
  651. function _validateFunctions($protocol, $functions, $parent = '')
  652. {
  653. if (!isset($functions[0])) {
  654. $functions = array($functions);
  655. }
  656. foreach ($functions as $function) {
  657. if (!isset($function['_content']) || empty($function['_content'])) {
  658. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
  659. array('parent' => $parent, 'protocol' => $protocol));
  660. }
  661. if ($protocol == 'rest') {
  662. if (!isset($function['attribs']['type']) ||
  663. empty($function['attribs']['type'])) {
  664. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
  665. array('parent' => $parent, 'protocol' => $protocol));
  666. }
  667. } else {
  668. if (!isset($function['attribs']['version']) ||
  669. empty($function['attribs']['version'])) {
  670. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
  671. array('parent' => $parent, 'protocol' => $protocol));
  672. }
  673. }
  674. }
  675. }
  676. /**
  677. * Test whether a string contains a valid channel server.
  678. * @param string $ver the package version to test
  679. * @return bool
  680. */
  681. function validChannelServer($server)
  682. {
  683. if ($server == '__uri') {
  684. return true;
  685. }
  686. return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
  687. }
  688. /**
  689. * @return string|false
  690. */
  691. function getName()
  692. {
  693. if (isset($this->_channelInfo['name'])) {
  694. return $this->_channelInfo['name'];
  695. }
  696. return false;
  697. }
  698. /**
  699. * @return string|false
  700. */
  701. function getServer()
  702. {
  703. if (isset($this->_channelInfo['name'])) {
  704. return $this->_channelInfo['name'];
  705. }
  706. return false;
  707. }
  708. /**
  709. * @return int|80 port number to connect to
  710. */
  711. function getPort($mirror = false)
  712. {
  713. if ($mirror) {
  714. if ($mir = $this->getMirror($mirror)) {
  715. if (isset($mir['attribs']['port'])) {
  716. return $mir['attribs']['port'];
  717. }
  718. if ($this->getSSL($mirror)) {
  719. return 443;
  720. }
  721. return 80;
  722. }
  723. return false;
  724. }
  725. if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
  726. return $this->_channelInfo['servers']['primary']['attribs']['port'];
  727. }
  728. if ($this->getSSL()) {
  729. return 443;
  730. }
  731. return 80;
  732. }
  733. /**
  734. * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
  735. */
  736. function getSSL($mirror = false)
  737. {
  738. if ($mirror) {
  739. if ($mir = $this->getMirror($mirror)) {
  740. if (isset($mir['attribs']['ssl'])) {
  741. return true;
  742. }
  743. return false;
  744. }
  745. return false;
  746. }
  747. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  748. return true;
  749. }
  750. return false;
  751. }
  752. /**
  753. * @return string|false
  754. */
  755. function getSummary()
  756. {
  757. if (isset($this->_channelInfo['summary'])) {
  758. return $this->_channelInfo['summary'];
  759. }
  760. return false;
  761. }
  762. /**
  763. * @param string protocol type
  764. * @param string Mirror name
  765. * @return array|false
  766. */
  767. function getFunctions($protocol, $mirror = false)
  768. {
  769. if ($this->getName() == '__uri') {
  770. return false;
  771. }
  772. $function = $protocol == 'rest' ? 'baseurl' : 'function';
  773. if ($mirror) {
  774. if ($mir = $this->getMirror($mirror)) {
  775. if (isset($mir[$protocol][$function])) {
  776. return $mir[$protocol][$function];
  777. }
  778. }
  779. return false;
  780. }
  781. if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
  782. return $this->_channelInfo['servers']['primary'][$protocol][$function];
  783. }
  784. return false;
  785. }
  786. /**
  787. * @param string Protocol type
  788. * @param string Function name (null to return the
  789. * first protocol of the type requested)
  790. * @param string Mirror name, if any
  791. * @return array
  792. */
  793. function getFunction($type, $name = null, $mirror = false)
  794. {
  795. $protocols = $this->getFunctions($type, $mirror);
  796. if (!$protocols) {
  797. return false;
  798. }
  799. foreach ($protocols as $protocol) {
  800. if ($name === null) {
  801. return $protocol;
  802. }
  803. if ($protocol['_content'] != $name) {
  804. continue;
  805. }
  806. return $protocol;
  807. }
  808. return false;
  809. }
  810. /**
  811. * @param string protocol type
  812. * @param string protocol name
  813. * @param string version
  814. * @param string mirror name
  815. * @return boolean
  816. */
  817. function supports($type, $name = null, $mirror = false, $version = '1.0')
  818. {
  819. $protocols = $this->getFunctions($type, $mirror);
  820. if (!$protocols) {
  821. return false;
  822. }
  823. foreach ($protocols as $protocol) {
  824. if ($protocol['attribs']['version'] != $version) {
  825. continue;
  826. }
  827. if ($name === null) {
  828. return true;
  829. }
  830. if ($protocol['_content'] != $name) {
  831. continue;
  832. }
  833. return true;
  834. }
  835. return false;
  836. }
  837. /**
  838. * Determines whether a channel supports Representational State Transfer (REST) protocols
  839. * for retrieving channel information
  840. * @param string
  841. * @return bool
  842. */
  843. function supportsREST($mirror = false)
  844. {
  845. if ($mirror == $this->_channelInfo['name']) {
  846. $mirror = false;
  847. }
  848. if ($mirror) {
  849. if ($mir = $this->getMirror($mirror)) {
  850. return isset($mir['rest']);
  851. }
  852. return false;
  853. }
  854. return isset($this->_channelInfo['servers']['primary']['rest']);
  855. }
  856. /**
  857. * Get the URL to access a base resource.
  858. *
  859. * Hyperlinks in the returned xml will be used to retrieve the proper information
  860. * needed. This allows extreme extensibility and flexibility in implementation
  861. * @param string Resource Type to retrieve
  862. */
  863. function getBaseURL($resourceType, $mirror = false)
  864. {
  865. if ($mirror == $this->_channelInfo['name']) {
  866. $mirror = false;
  867. }
  868. if ($mirror) {
  869. $mir = $this->getMirror($mirror);
  870. if (!$mir) {
  871. return false;
  872. }
  873. $rest = $mir['rest'];
  874. } else {
  875. $rest = $this->_channelInfo['servers']['primary']['rest'];
  876. }
  877. if (!isset($rest['baseurl'][0])) {
  878. $rest['baseurl'] = array($rest['baseurl']);
  879. }
  880. foreach ($rest['baseurl'] as $baseurl) {
  881. if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
  882. return $baseurl['_content'];
  883. }
  884. }
  885. return false;
  886. }
  887. /**
  888. * Since REST does not implement RPC, provide this as a logical wrapper around
  889. * resetFunctions for REST
  890. * @param string|false mirror name, if any
  891. */
  892. function resetREST($mirror = false)
  893. {
  894. return $this->resetFunctions('rest', $mirror);
  895. }
  896. /**
  897. * Empty all protocol definitions
  898. * @param string protocol type
  899. * @param string|false mirror name, if any
  900. */
  901. function resetFunctions($type, $mirror = false)
  902. {
  903. if ($mirror) {
  904. if (isset($this->_channelInfo['servers']['mirror'])) {
  905. $mirrors = $this->_channelInfo['servers']['mirror'];
  906. if (!isset($mirrors[0])) {
  907. $mirrors = array($mirrors);
  908. }
  909. foreach ($mirrors as $i => $mir) {
  910. if ($mir['attribs']['host'] == $mirror) {
  911. if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
  912. unset($this->_channelInfo['servers']['mirror'][$i][$type]);
  913. }
  914. return true;
  915. }
  916. }
  917. return false;
  918. }
  919. return false;
  920. }
  921. if (isset($this->_channelInfo['servers']['primary'][$type])) {
  922. unset($this->_channelInfo['servers']['primary'][$type]);
  923. }
  924. return true;
  925. }
  926. /**
  927. * Set a channel's protocols to the protocols supported by pearweb
  928. */
  929. function setDefaultPEARProtocols($version = '1.0', $mirror = false)
  930. {
  931. switch ($version) {
  932. case '1.0' :
  933. $this->resetREST($mirror);
  934. if (!isset($this->_channelInfo['servers'])) {
  935. $this->_channelInfo['servers'] = array('primary' =>
  936. array('rest' => array()));
  937. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  938. $this->_channelInfo['servers']['primary'] = array('rest' => array());
  939. }
  940. return true;
  941. break;
  942. default :
  943. return false;
  944. break;
  945. }
  946. }
  947. /**
  948. * @return array
  949. */
  950. function getMirrors()
  951. {
  952. if (isset($this->_channelInfo['servers']['mirror'])) {
  953. $mirrors = $this->_channelInfo['servers']['mirror'];
  954. if (!isset($mirrors[0])) {
  955. $mirrors = array($mirrors);
  956. }
  957. return $mirrors;
  958. }
  959. return array();
  960. }
  961. /**
  962. * Get the unserialized XML representing a mirror
  963. * @return array|false
  964. */
  965. function getMirror($server)
  966. {
  967. foreach ($this->getMirrors() as $mirror) {
  968. if ($mirror['attribs']['host'] == $server) {
  969. return $mirror;
  970. }
  971. }
  972. return false;
  973. }
  974. /**
  975. * @param string
  976. * @return string|false
  977. * @error PEAR_CHANNELFILE_ERROR_NO_NAME
  978. * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
  979. */
  980. function setName($name)
  981. {
  982. return $this->setServer($name);
  983. }
  984. /**
  985. * Set the socket number (port) that is used to connect to this channel
  986. * @param integer
  987. * @param string|false name of the mirror server, or false for the primary
  988. */
  989. function setPort($port, $mirror = false)
  990. {
  991. if ($mirror) {
  992. if (!isset($this->_channelInfo['servers']['mirror'])) {
  993. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  994. array('mirror' => $mirror));
  995. return false;
  996. }
  997. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  998. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  999. if ($mirror == $mir['attribs']['host']) {
  1000. $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
  1001. return true;
  1002. }
  1003. }
  1004. return false;
  1005. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1006. $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
  1007. $this->_isValid = false;
  1008. return true;
  1009. }
  1010. }
  1011. $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
  1012. $this->_isValid = false;
  1013. return true;
  1014. }
  1015. /**
  1016. * Set the socket number (port) that is used to connect to this channel
  1017. * @param bool Determines whether to turn on SSL support or turn it off
  1018. * @param string|false name of the mirror server, or false for the primary
  1019. */
  1020. function setSSL($ssl = true, $mirror = false)
  1021. {
  1022. if ($mirror) {
  1023. if (!isset($this->_channelInfo['servers']['mirror'])) {
  1024. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1025. array('mirror' => $mirror));
  1026. return false;
  1027. }
  1028. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1029. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1030. if ($mirror == $mir['attribs']['host']) {
  1031. if (!$ssl) {
  1032. if (isset($this->_channelInfo['servers']['mirror'][$i]
  1033. ['attribs']['ssl'])) {
  1034. unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
  1035. }
  1036. } else {
  1037. $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
  1038. }
  1039. return true;
  1040. }
  1041. }
  1042. return false;
  1043. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1044. if (!$ssl) {
  1045. if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
  1046. unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
  1047. }
  1048. } else {
  1049. $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
  1050. }
  1051. $this->_isValid = false;
  1052. return true;
  1053. }
  1054. }
  1055. if ($ssl) {
  1056. $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
  1057. } else {
  1058. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  1059. unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
  1060. }
  1061. }
  1062. $this->_isValid = false;
  1063. return true;
  1064. }
  1065. /**
  1066. * @param string
  1067. * @return string|false
  1068. * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
  1069. * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
  1070. */
  1071. function setServer($server, $mirror = false)
  1072. {
  1073. if (empty($server)) {
  1074. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
  1075. return false;
  1076. } elseif (!$this->validChannelServer($server)) {
  1077. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  1078. array('tag' => 'name', 'name' => $server));
  1079. return false;
  1080. }
  1081. if ($mirror) {
  1082. $found = false;
  1083. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1084. if ($mirror == $mir['attribs']['host']) {
  1085. $found = true;
  1086. break;
  1087. }
  1088. }
  1089. if (!$found) {
  1090. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1091. array('mirror' => $mirror));
  1092. return false;
  1093. }
  1094. $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
  1095. return true;
  1096. }
  1097. $this->_channelInfo['name'] = $server;
  1098. return true;
  1099. }
  1100. /**
  1101. * @param string
  1102. * @return boolean success
  1103. * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
  1104. * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
  1105. */
  1106. function setSummary($summary)
  1107. {
  1108. if (empty($summary)) {
  1109. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  1110. return false;
  1111. } elseif (strpos(trim($summary), "\n") !== false) {
  1112. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  1113. array('summary' => $summary));
  1114. }
  1115. $this->_channelInfo['summary'] = $summary;
  1116. return true;
  1117. }
  1118. /**
  1119. * @param string
  1120. * @param boolean determines whether the alias is in channel.xml or local
  1121. * @return boolean success
  1122. */
  1123. function setAlias($alias, $local = false)
  1124. {
  1125. if (!$this->validChannelServer($alias)) {
  1126. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  1127. array('tag' => 'suggestedalias', 'name' => $alias));
  1128. return false;
  1129. }
  1130. if ($local) {
  1131. $this->_channelInfo['localalias'] = $alias;
  1132. } else {
  1133. $this->_channelInfo['suggestedalias'] = $alias;
  1134. }
  1135. return true;
  1136. }
  1137. /**
  1138. * @return string
  1139. */
  1140. function getAlias()
  1141. {
  1142. if (isset($this->_channelInfo['localalias'])) {
  1143. return $this->_channelInfo['localalias'];
  1144. }
  1145. if (isset($this->_channelInfo['suggestedalias'])) {
  1146. return $this->_channelInfo['suggestedalias'];
  1147. }
  1148. if (isset($this->_channelInfo['name'])) {
  1149. return $this->_channelInfo['name'];
  1150. }
  1151. return '';
  1152. }
  1153. /**
  1154. * Set the package validation object if it differs from PEAR's default
  1155. * The class must be includeable via changing _ in the classname to path separator,
  1156. * but no checking of this is made.
  1157. * @param string|false pass in false to reset to the default packagename regex
  1158. * @return boolean success
  1159. */
  1160. function setValidationPackage($validateclass, $version)
  1161. {
  1162. if (empty($validateclass)) {
  1163. unset($this->_channelInfo['validatepackage']);
  1164. }
  1165. $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
  1166. $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
  1167. }
  1168. /**
  1169. * Add a protocol to the provides section
  1170. * @param string protocol type
  1171. * @param string protocol version
  1172. * @param string protocol name, if any
  1173. * @param string mirror name, if this is a mirror's protocol
  1174. * @return bool
  1175. */
  1176. function addFunction($type, $version, $name = '', $mirror = false)
  1177. {
  1178. if ($mirror) {
  1179. return $this->addMirrorFunction($mirror, $type, $version, $name);
  1180. }
  1181. $set = array('attribs' => array('version' => $version), '_content' => $name);
  1182. if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
  1183. if (!isset($this->_channelInfo['servers'])) {
  1184. $this->_channelInfo['servers'] = array('primary' =>
  1185. array($type => array()));
  1186. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  1187. $this->_channelInfo['servers']['primary'] = array($type => array());
  1188. }
  1189. $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
  1190. $this->_isValid = false;
  1191. return true;
  1192. } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
  1193. $this->_channelInfo['servers']['primary'][$type]['function'] = array(
  1194. $this->_channelInfo['servers']['primary'][$type]['function']);
  1195. }
  1196. $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
  1197. return true;
  1198. }
  1199. /**
  1200. * Add a protocol to a mirror's provides section
  1201. * @param string mirror name (server)
  1202. * @param string protocol type
  1203. * @param string protocol version
  1204. * @param string protocol name, if any
  1205. */
  1206. function addMirrorFunction($mirror, $type, $version, $name = '')
  1207. {
  1208. if (!isset($this->_channelInfo['servers']['mirror'])) {
  1209. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1210. array('mirror' => $mirror));
  1211. return false;
  1212. }
  1213. $setmirror = false;
  1214. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1215. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1216. if ($mirror == $mir['attribs']['host']) {
  1217. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  1218. break;
  1219. }
  1220. }
  1221. } else {
  1222. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1223. $setmirror = &$this->_channelInfo['servers']['mirror'];
  1224. }
  1225. }
  1226. if (!$setmirror) {
  1227. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1228. array('mirror' => $mirror));
  1229. return false;
  1230. }
  1231. $set = array('attribs' => array('version' => $version), '_content' => $name);
  1232. if (!isset($setmirror[$type]['function'])) {
  1233. $setmirror[$type]['function'] = $set;
  1234. $this->_isValid = false;
  1235. return true;
  1236. } elseif (!isset($setmirror[$type]['function'][0])) {
  1237. $setmirror[$type]['function'] = array($setmirror[$type]['function']);
  1238. }
  1239. $setmirror[$type]['function'][] = $set;
  1240. $this->_isValid = false;
  1241. return true;
  1242. }
  1243. /**
  1244. * @param string Resource Type this url links to
  1245. * @param string URL
  1246. * @param string|false mirror name, if this is not a primary server REST base URL
  1247. */
  1248. function setBaseURL($resourceType, $url, $mirror = false)
  1249. {
  1250. if ($mirror) {
  1251. if (!isset($this->_channelInfo['servers']['mirror'])) {
  1252. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1253. array('mirror' => $mirror));
  1254. return false;
  1255. }
  1256. $setmirror = false;
  1257. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1258. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1259. if ($mirror == $mir['attribs']['host']) {
  1260. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  1261. break;
  1262. }
  1263. }
  1264. } else {
  1265. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1266. $setmirror = &$this->_channelInfo['servers']['mirror'];
  1267. }
  1268. }
  1269. } else {
  1270. $setmirror = &$this->_channelInfo['servers']['primary'];
  1271. }
  1272. $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
  1273. if (!isset($setmirror['rest'])) {
  1274. $setmirror['rest'] = array();
  1275. }
  1276. if (!isset($setmirror['rest']['baseurl'])) {
  1277. $setmirror['rest']['baseurl'] = $set;
  1278. $this->_isValid = false;
  1279. return true;
  1280. } elseif (!isset($setmirror['rest']['baseurl'][0])) {
  1281. $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
  1282. }
  1283. foreach ($setmirror['rest']['baseurl'] as $i => $url) {
  1284. if ($url['attribs']['type'] == $resourceType) {
  1285. $this->_isValid = false;
  1286. $setmirror['rest']['baseurl'][$i] = $set;
  1287. return true;
  1288. }
  1289. }
  1290. $setmirror['rest']['baseurl'][] = $set;
  1291. $this->_isValid = false;
  1292. return true;
  1293. }
  1294. /**
  1295. * @param string mirror server
  1296. * @param int mirror http port
  1297. * @return boolean
  1298. */
  1299. function addMirror($server, $port = null)
  1300. {
  1301. if ($this->_channelInfo['name'] == '__uri') {
  1302. return false; // the __uri channel cannot have mirrors by definition
  1303. }
  1304. $set = array('attribs' => array('host' => $server));
  1305. if (is_numeric($port)) {
  1306. $set['attribs']['port'] = $port;
  1307. }
  1308. if (!isset($this->_channelInfo['servers']['mirror'])) {
  1309. $this->_channelInfo['servers']['mirror'] = $set;
  1310. return true;
  1311. }
  1312. if (!isset($this->_channelInfo['servers']['mirror'][0])) {
  1313. $this->_channelInfo['servers']['mirror'] =
  1314. array($this->_channelInfo['servers']['mirror']);
  1315. }
  1316. $this->_channelInfo['servers']['mirror'][] = $set;
  1317. return true;
  1318. }
  1319. /**
  1320. * Retrieve the name of the validation package for this channel
  1321. * @return string|false
  1322. */
  1323. function getValidationPackage()
  1324. {
  1325. if (!$this->_isValid && !$this->validate()) {
  1326. return false;
  1327. }
  1328. if (!isset($this->_channelInfo['validatepackage'])) {
  1329. return array('attribs' => array('version' => 'default'),
  1330. '_content' => 'PEAR_Validate');
  1331. }
  1332. return $this->_channelInfo['validatepackage'];
  1333. }
  1334. /**
  1335. * Retrieve the object that can be used for custom validation
  1336. * @param string|false the name of the package to validate. If the package is
  1337. * the channel validation package, PEAR_Validate is returned
  1338. * @return PEAR_Validate|false false is returned if the validation package
  1339. * cannot be located
  1340. */
  1341. function &getValidationObject($package = false)
  1342. {
  1343. if (!class_exists('PEAR_Validate')) {
  1344. require_once 'PEAR/Validate.php';
  1345. }
  1346. if (!$this->_isValid) {
  1347. if (!$this->validate()) {
  1348. $a = false;
  1349. return $a;
  1350. }
  1351. }
  1352. if (isset($this->_channelInfo['validatepackage'])) {
  1353. if ($package == $this->_channelInfo['validatepackage']) {
  1354. // channel validation packages are always validated by PEAR_Validate
  1355. $val = new PEAR_Validate;
  1356. return $val;
  1357. }
  1358. if (!class_exists(str_replace('.', '_',
  1359. $this->_channelInfo['validatepackage']['_content']))) {
  1360. if ($this->isIncludeable(str_replace('_', '/',
  1361. $this->_channelInfo['validatepackage']['_content']) . '.php')) {
  1362. include_once str_replace('_', '/',
  1363. $this->_channelInfo['validatepackage']['_content']) . '.php';
  1364. $vclass = str_replace('.', '_',
  1365. $this->_channelInfo['validatepackage']['_content']);
  1366. $val = new $vclass;
  1367. } else {
  1368. $a = false;
  1369. return $a;
  1370. }
  1371. } else {
  1372. $vclass = str_replace('.', '_',
  1373. $this->_channelInfo['validatepackage']['_content']);
  1374. $val = new $vclass;
  1375. }
  1376. } else {
  1377. $val = new PEAR_Validate;
  1378. }
  1379. return $val;
  1380. }
  1381. function isIncludeable($path)
  1382. {
  1383. $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
  1384. foreach ($possibilities as $dir) {
  1385. if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
  1386. && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
  1387. return true;
  1388. }
  1389. }
  1390. return false;
  1391. }
  1392. /**
  1393. * This function is used by the channel updater and retrieves a value set by
  1394. * the registry, or the current time if it has not been set
  1395. * @return string
  1396. */
  1397. function lastModified()
  1398. {
  1399. if (isset($this->_channelInfo['_lastmodified'])) {
  1400. return $this->_channelInfo['_lastmodified'];
  1401. }
  1402. return time();
  1403. }
  1404. }