XRI.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. /**
  3. * Routines for XRI resolution.
  4. *
  5. * @package OpenID
  6. * @author JanRain, Inc. <openid@janrain.com>
  7. * @copyright 2005-2008 Janrain, Inc.
  8. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
  9. */
  10. require_once 'Auth/Yadis/Misc.php';
  11. require_once 'Auth/Yadis/Yadis.php';
  12. require_once 'Auth/OpenID.php';
  13. function Auth_Yadis_getDefaultProxy()
  14. {
  15. return 'http://xri.net/';
  16. }
  17. function Auth_Yadis_getXRIAuthorities()
  18. {
  19. return array('!', '=', '@', '+', '$', '(');
  20. }
  21. function Auth_Yadis_getEscapeRE()
  22. {
  23. $parts = array();
  24. foreach (array_merge(Auth_Yadis_getUCSChars(),
  25. Auth_Yadis_getIPrivateChars()) as $pair) {
  26. list($m, $n) = $pair;
  27. $parts[] = sprintf("%s-%s", chr($m), chr($n));
  28. }
  29. return sprintf('/[%s]/', implode('', $parts));
  30. }
  31. function Auth_Yadis_getXrefRE()
  32. {
  33. return '/\((.*?)\)/';
  34. }
  35. function Auth_Yadis_identifierScheme($identifier)
  36. {
  37. if (Auth_Yadis_startswith($identifier, 'xri://') ||
  38. ($identifier &&
  39. in_array($identifier[0], Auth_Yadis_getXRIAuthorities()))) {
  40. return "XRI";
  41. } else {
  42. return "URI";
  43. }
  44. }
  45. function Auth_Yadis_toIRINormal($xri)
  46. {
  47. if (!Auth_Yadis_startswith($xri, 'xri://')) {
  48. $xri = 'xri://' . $xri;
  49. }
  50. return Auth_Yadis_escapeForIRI($xri);
  51. }
  52. function _escape_xref($xref_match)
  53. {
  54. $xref = $xref_match[0];
  55. $xref = str_replace('/', '%2F', $xref);
  56. $xref = str_replace('?', '%3F', $xref);
  57. $xref = str_replace('#', '%23', $xref);
  58. return $xref;
  59. }
  60. function Auth_Yadis_escapeForIRI($xri)
  61. {
  62. $xri = str_replace('%', '%25', $xri);
  63. $xri = preg_replace_callback(Auth_Yadis_getXrefRE(),
  64. '_escape_xref', $xri);
  65. return $xri;
  66. }
  67. function Auth_Yadis_toURINormal($xri)
  68. {
  69. return Auth_Yadis_iriToURI(Auth_Yadis_toIRINormal($xri));
  70. }
  71. function Auth_Yadis_iriToURI($iri)
  72. {
  73. if (1) {
  74. return $iri;
  75. } else {
  76. // According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
  77. return preg_replace_callback(Auth_Yadis_getEscapeRE(),
  78. 'Auth_Yadis_pct_escape_unicode', $iri);
  79. }
  80. }
  81. function Auth_Yadis_XRIAppendArgs($url, $args)
  82. {
  83. // Append some arguments to an HTTP query. Yes, this is just like
  84. // OpenID's appendArgs, but with special seasoning for XRI
  85. // queries.
  86. if (count($args) == 0) {
  87. return $url;
  88. }
  89. // Non-empty array; if it is an array of arrays, use multisort;
  90. // otherwise use sort.
  91. if (array_key_exists(0, $args) &&
  92. is_array($args[0])) {
  93. // Do nothing here.
  94. } else {
  95. $keys = array_keys($args);
  96. sort($keys);
  97. $new_args = array();
  98. foreach ($keys as $key) {
  99. $new_args[] = array($key, $args[$key]);
  100. }
  101. $args = $new_args;
  102. }
  103. // According to XRI Resolution section "QXRI query parameters":
  104. //
  105. // "If the original QXRI had a null query component (only a
  106. // leading question mark), or a query component consisting of
  107. // only question marks, one additional leading question mark MUST
  108. // be added when adding any XRI resolution parameters."
  109. if (strpos(rtrim($url, '?'), '?') !== false) {
  110. $sep = '&';
  111. } else {
  112. $sep = '?';
  113. }
  114. return $url . $sep . Auth_OpenID::httpBuildQuery($args);
  115. }
  116. function Auth_Yadis_providerIsAuthoritative($providerID, $canonicalID)
  117. {
  118. $lastbang = strrpos($canonicalID, '!');
  119. $p = substr($canonicalID, 0, $lastbang);
  120. return $p == $providerID;
  121. }
  122. function Auth_Yadis_rootAuthority($xri)
  123. {
  124. // Return the root authority for an XRI.
  125. $root = null;
  126. if (Auth_Yadis_startswith($xri, 'xri://')) {
  127. $xri = substr($xri, 6);
  128. }
  129. $authority = explode('/', $xri, 2);
  130. $authority = $authority[0];
  131. if ($authority[0] == '(') {
  132. // Cross-reference.
  133. // XXX: This is incorrect if someone nests cross-references so
  134. // there is another close-paren in there. Hopefully nobody
  135. // does that before we have a real xriparse function.
  136. // Hopefully nobody does that *ever*.
  137. $root = substr($authority, 0, strpos($authority, ')') + 1);
  138. } else if (in_array($authority[0], Auth_Yadis_getXRIAuthorities())) {
  139. // Other XRI reference.
  140. $root = $authority[0];
  141. } else {
  142. // IRI reference.
  143. $_segments = explode("!", $authority);
  144. $segments = array();
  145. foreach ($_segments as $s) {
  146. $segments = array_merge($segments, explode("*", $s));
  147. }
  148. $root = $segments[0];
  149. }
  150. return Auth_Yadis_XRI($root);
  151. }
  152. function Auth_Yadis_XRI($xri)
  153. {
  154. if (!Auth_Yadis_startswith($xri, 'xri://')) {
  155. $xri = 'xri://' . $xri;
  156. }
  157. return $xri;
  158. }
  159. function Auth_Yadis_getCanonicalID($iname, $xrds)
  160. {
  161. // Returns false or a canonical ID value.
  162. // Now nodes are in reverse order.
  163. $xrd_list = array_reverse($xrds->allXrdNodes);
  164. $parser = $xrds->parser;
  165. $node = $xrd_list[0];
  166. $canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node);
  167. if (!$canonicalID_nodes) {
  168. return false;
  169. }
  170. $canonicalID = $canonicalID_nodes[0];
  171. $canonicalID = Auth_Yadis_XRI($parser->content($canonicalID));
  172. $childID = $canonicalID;
  173. for ($i = 1; $i < count($xrd_list); $i++) {
  174. $xrd = $xrd_list[$i];
  175. $parent_sought = substr($childID, 0, strrpos($childID, '!'));
  176. $parentCID = $parser->evalXPath('xrd:CanonicalID', $xrd);
  177. if (!$parentCID) {
  178. return false;
  179. }
  180. $parentCID = Auth_Yadis_XRI($parser->content($parentCID[0]));
  181. if (strcasecmp($parent_sought, $parentCID)) {
  182. // raise XRDSFraud.
  183. return false;
  184. }
  185. $childID = $parent_sought;
  186. }
  187. $root = Auth_Yadis_rootAuthority($iname);
  188. if (!Auth_Yadis_providerIsAuthoritative($root, $childID)) {
  189. // raise XRDSFraud.
  190. return false;
  191. }
  192. return $canonicalID;
  193. }