commit bd7143516b6b7a3beb93713f6dfc7752723d0208
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sun Sep 27 20:06:46 2009 +0200

    [evol] added search command (working, but items are not recognized yet)

diff --git a/bin/shadowwalker b/bin/shadowwalker
index c4d1b65..3668a2d 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -455,6 +455,26 @@ class CreateCommand < Command
 end
 cmdparser.add_command(CreateCommand.new)
 
+class SearchCommand < Command
+  def initialize
+    super('search', false)
+
+    self.short_desc = "Search items"
+    self.usages_params = [
+                          "[:objects=<object>[,<object>]...] [:aspects=<aspect>[,<aspect>]...] [<field>=<value>] ..."
+                         ]
+  end
+
+  def execute (args)
+    raise SyntaxError, _("no search arguments") if args.empty?
+
+    res = LdapObject.items_search_from_strings(args)
+    res2 =  Hash[res]
+    puts res2.keys.sort.join("\n")
+  end
+end
+cmdparser.add_command(SearchCommand.new)
+
 
 begin
   cmdparser.parse
diff --git a/lib/ldap_shadows/activeldap_fixes.rb b/lib/ldap_shadows/activeldap_fixes.rb
index 0f42256..b08cc58 100644
--- a/lib/ldap_shadows/activeldap_fixes.rb
+++ b/lib/ldap_shadows/activeldap_fixes.rb
@@ -134,4 +134,11 @@ module ActiveLdap
       @initial_attribute_list ||= self.nonempty_attributes
     end
   end
+
+  # This is useful with Base.search()
+  module Adapter
+    class Base
+      public :escape_filter_value
+    end
+  end
 end
diff --git a/lib/ldap_shadows/controller.rb b/lib/ldap_shadows/controller.rb
index 03bc2ee..cc9ed6d 100755
--- a/lib/ldap_shadows/controller.rb
+++ b/lib/ldap_shadows/controller.rb
@@ -32,6 +32,8 @@ module LdapShadows
       @aspects = {}
       # should replace @aspects properly one day
       @aspects2 = {}
+
+      LdapShadows::LdapObject.mapper = self
     end
 
     def set_global_config(global_config)
diff --git a/lib/ldap_shadows/object.rb b/lib/ldap_shadows/object.rb
index e9e0458..206ec40 100755
--- a/lib/ldap_shadows/object.rb
+++ b/lib/ldap_shadows/object.rb
@@ -373,6 +373,105 @@ module LdapShadows
       true
     end
 
+    def self.items_search_from_strings(str_list)
+      ldap_search_objects = "(objectClass=*)"
+      ldap_search_aspects = ""
+      ldap_search_fields = []
+      str_list.each do |str|
+        unless str =~ /^([a-zA-Z]*(?::[a-zA-Z]+)?)(=|~=)(.*)$/
+          raise SyntaxError, _("search parameter '%s' is invalid") % str
+        end
+        key = $1
+        op = $2
+        val = $3
+
+        if key.index(":")
+          type, field = key.split(":")
+        else
+          type = nil
+          field = key
+        end
+
+        case type
+        when nil
+          ldap_search_fields << ldap_search_string_field(field, op, val)
+        when 'rel'
+          raise PreProcessingError, _("Searching relations is not implemented yet")
+        when ''
+          case field
+          when 'objects'
+            ldap_search_objects = ldap_search_string_objects(field, op, val)
+          when 'aspects'
+            ldap_search_aspects = ldap_search_string_aspects(field, op, val)
+          else
+            raise PreProcessingError, _("Unknown core field '%s'") % field
+          end
+        else
+          raise PreProcessingError, _("Unknown type '%s' for field '%s'") % [type, field]
+        end
+      end
+      ldap_search_string = "(&" + ldap_search_objects + ldap_search_aspects + ldap_search_fields.join + ")"
+
+      search(:scope => :sub, :filter => ldap_search_string, :attributes => "objectClass")
+    end
+
+    def self.ldap_search_string_field(field, op, val)
+      esc_val = self.connection.escape_filter_value(val)
+      case op
+      when "="
+        "(#{field}=#{esc_val})"
+
+      when "~="
+        raise PreProcessingError, _("Searching with regex is not implemented yet")
+
+      else
+        raise SyntaxError, _("Unknown operator '%s'") % op
+      end
+    end
+
+    def self.ldap_search_string_objects(field, op, val_list)
+      ldap_search_parts = val_list.split(",").collect do |val|
+        obj_hdl = val.downcase.singularize
+        obj_klass = $ldapctl.find_klass(obj_hdl)
+        raise PreProcessingError, _("No such object '%s'") % val if obj_klass.nil?
+
+        ldap_classes = obj_klass.required_classes
+
+        case op
+        when "="
+          "(&" + ldap_classes.collect{|cl| "(objectClass=#{cl})" }.join + ")"
+
+        when "~="
+          raise PreProcessingError, _("Searching with regex is not possible with objects")
+
+        else
+          raise SyntaxError, _("Unknown operator '%s'") % op
+        end
+      end
+      "(|" + ldap_search_parts.join + ")"
+    end
+
+    def self.ldap_search_string_aspects(field, op, val_list)
+      ldap_search_parts = val_list.split(",").collect do |val|
+        aspect_data = self.mapper.get_aspect(val)
+        raise PreProcessingError, _("No such aspect '%s'") % val if aspect_data.nil?
+
+        ldap_classes = aspect_data[:mapping][:classes]
+
+        case op
+        when "="
+          "(&" + ldap_classes.collect{|cl| "(objectClass=#{cl})" }.join + ")"
+
+        when "~="
+          raise PreProcessingError, _("Searching with regex is not possible with aspects")
+
+        else
+          raise SyntaxError, _("Unknown operator '%s'") % op
+        end
+      end
+      "(&" + ldap_search_parts.join + ")"
+    end
+
     def family_parent_dn
       pdn = self.dn_obj.dup
       pdn.shift
