[DRE-commits] r1293 - in packages/libxmpp4r-ruby/trunk: . debian lib lib/xmpp4r test

Lucas Nussbaum lucas at alioth.debian.org
Fri Feb 23 19:42:15 CET 2007


Author: lucas
Date: 2007-02-23 19:42:14 +0100 (Fri, 23 Feb 2007)
New Revision: 1293

Added:
   packages/libxmpp4r-ruby/trunk/lib/
   packages/libxmpp4r-ruby/trunk/lib/xmpp4r/
   packages/libxmpp4r-ruby/trunk/lib/xmpp4r/rexmladdons.rb
   packages/libxmpp4r-ruby/trunk/lib/xmpp4r/sasl.rb
   packages/libxmpp4r-ruby/trunk/test/
   packages/libxmpp4r-ruby/trunk/test/tc_rexml.rb
Modified:
   packages/libxmpp4r-ruby/trunk/debian/changelog
   packages/libxmpp4r-ruby/trunk/debian/control
Log:
libxmpp4r-ruby: Backported bugfixes from svn


Modified: packages/libxmpp4r-ruby/trunk/debian/changelog
===================================================================
--- packages/libxmpp4r-ruby/trunk/debian/changelog	2007-02-23 10:50:19 UTC (rev 1292)
+++ packages/libxmpp4r-ruby/trunk/debian/changelog	2007-02-23 18:42:14 UTC (rev 1293)
@@ -1,9 +1,17 @@
-libxmpp4r-ruby (0.3-2) UNRELEASED; urgency=low
+libxmpp4r-ruby (0.3-2) unstable; urgency=low
 
-  * NOT RELEASED YET
+  * Backported two important bug fixes from the svn tree, after
+    discussion with the upstream developer.
 
- -- Paul van Tilburg <paulvt at debian.org>  Fri, 28 Jul 2006 09:03:48 +0200
+    + lib/xmpp4r/sasl.rb: Fixes parsing of the SASL challenge. This caused
+      connection to servers using Wildfire to fail.
 
+    + lib/xmpp4r/rexmladdons.rb: Strange stuff happened when building
+      Jabber messages containing XML entities (&amp;, for example). This
+      change fixes this, and test/tc_rexml.rb adds tests for that.
+
+ -- Lucas Nussbaum <lucas at lucas-nussbaum.net>  Fri, 23 Feb 2007 15:54:15 +0100
+
 libxmpp4r-ruby (0.3-1) unstable; urgency=low
 
   * New upstream release.

Modified: packages/libxmpp4r-ruby/trunk/debian/control
===================================================================
--- packages/libxmpp4r-ruby/trunk/debian/control	2007-02-23 10:50:19 UTC (rev 1292)
+++ packages/libxmpp4r-ruby/trunk/debian/control	2007-02-23 18:42:14 UTC (rev 1293)
@@ -2,7 +2,7 @@
 Section: net
 Priority: optional
 Maintainer: Lucas Nussbaum <lucas at lucas-nussbaum.net>
-Uploaders: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers at lists.alioth.debian.org>, Antonio S. de A. Terceiro <asaterceiro at inf.ufrgs.br>, David Moreno Garza <damog at debian.org>, David Nusinow <dnusinow at debian.org>, Paul van Tilburg <paulvt at debian.org>, Esteban Manchado Velázquez <zoso at debian.org>, Arnaud Cornet <arnaud.cornet at gmail.com>, Thierry Reding <thierry at doppeltgemoppelt.de>, Marc Dequènes (Duck) <Duck at DuckCorp.org>, Ari Pollak <ari at debian.org>, Daigo Moriwaki <daigo at debian.org>, Vincent Fourmond <vincent.fourmond at 9online.fr>
+Uploaders: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers at lists.alioth.debian.org>, Antonio S. de A. Terceiro <asaterceiro at inf.ufrgs.br>, David Moreno Garza <damog at debian.org>, David Nusinow <dnusinow at debian.org>, Paul van Tilburg <paulvt at debian.org>, Esteban Manchado Velázquez <zoso at debian.org>, Arnaud Cornet <arnaud.cornet at gmail.com>, Thierry Reding <thierry at doppeltgemoppelt.de>, Marc Dequènes (Duck) <Duck at DuckCorp.org>, Ari Pollak <ari at debian.org>, Daigo Moriwaki <daigo at debian.org>, Vincent Fourmond <vincent.fourmond at 9online.fr>, Rudi Cilibrasi <cilibrar at cilibrar.com>, Patrick Ringl <patrick_ at freenet.de>, Filipe Lautert <filipelautert at celepar.pr.gov.br>, Florian Ragwitz <rafl at debian.org>
 Build-Depends-Indep: ruby1.8, graphviz, ruby-pkg-tools, rdoc, graphviz
 Build-Depends: debhelper (>= 5), cdbs
 Standards-Version: 3.7.2

Added: packages/libxmpp4r-ruby/trunk/lib/xmpp4r/rexmladdons.rb
===================================================================
--- packages/libxmpp4r-ruby/trunk/lib/xmpp4r/rexmladdons.rb	2007-02-23 10:50:19 UTC (rev 1292)
+++ packages/libxmpp4r-ruby/trunk/lib/xmpp4r/rexmladdons.rb	2007-02-23 18:42:14 UTC (rev 1293)
@@ -0,0 +1,888 @@
+# =XMPP4R - XMPP Library for Ruby
+# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
+# Website::http://home.gna.org/xmpp4r/
+
+require 'rexml/document'
+require 'rexml/parsers/xpathparser'
+require 'rexml/source'
+
+# Turn $VERBOSE off to suppress warnings about redefinition
+oldverbose = $VERBOSE
+$VERBOSE = false
+
+# REXML module. This file only adds the following methods to the REXML module, to
+# ease the coding:
+# * replace_element_text
+# * first_element
+# * first_element_text
+# * typed_add
+# * import
+# * self.import
+# * delete_elements
+#
+# Further definitions are just copied from REXML out of Ruby-1.8.4 to solve issues
+# with REXML in Ruby-1.8.2.
+#
+# The redefinitions of Text::normalize and Attribute#initialize address an issue
+# where entities in element texts and attributes were not escaped. This modifies
+# the behavious of REXML a bit but Sean Russell intends a similar behaviour for
+# the future of REXML.
+module REXML
+  # this class adds a few helper methods to REXML::Element
+  class Element
+    ##
+    # Replaces or add a child element of name <tt>e</tt> with text <tt>t</tt>.
+    def replace_element_text(e, t)
+      el = first_element(e)
+      if el.nil?
+        el = REXML::Element::new(e)
+        add_element(el)
+      end
+      if t
+        el.text = t
+      end
+      self
+    end
+
+    ##
+    # Returns first element of name <tt>e</tt>
+    def first_element(e)
+      each_element(e) { |el| return el }
+      return nil
+    end
+
+    ##
+    # Returns text of first element of name <tt>e</tt>
+    def first_element_text(e)
+      el = first_element(e)
+      if el
+        return el.text
+      else
+        return nil
+      end
+    end
+
+    # This method does exactly the same thing as add(), but it can be
+    # overriden by subclasses to provide on-the-fly object creations.
+    # For example, if you import a REXML::Element of name 'plop', and you
+    # have a Plop class that subclasses REXML::Element, with typed_add you
+    # can get your REXML::Element to be "magically" converted to Plop.
+    def typed_add(e)
+      add(e)
+    end
+
+    ##
+    # import this element's children and attributes
+    def import(xmlelement)
+      if @name and @name != xmlelement.name
+        raise "Trying to import an #{xmlelement.name} to a #{@name} !"
+      end
+      add_attributes(xmlelement.attributes.clone)
+      @context = xmlelement.context
+      xmlelement.each do |e|
+        if e.kind_of? REXML::Element
+          typed_add(e.deep_clone)
+        else # text element, probably.
+          add(e.clone)
+        end
+      end
+      self
+    end
+
+    def self.import(xmlelement)
+      self.new(xmlelement.name).import(xmlelement)
+    end
+
+    ##
+    # Deletes one or more children elements,
+    # not just one like REXML::Element#delete_element
+    def delete_elements(element)
+      while(delete_element(element)) do end
+    end
+
+  end
+
+  # very dirty fix for the :progress problem in REXML from Ruby 1.8.3
+  # http://www.germane-software.com/projects/rexml/ticket/34
+  class IOSource
+    def position
+      0
+    end
+
+    def current_line
+      [0, 0, ""]
+    end
+  end
+
+############################################################################
+  # The XPath parser has bugs. Here is a patch.
+############################################################################
+  # You don't want to use this class.  Really.  Use XPath, which is a wrapper
+  # for this class.  Believe me.  You don't want to poke around in here.
+  # There is strange, dark magic at work in this code.  Beware.  Go back!  Go
+  # back while you still can!
+  class XPathParser
+    include XMLTokens
+#    LITERAL    = /^'([^']*)'|^"([^"]*)"/u
+
+    def initialize( )
+      @parser = REXML::Parsers::XPathParser.new
+      @namespaces = {}
+      @variables = {}
+    end
+
+    def namespaces=( namespaces )
+      namespaces ||= {}
+      Functions::namespace_context = namespaces
+      @namespaces = namespaces
+    end
+
+    def variables=( vars={} )
+      Functions::variables = vars
+      @variables = vars
+    end
+
+    def parse path, nodeset
+     #puts "#"*40
+     path_stack = @parser.parse( path )
+     #puts "PARSE: #{path} => #{path_stack.inspect}"
+     #puts "PARSE: nodeset = #{nodeset.inspect}"
+     match( path_stack, nodeset )
+    end
+
+    def get_first path, nodeset
+     #puts "#"*40
+     path_stack = @parser.parse( path )
+     #puts "PARSE: #{path} => #{path_stack.inspect}"
+     #puts "PARSE: nodeset = #{nodeset.inspect}"
+     first( path_stack, nodeset )
+    end
+
+    def predicate path, nodeset
+      path_stack = @parser.parse( path )
+      expr( path_stack, nodeset )
+    end
+
+    def []=( variable_name, value )
+      @variables[ variable_name ] = value
+    end
+
+
+    # Performs a depth-first (document order) XPath search, and returns the
+    # first match.  This is the fastest, lightest way to return a single result.
+    def first( path_stack, node )
+      #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
+      return nil if path.size == 0
+
+      case path[0]
+      when :document
+        # do nothing 
+        return first( path[1..-1], node )
+      when :child
+        for c in node.children
+          #puts "#{depth}) CHILD checking #{name(c)}"
+          r = first( path[1..-1], c )
+          #puts "#{depth}) RETURNING #{r.inspect}" if r
+          return r if r
+        end
+      when :qname
+        name = path[2]
+        #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
+        if node.name == name
+          #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
+          return node if path.size == 3
+          return first( path[3..-1], node )
+        else
+          return nil
+        end
+      when :descendant_or_self
+        r = first( path[1..-1], node )
+        return r if r
+        for c in node.children
+          r = first( path, c )
+          return r if r
+        end
+      when :node
+        return first( path[1..-1], node )
+      when :any
+        return first( path[1..-1], node )
+      end
+      return nil
+    end
+
+
+    def match( path_stack, nodeset ) 
+      #puts "MATCH: path_stack = #{path_stack.inspect}"
+      #puts "MATCH: nodeset = #{nodeset.inspect}"
+      r = expr( path_stack, nodeset )
+      #puts "MAIN EXPR => #{r.inspect}"
+      r
+      
+      #while ( path_stack.size > 0 and nodeset.size > 0 ) 
+      #  #puts "MATCH: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
+      #  nodeset = expr( path_stack, nodeset )
+      #  #puts "NODESET: #{nodeset.inspect}"
+      #  #puts "PATH_STACK: #{path_stack.inspect}"
+      #end
+      #nodeset
+    end
+
+    private
+
+
+    # Expr takes a stack of path elements and a set of nodes (either a Parent
+    # or an Array and returns an Array of matching nodes
+    ALL = [ :attribute, :element, :text, :processing_instruction, :comment ] unless defined?(ALL)
+    ELEMENTS = [ :element ] unless defined?(ELEMENTS)
+    def expr( path_stack, nodeset, context=nil )
+      #puts "#"*15
+      #puts "In expr with #{path_stack.inspect}"
+      #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
+      node_types = ELEMENTS
+      return nodeset if path_stack.length == 0 || nodeset.length == 0
+      while path_stack.length > 0
+        #puts "Path stack = #{path_stack.inspect}"
+        #puts "Nodeset is #{nodeset.inspect}"
+        case (op = path_stack.shift)
+        when :document
+          nodeset = [ nodeset[0].root_node ]
+          #puts ":document, nodeset = #{nodeset.inspect}"
+
+        when :qname
+          #puts "IN QNAME"
+          prefix = path_stack.shift
+          name = path_stack.shift
+          default_ns = @namespaces[prefix]
+          default_ns = default_ns ? default_ns : ''
+          nodeset.delete_if do |node|
+            ns = default_ns
+            # FIXME: This DOUBLES the time XPath searches take
+            ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
+            #puts "NS = #{ns.inspect}"
+            #puts "node.node_type == :element => #{node.node_type == :element}"
+            if node.node_type == :element
+              #puts "node.name == #{name} => #{node.name == name}"
+              if node.name == name
+                #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
+              end
+            end
+            !(node.node_type == :element and 
+              node.name == name and 
+              node.namespace == ns )
+          end
+          node_types = ELEMENTS
+
+        when :any
+          #puts "ANY 1: nodeset = #{nodeset.inspect}"
+          #puts "ANY 1: node_types = #{node_types.inspect}"
+          nodeset.delete_if { |node| !node_types.include?(node.node_type) }
+          #puts "ANY 2: nodeset = #{nodeset.inspect}"
+
+        when :self
+          # This space left intentionally blank
+
+        when :processing_instruction
+          target = path_stack.shift
+          nodeset.delete_if do |node|
+            (node.node_type != :processing_instruction) or 
+            ( target!='' and ( node.target != target ) )
+          end
+
+        when :text
+          nodeset.delete_if { |node| node.node_type != :text }
+
+        when :comment
+          nodeset.delete_if { |node| node.node_type != :comment }
+
+        when :node
+          # This space left intentionally blank
+          node_types = ALL
+
+        when :child
+          new_nodeset = []
+          nt = nil
+          for node in nodeset
+            nt = node.node_type
+            new_nodeset += node.children if nt == :element or nt == :document
+          end
+          nodeset = new_nodeset
+          node_types = ELEMENTS
+
+        when :literal
+          literal = path_stack.shift
+          if literal =~ /^\d+(\.\d+)?$/
+            return ($1 ? literal.to_f : literal.to_i) 
+          end
+          return literal
+        
+        when :attribute
+          new_nodeset = []
+          case path_stack.shift
+          when :qname
+            prefix = path_stack.shift
+            name = path_stack.shift
+            for element in nodeset
+              if element.node_type == :element
+                #puts element.name
+                attr = element.attribute( name, @namespaces[prefix] )
+                new_nodeset << attr if attr
+              end
+            end
+          when :any
+            #puts "ANY"
+            for element in nodeset
+              if element.node_type == :element
+                new_nodeset += element.attributes.to_a
+              end
+            end
+          end
+          nodeset = new_nodeset
+
+        when :parent
+          #puts "PARENT 1: nodeset = #{nodeset}"
+          nodeset = nodeset.collect{|n| n.parent}.compact
+          #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
+          #puts "PARENT 2: nodeset = #{nodeset.inspect}"
+          node_types = ELEMENTS
+
+        when :ancestor
+          new_nodeset = []
+          for node in nodeset
+            while node.parent
+              node = node.parent
+              new_nodeset << node unless new_nodeset.include? node
+            end
+          end
+          nodeset = new_nodeset
+          node_types = ELEMENTS
+
+        when :ancestor_or_self
+          new_nodeset = []
+          for node in nodeset
+            if node.node_type == :element
+              new_nodeset << node
+              while ( node.parent )
+                node = node.parent
+                new_nodeset << node unless new_nodeset.include? node
+              end
+            end
+          end
+          nodeset = new_nodeset
+          node_types = ELEMENTS
+
+        when :predicate
+          new_nodeset = []
+          subcontext = { :size => nodeset.size }
+          pred = path_stack.shift
+          nodeset.each_with_index { |node, index|
+            subcontext[ :node ] = node
+            #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
+            subcontext[ :index ] = index+1
+            pc = pred.dclone
+            #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
+            result = expr( pc, [node], subcontext )
+            result = result[0] if result.kind_of? Array and result.length == 1
+            #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
+            if result.kind_of? Numeric
+              #puts "Adding node #{node.inspect}" if result == (index+1)
+              new_nodeset << node if result == (index+1)
+            elsif result.instance_of? Array
+              #puts "Adding node #{node.inspect}" if result.size > 0
+              new_nodeset << node if result.size > 0
+            else
+              #puts "Adding node #{node.inspect}" if result
+              new_nodeset << node if result
+            end
+          }
+          #puts "New nodeset = #{new_nodeset.inspect}"
+          #puts "Path_stack  = #{path_stack.inspect}"
+          nodeset = new_nodeset
+=begin
+          predicate = path_stack.shift
+          ns = nodeset.clone
+          result = expr( predicate, ns )
+          #puts "Result = #{result.inspect} (#{result.class.name})"
+          #puts "nodeset = #{nodeset.inspect}"
+          if result.kind_of? Array
+            nodeset = result.zip(ns).collect{|m,n| n if m}.compact
+          else
+            nodeset = result ? nodeset : []
+          end
+          #puts "Outgoing NS = #{nodeset.inspect}"
+=end
+
+        when :descendant_or_self
+          rv = descendant_or_self( path_stack, nodeset )
+          path_stack.clear
+          nodeset = rv
+          node_types = ELEMENTS
+
+        when :descendant
+          results = []
+          nt = nil
+          for node in nodeset
+            nt = node.node_type
+            results += expr( path_stack.dclone.unshift( :descendant_or_self ),
+              node.children ) if nt == :element or nt == :document
+          end
+          nodeset = results
+          node_types = ELEMENTS
+
+        when :following_sibling
+          #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
+          results = []
+          for node in nodeset
+            all_siblings = node.parent.children
+            current_index = all_siblings.index( node )
+            following_siblings = all_siblings[ current_index+1 .. -1 ]
+            results += expr( path_stack.dclone, following_siblings )
+          end
+          #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
+          nodeset = results
+
+        when :preceding_sibling
+          results = []
+          for node in nodeset
+            all_siblings = node.parent.children
+            current_index = all_siblings.index( node )
+            preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
+            #results += expr( path_stack.dclone, preceding_siblings )
+          end
+          nodeset = preceding_siblings
+          node_types = ELEMENTS
+
+        when :preceding
+          new_nodeset = []
+          for node in nodeset
+            new_nodeset += preceding( node )
+          end
+          #puts "NEW NODESET => #{new_nodeset.inspect}"
+          nodeset = new_nodeset
+          node_types = ELEMENTS
+
+        when :following
+          new_nodeset = []
+          for node in nodeset
+            new_nodeset += following( node )
+          end
+          nodeset = new_nodeset
+          node_types = ELEMENTS
+
+        when :namespace
+          new_set = []
+          for node in nodeset
+            new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
+          end
+          nodeset = new_nodeset
+
+        when :variable
+          var_name = path_stack.shift
+          return @variables[ var_name ]
+
+        # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
+        when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
+          left = expr( path_stack.shift, nodeset, context )
+          #puts "LEFT => #{left.inspect} (#{left.class.name})"
+          right = expr( path_stack.shift, nodeset, context )
+          #puts "RIGHT => #{right.inspect} (#{right.class.name})"
+          res = equality_relational_compare( left, op, right )
+          #puts "RES => #{res.inspect}"
+          return res
+
+        when :div
+          left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
+          right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
+          return (left / right)
+
+        when :mod
+          left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          return (left % right)
+
+        when :mult
+          left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          return (left * right)
+
+        when :plus
+          left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          return (left + right)
+
+        when :minus
+          left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+          return (left - right)
+
+        when :union
+          left = expr( path_stack.shift, nodeset, context )
+          right = expr( path_stack.shift, nodeset, context )
+          return (left | right)
+
+        when :neg
+          res = expr( path_stack, nodeset, context )
+          return -(res.to_f)
+
+        when :not
+        when :function
+          func_name = path_stack.shift.tr('-','_')
+          arguments = path_stack.shift
+          #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})" 
+          subcontext = context ? nil : { :size => nodeset.size }
+
+          res = []
+          cont = context
+          nodeset.each_with_index { |n, i| 
+            if subcontext
+              subcontext[:node]  = n
+              subcontext[:index] = i
+              cont = subcontext
+            end
+            arg_clone = arguments.dclone
+            args = arg_clone.collect { |arg| 
+              #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
+              expr( arg, [n], cont ) 
+            }
+            #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})" 
+            Functions.context = cont
+            res << Functions.send( func_name, *args )
+            #puts "FUNCTION 3: #{res[-1].inspect}"
+          }
+          return res
+
+        end
+      end # while
+      #puts "EXPR returning #{nodeset.inspect}"
+      return nodeset
+    end
+
+
+    ##########################################################
+    # FIXME
+    # The next two methods are BAD MOJO!
+    # This is my achilles heel.  If anybody thinks of a better
+    # way of doing this, be my guest.  This really sucks, but 
+    # it took me three days to get it to work at all.
+    # ########################################################
+    
+    def descendant_or_self( path_stack, nodeset )
+      rs = []
+      d_o_s( path_stack, nodeset, rs )
+      #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
+      document_order(rs.flatten.compact)
+      #rs.flatten.compact
+    end
+
+    def d_o_s( p, ns, r )
+      #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
+      nt = nil
+      ns.each_index do |i|
+        n = ns[i]
+        #puts "P => #{p.inspect}"
+        x = expr( p.dclone, [ n ] )
+        nt = n.node_type
+        d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
+        r.concat(x) if x.size > 0
+      end
+    end
+
+
+    # Reorders an array of nodes so that they are in document order
+    # It tries to do this efficiently.
+    #
+    # FIXME: I need to get rid of this, but the issue is that most of the XPath 
+    # interpreter functions as a filter, which means that we lose context going
+    # in and out of function calls.  If I knew what the index of the nodes was,
+    # I wouldn't have to do this.  Maybe add a document IDX for each node?
+    # Problems with mutable documents.  Or, rewrite everything.
+    def document_order( array_of_nodes )
+      new_arry = []
+      array_of_nodes.each { |node|
+        node_idx = [] 
+        np = node.node_type == :attribute ? node.element : node
+        while np.parent and np.parent.node_type == :element
+          node_idx << np.parent.index( np )
+          np = np.parent
+        end
+        new_arry << [ node_idx.reverse, node ]
+      }
+      #puts "new_arry = #{new_arry.inspect}"
+      new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
+    end
+
+
+    def recurse( nodeset, &block )
+      for node in nodeset
+        yield node
+        recurse( node, &block ) if node.node_type == :element
+      end
+    end
+
+
+
+    # Builds a nodeset of all of the preceding nodes of the supplied node,
+    # in reverse document order
+    # preceding:: includes every element in the document that precedes this node, 
+    # except for ancestors
+    def preceding( node )
+      #puts "IN PRECEDING"
+      ancestors = []
+      p = node.parent
+      while p
+        ancestors << p
+        p = p.parent
+      end
+
+      acc = []
+      p = preceding_node_of( node )
+      #puts "P = #{p.inspect}"
+      while p
+        if ancestors.include? p
+          ancestors.delete(p)
+        else
+          acc << p
+        end
+        p = preceding_node_of( p )
+        #puts "P = #{p.inspect}"
+      end
+      acc
+    end
+
+    def preceding_node_of( node )
+     #puts "NODE: #{node.inspect}"
+     #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
+     #puts "PARENT NODE: #{node.parent}"
+      psn = node.previous_sibling_node 
+      if psn.nil?
+        if node.parent.nil? or node.parent.class == Document 
+          return nil
+        end
+        return node.parent
+        #psn = preceding_node_of( node.parent )
+      end
+      while psn and psn.kind_of? Element and psn.children.size > 0
+        psn = psn.children[-1]
+      end
+      psn
+    end
+
+    def following( node )
+      #puts "IN PRECEDING"
+      acc = []
+      p = next_sibling_node( node )
+      #puts "P = #{p.inspect}"
+      while p
+        acc << p
+        p = following_node_of( p )
+        #puts "P = #{p.inspect}"
+      end
+      acc
+    end
+
+    def following_node_of( node )
+      #puts "NODE: #{node.inspect}"
+      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
+      #puts "PARENT NODE: #{node.parent}"
+      if node.kind_of? Element and node.children.size > 0
+        return node.children[0]
+      end
+      return next_sibling_node(node)
+    end
+
+    def next_sibling_node(node)
+      psn = node.next_sibling_node 
+      while psn.nil?
+        if node.parent.nil? or node.parent.class == Document 
+          return nil
+        end
+        node = node.parent
+        psn = node.next_sibling_node
+        #puts "psn = #{psn.inspect}"
+      end
+      return psn
+    end
+
+    def norm b
+      case b
+      when true, false
+        return b
+      when 'true', 'false'
+        return Functions::boolean( b )
+      when /^\d+(\.\d+)?$/
+        return Functions::number( b )
+      else
+        return Functions::string( b )
+      end
+    end
+
+    def equality_relational_compare( set1, op, set2 )
+      #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
+      if set1.kind_of? Array and set2.kind_of? Array
+			  #puts "#{set1.size} & #{set2.size}"
+        if set1.size == 1 and set2.size == 1
+          set1 = set1[0]
+          set2 = set2[0]
+        elsif set1.size == 0 or set2.size == 0
+          nd = set1.size==0 ? set2 : set1
+          rv = nd.collect { |il| compare( il, op, nil ) }
+          #puts "RV = #{rv.inspect}"
+          return rv
+        else
+          res = []
+          enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
+            #puts "i1 = #{i1.inspect} (#{i1.class.name})"
+            #puts "i2 = #{i2.inspect} (#{i2.class.name})"
+            i1 = norm( i1 )
+            i2 = norm( i2 )
+            res << compare( i1, op, i2 )
+          }
+          return res
+        end
+      end
+		  #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
+      #puts "COMPARING VALUES"
+      # If one is nodeset and other is number, compare number to each item
+      # in nodeset s.t. number op number(string(item))
+      # If one is nodeset and other is string, compare string to each item
+      # in nodeset s.t. string op string(item)
+      # If one is nodeset and other is boolean, compare boolean to each item
+      # in nodeset s.t. boolean op boolean(item)
+      if set1.kind_of? Array or set2.kind_of? Array
+			  #puts "ISA ARRAY"
+        if set1.kind_of? Array
+          a = set1
+          b = set2
+        else
+          a = set2
+          b = set1
+        end
+
+        case b
+        when true, false
+          return a.collect {|v| compare( Functions::boolean(v), op, b ) }
+        when Numeric
+          return a.collect {|v| compare( Functions::number(v), op, b )}
+        when /^\d+(\.\d+)?$/
+          b = Functions::number( b )
+          #puts "B = #{b.inspect}"
+          return a.collect {|v| compare( Functions::number(v), op, b )}
+        else
+				  #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
+          b = Functions::string( b )
+          return a.collect { |v| compare( Functions::string(v), op, b ) }
+        end
+      else
+        # If neither is nodeset,
+        #   If op is = or !=
+        #     If either boolean, convert to boolean
+        #     If either number, convert to number
+        #     Else, convert to string
+        #   Else
+        #     Convert both to numbers and compare
+        s1 = set1.to_s
+        s2 = set2.to_s
+        #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
+        if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
+          #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
+          #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
+          set1 = Functions::boolean( set1 )
+          set2 = Functions::boolean( set2 )
+        else
+          if op == :eq or op == :neq
+            if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
+              set1 = Functions::number( s1 )
+              set2 = Functions::number( s2 )
+            else
+              set1 = Functions::string( set1 )
+              set2 = Functions::string( set2 )
+            end
+          else
+            set1 = Functions::number( set1 )
+            set2 = Functions::number( set2 )
+          end
+        end
+        #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
+        #puts ">>> #{compare( set1, op, set2 )}"
+        return compare( set1, op, set2 )
+      end
+      return false
+    end
+
+    def compare a, op, b
+      #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
+      case op
+      when :eq
+        a == b
+      when :neq
+        a != b
+      when :lt
+        a < b
+      when :lteq
+        a <= b
+      when :gt
+        a > b
+      when :gteq
+        a >= b
+      when :and
+        a and b
+      when :or
+        a or b
+      else
+        false
+      end
+    end
+  end
+
+
+############################################################################
+ 
+  class Text
+    # Escapes all possible entities
+    def Text::normalize( input, doctype=nil, entity_filter=nil )
+      copy = input
+      # Doing it like this rather than in a loop improves the speed
+      if doctype
+        # Replace all ampersands that aren't part of an entity
+        copy = copy.gsub( EREFERENCE, '&amp;' )
+        doctype.entities.each_value do |entity|
+          copy = copy.gsub( entity.value, 
+            "&#{entity.name};" ) if entity.value and 
+              not( entity_filter and entity_filter.include?(entity) )
+        end
+      else
+        # Replace all ampersands
+        copy = copy.gsub( '&', '&amp;' )
+        DocType::DEFAULT_ENTITIES.each_value do |entity|
+          copy = copy.gsub(entity.value, "&#{entity.name};" )
+        end
+      end
+      copy
+    end
+  end
+
+	class Attribute
+		def initialize( first, second=nil, parent=nil )
+			@normalized = @unnormalized = @element = nil
+			if first.kind_of? Attribute
+				self.name = first.expanded_name
+				@value = first.value
+				if second.kind_of? Element
+					@element = second
+				else
+					@element = first.element
+				end
+			elsif first.kind_of? String
+				@element = parent if parent.kind_of? Element
+				self.name = first
+				@value = Text::normalize(second.to_s)
+			else
+				raise "illegal argument #{first.class.name} to Attribute constructor"
+			end
+		end
+  end
+
+end
+
+# Restore the old $VERBOSE setting
+$VERBOSE = oldverbose
+

Added: packages/libxmpp4r-ruby/trunk/lib/xmpp4r/sasl.rb
===================================================================
--- packages/libxmpp4r-ruby/trunk/lib/xmpp4r/sasl.rb	2007-02-23 10:50:19 UTC (rev 1292)
+++ packages/libxmpp4r-ruby/trunk/lib/xmpp4r/sasl.rb	2007-02-23 18:42:14 UTC (rev 1293)
@@ -0,0 +1,216 @@
+# =XMPP4R - XMPP Library for Ruby
+# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
+# Website::http://home.gna.org/xmpp4r/
+
+require 'base64'
+require 'digest/md5'
+
+module Jabber
+  ##
+  # Helpers for SASL authentication (RFC2222)
+  #
+  # You might not need to use them directly, they are
+  # invoked by Jabber::Client#auth
+  module SASL
+    NS_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl'
+
+    ##
+    # Factory function to obtain a SASL helper for the specified mechanism
+    def SASL::new(stream, mechanism)
+      case mechanism
+        when 'DIGEST-MD5'
+          DigestMD5.new(stream)
+        when 'PLAIN'
+          Plain.new(stream)
+        else
+          raise "Unknown SASL mechanism: #{mechanism}"
+      end
+    end
+
+    ##
+    # SASL mechanism base class (stub)
+    class Base
+      def initialize(stream)
+        @stream = stream
+      end
+
+      private
+
+      def generate_auth(mechanism, text=nil)
+        auth = REXML::Element.new 'auth'
+        auth.add_namespace NS_SASL
+        auth.attributes['mechanism'] = mechanism
+        auth.text = text
+        auth
+      end
+
+      def generate_nonce
+        Digest::MD5.hexdigest(Time.new.to_f.to_s)
+      end
+    end
+
+    ##
+    # SASL PLAIN authentication helper (RFC2595)
+    class Plain < Base
+      ##
+      # Authenticate via sending password in clear-text
+      def auth(password)
+        auth_text = "#{@stream.jid.strip}\x00#{@stream.jid.node}\x00#{password}"
+        error = nil
+        @stream.send(generate_auth('PLAIN', Base64::encode64(auth_text).strip)) { |reply|
+          if reply.name != 'success'
+            error = reply.first_element(nil).name
+          end
+          true
+        }
+        
+        raise error if error
+      end
+    end
+
+    ##
+    # SASL DIGEST-MD5 authentication helper (RFC2831)
+    class DigestMD5 < Base
+      ##
+      # Sends the wished auth mechanism and wait for a challenge
+      #
+      # (proceed with DigestMD5#auth)
+      def initialize(stream)
+        super
+
+        challenge = {}
+        error = nil
+        @stream.send(generate_auth('DIGEST-MD5')) { |reply|
+          if reply.name == 'challenge' and reply.namespace == NS_SASL
+            challenge = decode_challenge(reply.text)
+          else
+            error = reply.first_element(nil).name
+          end
+          true
+        }
+        raise error if error
+
+        @nonce = challenge['nonce']
+        @realm = challenge['realm']
+      end
+
+      def decode_challenge(challenge)
+        text = Base64::decode64(challenge)
+        res = {}
+
+        state = :key
+        key = ''
+        value = ''
+
+        text.scan(/./) do |ch|
+          if state == :key
+            if ch == '='
+              state = :value
+            else
+              key += ch
+            end
+
+          elsif state == :value
+            if ch == ','
+              res[key] = value
+              key = ''
+              value = ''
+              state = :key
+            elsif ch == '"' and value == ''
+              state = :quote
+            else
+              value += ch
+            end
+
+          elsif state == :quote
+            if ch == '"'
+              state = :value
+            else
+              value += ch
+            end
+          end
+        end
+        res[key] = value unless key == ''
+
+        Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text.inspect}\n#{res.inspect}")
+
+        res
+      end
+
+      ##
+      # * Send a response
+      # * Wait for the server's challenge (which aren't checked)
+      # * Send a blind response to the server's challenge
+      def auth(password)
+        response = {}
+        response['nonce'] = @nonce
+        response['charset'] = 'utf-8'
+        response['username'] = @stream.jid.node
+        response['realm'] = @realm || @stream.jid.domain
+        response['cnonce'] = generate_nonce
+        response['nc'] = '00000001'
+        response['qop'] = 'auth'
+        response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
+        response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
+        response.each { |key,value|
+          unless %w(nc qop response charset).include? key
+            response[key] = "\"#{value}\""
+          end
+        }
+
+        response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
+        Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}")
+
+        r = REXML::Element.new('response')
+        r.add_namespace NS_SASL
+        r.text = Base64::encode64(response_text).gsub(/\s/, '')
+
+        success_already = false
+        error = nil
+        @stream.send(r) { |reply|
+          if reply.name == 'success'
+            success_already = true
+          elsif reply.name != 'challenge'
+            error = reply.first_element(nil).name
+          end
+          true
+        }
+        
+        return if success_already
+        raise error if error
+
+        # TODO: check the challenge from the server
+
+        r.text = nil
+        @stream.send(r) { |reply|
+          if reply.name != 'success'
+            error = reply.first_element(nil).name
+          end
+          true
+        }
+        
+        raise error if error
+      end
+
+      private
+
+      ##
+      # Function from RFC2831
+      def h(s); Digest::MD5.digest(s); end
+      ##
+      # Function from RFC2831
+      def hh(s); Digest::MD5.hexdigest(s); end
+      
+      ##
+      # Calculate the value for the response field
+      def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
+        a1_h = h("#{username}:#{realm}:#{passwd}")
+        a1 = "#{a1_h}:#{nonce}:#{cnonce}"
+        #a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}"
+        a2 = "AUTHENTICATE:#{digest_uri}"
+
+        hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
+      end
+    end
+  end
+end

Added: packages/libxmpp4r-ruby/trunk/test/tc_rexml.rb
===================================================================
--- packages/libxmpp4r-ruby/trunk/test/tc_rexml.rb	2007-02-23 10:50:19 UTC (rev 1292)
+++ packages/libxmpp4r-ruby/trunk/test/tc_rexml.rb	2007-02-23 18:42:14 UTC (rev 1293)
@@ -0,0 +1,60 @@
+#!/usr/bin/ruby
+
+$:.unshift '../lib'
+
+require 'test/unit'
+require 'xmpp4r/rexmladdons'
+
+class REXMLTest < Test::Unit::TestCase
+  def test_simple
+    e = REXML::Element.new('e')
+    assert_kind_of(REXML::Element, e)
+    assert_nil(e.text)
+    assert_nil(e.attributes['x'])
+  end
+
+  def test_normalize
+    assert_equal('&amp;', REXML::Text::normalize('&'))
+    assert_equal('&amp;amp;', REXML::Text::normalize('&amp;'))
+    assert_equal('&amp;amp;amp;', REXML::Text::normalize('&amp;amp;'))
+    assert_equal('&amp;nbsp;', REXML::Text::normalize('&nbsp;'))
+  end
+
+  def test_unnormalize
+    assert_equal('&', REXML::Text::unnormalize('&amp;'))
+    assert_equal('&amp;', REXML::Text::unnormalize('&amp;amp;'))
+    assert_equal('&amp;amp;', REXML::Text::unnormalize('&amp;amp;amp;'))
+    assert_equal('&nbsp;', REXML::Text::unnormalize('&amp;nbsp;'))
+    assert_equal('&nbsp;', REXML::Text::unnormalize('&nbsp;'))  # ?
+  end
+
+  def test_text_entities
+    e = REXML::Element.new('e')
+    e.text = '&'
+    assert_equal('<e>&amp;</e>', e.to_s)
+    e.text = '&amp;'
+    assert_equal('<e>&amp;amp;</e>', e.to_s)
+    e.text = '&nbsp'
+    assert_equal('<e>&amp;nbsp</e>', e.to_s)
+    e.text = '&nbsp;'
+    assert_equal('<e>&amp;nbsp;</e>', e.to_s)
+    e.text = '&<;'
+    assert_equal('<e>&amp;&lt;;</e>', e.to_s)
+    e.text = '<>"\''
+    assert_equal('<e>&lt;&gt;&quot;&apos;</e>', e.to_s)
+    e.text = '<x>&amp;</x>'
+    assert_equal('<e>&lt;x&gt;&amp;amp;&lt;/x&gt;</e>', e.to_s)
+  end
+
+  def test_attribute_entites
+    e = REXML::Element.new('e')
+    e.attributes['x'] = '&'
+    assert_equal('&', e.attributes['x'])
+    e.attributes['x'] = '&amp;'
+    assert_equal('&amp;', e.attributes['x'])
+    e.attributes['x'] = '&nbsp'
+    assert_equal('&nbsp', e.attributes['x'])
+    e.attributes['x'] = '&nbsp;'
+    assert_equal('&nbsp;', e.attributes['x'])
+  end
+end




More information about the Pkg-ruby-extras-commits mailing list