diff options
-rw-r--r-- | Services/Yadis/XRDS.php | 9 | ||||
-rw-r--r-- | Services/Yadis/XRI.php | 132 | ||||
-rw-r--r-- | Services/Yadis/XRIRes.php | 65 | ||||
-rw-r--r-- | Tests/Services/Yadis/XRDS.php | 42 | ||||
-rw-r--r-- | Tests/Services/Yadis/XRI.php | 34 |
5 files changed, 229 insertions, 53 deletions
diff --git a/Services/Yadis/XRDS.php b/Services/Yadis/XRDS.php index 55674c4..2d3a9bc 100644 --- a/Services/Yadis/XRDS.php +++ b/Services/Yadis/XRDS.php @@ -196,10 +196,11 @@ class Services_Yadis_XRDS { * Instantiate a Services_Yadis_XRDS object. Requires an XPath * instance which has been used to parse a valid XRDS document. */ - function Services_Yadis_XRDS(&$xmlParser, &$xrdNode) + function Services_Yadis_XRDS(&$xmlParser, &$xrdNodes) { $this->parser =& $xmlParser; - $this->xrdNode = $xrdNode; + $this->xrdNode = $xrdNodes[count($xrdNodes) - 1]; + $this->allXrdNodes =& $xrdNodes; $this->serviceList = array(); $this->_parse(); } @@ -255,13 +256,13 @@ class Services_Yadis_XRDS { } // Get the last XRD node. - $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD[last()]'); + $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD'); if (!$xrd_nodes) { return null; } - $xrds = new Services_Yadis_XRDS($parser, $xrd_nodes[0]); + $xrds = new Services_Yadis_XRDS($parser, $xrd_nodes); return $xrds; } diff --git a/Services/Yadis/XRI.php b/Services/Yadis/XRI.php index c4b49ba..df943be 100644 --- a/Services/Yadis/XRI.php +++ b/Services/Yadis/XRI.php @@ -11,7 +11,6 @@ require_once 'Services/Yadis/Misc.php'; require_once 'Services/Yadis/Yadis.php'; -require_once 'Services/Yadis/XRDS.php'; require_once 'Auth/OpenID.php'; $DEFAULT_PROXY = 'http://proxy.xri.net/'; @@ -82,54 +81,6 @@ function Services_Yadis_iriToURI($iri) } } -class Services_Yadis_ProxyResolver { - function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null) - { - global $DEFAULT_PROXY; - - $this->fetcher =& $fetcher; - $this->proxy_url = $proxy_url; - if (!$this->proxy_url) { - $this->proxy_url = $DEFAULT_PROXY; - } - } - - function queryURL($xri, $service_type) - { - // trim off the xri:// prefix - $qxri = substr(Services_Yadis_toURINormal($xri), 6); - $hxri = $this->proxy_url . $qxri; - $args = array( - '_xrd_r' => 'application/xrds+xml', - '_xrd_t' => $service_type - ); - $query = Services_Yadis_XRIAppendArgs($hxri, $args); - return $query; - } - - function query($xri, $service_types, $filters = array()) - { - $services = array(); - foreach ($service_types as $service_type) { - $url = $this->queryURL($xri, $service_type); - $response = $this->fetcher->get($url); - if ($response->status != 200) { - continue; - } - $xrds = Services_Yadis_XRDS::parseXRDS($response->body); - if (!$xrds) { - continue; - } - $some_services = $xrds->services($filters); - $services = array_merge($services, $some_services); - // TODO: - // * If we do get hits for multiple service_types, we're - // almost certainly going to have duplicated service - // entries and broken priority ordering. - } - return $services; - } -} function Services_Yadis_XRIAppendArgs($url, $args) { @@ -171,4 +122,87 @@ function Services_Yadis_XRIAppendArgs($url, $args) return $url . $sep . Auth_OpenID::httpBuildQuery($args); } +function Services_Yadis_providerIsAuthoritative($providerID, $canonicalID) +{ + $lastbang = strrpos($canonicalID, '!'); + $p = substr($canonicalID, 0, $lastbang); + return $p == $providerID; +} + +function Services_Yadis_rootAuthority($xri) +{ + global $XRI_AUTHORITIES; + + // Return the root authority for an XRI. + + $authority = explode('/', $xri, 2); + $authority = $authority[0]; + if ($authority[0] == '(') { + // Cross-reference. + // XXX: This is incorrect if someone nests cross-references so + // there is another close-paren in there. Hopefully nobody + // does that before we have a real xriparse function. + // Hopefully nobody does that *ever*. + return substr($authority, 0, strpos($authority, ')') + 1); + } else if (in_array($authority[0], $XRI_AUTHORITIES)) { + // Other XRI reference. + return $authority[0]; + } else { + // IRI reference. + $_segments = explode("!", $authority); + $segments = array(); + foreach ($_segments as $s) { + $segments = array_merge($segments, explode("*", $s)); + } + return $segments[0]; + } +} + +function Services_Yadis_getCanonicalID($iname, $xrds) +{ + // Returns FALSE or a canonical ID value. + + // Now nodes are in reverse order. + $xrd_list = array_reverse($xrds->allXrdNodes); + $parser =& $xrds->parser; + $node = $xrd_list[0]; + + $canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node); + + if (!$canonicalID_nodes) { + return false; + } + + $canonicalID = $canonicalID_nodes[count($canonicalID_nodes) - 1]; + $canonicalID = $parser->content($canonicalID); + + $childID = $canonicalID; + + for ($i = 1; $i < count($xrd_list); $i++) { + $xrd = $xrd_list[$i]; + + $parent_sought = substr($childID, 0, strrpos($childID, '!')); + $parent_list = array(); + + foreach ($parser->evalXPath('xrd:CanonicalID', $xrd) as $c) { + $parent_list[] = $parser->content($c); + } + + if (!in_array($parent_sought, $parent_list)) { + // raise XRDSFraud. + return false; + } + + $childID = $parent_sought; + } + + $root = Services_Yadis_rootAuthority($iname); + if (!Services_Yadis_providerIsAuthoritative($root, $childID)) { + // raise XRDSFraud. + return false; + } + + return $canonicalID; +} + ?>
\ No newline at end of file diff --git a/Services/Yadis/XRIRes.php b/Services/Yadis/XRIRes.php new file mode 100644 index 0000000..106aaeb --- /dev/null +++ b/Services/Yadis/XRIRes.php @@ -0,0 +1,65 @@ +<?php + +require_once 'Services/Yadis/XRDS.php'; +require_once 'Services/Yadis/XRI.php'; + +class Services_Yadis_ProxyResolver { + function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null) + { + global $DEFAULT_PROXY; + + $this->fetcher =& $fetcher; + $this->proxy_url = $proxy_url; + if (!$this->proxy_url) { + $this->proxy_url = $DEFAULT_PROXY; + } + } + + function queryURL($xri, $service_type = null) + { + // trim off the xri:// prefix + $qxri = substr(Services_Yadis_toURINormal($xri), 6); + $hxri = $this->proxy_url . $qxri; + $args = array( + '_xrd_r' => 'application/xrds+xml' + ); + + if ($service_type) { + $args['_xrd_t'] = $service_type; + } else { + // Don't perform service endpoint selection. + $args['_xrd_r'] .= ';sep=false'; + } + + $query = Services_Yadis_XRIAppendArgs($hxri, $args); + return $query; + } + + function query($xri, $service_types, $filters = array()) + { + $services = array(); + $canonicalID = null; + foreach ($service_types as $service_type) { + $url = $this->queryURL($xri, $service_type); + $response = $this->fetcher->get($url); + if ($response->status != 200) { + continue; + } + $xrds = Services_Yadis_XRDS::parseXRDS($response->body); + if (!$xrds) { + continue; + } + $canonicalID = Services_Yadis_getCanonicalID($xri, + $xrds->allXrdNodes); + $some_services = $xrds->services($filters); + $services = array_merge($services, $some_services); + // TODO: + // * If we do get hits for multiple service_types, we're + // almost certainly going to have duplicated service + // entries and broken priority ordering. + } + return array($canonicalID, $services); + } +} + +?>
\ No newline at end of file diff --git a/Tests/Services/Yadis/XRDS.php b/Tests/Services/Yadis/XRDS.php index 8b24de3..2c386da 100644 --- a/Tests/Services/Yadis/XRDS.php +++ b/Tests/Services/Yadis/XRDS.php @@ -6,6 +6,8 @@ require_once 'PHPUnit.php'; require_once 'Services/Yadis/XRDS.php'; +require_once 'Services/Yadis/XRIRes.php'; +require_once 'Services/Yadis/XRI.php'; require_once 'Tests/Services/Yadis/TestUtil.php'; class Tests_Services_Yadis_XRDS extends PHPUnit_TestCase { @@ -75,6 +77,46 @@ class Tests_Services_Yadis_XRDS extends PHPUnit_TestCase { $this->assertTrue(Services_Yadis_XRDS::parseXRDS("\x00") === null); } + function test_getCanonicalID() + { + $canonicalIDtests = array( + array("@ootao*test1", "delegated-20060809.xrds", + "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"), + array("@ootao*test1", "delegated-20060809-r1.xrds", + "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"), + array("@ootao*test1", "delegated-20060809-r2.xrds", + "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"), + array("=keturn*isDrummond", "spoof1.xrds", null), + array("=keturn*isDrummond", "spoof2.xrds", null), + array("@keturn*is*drummond", "spoof3.xrds", null), + // Don't let IRI authorities be canonical for the GCS. + array("phreak.example.com", "delegated-20060809-r2.xrds", null) + // TODO: Refs + // ("@ootao*test.ref", "ref.xrds", "@!BAE.A650.823B.2475") + ); + + foreach ($canonicalIDtests as $tupl) { + list($iname, $filename, $expectedID) = $tupl; + + $xml = Tests_Services_Yadis_readdata($filename); + $xrds = Services_Yadis_XRDS::parseXRDS($xml); + $this->_getCanonicalID($iname, $xrds, $expectedID); + } + } + + function _getCanonicalID($iname, $xrds, $expectedID) + { + if ($expectedID === null) { + $result =Services_Yadis_getCanonicalID($iname, $xrds); + if ($result !== false) { + $this->fail($iname.' (got '.$result.')'); + } + } else { + $cid = Services_Yadis_getCanonicalID($iname, $xrds); + $this->assertEquals($expectedID, $cid); + } + } + function test_services_filters() { // First, just be sure that service objects do the right diff --git a/Tests/Services/Yadis/XRI.php b/Tests/Services/Yadis/XRI.php index 5d17c93..c37128c 100644 --- a/Tests/Services/Yadis/XRI.php +++ b/Tests/Services/Yadis/XRI.php @@ -10,7 +10,9 @@ */ require_once "PHPUnit.php"; +require_once "Services/Yadis/XRIRes.php"; require_once "Services/Yadis/XRI.php"; +require_once "Services/Yadis/Yadis.php"; class Tests_Services_Yadis_XriDiscoveryTestCase extends PHPUnit_TestCase { function runTest() @@ -90,6 +92,37 @@ class Tests_Services_Yadis_ProxyQueryTestCase extends PHPUnit_TestCase { } } +class Tests_Services_Yadis_TestGetRootAuthority extends PHPUnit_TestCase { + function runTest() + { + $xris = array( + array("@foo", "@"), + array("@foo*bar", "@"), + array("@*foo*bar", "@"), + array("@foo/bar", "@"), + array("!!990!991", "!"), + array("!1001!02", "!"), + array("=foo*bar", "="), + array("(example.com)/foo", "(example.com)"), + array("(example.com)*bar/foo", "(example.com)"), + array("baz.example.com/foo", "baz.example.com"), + array("baz.example.com:8080/foo", "baz.example.com:8080") + // Looking at the ABNF in XRI Syntax 2.0, I don't think you can + // have example.com*bar. You can do (example.com)*bar, but that + // would mean something else. + // ("example.com*bar/(=baz)", "example.com*bar"), + // ("baz.example.com!01/foo", "baz.example.com!01"), + ); + + foreach ($xris as $tupl) { + list($thexri, $expected_root) = $tupl; + $this->assertEquals($expected_root, + Services_Yadis_rootAuthority($thexri), + 'rootAuthority test ('.$thexri.')'); + } + } +} + class Tests_Services_Yadis_XRI extends PHPUnit_TestSuite { function getName() { @@ -101,6 +134,7 @@ class Tests_Services_Yadis_XRI extends PHPUnit_TestSuite { $this->addTest(new Tests_Services_Yadis_ProxyQueryTestCase()); $this->addTest(new Tests_Services_Yadis_XriEscapingTestCase()); $this->addTest(new Tests_Services_Yadis_XriDiscoveryTestCase()); + $this->addTest(new Tests_Services_Yadis_TestGetRootAuthority()); } } |