require 'rexml/document' require 'rexml/element' require 'rexml/xpath' require 'openid/yadis/xri' module OpenID module Yadis XRD_NS_2_0 = 'xri://$xrd*($v*2.0)' XRDS_NS = 'xri://$xrds' XRDS_NAMESPACES = { 'xrds' => XRDS_NS, 'xrd' => XRD_NS_2_0, } class XRDSError < StandardError; end # Raised when there's an assertion in the XRDS that it does not # have the authority to make. class XRDSFraud < XRDSError end def Yadis::get_canonical_id(iname, xrd_tree) # Return the CanonicalID from this XRDS document. # # @param iname: the XRI being resolved. # @type iname: unicode # # @param xrd_tree: The XRDS output from the resolver. # # @returns: The XRI CanonicalID or None. # @returntype: unicode or None xrd_list = [] REXML::XPath::match(xrd_tree.root, '/xrds:XRDS/xrd:XRD', XRDS_NAMESPACES).each { |el| xrd_list << el } xrd_list.reverse! cid_elements = [] if !xrd_list.empty? xrd_list[0].elements.each { |e| if !e.respond_to?('name') next end if e.name == 'CanonicalID' cid_elements << e end } end cid_element = cid_elements[0] if !cid_element return nil end canonicalID = XRI.make_xri(cid_element.text) childID = canonicalID.downcase xrd_list[1..-1].each { |xrd| parent_sought = childID[0...childID.rindex('!')] parent = XRI.make_xri(xrd.elements["CanonicalID"].text) if parent_sought != parent.downcase raise XRDSFraud.new(sprintf("%s can not come from %s", parent_sought, parent)) end childID = parent_sought } root = XRI.root_authority(iname) if not XRI.provider_is_authoritative(root, childID) raise XRDSFraud.new(sprintf("%s can not come from root %s", childID, root)) end return canonicalID end class XRDSError < StandardError end def Yadis::parseXRDS(text) disable_entity_expansion do if text.nil? raise XRDSError.new("Not an XRDS document.") end begin d = REXML::Document.new(text) rescue RuntimeError raise XRDSError.new("Not an XRDS document. Failed to parse XML.") end if is_xrds?(d) return d else raise XRDSError.new("Not an XRDS document.") end end end def Yadis::disable_entity_expansion _previous_ = REXML::Document::entity_expansion_limit REXML::Document::entity_expansion_limit = 0 yield ensure REXML::Document::entity_expansion_limit = _previous_ end def Yadis::is_xrds?(xrds_tree) xrds_root = xrds_tree.root return (!xrds_root.nil? and xrds_root.name == 'XRDS' and xrds_root.namespace == XRDS_NS) end def Yadis::get_yadis_xrd(xrds_tree) REXML::XPath.each(xrds_tree.root, '/xrds:XRDS/xrd:XRD[last()]', XRDS_NAMESPACES) { |el| return el } raise XRDSError.new("No XRD element found.") end # aka iterServices in Python def Yadis::each_service(xrds_tree, &block) xrd = get_yadis_xrd(xrds_tree) xrd.each_element('Service', &block) end def Yadis::services(xrds_tree) s = [] each_service(xrds_tree) { |service| s << service } return s end def Yadis::expand_service(service_element) es = service_element.elements uris = es.each('URI') { |u| } uris = prio_sort(uris) types = es.each('Type/text()') # REXML::Text objects are not strings. types = types.collect { |t| t.to_s } uris.collect { |uri| [types, uri.text, service_element] } end # Sort a list of elements that have priority attributes. def Yadis::prio_sort(elements) elements.sort { |a,b| a.attribute('priority').to_s.to_i <=> b.attribute('priority').to_s.to_i } end end end