diff vendor/plugins/ruby-net-ldap-0.0.4/lib/net/ber.rb @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/ruby-net-ldap-0.0.4/lib/net/ber.rb	Fri Jul 23 15:52:44 2010 +0100
@@ -0,0 +1,294 @@
+# $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
+#
+# NET::BER
+# Mixes ASN.1/BER convenience methods into several standard classes.
+# Also provides BER parsing functionality.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
+#
+# Gmail: garbagecat10
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+#---------------------------------------------------------------------------
+#
+#
+
+
+
+
+module Net
+
+  module BER
+
+  class BerError < Exception; end
+
+
+  # This module is for mixing into IO and IO-like objects.
+  module BERParser
+
+    # The order of these follows the class-codes in BER.
+    # Maybe this should have been a hash.
+    TagClasses = [:universal, :application, :context_specific, :private]
+
+    BuiltinSyntax = {
+      :universal => {
+        :primitive => {
+          1 => :boolean,
+          2 => :integer,
+          4 => :string,
+          10 => :integer,
+        },
+        :constructed => {
+          16 => :array,
+          17 => :array
+        }
+      }
+    }
+
+    #
+    # read_ber
+    # TODO: clean this up so it works properly with partial
+    # packets coming from streams that don't block when
+    # we ask for more data (like StringIOs). At it is,
+    # this can throw TypeErrors and other nasties.
+    #
+    def read_ber syntax=nil
+      return nil if (StringIO == self.class) and  eof?
+
+      id = getc  # don't trash this value, we'll use it later
+      tag = id & 31
+      tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
+      tagclass = TagClasses[ id >> 6 ]
+      encoding = (id & 0x20 != 0) ? :constructed : :primitive
+
+      n = getc
+      lengthlength,contentlength = if n <= 127
+        [1,n]
+      else
+        j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
+        [1 + (n & 127), j]
+      end
+
+      newobj = read contentlength
+
+      objtype = nil
+      [syntax, BuiltinSyntax].each {|syn|
+        if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
+          objtype = ot[tag]
+          break
+        end
+      }
+      
+      obj = case objtype
+      when :boolean
+        newobj != "\000"
+      when :string
+        (newobj || "").dup
+      when :integer
+        j = 0
+        newobj.each_byte {|b| j = (j << 8) + b}
+        j
+      when :array
+        seq = []
+        sio = StringIO.new( newobj || "" )
+        # Interpret the subobject, but note how the loop
+        # is built: nil ends the loop, but false (a valid
+        # BER value) does not!
+        while (e = sio.read_ber(syntax)) != nil
+          seq << e
+        end
+        seq
+      else
+        raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
+      end
+
+      # Add the identifier bits into the object if it's a String or an Array.
+      # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
+      obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
+      obj
+
+    end
+
+  end # module BERParser
+  end # module BER
+
+end # module Net
+
+
+class IO
+  include Net::BER::BERParser
+end
+
+require "stringio"
+class StringIO
+  include Net::BER::BERParser
+end
+
+begin
+  require 'openssl'
+  class OpenSSL::SSL::SSLSocket
+    include Net::BER::BERParser
+  end
+rescue LoadError
+# Ignore LoadError.
+# DON'T ignore NameError, which means the SSLSocket class
+# is somehow unavailable on this implementation of Ruby's openssl.
+# This may be WRONG, however, because we don't yet know how Ruby's
+# openssl behaves on machines with no OpenSSL library. I suppose
+# it's possible they do not fail to require 'openssl' but do not
+# create the classes. So this code is provisional.
+# Also, you might think that OpenSSL::SSL::SSLSocket inherits from
+# IO so we'd pick it up above. But you'd be wrong.
+end
+
+class String
+  def read_ber syntax=nil
+    StringIO.new(self).read_ber(syntax)
+  end
+end
+
+
+
+#----------------------------------------------
+
+
+class FalseClass
+  #
+  # to_ber
+  #
+  def to_ber
+    "\001\001\000"
+  end
+end
+
+
+class TrueClass
+  #
+  # to_ber
+  #
+  def to_ber
+    "\001\001\001"
+  end
+end
+
+
+
+class Fixnum
+  #
+  # to_ber
+  #
+  def to_ber
+    i = [self].pack('w')
+    [2, i.length].pack("CC") + i
+  end
+
+  #
+  # to_ber_enumerated
+  #
+  def to_ber_enumerated
+    i = [self].pack('w')
+    [10, i.length].pack("CC") + i
+  end
+
+  #
+  # to_ber_length_encoding
+  #
+  def to_ber_length_encoding
+    if self <= 127
+      [self].pack('C')
+    else
+      i = [self].pack('N').sub(/^[\0]+/,"")
+      [0x80 + i.length].pack('C') + i
+    end
+  end
+
+end # class Fixnum
+
+
+class Bignum
+
+  def to_ber
+    i = [self].pack('w')
+    i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
+    [2, i.length].pack("CC") + i
+  end
+
+end
+
+
+
+class String
+  #
+  # to_ber
+  # A universal octet-string is tag number 4,
+  # but others are possible depending on the context, so we
+  # let the caller give us one.
+  # The preferred way to do this in user code is via to_ber_application_sring
+  # and to_ber_contextspecific.
+  #
+  def to_ber code = 4
+    [code].pack('C') + length.to_ber_length_encoding + self
+  end
+
+  #
+  # to_ber_application_string
+  #
+  def to_ber_application_string code
+    to_ber( 0x40 + code )
+  end
+
+  #
+  # to_ber_contextspecific
+  #
+  def to_ber_contextspecific code
+    to_ber( 0x80 + code )
+  end
+
+end # class String
+
+
+
+class Array
+  #
+  # to_ber_appsequence
+  # An application-specific sequence usually gets assigned
+  # a tag that is meaningful to the particular protocol being used.
+  # This is different from the universal sequence, which usually
+  # gets a tag value of 16.
+  # Now here's an interesting thing: We're adding the X.690
+  # "application constructed" code at the top of the tag byte (0x60),
+  # but some clients, notably ldapsearch, send "context-specific
+  # constructed" (0xA0). The latter would appear to violate RFC-1777,
+  # but what do I know? We may need to change this.
+  #
+
+  def to_ber                 id = 0; to_ber_seq_internal( 0x30 + id ); end
+  def to_ber_set             id = 0; to_ber_seq_internal( 0x31 + id ); end
+  def to_ber_sequence        id = 0; to_ber_seq_internal( 0x30 + id ); end
+  def to_ber_appsequence     id = 0; to_ber_seq_internal( 0x60 + id ); end
+  def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
+
+  private
+  def to_ber_seq_internal code
+    s = self.to_s
+    [code].pack('C') + s.length.to_ber_length_encoding + s
+  end
+
+end # class Array
+
+