From 33e248cdc366bf3194a89f3986ce99e6124161cf Mon Sep 17 00:00:00 2001 From: blackhedd Date: Sun, 16 Apr 2006 11:52:37 +0000 Subject: [PATCH] removed files that now live in lib/net. --- lib/ldappdu.rb | 154 ----------------- lib/netber.rb | 282 ------------------------------- lib/netldap.rb | 386 ------------------------------------------- lib/netldapfilter.rb | 185 --------------------- 4 files changed, 1007 deletions(-) delete mode 100644 lib/ldappdu.rb delete mode 100644 lib/netber.rb delete mode 100644 lib/netldap.rb delete mode 100644 lib/netldapfilter.rb diff --git a/lib/ldappdu.rb b/lib/ldappdu.rb deleted file mode 100644 index 236fb75..0000000 --- a/lib/ldappdu.rb +++ /dev/null @@ -1,154 +0,0 @@ -# $Id$ -# -# LDAP PDU support classes -# -# -#---------------------------------------------------------------------------- -# -# 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 - - -class LdapPduError < Exception; end - - -class LdapPdu - - BindResult = 1 - SearchReturnedData = 4 - SearchResult = 5 - ModifyResponse = 7 - AddResponse = 9 - ModifyRDNResponse = 13 - - attr_reader :msg_id, :app_tag - attr_reader :search_dn, :search_attributes - - # - # initialize - # An LDAP PDU always looks like a BerSequence with - # two elements: an integer (message-id number), and - # an application-specific sequence. - # The application-specific tag in the sequence tells - # us what kind of packet it is, and each kind has its - # own format, defined in RFC-1777. - # Observe that many clients (such as ldapsearch) - # do not necessarily enforce the expected application - # tags on received protocol packets. This implementation - # does interpret the RFC strictly in this regard, and - # it remains to be seen whether there are servers out - # there that will not work well with our approach. - # - def initialize ber_object - begin - @msg_id = ber_object[0].to_i - @app_tag = ber_object[1].ber_identifier - 0x60 - rescue - # any error becomes a data-format error - raise LdapPduError.new( "ldap-pdu format error" ) - end - - case @app_tag - when BindResult - parse_ldap_result ber_object[1] - when SearchReturnedData - parse_search_return ber_object[1] - when SearchResult - parse_ldap_result ber_object[1] - when ModifyResponse - parse_ldap_result ber_object[1] - when AddResponse - parse_ldap_result ber_object[1] - when ModifyRDNResponse - parse_ldap_result ber_object[1] - else - raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" ) - end - end - - # - # result_code - # This returns an LDAP result code taken from the PDU, - # but it will be nil if there wasn't a result code. - # That can easily happen depending on the type of packet. - # - def result_code code = :resultCode - @ldap_result and @ldap_result[code] - end - - - private - - # - # parse_ldap_result - # - def parse_ldap_result sequence - sequence.length >= 3 or raise LdapPduError - @ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]} - end - - # - # parse_search_return - # Definition from RFC 1777 (we're handling application-4 here) - # - # Search Response ::= - # CHOICE { - # entry [APPLICATION 4] SEQUENCE { - # objectName LDAPDN, - # attributes SEQUENCE OF SEQUENCE { - # AttributeType, - # SET OF AttributeValue - # } - # }, - # resultCode [APPLICATION 5] LDAPResult - # } - # - # We concoct a search response that is a hash of the returned attribute values. - # NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES. - # This is to make them more predictable for user programs, but it - # may not be a good idea. Maybe this should be configurable. - # - def parse_search_return sequence - sequence.length >= 2 or raise LdapPduError - @search_dn = sequence[0] - @search_attributes = {} - sequence[1].each {|seq| - @search_attributes[seq[0].downcase.intern] = seq[1] - } - end - - -end - - -end # module Net - -#------------------------------------------- - -if __FILE__ == $0 - puts "No default action for this file" -end - - diff --git a/lib/netber.rb b/lib/netber.rb deleted file mode 100644 index 21f4dcc..0000000 --- a/lib/netber.rb +++ /dev/null @@ -1,282 +0,0 @@ -# $Id$ -# -# 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 - eof? and return nil - - 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 || "" ) - while e = sio.read_ber(syntax); 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 - - -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. - # - def to_ber code = 4 - [code].pack('C') + length.to_ber_length_encoding + self - end - - # - # to_ber_application_string - # TODO. WARNING, IS THIS WRONG? Shouldn't app-specific string - # have a prefix of 0x40? - # - def to_ber_application_string code - to_ber( 0x80 + 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 - - - -#---------------------------------------------- - -if __FILE__ == $0 - puts "No default action" -end - - - diff --git a/lib/netldap.rb b/lib/netldap.rb deleted file mode 100644 index 1c6eb20..0000000 --- a/lib/netldap.rb +++ /dev/null @@ -1,386 +0,0 @@ -# $Id$ -# -# Net::LDAP for Ruby -# -# -# -# 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 -# -# -# == Miscellaneous -# -# For reasons relating to the source-code layout, this file doesn't -# require all the outboard stuff it actually needs, like netber. -# Until we figure out how to do that without damaging the directory -# structure, we're reliant on user programs to explicitly require -# everything, and in the correct order too! -# -# == BUGS: -# -# Try querying the objectGUID attribute from an A/D. It's a binary value -# which we're reading correctly, but we need to make sure it gets base64-encoded -# if we're going to put it out to an LDIF. -# - - -#require 'rubygems' -#require_gem "eventmachine", ">= 0.3.1" - -require 'socket' - - -module Net - - - # - # class LDAP - # - class LDAP - - class LdapError < Exception; end - - AsnSyntax = { - :application => { - :constructed => { - 0 => :array, # BindRequest - 1 => :array, # BindResponse - 2 => :array, # UnbindRequest - 3 => :array, # SearchRequest - 4 => :array, # SearchData - 5 => :array, # SearchResult - 6 => :array, # ModifyRequest - 7 => :array, # ModifyResponse - 8 => :array, # AddRequest - 9 => :array, # AddResponse - 10 => :array, # DelRequest - 11 => :array, # DelResponse - 12 => :array, # ModifyRdnRequest - 13 => :array, # ModifyRdnResponse - 14 => :array, # CompareRequest - 15 => :array, # CompareResponse - 16 => :array, # AbandonRequest - } - }, - :context_specific => { - :primitive => { - 0 => :string, # password - 1 => :string, # Kerberos v4 - 2 => :string, # Kerberos v5 - } - } - } - - DefaultHost = "127.0.0.1" - DefaultPort = 389 - DefaultAuth = {:method => :anonymous} - - - ResultStrings = { - 0 => "Success", - 1 => "Operations Error", - 16 => "No Such Attribute", - 17 => "Undefined Attribute Type", - 20 => "Attribute or Value Exists", - 32 => "No Such Object", - 34 => "Invalid DN Syntax", - 48 => "Invalid DN Syntax", - 48 => "Inappropriate Authentication", - 49 => "Invalid Credentials", - 50 => "Insufficient Access Rights", - 51 => "Busy", - 52 => "Unavailable", - 53 => "Unwilling to perform", - 68 => "Entry Already Exists" - } - - # - # LDAP::result2string - # - def LDAP::result2string code - ResultStrings[code] || "unknown result (#{code})" - end - - # - # initialize - # - def initialize args - @host = args[:host] || DefaultHost - @port = args[:port] || DefaultPort - @verbose = false # Make this configurable with a switch on the class. - @auth = args[:auth] || DefaultAuth - - # This variable is only set when we are created with LDAP::open. - # All of our internal methods will connect using it, or else - # they will create their own. - @open_connection = nil - end - - # - # open - # - def LDAP::open - end - - # - # search - # - def search args - conn = Connection.new( :host => @host, :port => @port ) - # TODO, hardcoded Ldap result code in next line - (rc = conn.bind @auth) == 0 or return rc - result_code = conn.search( args ) {|values| - block_given? and yield( values ) - } - result_code - end - - # - # bind - # Bind and unbind. - # Can serve as a connectivity test as well as an auth test. - # - def bind - conn = Connection.new( :host => @host, :port => @port ) - conn.bind @auth - end - - # - # bind_as - # This is for testing authentication credentials. - # Most likely a "standard" name (like a CN or an email - # address) will be presented along with a password. - # We'll bind with the main credential given in the - # constructor, query the full DN of the user given - # to us as a parameter, then unbind and rebind as the - # new user. - # - def bind_as - end - - # - # add - # Add a full RDN to the remote DIS. - # - def add args - conn = Connection.new( :host => @host, :port => @port ) - # TODO, hardcoded Ldap result code in next line - (rc = conn.bind @auth) == 0 or return rc - conn.add( args ) - end - - - # - # modify - # Modify the attributes of an entry on the remote DIS. - # - def modify args - conn = Connection.new( :host => @host, :port => @port ) - # TODO, hardcoded Ldap result code in next line - (rc = conn.bind @auth) == 0 or return rc - conn.modify( args ) - end - - # - # rename - # Rename an entry on the remote DIS by changing the last RDN of its DN. - # - def rename args - conn = Connection.new( :host => @host, :port => @port ) - # TODO, hardcoded Ldap result code in next line - (rc = conn.bind @auth) == 0 or return rc - conn.rename( args ) - end - - end # class LDAP - - - - class LDAP - class Connection - - LdapVersion = 3 - - - # - # initialize - # - def initialize server - begin - @conn = TCPsocket.new( server[:host], server[:port] ) - rescue - raise LdapError.new( "no connection to server" ) - end - - block_given? and yield self - end - - # - # next_msgid - # - def next_msgid - @msgid ||= 0 - @msgid += 1 - end - - - # - # bind - # - def bind auth - user,psw = case auth[:method] - when :anonymous - ["",""] - when :simple - [auth[:username] || auth[:dn], auth[:password]] - end - raise LdapError.new( "invalid binding information" ) unless (user && psw) - - msgid = next_msgid.to_ber - request = [LdapVersion.to_ber, user.to_ber, psw.to_ber_contextspecific(0)].to_ber_appsequence(0) - request_pkt = [msgid, request].to_ber_sequence - @conn.write request_pkt - - (be = @conn.read_ber(AsnSyntax) and pdu = Net::LdapPdu.new( be )) or raise LdapError.new( "no bind result" ) - pdu.result_code - end - - # - # search - # TODO, certain search parameters are hardcoded. - # TODO, if we mis-parse the server results or the results are wrong, we can block - # forever. That's because we keep reading results until we get a type-5 packet, - # which might never come. We need to support the time-limit in the protocol. - # - def search args - search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" ) - search_base = (args && args[:base]) || "dc=example,dc=com" - search_attributes = ((args && args[:attributes]) || []).map {|attr| attr.to_s.to_ber} - request = [ - search_base.to_ber, - 2.to_ber_enumerated, - 0.to_ber_enumerated, - 0.to_ber, - 0.to_ber, - false.to_ber, - search_filter.to_ber, - search_attributes.to_ber_sequence - ].to_ber_appsequence(3) - pkt = [next_msgid.to_ber, request].to_ber_sequence - @conn.write pkt - - search_results = {} - result_code = 0 - - while (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) - case pdu.app_tag - when 4 # search-data - search_results [pdu.search_dn] = pdu.search_attributes - when 5 # search-result - result_code = pdu.result_code - block_given? and yield( search_results ) - break - else - raise LdapError.new( "invalid response-type in search: #{pdu.app_tag}" ) - end - end - - result_code - end - - # - # modify - # TODO, need to support a time limit, in case the server fails to respond. - # TODO!!! We're throwing an exception here on empty DN. - # Should return a proper error instead, probaby from farther up the chain. - # TODO!!! If the user specifies a bogus opcode, we'll throw a - # confusing error here ("to_ber_enumerated is not defined on nil"). - # - def modify args - modify_dn = args[:dn] or raise "Unable to modify empty DN" - modify_ops = [] - a = args[:operations] and a.each {|op, attr, values| - # TODO, fix the following line, which gives a bogus error - # if the opcode is invalid. - op_1 = {:add => 0, :delete => 1, :replace => 2} [op.to_sym].to_ber_enumerated - modify_ops << [op_1, [attr.to_s.to_ber, values.to_a.map {|v| v.to_ber}.to_ber_set].to_ber_sequence].to_ber_sequence - } - - request = [modify_dn.to_ber, modify_ops.to_ber_sequence].to_ber_appsequence(6) - pkt = [next_msgid.to_ber, request].to_ber_sequence - @conn.write pkt - - (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 7) or raise LdapError.new( "response missing or invalid" ) - pdu.result_code - end - - - # - # add - # TODO, need to support a time limit, in case the server fails to respond. - # - def add args - add_dn = args[:dn] or raise LdapError.new("Unable to add empty DN") - add_attrs = [] - a = args[:attributes] and a.each {|k,v| - add_attrs << [ k.to_s.to_ber, v.to_a.map {|m| m.to_ber}.to_ber_set ].to_ber_sequence - } - - request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(8) - pkt = [next_msgid.to_ber, request].to_ber_sequence - @conn.write pkt - - (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 9) or raise LdapError.new( "response missing or invalid" ) - pdu.result_code - end - - - # - # rename - # TODO, need to support a time limit, in case the server fails to respond. - # - def rename args - old_dn = args[:olddn] or raise "Unable to rename empty DN" - new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN" - delete_attrs = args[:delete_attributes] ? true : false - - request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber].to_ber_appsequence(12) - pkt = [next_msgid.to_ber, request].to_ber_sequence - @conn.write pkt - - (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 13) or raise LdapError.new( "response missing or invalid" ) - pdu.result_code - end - - - end # class Connection - end # class LDAP - - -end # module Net - - -#------------------------------------------------------ - -if __FILE__ == $0 - puts "No default action" -end - - - - diff --git a/lib/netldapfilter.rb b/lib/netldapfilter.rb deleted file mode 100644 index 97c4e86..0000000 --- a/lib/netldapfilter.rb +++ /dev/null @@ -1,185 +0,0 @@ -# $Id$ -# -# -#---------------------------------------------------------------------------- -# -# 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 -class LDAP - -class Filter - - def initialize op, a, b - @op = op - @left = a - @right = b - end - - def Filter::eq a, b; Filter.new :eq, a, b; end - def Filter::ne a, b; Filter.new :ne, a, b; end - def Filter::gt a, b; Filter.new :gt, a, b; end - def Filter::lt a, b; Filter.new :lt, a, b; end - def Filter::ge a, b; Filter.new :ge, a, b; end - def Filter::le a, b; Filter.new :le, a, b; end - - def & a; Filter.new :and, self, a; end - def | a; Filter.new :or, self, a; end - - # This operator can't be !, evidently. Try it. - def ~@; Filter.new :not, self, nil; end - - def to_s - case @op - when :ne - "(!(#{@left}=#{@right}))" - when :eq - "(#{@left}=#{@right})" - when :gt - "#{@left}>#{@right}" - when :lt - "#{@left}<#{@right}" - when :ge - "#{@left}>=#{@right}" - when :le - "#{@left}<=#{@right}" - when :and - "(&(#{@left})(#{@right}))" - when :or - "(|(#{@left})(#{@right}))" - when :not - "(!(#{@left}))" - else - raise "invalid or unsupported operator in LDAP Filter" - end - end - - - # - # to_ber - # Filter ::= - # CHOICE { - # and [0] SET OF Filter, - # or [1] SET OF Filter, - # not [2] Filter, - # equalityMatch [3] AttributeValueAssertion, - # substrings [4] SubstringFilter, - # greaterOrEqual [5] AttributeValueAssertion, - # lessOrEqual [6] AttributeValueAssertion, - # present [7] AttributeType, - # approxMatch [8] AttributeValueAssertion - # } - # - # SubstringFilter - # SEQUENCE { - # type AttributeType, - # SEQUENCE OF CHOICE { - # initial [0] LDAPString, - # any [1] LDAPString, - # final [2] LDAPString - # } - # } - # - # Parsing substrings is a little tricky. - # We use the split method to break a string into substrings - # delimited by the * (star) character. But we also need - # to know whether there is a star at the head and tail - # of the string. A Ruby particularity comes into play here: - # if you split on * and the first character of the string is - # a star, then split will return an array whose first element - # is an _empty_ string. But if the _last_ character of the - # string is star, then split will return an array that does - # _not_ add an empty string at the end. So we have to deal - # with all that specifically. - # - def to_ber - case @op - when :eq - if @right == "*" # present - @left.to_ber_application_string 7 - elsif @right =~ /[\*]/ #substring - ary = @right.split( /[\*]+/ ) - final_star = @right =~ /[\*]$/ - initial_star = ary.first == "" and ary.shift - - seq = [] - unless initial_star - seq << ary.shift.to_ber_contextspecific(0) - end - n_any_strings = ary.length - (final_star ? 0 : 1) - p n_any_strings - n_any_strings.times { - seq << ary.shift.to_ber_contextspecific(1) - } - unless final_star - seq << ary.shift.to_ber_contextspecific(2) - end - [@left.to_ber, seq.to_ber].to_ber_contextspecific 4 - else #equality - [@left.to_ber, @right.to_ber].to_ber_contextspecific 3 - end - when :and - ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten - ary.map {|a| a.to_ber}.to_ber_contextspecific( 0 ) - when :or - ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten - ary.map {|a| a.to_ber}.to_ber_contextspecific( 1 ) - when :not - [@left.to_ber].to_ber_contextspecific 2 - else - # ERROR, we'll return objectclass=* to keep things from blowing up, - # but that ain't a good answer and we need to kick out an error of some kind. - raise "unimplemented search filter" - end - end - - # - # coalesce - # This is a private helper method for dealing with chains of ANDs and ORs - # that are longer than two. If BOTH of our branches are of the specified - # type of joining operator, then return both of them as an array (calling - # coalesce recursively). If they're not, then return an array consisting - # only of self. - # - def coalesce operator - if @op == operator - [@left.coalesce( operator ), @right.coalesce( operator )] - else - [self] - end - end - - -end # class Net::LDAP::Filter - -end # class Net::LDAP -end # module Net - - -#----------------------------------- - -if __FILE__ == $0 - puts "No default action" -end -