lib/net/ldap/dataset.rb in net-ldap-0.1.1 vs lib/net/ldap/dataset.rb in net-ldap-0.2

- old
+ new

@@ -1,99 +1,154 @@ -#---------------------------------------------------------------------------- -# -# 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 -# -#--------------------------------------------------------------------------- +# -*- ruby encoding: utf-8 -*- +## +# An LDAP Dataset. Used primarily as an intermediate format for converting +# to and from LDIF strings and Net::LDAP::Entry objects. +class Net::LDAP::Dataset < Hash + ## + # Dataset object comments. + attr_reader :comments -module Net - class LDAP - class Dataset < Hash - attr_reader :comments + def initialize(*args, &block) # :nodoc: + super + @comments = [] + end - class IOFilter - def initialize(io) - @io = io + ## + # Outputs an LDAP Dataset as an array of strings representing LDIF + # entries. + def to_ldif + ary = [] + ary += @comments unless @comments.empty? + keys.sort.each do |dn| + ary << "dn: #{dn}" + + attributes = self[dn].keys.map { |attr| attr.to_s }.sort + attributes.each do |attr| + self[dn][attr.to_sym].each do |value| + if attr == "userpassword" or value_is_binary?(value) + value = [value].pack("m").chomp.gsub(/\n/m, "\n ") + ary << "#{attr}:: #{value}" + else + ary << "#{attr}: #{value}" + end end - def gets - s = @io.gets - s.chomp if s - end end - def self.read_ldif io - ds = Dataset.new - io = IOFilter.new(io) + ary << "" + end + block_given? and ary.each { |line| yield line} - line = io.gets - dn = nil + ary + end - while line - new_line = io.gets - if new_line =~ /^[\s]+/ - line << " " << $' - else - nextline = new_line + ## + # Outputs an LDAP Dataset as an LDIF string. + def to_ldif_string + to_ldif.join("\n") + end - if line =~ /^\#/ - ds.comments << line - elsif line =~ /^dn:[\s]*/i - dn = $' - ds[dn] = Hash.new {|k,v| k[v] = []} - elsif line.length == 0 - dn = nil - elsif line =~ /^([^:]+):([\:]?)[\s]*/ - # $1 is the attribute name - # $2 is a colon iff the attr-value is base-64 encoded - # $' is the attr-value - # Avoid the Base64 class because not all Ruby versions have it. - attrvalue = ($2 == ":") ? $'.unpack('m').shift : $' - ds[dn][$1.downcase.intern] << attrvalue - end + ## + # Convert the parsed LDIF objects to Net::LDAP::Entry objects. + def to_entries + ary = [] + keys.each do |dn| + entry = Net::LDAP::Entry.new(dn) + self[dn].each do |attr, value| + entry[attr] = value + end + ary << entry + end + ary + end - line = nextline - end - end + ## + # This is an internal convenience method to determine if a value requires + # base64-encoding before conversion to LDIF output. The standard approach + # in most LDAP tools is to check whether the value is a password, or if + # the first or last bytes are non-printable. Microsoft Active Directory, + # on the other hand, sometimes sends values that are binary in the middle. + # + # In the worst cases, this could be a nasty performance killer, which is + # why we handle the simplest cases first. Ideally, we would also test the + # first/last byte, but it's a bit harder to do this in a way that's + # compatible with both 1.8.6 and 1.8.7. + def value_is_binary?(value) # :nodoc: + value = value.to_s + return true if value[0] == ?: or value[0] == ?< + value.each_byte { |byte| return true if (byte < 32) || (byte > 126) } + false + end + private :value_is_binary? - ds + class << self + class ChompedIO # :nodoc: + def initialize(io) + @io = io end + def gets + s = @io.gets + s.chomp if s + end + end - - def initialize - @comments = [] + ## + # Creates a Dataset object from an Entry object. Used mostly to assist + # with the conversion of + def from_entry(entry) + dataset = Net::LDAP::Dataset.new + hash = { } + entry.each_attribute do |attribute, value| + next if attribute == :dn + hash[attribute] = value end + dataset[entry.dn] = hash + dataset + end + ## + # Reads an object that returns data line-wise (using #gets) and parses + # LDIF data into a Dataset object. + def read_ldif(io) + ds = Net::LDAP::Dataset.new + io = ChompedIO.new(io) - def to_ldif - ary = [] - ary += (@comments || []) - keys.sort.each do |dn| - ary << "dn: #{dn}" + line = io.gets + dn = nil - self[dn].keys.map {|sym| sym.to_s}.sort.each do |attr| - self[dn][attr.intern].each {|val| ary << "#{attr}: #{val}" } + while line + new_line = io.gets + + if new_line =~ /^[\s]+/ + line << " " << $' + else + nextline = new_line + + if line =~ /^#/ + ds.comments << line + yield :comment, line if block_given? + elsif line =~ /^dn:[\s]*/i + dn = $' + ds[dn] = Hash.new { |k,v| k[v] = [] } + yield :dn, dn if block_given? + elsif line.empty? + dn = nil + yield :end, nil if block_given? + elsif line =~ /^([^:]+):([\:]?)[\s]*/ + # $1 is the attribute name + # $2 is a colon iff the attr-value is base-64 encoded + # $' is the attr-value + # Avoid the Base64 class because not all Ruby versions have it. + attrvalue = ($2 == ":") ? $'.unpack('m').shift : $' + ds[dn][$1.downcase.to_sym] << attrvalue + yield :attr, [$1.downcase.to_sym, attrvalue] if block_given? end - ary << "" + line = nextline end - block_given? and ary.each {|line| yield line} - ary end - end - end + ds + end + end end + +require 'net/ldap/entry' unless defined? Net::LDAP::Entry