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