home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / tmp / PEAR-1.7.1 / PEAR / ChannelFile.php < prev    next >
Encoding:
PHP Script  |  2008-02-15  |  55.6 KB  |  1,616 lines

  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-2008 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.80 2008/01/03 20:26:34 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.  */
  26. require_once 'PEAR/ErrorStack.php';
  27. require_once 'PEAR/XMLParser.php';
  28. require_once 'PEAR/Common.php';
  29.  
  30. /**
  31.  * Error code if the channel.xml <channel> tag does not contain a valid version
  32.  */
  33. define('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.  */
  38. define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
  39.  
  40. /**
  41.  * Error code if parsing is attempted with no xml extension
  42.  */
  43. define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
  44.  
  45. /**
  46.  * Error code if creating the xml parser resource fails
  47.  */
  48. define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
  49.  
  50. /**
  51.  * Error code used for all sax xml parsing errors
  52.  */
  53. define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
  54.  
  55. /**#@+
  56.  * Validation errors
  57.  */
  58. /**
  59.  * Error code when channel name is missing
  60.  */
  61. define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
  62. /**
  63.  * Error code when channel name is invalid
  64.  */
  65. define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
  66. /**
  67.  * Error code when channel summary is missing
  68.  */
  69. define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
  70. /**
  71.  * Error code when channel summary is multi-line
  72.  */
  73. define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
  74. /**
  75.  * Error code when channel server is missing for xmlrpc or soap protocol
  76.  */
  77. define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
  78. /**
  79.  * Error code when channel server is invalid for xmlrpc or soap protocol
  80.  */
  81. define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
  82. /**
  83.  * Error code when a mirror name is invalid
  84.  */
  85. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
  86. /**
  87.  * Error code when a mirror type is invalid
  88.  */
  89. define('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.  */
  93. define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
  94. /**
  95.  * Error code when an empty package name validate regex is passed in
  96.  */
  97. define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
  98. /**
  99.  * Error code when a <function> tag has no version
  100.  */
  101. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
  102. /**
  103.  * Error code when a <function> tag has no name
  104.  */
  105. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
  106. /**
  107.  * Error code when a <validatepackage> tag has no name
  108.  */
  109. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
  110. /**
  111.  * Error code when a <validatepackage> tag has no version attribute
  112.  */
  113. define('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.  */
  118. define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
  119. /**
  120.  * Error code when a server port is not numeric
  121.  */
  122. define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
  123. /**
  124.  * Error code when <static> contains no version attribute
  125.  */
  126. define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
  127. /**
  128.  * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
  129.  */
  130. define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
  131. /** 
  132.  * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
  133.  */
  134. define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
  135. /** 
  136.  * Error code when ssl attribute is present and is not "yes"
  137.  */
  138. define('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-2008 The PHP Group
  154.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  155.  * @version    Release: 1.7.1
  156.  * @link       http://pear.php.net/package/PEAR
  157.  * @since      Class available since Release 1.4.0a1
  158.  */
  159. class 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' => $result->getMessage()));
  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 (!file_exists($descfile) || !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.         fclose($fp);
  403.         $data = file_get_contents($descfile);
  404.         return $this->fromXmlString($data);
  405.     }
  406.  
  407.     /**
  408.      * Parse channel information from different sources
  409.      *
  410.      * This method is able to extract information about a channel
  411.      * from an .xml file or a string
  412.      *
  413.      * @access public
  414.      * @param  string Filename of the source or the source itself
  415.      * @return bool
  416.      */
  417.     function fromAny($info)
  418.     {
  419.         if (is_string($info) && file_exists($info) && strlen($info) < 255) {
  420.             $tmp = substr($info, -4);
  421.             if ($tmp == '.xml') {
  422.                 $info = $this->fromXmlFile($info);
  423.             } else {
  424.                 $fp = fopen($info, "r");
  425.                 $test = fread($fp, 5);
  426.                 fclose($fp);
  427.                 if ($test == "<?xml") {
  428.                     $info = $this->fromXmlFile($info);
  429.                 }
  430.             }
  431.             if (PEAR::isError($info)) {
  432.                 require_once 'PEAR.php';
  433.                 return PEAR::raiseError($info);
  434.             }
  435.         }
  436.         if (is_string($info)) {
  437.             $info = $this->fromXmlString($info);
  438.         }
  439.         return $info;
  440.     }
  441.  
  442.     /**
  443.      * Return an XML document based on previous parsing and modifications
  444.      *
  445.      * @return string XML data
  446.      *
  447.      * @access public
  448.      */
  449.     function toXml()
  450.     {
  451.         if (!$this->_isValid && !$this->validate()) {
  452.             $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
  453.             return false;
  454.         }
  455.         if (!isset($this->_channelInfo['attribs']['version'])) {
  456.             $this->_channelInfo['attribs']['version'] = '1.0';
  457.         }
  458.         $channelInfo = $this->_channelInfo;
  459.         $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  460.         $ret .= "<channel version=\"" .
  461.             $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  462.   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  463.   xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
  464.             . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
  465.             $channelInfo['attribs']['version'] . ".xsd\">
  466.  <name>$channelInfo[name]</name>
  467.  <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
  468. ";
  469.         if (isset($channelInfo['suggestedalias'])) {
  470.             $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
  471.         }
  472.         if (isset($channelInfo['validatepackage'])) {
  473.             $ret .= ' <validatepackage version="' .
  474.                 $channelInfo['validatepackage']['attribs']['version']. '">' .
  475.                 htmlspecialchars($channelInfo['validatepackage']['_content']) .
  476.                 "</validatepackage>\n";
  477.         }
  478.         $ret .= " <servers>\n";
  479.         $ret .= '  <primary';
  480.         if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
  481.             $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
  482.         }
  483.         if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
  484.             $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
  485.         }
  486.         $ret .= ">\n";
  487.         if (isset($channelInfo['servers']['primary']['xmlrpc'])) {
  488.             $ret .= $this->_makeXmlrpcXml($channelInfo['servers']['primary']['xmlrpc'], '   ');
  489.         }
  490.         if (isset($channelInfo['servers']['primary']['rest'])) {
  491.             $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
  492.         }
  493.         if (isset($channelInfo['servers']['primary']['soap'])) {
  494.             $ret .= $this->_makeSoapXml($channelInfo['servers']['primary']['soap'], '   ');
  495.         }
  496.         $ret .= "  </primary>\n";
  497.         if (isset($channelInfo['servers']['mirror'])) {
  498.             $ret .= $this->_makeMirrorsXml($channelInfo);
  499.         }
  500.         $ret .= " </servers>\n";
  501.         $ret .= "</channel>";
  502.         return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
  503.     }
  504.  
  505.     /**
  506.      * Generate the <xmlrpc> tag
  507.      * @access private
  508.      */
  509.     function _makeXmlrpcXml($info, $indent)
  510.     {
  511.         $ret = $indent . "<xmlrpc";
  512.         if (isset($info['attribs']['path'])) {
  513.             $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
  514.         }
  515.         $ret .= ">\n";
  516.         $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
  517.         $ret .= $indent . "</xmlrpc>\n";
  518.         return $ret;
  519.     }
  520.  
  521.     /**
  522.      * Generate the <soap> tag
  523.      * @access private
  524.      */
  525.     function _makeSoapXml($info, $indent)
  526.     {
  527.         $ret = $indent . "<soap";
  528.         if (isset($info['attribs']['path'])) {
  529.             $ret .= ' path="' . htmlspecialchars($info['attribs']['path']) . '"';
  530.         }
  531.         $ret .= ">\n";
  532.         $ret .= $this->_makeFunctionsXml($info['function'], "$indent ");
  533.         $ret .= $indent . "</soap>\n";
  534.         return $ret;
  535.     }
  536.  
  537.     /**
  538.      * Generate the <rest> tag
  539.      * @access private
  540.      */
  541.     function _makeRestXml($info, $indent)
  542.     {
  543.         $ret = $indent . "<rest>\n";
  544.         if (!isset($info['baseurl'][0])) {
  545.             $info['baseurl'] = array($info['baseurl']);
  546.         }
  547.         foreach ($info['baseurl'] as $url) {
  548.             $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
  549.             $ret .= ">" . $url['_content'] . "</baseurl>\n";
  550.         }
  551.         $ret .= $indent . "</rest>\n";
  552.         return $ret;
  553.     }
  554.  
  555.     /**
  556.      * Generate the <mirrors> tag
  557.      * @access private
  558.      */
  559.     function _makeMirrorsXml($channelInfo)
  560.     {
  561.         $ret = "";
  562.         if (!isset($channelInfo['servers']['mirror'][0])) {
  563.             $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
  564.         }
  565.         foreach ($channelInfo['servers']['mirror'] as $mirror) {
  566.             $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
  567.             if (isset($mirror['attribs']['port'])) {
  568.                 $ret .= ' port="' . $mirror['attribs']['port'] . '"';
  569.             }
  570.             if (isset($mirror['attribs']['ssl'])) {
  571.                 $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
  572.             }
  573.             $ret .= ">\n";
  574.             if (isset($mirror['xmlrpc']) || isset($mirror['soap'])) {
  575.                 if (isset($mirror['xmlrpc'])) {
  576.                     $ret .= $this->_makeXmlrpcXml($mirror['xmlrpc'], '   ');
  577.                 }
  578.                 if (isset($mirror['rest'])) {
  579.                     $ret .= $this->_makeRestXml($mirror['rest'], '   ');
  580.                 }
  581.                 if (isset($mirror['soap'])) {
  582.                     $ret .= $this->_makeSoapXml($mirror['soap'], '   ');
  583.                 }
  584.                 $ret .= "  </mirror>\n";
  585.             } else {
  586.                 $ret .= "/>\n";
  587.             }
  588.         }
  589.         return $ret;
  590.     }
  591.  
  592.     /**
  593.      * Generate the <functions> tag
  594.      * @access private
  595.      */
  596.     function _makeFunctionsXml($functions, $indent, $rest = false)
  597.     {
  598.         $ret = '';
  599.         if (!isset($functions[0])) {
  600.             $functions = array($functions);
  601.         }
  602.         foreach ($functions as $function) {
  603.             $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
  604.             if ($rest) {
  605.                 $ret .= ' uri="' . $function['attribs']['uri'] . '"';
  606.             }
  607.             $ret .= ">" . $function['_content'] . "</function>\n";
  608.         }
  609.         return $ret;
  610.     }
  611.  
  612.     /**
  613.      * Validation error.  Also marks the object contents as invalid
  614.      * @param error code
  615.      * @param array error information
  616.      * @access private
  617.      */
  618.     function _validateError($code, $params = array())
  619.     {
  620.         $this->_stack->push($code, 'error', $params);
  621.         $this->_isValid = false;
  622.     }
  623.  
  624.     /**
  625.      * Validation warning.  Does not mark the object contents invalid.
  626.      * @param error code
  627.      * @param array error information
  628.      * @access private
  629.      */
  630.     function _validateWarning($code, $params = array())
  631.     {
  632.         $this->_stack->push($code, 'warning', $params);
  633.     }
  634.  
  635.     /**
  636.      * Validate parsed file.
  637.      *
  638.      * @access public
  639.      * @return boolean
  640.      */
  641.     function validate()
  642.     {
  643.         $this->_isValid = true;
  644.         $info = $this->_channelInfo;
  645.         if (empty($info['name'])) {
  646.             $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
  647.         } elseif (!$this->validChannelServer($info['name'])) {
  648.             if ($info['name'] != '__uri') {
  649.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
  650.                     'name' => $info['name']));
  651.             }
  652.         }
  653.         if (empty($info['summary'])) {
  654.             $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  655.         } elseif (strpos(trim($info['summary']), "\n") !== false) {
  656.             $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  657.                 array('summary' => $info['summary']));
  658.         }
  659.         if (isset($info['suggestedalias'])) {
  660.             if (!$this->validChannelServer($info['suggestedalias'])) {
  661.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  662.                     array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
  663.             }
  664.         }
  665.         if (isset($info['localalias'])) {
  666.             if (!$this->validChannelServer($info['localalias'])) {
  667.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  668.                     array('tag' => 'localalias', 'name' =>$info['localalias']));
  669.             }
  670.         }
  671.         if (isset($info['validatepackage'])) {
  672.             if (!isset($info['validatepackage']['_content'])) {
  673.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
  674.             }
  675.             if (!isset($info['validatepackage']['attribs']['version'])) {
  676.                 $content = isset($info['validatepackage']['_content']) ?
  677.                     $info['validatepackage']['_content'] :
  678.                     null;
  679.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
  680.                     array('package' => $content));
  681.             }
  682.         }
  683.         if (isset($info['servers']['primary']['attribs']['port']) &&
  684.               !is_numeric($info['servers']['primary']['attribs']['port'])) {
  685.             $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
  686.                 array('port' => $info['servers']['primary']['attribs']['port']));
  687.         }
  688.         if (isset($info['servers']['primary']['attribs']['ssl']) &&
  689.               $info['servers']['primary']['attribs']['ssl'] != 'yes') {
  690.             $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  691.                 array('ssl' => $info['servers']['primary']['attribs']['ssl'],
  692.                     'server' => $info['name']));
  693.         }
  694.  
  695.         if (isset($info['servers']['primary']['xmlrpc']) &&
  696.               isset($info['servers']['primary']['xmlrpc']['function'])) {
  697.             $this->_validateFunctions('xmlrpc', $info['servers']['primary']['xmlrpc']['function']);
  698.         }
  699.         if (isset($info['servers']['primary']['soap']) &&
  700.               isset($info['servers']['primary']['soap']['function'])) {
  701.             $this->_validateFunctions('soap', $info['servers']['primary']['soap']['function']);
  702.         }
  703.         if (isset($info['servers']['primary']['rest']) &&
  704.               isset($info['servers']['primary']['rest']['baseurl'])) {
  705.             $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
  706.         }
  707.         if (isset($info['servers']['mirror'])) {
  708.             if ($this->_channelInfo['name'] == '__uri') {
  709.                 $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
  710.             }
  711.             if (!isset($info['servers']['mirror'][0])) {
  712.                 $info['servers']['mirror'] = array($info['servers']['mirror']);
  713.             }
  714.             foreach ($info['servers']['mirror'] as $mirror) {
  715.                 if (!isset($mirror['attribs']['host'])) {
  716.                     $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
  717.                       array('type' => 'mirror'));
  718.                 } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
  719.                     $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
  720.                         array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
  721.                 }
  722.                 if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
  723.                     $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  724.                         array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
  725.                 }
  726.                 if (isset($mirror['xmlrpc'])) {
  727.                     $this->_validateFunctions('xmlrpc',
  728.                         $mirror['xmlrpc']['function'], $mirror['attribs']['host']);
  729.                 }
  730.                 if (isset($mirror['soap'])) {
  731.                     $this->_validateFunctions('soap', $mirror['soap']['function'],
  732.                         $mirror['attribs']['host']);
  733.                 }
  734.                 if (isset($mirror['rest'])) {
  735.                     $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
  736.                         $mirror['attribs']['host']);
  737.                 }
  738.             }
  739.         }
  740.         return $this->_isValid;
  741.     }
  742.  
  743.     /**
  744.      * @param string xmlrpc or soap - protocol name this function applies to
  745.      * @param array the functions
  746.      * @param string the name of the parent element (mirror name, for instance)
  747.      */
  748.     function _validateFunctions($protocol, $functions, $parent = '')
  749.     {
  750.         if (!isset($functions[0])) {
  751.             $functions = array($functions);
  752.         }
  753.         foreach ($functions as $function) {
  754.             if (!isset($function['_content']) || empty($function['_content'])) {
  755.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
  756.                     array('parent' => $parent, 'protocol' => $protocol));
  757.             }
  758.             if ($protocol == 'rest') {
  759.                 if (!isset($function['attribs']['type']) ||
  760.                       empty($function['attribs']['type'])) {
  761.                     $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_BASEURLTYPE,
  762.                         array('parent' => $parent, 'protocol' => $protocol));
  763.                 }
  764.             } else {
  765.                 if (!isset($function['attribs']['version']) ||
  766.                       empty($function['attribs']['version'])) {
  767.                     $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
  768.                         array('parent' => $parent, 'protocol' => $protocol));
  769.                 }
  770.             }
  771.         }
  772.     }
  773.  
  774.     /**
  775.      * Test whether a string contains a valid channel server.
  776.      * @param string $ver the package version to test
  777.      * @return bool
  778.      */
  779.     function validChannelServer($server)
  780.     {
  781.         if ($server == '__uri') {
  782.             return true;
  783.         }
  784.         return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
  785.     }
  786.  
  787.     /**
  788.      * @return string|false
  789.      */
  790.     function getName()
  791.     {
  792.         if (isset($this->_channelInfo['name'])) {
  793.             return $this->_channelInfo['name'];
  794.         } else {
  795.             return false;
  796.         }
  797.     }
  798.  
  799.     /**
  800.      * @return string|false
  801.      */
  802.     function getServer()
  803.     {
  804.         if (isset($this->_channelInfo['name'])) {
  805.             return $this->_channelInfo['name'];
  806.         } else {
  807.             return false;
  808.         }
  809.     }
  810.  
  811.     /**
  812.      * @return int|80 port number to connect to
  813.      */
  814.     function getPort($mirror = false)
  815.     {
  816.         if ($mirror) {
  817.             if ($mir = $this->getMirror($mirror)) {
  818.                 if (isset($mir['attribs']['port'])) {
  819.                     return $mir['attribs']['port'];
  820.                 } else {
  821.                     if ($this->getSSL($mirror)) {
  822.                         return 443;
  823.                     }
  824.                     return 80;
  825.                 }
  826.             }
  827.             return false;
  828.         }
  829.         if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
  830.             return $this->_channelInfo['servers']['primary']['attribs']['port'];
  831.         }
  832.         if ($this->getSSL()) {
  833.             return 443;
  834.         }
  835.         return 80;
  836.     }
  837.  
  838.     /**
  839.      * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
  840.      */
  841.     function getSSL($mirror = false)
  842.     {
  843.         if ($mirror) {
  844.             if ($mir = $this->getMirror($mirror)) {
  845.                 if (isset($mir['attribs']['ssl'])) {
  846.                     return true;
  847.                 } else {
  848.                     return false;
  849.                 }
  850.             }
  851.             return false;
  852.         }
  853.         if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  854.             return true;
  855.         }
  856.         return false;
  857.     }
  858.  
  859.     /**
  860.      * @return string|false
  861.      */
  862.     function getSummary()
  863.     {
  864.         if (isset($this->_channelInfo['summary'])) {
  865.             return $this->_channelInfo['summary'];
  866.         } else {
  867.             return false;
  868.         }
  869.     }
  870.  
  871.     /**
  872.      * @param string xmlrpc or soap
  873.      * @param string|false mirror name or false for primary server
  874.      */
  875.     function getPath($protocol, $mirror = false)
  876.     {   
  877.         if (!in_array($protocol, array('xmlrpc', 'soap'))) {
  878.             return false;
  879.         }
  880.         if ($mirror) {
  881.             if (!($mir = $this->getMirror($mirror))) {
  882.                 return false;
  883.             }
  884.             if (isset($mir[$protocol]['attribs']['path'])) {
  885.                 return $mir[$protocol]['attribs']['path'];
  886.             } else {
  887.                 return $protocol . '.php';
  888.             }
  889.         } elseif (isset($this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'])) {
  890.             return $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'];
  891.         }
  892.         return $protocol . '.php';
  893.     }
  894.  
  895.     /**
  896.      * @param string protocol type (xmlrpc, soap)
  897.      * @param string Mirror name
  898.      * @return array|false
  899.      */
  900.     function getFunctions($protocol, $mirror = false)
  901.     {
  902.         if ($this->getName() == '__uri') {
  903.             return false;
  904.         }
  905.         if ($protocol == 'rest') {
  906.             $function = 'baseurl';
  907.         } else {
  908.             $function = 'function';
  909.         }
  910.         if ($mirror) {
  911.             if ($mir = $this->getMirror($mirror)) {
  912.                 if (isset($mir[$protocol][$function])) {
  913.                     return $mir[$protocol][$function];
  914.                 }
  915.             }
  916.             return false;
  917.         }
  918.         if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
  919.             return $this->_channelInfo['servers']['primary'][$protocol][$function];
  920.         } else {
  921.             return false;
  922.         }
  923.     }
  924.  
  925.     /**
  926.      * @param string Protocol type
  927.      * @param string Function name (null to return the
  928.      *               first protocol of the type requested)
  929.      * @param string Mirror name, if any
  930.      * @return array
  931.      */
  932.      function getFunction($type, $name = null, $mirror = false)
  933.      {
  934.         $protocols = $this->getFunctions($type, $mirror);
  935.         if (!$protocols) {
  936.             return false;
  937.         }
  938.         foreach ($protocols as $protocol) {
  939.             if ($name === null) {
  940.                 return $protocol;
  941.             }
  942.             if ($protocol['_content'] != $name) {
  943.                 continue;
  944.             }
  945.             return $protocol;
  946.         }
  947.         return false;
  948.      }
  949.  
  950.     /**
  951.      * @param string protocol type
  952.      * @param string protocol name
  953.      * @param string version
  954.      * @param string mirror name
  955.      * @return boolean
  956.      */
  957.     function supports($type, $name = null, $mirror = false, $version = '1.0')
  958.     {
  959.         $protocols = $this->getFunctions($type, $mirror);
  960.         if (!$protocols) {
  961.             return false;
  962.         }
  963.         foreach ($protocols as $protocol) {
  964.             if ($protocol['attribs']['version'] != $version) {
  965.                 continue;
  966.             }
  967.             if ($name === null) {
  968.                 return true;
  969.             }
  970.             if ($protocol['_content'] != $name) {
  971.                 continue;
  972.             }
  973.             return true;
  974.         }
  975.         return false;
  976.     }
  977.  
  978.     /**
  979.      * Determines whether a channel supports Representational State Transfer (REST) protocols
  980.      * for retrieving channel information
  981.      * @param string
  982.      * @return bool
  983.      */
  984.     function supportsREST($mirror = false)
  985.     {
  986.         if ($mirror == $this->_channelInfo['name']) {
  987.             $mirror = false;
  988.         }
  989.         if ($mirror) {
  990.             if ($mir = $this->getMirror($mirror)) {
  991.                 return isset($mir['rest']);
  992.             }
  993.             return false;
  994.         }
  995.         return isset($this->_channelInfo['servers']['primary']['rest']);
  996.     }
  997.  
  998.     /**
  999.      * Get the URL to access a base resource.
  1000.      *
  1001.      * Hyperlinks in the returned xml will be used to retrieve the proper information
  1002.      * needed.  This allows extreme extensibility and flexibility in implementation
  1003.      * @param string Resource Type to retrieve
  1004.      */
  1005.     function getBaseURL($resourceType, $mirror = false)
  1006.     {
  1007.         if ($mirror == $this->_channelInfo['name']) {
  1008.             $mirror = false;
  1009.         }
  1010.         if ($mirror) {
  1011.             if ($mir = $this->getMirror($mirror)) {
  1012.                 $rest = $mir['rest'];
  1013.             } else {
  1014.                 return false;
  1015.             }
  1016.         } else {
  1017.             $rest = $this->_channelInfo['servers']['primary']['rest'];
  1018.         }
  1019.         if (!isset($rest['baseurl'][0])) {
  1020.             $rest['baseurl'] = array($rest['baseurl']);
  1021.         }
  1022.         foreach ($rest['baseurl'] as $baseurl) {
  1023.             if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
  1024.                 return $baseurl['_content'];
  1025.             }
  1026.         }
  1027.         return false;
  1028.     }
  1029.  
  1030.     /**
  1031.      * Since REST does not implement RPC, provide this as a logical wrapper around
  1032.      * resetFunctions for REST
  1033.      * @param string|false mirror name, if any
  1034.      */
  1035.     function resetREST($mirror = false)
  1036.     {
  1037.         return $this->resetFunctions('rest', $mirror);
  1038.     }
  1039.  
  1040.     /**
  1041.      * Empty all protocol definitions
  1042.      * @param string protocol type (xmlrpc, soap)
  1043.      * @param string|false mirror name, if any
  1044.      */
  1045.     function resetFunctions($type, $mirror = false)
  1046.     {
  1047.         if ($mirror) {
  1048.             if (isset($this->_channelInfo['servers']['mirror'])) {
  1049.                 $mirrors = $this->_channelInfo['servers']['mirror'];
  1050.                 if (!isset($mirrors[0])) {
  1051.                     $mirrors = array($mirrors);
  1052.                 }
  1053.                 foreach ($mirrors as $i => $mir) {
  1054.                     if ($mir['attribs']['host'] == $mirror) {
  1055.                         if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
  1056.                             unset($this->_channelInfo['servers']['mirror'][$i][$type]);
  1057.                         }
  1058.                         return true;
  1059.                     }
  1060.                 }
  1061.                 return false;
  1062.             } else {
  1063.                 return false;
  1064.             }
  1065.         } else {
  1066.             if (isset($this->_channelInfo['servers']['primary'][$type])) {
  1067.                 unset($this->_channelInfo['servers']['primary'][$type]);
  1068.             }
  1069.             return true;
  1070.         }
  1071.     }
  1072.  
  1073.     /**
  1074.      * Set a channel's protocols to the protocols supported by pearweb
  1075.      */
  1076.     function setDefaultPEARProtocols($version = '1.0', $mirror = false)
  1077.     {
  1078.         switch ($version) {
  1079.             case '1.0' :
  1080.                 $this->resetFunctions('xmlrpc', $mirror);
  1081.                 $this->resetFunctions('soap', $mirror);
  1082.                 $this->resetREST($mirror);
  1083.                 $this->addFunction('xmlrpc', '1.0', 'logintest', $mirror);
  1084.                 $this->addFunction('xmlrpc', '1.0', 'package.listLatestReleases', $mirror);
  1085.                 $this->addFunction('xmlrpc', '1.0', 'package.listAll', $mirror);
  1086.                 $this->addFunction('xmlrpc', '1.0', 'package.info', $mirror);
  1087.                 $this->addFunction('xmlrpc', '1.0', 'package.getDownloadURL', $mirror);
  1088.                 $this->addFunction('xmlrpc', '1.1', 'package.getDownloadURL', $mirror);
  1089.                 $this->addFunction('xmlrpc', '1.0', 'package.getDepDownloadURL', $mirror);
  1090.                 $this->addFunction('xmlrpc', '1.1', 'package.getDepDownloadURL', $mirror);
  1091.                 $this->addFunction('xmlrpc', '1.0', 'package.search', $mirror);
  1092.                 $this->addFunction('xmlrpc', '1.0', 'channel.listAll', $mirror);
  1093.                 return true;
  1094.             break;
  1095.             default :
  1096.                 return false;
  1097.             break;
  1098.         }
  1099.     }
  1100.     
  1101.     /**
  1102.      * @return array
  1103.      */
  1104.     function getMirrors()
  1105.     {
  1106.         if (isset($this->_channelInfo['servers']['mirror'])) {
  1107.             $mirrors = $this->_channelInfo['servers']['mirror'];
  1108.             if (!isset($mirrors[0])) {
  1109.                 $mirrors = array($mirrors);
  1110.             }
  1111.             return $mirrors;
  1112.         } else {
  1113.             return array();
  1114.         }
  1115.     }
  1116.  
  1117.     /**
  1118.      * Get the unserialized XML representing a mirror
  1119.      * @return array|false
  1120.      */
  1121.     function getMirror($server)
  1122.     {
  1123.         foreach ($this->getMirrors() as $mirror) {
  1124.             if ($mirror['attribs']['host'] == $server) {
  1125.                 return $mirror;
  1126.             }
  1127.         }
  1128.         return false;
  1129.     }
  1130.  
  1131.     /**
  1132.      * @param string
  1133.      * @return string|false
  1134.      * @error PEAR_CHANNELFILE_ERROR_NO_NAME
  1135.      * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
  1136.      */
  1137.     function setName($name)
  1138.     {
  1139.         return $this->setServer($name);
  1140.     }
  1141.  
  1142.     /**
  1143.      * Set the socket number (port) that is used to connect to this channel
  1144.      * @param integer
  1145.      * @param string|false name of the mirror server, or false for the primary
  1146.      */
  1147.     function setPort($port, $mirror = false)
  1148.     {
  1149.         if ($mirror) {
  1150.             if (!isset($this->_channelInfo['servers']['mirror'])) {
  1151.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1152.                     array('mirror' => $mirror));
  1153.                 return false;
  1154.             }
  1155.             if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1156.                 foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1157.                     if ($mirror == $mir['attribs']['host']) {
  1158.                         $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
  1159.                         return true;
  1160.                     }
  1161.                 }
  1162.                 return false;
  1163.             } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1164.                 $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
  1165.                 $this->_isValid = false;
  1166.                 return true;
  1167.             }
  1168.         }
  1169.         $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
  1170.         $this->_isValid = false;
  1171.         return true;
  1172.     }
  1173.  
  1174.     /**
  1175.      * Set the socket number (port) that is used to connect to this channel
  1176.      * @param bool Determines whether to turn on SSL support or turn it off
  1177.      * @param string|false name of the mirror server, or false for the primary
  1178.      */
  1179.     function setSSL($ssl = true, $mirror = false)
  1180.     {
  1181.         if ($mirror) {
  1182.             if (!isset($this->_channelInfo['servers']['mirror'])) {
  1183.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1184.                     array('mirror' => $mirror));
  1185.                 return false;
  1186.             }
  1187.             if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1188.                 foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1189.                     if ($mirror == $mir['attribs']['host']) {
  1190.                         if (!$ssl) {
  1191.                             if (isset($this->_channelInfo['servers']['mirror'][$i]
  1192.                                   ['attribs']['ssl'])) {
  1193.                                 unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
  1194.                             }
  1195.                         } else {
  1196.                             $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
  1197.                         }
  1198.                         return true;
  1199.                     }
  1200.                 }
  1201.                 return false;
  1202.             } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1203.                 if (!$ssl) {
  1204.                     if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
  1205.                         unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
  1206.                     }
  1207.                 } else {
  1208.                     $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
  1209.                 }
  1210.                 $this->_isValid = false;
  1211.                 return true;
  1212.             }
  1213.         }
  1214.         if ($ssl) {
  1215.             $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
  1216.         } else {
  1217.             if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  1218.                 unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
  1219.             }
  1220.         }
  1221.         $this->_isValid = false;
  1222.         return true;
  1223.     }
  1224.  
  1225.     /**
  1226.      * Set the socket number (port) that is used to connect to this channel
  1227.      * @param integer
  1228.      * @param string|false name of the mirror server, or false for the primary
  1229.      */
  1230.     function setPath($protocol, $path, $mirror = false)
  1231.     {
  1232.         if (!in_array($protocol, array('xmlrpc', 'soap'))) {
  1233.             return false;
  1234.         }
  1235.         if ($mirror) {
  1236.             if (!isset($this->_channelInfo['servers']['mirror'])) {
  1237.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1238.                     array('mirror' => $mirror));
  1239.                 return false;
  1240.             }
  1241.             if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1242.                 foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1243.                     if ($mirror == $mir['attribs']['host']) {
  1244.                         $this->_channelInfo['servers']['mirror'][$i][$protocol]['attribs']['path'] =
  1245.                             $path;
  1246.                         return true;
  1247.                     }
  1248.                 }
  1249.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1250.                     array('mirror' => $mirror));
  1251.                 return false;
  1252.             } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1253.                 $this->_channelInfo['servers']['mirror'][$protocol]['attribs']['path'] = $path;
  1254.                 $this->_isValid = false;
  1255.                 return true;
  1256.             }
  1257.         }
  1258.         $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'] = $path;
  1259.         $this->_isValid = false;
  1260.         return true;
  1261.     }
  1262.  
  1263.     /**
  1264.      * @param string
  1265.      * @return string|false
  1266.      * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
  1267.      * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
  1268.      */
  1269.     function setServer($server, $mirror = false)
  1270.     {
  1271.         if (empty($server)) {
  1272.             $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
  1273.             return false;
  1274.         } elseif (!$this->validChannelServer($server)) {
  1275.             $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  1276.                 array('tag' => 'name', 'name' => $server));
  1277.             return false;
  1278.         }
  1279.         if ($mirror) {
  1280.             $found = false;
  1281.             foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1282.                 if ($mirror == $mir['attribs']['host']) {
  1283.                     $found = true;
  1284.                     break;
  1285.                 }
  1286.             }
  1287.             if (!$found) {
  1288.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1289.                     array('mirror' => $mirror));
  1290.                 return false;
  1291.             }
  1292.             $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
  1293.             return true;
  1294.         }
  1295.         $this->_channelInfo['name'] = $server;
  1296.         return true;
  1297.     }
  1298.  
  1299.     /**
  1300.      * @param string
  1301.      * @return boolean success
  1302.      * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
  1303.      * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
  1304.      */
  1305.     function setSummary($summary)
  1306.     {
  1307.         if (empty($summary)) {
  1308.             $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  1309.             return false;
  1310.         } elseif (strpos(trim($summary), "\n") !== false) {
  1311.             $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  1312.                 array('summary' => $summary));
  1313.         }
  1314.         $this->_channelInfo['summary'] = $summary;
  1315.         return true;
  1316.     }
  1317.  
  1318.     /**
  1319.      * @param string
  1320.      * @param boolean determines whether the alias is in channel.xml or local
  1321.      * @return boolean success
  1322.      */
  1323.     function setAlias($alias, $local = false)
  1324.     {
  1325.         if (!$this->validChannelServer($alias)) {
  1326.             $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  1327.                 array('tag' => 'suggestedalias', 'name' => $alias));
  1328.             return false;
  1329.         }
  1330.         if ($local) {
  1331.             $this->_channelInfo['localalias'] = $alias;
  1332.         } else {
  1333.             $this->_channelInfo['suggestedalias'] = $alias;
  1334.         }
  1335.         return true;
  1336.     }
  1337.  
  1338.     /**
  1339.      * @return string
  1340.      */
  1341.     function getAlias()
  1342.     {
  1343.         if (isset($this->_channelInfo['localalias'])) {
  1344.             return $this->_channelInfo['localalias'];
  1345.         }
  1346.         if (isset($this->_channelInfo['suggestedalias'])) {
  1347.             return $this->_channelInfo['suggestedalias'];
  1348.         }
  1349.         if (isset($this->_channelInfo['name'])) {
  1350.             return $this->_channelInfo['name'];
  1351.         }
  1352.         return '';
  1353.     }
  1354.  
  1355.     /**
  1356.      * Set the package validation object if it differs from PEAR's default
  1357.      * The class must be includeable via changing _ in the classname to path separator,
  1358.      * but no checking of this is made.
  1359.      * @param string|false pass in false to reset to the default packagename regex
  1360.      * @return boolean success
  1361.      */
  1362.     function setValidationPackage($validateclass, $version)
  1363.     {
  1364.         if (empty($validateclass)) {
  1365.             unset($this->_channelInfo['validatepackage']);
  1366.         }
  1367.         $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
  1368.         $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
  1369.     }
  1370.  
  1371.     /**
  1372.      * Add a protocol to the provides section
  1373.      * @param string protocol type
  1374.      * @param string protocol version
  1375.      * @param string protocol name, if any
  1376.      * @param string mirror name, if this is a mirror's protocol
  1377.      * @return bool
  1378.      */
  1379.     function addFunction($type, $version, $name = '', $mirror = false)
  1380.     {
  1381.         if ($mirror) {
  1382.             return $this->addMirrorFunction($mirror, $type, $version, $name);
  1383.         }
  1384.         $set = array('attribs' => array('version' => $version), '_content' => $name);
  1385.         if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
  1386.             if (!isset($this->_channelInfo['servers'])) {
  1387.                 $this->_channelInfo['servers'] = array('primary' =>
  1388.                     array($type => array()));
  1389.             } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  1390.                 $this->_channelInfo['servers']['primary'] = array($type => array());
  1391.             }
  1392.             $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
  1393.             $this->_isValid = false;
  1394.             return true;
  1395.         } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
  1396.             $this->_channelInfo['servers']['primary'][$type]['function'] = array(
  1397.                 $this->_channelInfo['servers']['primary'][$type]['function']);
  1398.         }
  1399.         $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
  1400.         return true;
  1401.     }
  1402.     /**
  1403.      * Add a protocol to a mirror's provides section
  1404.      * @param string mirror name (server)
  1405.      * @param string protocol type
  1406.      * @param string protocol version
  1407.      * @param string protocol name, if any
  1408.      */
  1409.     function addMirrorFunction($mirror, $type, $version, $name = '')
  1410.     {
  1411.         if (!isset($this->_channelInfo['servers']['mirror'])) {
  1412.             $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1413.                 array('mirror' => $mirror));
  1414.             return false;
  1415.         }
  1416.         $setmirror = false;
  1417.         if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1418.             foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1419.                 if ($mirror == $mir['attribs']['host']) {
  1420.                     $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  1421.                     break;
  1422.                 }
  1423.             }
  1424.         } else {
  1425.             if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1426.                 $setmirror = &$this->_channelInfo['servers']['mirror'];
  1427.             }
  1428.         }
  1429.         if (!$setmirror) {
  1430.             $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1431.                 array('mirror' => $mirror));
  1432.             return false;
  1433.         }
  1434.         $set = array('attribs' => array('version' => $version), '_content' => $name);
  1435.         if (!isset($setmirror[$type]['function'])) {
  1436.             $setmirror[$type]['function'] = $set;
  1437.             $this->_isValid = false;
  1438.             return true;
  1439.         } elseif (!isset($setmirror[$type]['function'][0])) {
  1440.             $setmirror[$type]['function'] = array($setmirror[$type]['function']);
  1441.         }
  1442.         $setmirror[$type]['function'][] = $set;
  1443.         $this->_isValid = false;
  1444.         return true;
  1445.     }
  1446.  
  1447.     /**
  1448.      * @param string Resource Type this url links to
  1449.      * @param string URL
  1450.      * @param string|false mirror name, if this is not a primary server REST base URL
  1451.      */
  1452.     function setBaseURL($resourceType, $url, $mirror = false)
  1453.     {
  1454.         if ($mirror) {
  1455.             if (!isset($this->_channelInfo['servers']['mirror'])) {
  1456.                 $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  1457.                     array('mirror' => $mirror));
  1458.                 return false;
  1459.             }
  1460.             $setmirror = false;
  1461.             if (isset($this->_channelInfo['servers']['mirror'][0])) {
  1462.                 foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  1463.                     if ($mirror == $mir['attribs']['host']) {
  1464.                         $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  1465.                         break;
  1466.                     }
  1467.                 }
  1468.             } else {
  1469.                 if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  1470.                     $setmirror = &$this->_channelInfo['servers']['mirror'];
  1471.                 }
  1472.             }
  1473.         } else {
  1474.             $setmirror = &$this->_channelInfo['servers']['primary'];
  1475.         }
  1476.         $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
  1477.         if (!isset($setmirror['rest'])) {
  1478.             $setmirror['rest'] = array();
  1479.         }
  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.