|
TYPO3 API
SVNRelease
|
00001 <?php 00002 00003 /** 00004 * This module contains the XRDS parsing code. 00005 * 00006 * PHP versions 4 and 5 00007 * 00008 * LICENSE: See the COPYING file included in this distribution. 00009 * 00010 * @package OpenID 00011 * @author JanRain, Inc. <openid@janrain.com> 00012 * @copyright 2005-2008 Janrain, Inc. 00013 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 00014 */ 00015 00016 /** 00017 * Require the XPath implementation. 00018 */ 00019 require_once 'Auth/Yadis/XML.php'; 00020 00021 /** 00022 * This match mode means a given service must match ALL filters passed 00023 * to the Auth_Yadis_XRDS::services() call. 00024 */ 00025 define('SERVICES_YADIS_MATCH_ALL', 101); 00026 00027 /** 00028 * This match mode means a given service must match ANY filters (at 00029 * least one) passed to the Auth_Yadis_XRDS::services() call. 00030 */ 00031 define('SERVICES_YADIS_MATCH_ANY', 102); 00032 00033 /** 00034 * The priority value used for service elements with no priority 00035 * specified. 00036 */ 00037 define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30)); 00038 00039 /** 00040 * XRD XML namespace 00041 */ 00042 define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)'); 00043 00044 /** 00045 * XRDS XML namespace 00046 */ 00047 define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds'); 00048 00049 function Auth_Yadis_getNSMap() 00050 { 00051 return array('xrds' => Auth_Yadis_XMLNS_XRDS, 00052 'xrd' => Auth_Yadis_XMLNS_XRD_2_0); 00053 } 00054 00055 /** 00056 * @access private 00057 */ 00058 function Auth_Yadis_array_scramble($arr) 00059 { 00060 $result = array(); 00061 00062 while (count($arr)) { 00063 $index = array_rand($arr, 1); 00064 $result[] = $arr[$index]; 00065 unset($arr[$index]); 00066 } 00067 00068 return $result; 00069 } 00070 00071 /** 00072 * This class represents a <Service> element in an XRDS document. 00073 * Objects of this type are returned by 00074 * Auth_Yadis_XRDS::services() and 00075 * Auth_Yadis_Yadis::services(). Each object corresponds directly 00076 * to a <Service> element in the XRDS and supplies a 00077 * getElements($name) method which you should use to inspect the 00078 * element's contents. See {@link Auth_Yadis_Yadis} for more 00079 * information on the role this class plays in Yadis discovery. 00080 * 00081 * @package OpenID 00082 */ 00083 class Auth_Yadis_Service { 00084 00085 /** 00086 * Creates an empty service object. 00087 */ 00088 function Auth_Yadis_Service() 00089 { 00090 $this->element = null; 00091 $this->parser = null; 00092 } 00093 00094 /** 00095 * Return the URIs in the "Type" elements, if any, of this Service 00096 * element. 00097 * 00098 * @return array $type_uris An array of Type URI strings. 00099 */ 00100 function getTypes() 00101 { 00102 $t = array(); 00103 foreach ($this->getElements('xrd:Type') as $elem) { 00104 $c = $this->parser->content($elem); 00105 if ($c) { 00106 $t[] = $c; 00107 } 00108 } 00109 return $t; 00110 } 00111 00112 function matchTypes($type_uris) 00113 { 00114 $result = array(); 00115 00116 foreach ($this->getTypes() as $typ) { 00117 if (in_array($typ, $type_uris)) { 00118 $result[] = $typ; 00119 } 00120 } 00121 00122 return $result; 00123 } 00124 00125 /** 00126 * Return the URIs in the "URI" elements, if any, of this Service 00127 * element. The URIs are returned sorted in priority order. 00128 * 00129 * @return array $uris An array of URI strings. 00130 */ 00131 function getURIs() 00132 { 00133 $uris = array(); 00134 $last = array(); 00135 00136 foreach ($this->getElements('xrd:URI') as $elem) { 00137 $uri_string = $this->parser->content($elem); 00138 $attrs = $this->parser->attributes($elem); 00139 if ($attrs && 00140 array_key_exists('priority', $attrs)) { 00141 $priority = intval($attrs['priority']); 00142 if (!array_key_exists($priority, $uris)) { 00143 $uris[$priority] = array(); 00144 } 00145 00146 $uris[$priority][] = $uri_string; 00147 } else { 00148 $last[] = $uri_string; 00149 } 00150 } 00151 00152 $keys = array_keys($uris); 00153 sort($keys); 00154 00155 // Rebuild array of URIs. 00156 $result = array(); 00157 foreach ($keys as $k) { 00158 $new_uris = Auth_Yadis_array_scramble($uris[$k]); 00159 $result = array_merge($result, $new_uris); 00160 } 00161 00162 $result = array_merge($result, 00163 Auth_Yadis_array_scramble($last)); 00164 00165 return $result; 00166 } 00167 00168 /** 00169 * Returns the "priority" attribute value of this <Service> 00170 * element, if the attribute is present. Returns null if not. 00171 * 00172 * @return mixed $result Null or integer, depending on whether 00173 * this Service element has a 'priority' attribute. 00174 */ 00175 function getPriority() 00176 { 00177 $attributes = $this->parser->attributes($this->element); 00178 00179 if (array_key_exists('priority', $attributes)) { 00180 return intval($attributes['priority']); 00181 } 00182 00183 return null; 00184 } 00185 00186 /** 00187 * Used to get XML elements from this object's <Service> element. 00188 * 00189 * This is what you should use to get all custom information out 00190 * of this element. This is used by service filter functions to 00191 * determine whether a service element contains specific tags, 00192 * etc. NOTE: this only considers elements which are direct 00193 * children of the <Service> element for this object. 00194 * 00195 * @param string $name The name of the element to look for 00196 * @return array $list An array of elements with the specified 00197 * name which are direct children of the <Service> element. The 00198 * nodes returned by this function can be passed to $this->parser 00199 * methods (see {@link Auth_Yadis_XMLParser}). 00200 */ 00201 function getElements($name) 00202 { 00203 return $this->parser->evalXPath($name, $this->element); 00204 } 00205 } 00206 00207 /* 00208 * Return the expiration date of this XRD element, or None if no 00209 * expiration was specified. 00210 * 00211 * @param $default The value to use as the expiration if no expiration 00212 * was specified in the XRD. 00213 */ 00214 function Auth_Yadis_getXRDExpiration($xrd_element, $default=null) 00215 { 00216 $expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires'); 00217 if ($expires_element === null) { 00218 return $default; 00219 } else { 00220 $expires_string = $expires_element->text; 00221 00222 // Will raise ValueError if the string is not the expected 00223 // format 00224 $t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ"); 00225 00226 if ($t === false) { 00227 return false; 00228 } 00229 00230 // [int $hour [, int $minute [, int $second [, 00231 // int $month [, int $day [, int $year ]]]]]] 00232 return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'], 00233 $t['tm_mon'], $t['tm_day'], $t['tm_year']); 00234 } 00235 } 00236 00237 /** 00238 * This class performs parsing of XRDS documents. 00239 * 00240 * You should not instantiate this class directly; rather, call 00241 * parseXRDS statically: 00242 * 00243 * <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre> 00244 * 00245 * If the XRDS can be parsed and is valid, an instance of 00246 * Auth_Yadis_XRDS will be returned. Otherwise, null will be 00247 * returned. This class is used by the Auth_Yadis_Yadis::discover 00248 * method. 00249 * 00250 * @package OpenID 00251 */ 00252 class Auth_Yadis_XRDS { 00253 00254 /** 00255 * Instantiate a Auth_Yadis_XRDS object. Requires an XPath 00256 * instance which has been used to parse a valid XRDS document. 00257 */ 00258 function Auth_Yadis_XRDS($xmlParser, $xrdNodes) 00259 { 00260 $this->parser =& $xmlParser; 00261 $this->xrdNode = $xrdNodes[count($xrdNodes) - 1]; 00262 $this->allXrdNodes =& $xrdNodes; 00263 $this->serviceList = array(); 00264 $this->_parse(); 00265 } 00266 00267 /** 00268 * Parse an XML string (XRDS document) and return either a 00269 * Auth_Yadis_XRDS object or null, depending on whether the 00270 * XRDS XML is valid. 00271 * 00272 * @param string $xml_string An XRDS XML string. 00273 * @return mixed $xrds An instance of Auth_Yadis_XRDS or null, 00274 * depending on the validity of $xml_string 00275 */ 00276 function &parseXRDS($xml_string, $extra_ns_map = null) 00277 { 00278 $_null = null; 00279 00280 if (!$xml_string) { 00281 return $_null; 00282 } 00283 00284 $parser = Auth_Yadis_getXMLParser(); 00285 00286 $ns_map = Auth_Yadis_getNSMap(); 00287 00288 if ($extra_ns_map && is_array($extra_ns_map)) { 00289 $ns_map = array_merge($ns_map, $extra_ns_map); 00290 } 00291 00292 if (!($parser && $parser->init($xml_string, $ns_map))) { 00293 return $_null; 00294 } 00295 00296 // Try to get root element. 00297 $root = $parser->evalXPath('/xrds:XRDS[1]'); 00298 if (!$root) { 00299 return $_null; 00300 } 00301 00302 if (is_array($root)) { 00303 $root = $root[0]; 00304 } 00305 00306 $attrs = $parser->attributes($root); 00307 00308 if (array_key_exists('xmlns:xrd', $attrs) && 00309 $attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) { 00310 return $_null; 00311 } else if (array_key_exists('xmlns', $attrs) && 00312 preg_match('/xri/', $attrs['xmlns']) && 00313 $attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) { 00314 return $_null; 00315 } 00316 00317 // Get the last XRD node. 00318 $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD'); 00319 00320 if (!$xrd_nodes) { 00321 return $_null; 00322 } 00323 00324 $xrds = new Auth_Yadis_XRDS($parser, $xrd_nodes); 00325 return $xrds; 00326 } 00327 00328 /** 00329 * @access private 00330 */ 00331 function _addService($priority, $service) 00332 { 00333 $priority = intval($priority); 00334 00335 if (!array_key_exists($priority, $this->serviceList)) { 00336 $this->serviceList[$priority] = array(); 00337 } 00338 00339 $this->serviceList[$priority][] = $service; 00340 } 00341 00342 /** 00343 * Creates the service list using nodes from the XRDS XML 00344 * document. 00345 * 00346 * @access private 00347 */ 00348 function _parse() 00349 { 00350 $this->serviceList = array(); 00351 00352 $services = $this->parser->evalXPath('xrd:Service', $this->xrdNode); 00353 00354 foreach ($services as $node) { 00355 $s = new Auth_Yadis_Service(); 00356 $s->element = $node; 00357 $s->parser =& $this->parser; 00358 00359 $priority = $s->getPriority(); 00360 00361 if ($priority === null) { 00362 $priority = SERVICES_YADIS_MAX_PRIORITY; 00363 } 00364 00365 $this->_addService($priority, $s); 00366 } 00367 } 00368 00369 /** 00370 * Returns a list of service objects which correspond to <Service> 00371 * elements in the XRDS XML document for this object. 00372 * 00373 * Optionally, an array of filter callbacks may be given to limit 00374 * the list of returned service objects. Furthermore, the default 00375 * mode is to return all service objects which match ANY of the 00376 * specified filters, but $filter_mode may be 00377 * SERVICES_YADIS_MATCH_ALL if you want to be sure that the 00378 * returned services match all the given filters. See {@link 00379 * Auth_Yadis_Yadis} for detailed usage information on filter 00380 * functions. 00381 * 00382 * @param mixed $filters An array of callbacks to filter the 00383 * returned services, or null if all services are to be returned. 00384 * @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or 00385 * SERVICES_YADIS_MATCH_ANY, depending on whether the returned 00386 * services should match ALL or ANY of the specified filters, 00387 * respectively. 00388 * @return mixed $services An array of {@link 00389 * Auth_Yadis_Service} objects if $filter_mode is a valid 00390 * mode; null if $filter_mode is an invalid mode (i.e., not 00391 * SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL). 00392 */ 00393 function services($filters = null, 00394 $filter_mode = SERVICES_YADIS_MATCH_ANY) 00395 { 00396 00397 $pri_keys = array_keys($this->serviceList); 00398 sort($pri_keys, SORT_NUMERIC); 00399 00400 // If no filters are specified, return the entire service 00401 // list, ordered by priority. 00402 if (!$filters || 00403 (!is_array($filters))) { 00404 00405 $result = array(); 00406 foreach ($pri_keys as $pri) { 00407 $result = array_merge($result, $this->serviceList[$pri]); 00408 } 00409 00410 return $result; 00411 } 00412 00413 // If a bad filter mode is specified, return null. 00414 if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY, 00415 SERVICES_YADIS_MATCH_ALL))) { 00416 return null; 00417 } 00418 00419 // Otherwise, use the callbacks in the filter list to 00420 // determine which services are returned. 00421 $filtered = array(); 00422 00423 foreach ($pri_keys as $priority_value) { 00424 $service_obj_list = $this->serviceList[$priority_value]; 00425 00426 foreach ($service_obj_list as $service) { 00427 00428 $matches = 0; 00429 00430 foreach ($filters as $filter) { 00431 if (call_user_func_array($filter, array(&$service))) { 00432 $matches++; 00433 00434 if ($filter_mode == SERVICES_YADIS_MATCH_ANY) { 00435 $pri = $service->getPriority(); 00436 if ($pri === null) { 00437 $pri = SERVICES_YADIS_MAX_PRIORITY; 00438 } 00439 00440 if (!array_key_exists($pri, $filtered)) { 00441 $filtered[$pri] = array(); 00442 } 00443 00444 $filtered[$pri][] = $service; 00445 break; 00446 } 00447 } 00448 } 00449 00450 if (($filter_mode == SERVICES_YADIS_MATCH_ALL) && 00451 ($matches == count($filters))) { 00452 00453 $pri = $service->getPriority(); 00454 if ($pri === null) { 00455 $pri = SERVICES_YADIS_MAX_PRIORITY; 00456 } 00457 00458 if (!array_key_exists($pri, $filtered)) { 00459 $filtered[$pri] = array(); 00460 } 00461 $filtered[$pri][] = $service; 00462 } 00463 } 00464 } 00465 00466 $pri_keys = array_keys($filtered); 00467 sort($pri_keys, SORT_NUMERIC); 00468 00469 $result = array(); 00470 foreach ($pri_keys as $pri) { 00471 $result = array_merge($result, $filtered[$pri]); 00472 } 00473 00474 return $result; 00475 } 00476 } 00477 00478 ?>
1.8.0