commit 258a439a59e2be7ce7dcb2e68acf3b744e01f91f
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sun Mar 6 23:01:49 2011 +0100

    [evol] Botnet: propagate API calls environment (user token and language prefs at the moment), propagate session a bit further too in order to implement a more dynamic API tree

diff --git a/bin/test_client b/bin/test_client
index 5aa719f..78355b2 100755
--- a/bin/test_client
+++ b/bin/test_client
@@ -57,7 +57,7 @@ module CyborgHood
           #ask "MapMaker", :search_slave, "/Zones/?", {:is_master => false}
           #know? "MapMaker", :k1, "/Zones"
           #know? "MapMaker", :k2, "/prout"
-          set_user "duck"
+          set_user "guihome"
           ask "Clerk", :dns_info, "/Commands/DNS/INFO"
           on_error do
             puts "PLOUF"
diff --git a/lib/cyborghood-librarian/interface/0_base.rb b/lib/cyborghood-librarian/interface/0_base.rb
index acf0f4c..7b64745 100644
--- a/lib/cyborghood-librarian/interface/0_base.rb
+++ b/lib/cyborghood-librarian/interface/0_base.rb
@@ -4,7 +4,7 @@ node 'Records' do
   children.collect!{|i| i.camelize }
 
   node children do
-    object_list = Proc.new do |*args|
+    object_list = Proc.new do |session, env, *args|
       obj_name, options = args
       options ||= {}
 
@@ -12,20 +12,24 @@ node 'Records' do
     end
 
     on_request do |request|
-      request.reply.results = object_list.call
+      request.reply.results = object_list.call(request.session, request.env)
       request.send_reply
     end
 
-    attr_search_node do |criterias|
+    attr_search_node do |session, env, criterias|
       {
         :post_filter  => false,
-        :list         => object_list.call(:search, :criterias => criterias, :format => :data_hash)
+        :list         => object_list.call(session, env, :search,
+                                          :criterias => criterias,
+                                          :format => :data_hash)
       }
     end
 
     node object_list do
       on_request do |request|
-        request.reply.results = object_list.call(node_name, :format => :data_hash)
+        request.reply.results = object_list.call(request.session, request.env,
+                                                 node_name,
+                                                 :format => :data_hash)
         request.send_reply
       end
     end
diff --git a/lib/cyborghood-mapmaker/interface/0_base.rb b/lib/cyborghood-mapmaker/interface/0_base.rb
index 9f9bd96..d96c69b 100644
--- a/lib/cyborghood-mapmaker/interface/0_base.rb
+++ b/lib/cyborghood-mapmaker/interface/0_base.rb
@@ -3,7 +3,7 @@ store.dns = bot.services.dns
 node 'Services', :dir => 'services'
 
 node 'Zones' do
-  zone_list = Proc.new do
+  zone_list = Proc.new do |session, env|
     store.dns.flush_cache
     store.dns.zones
   end
diff --git a/lib/cyborghood/cyborg/botnet/conversation.rb b/lib/cyborghood/cyborg/botnet/conversation.rb
index aca3874..b8cbbf0 100644
--- a/lib/cyborghood/cyborg/botnet/conversation.rb
+++ b/lib/cyborghood/cyborg/botnet/conversation.rb
@@ -20,7 +20,6 @@ require 'yaml'
 require 'cyborghood/cyborg/botnet/session'
 require 'cyborghood/cyborg/botnet/protocol'
 require 'set'
-require 'thread'
 
 
 module CyborgHood
@@ -59,13 +58,13 @@ module CyborgHood
     end
 
     # convenience method
-    def call(*args, &callback)
-      @conversation.protocol.send_request_call(self, *args, &callback)
+    def call(env, node, *args, &callback)
+      @conversation.protocol.send_request_call(self, env, node, *args, &callback)
     end
 
     # convenience method
-    def exists?(node, &callback)
-      @conversation.protocol.send_request_exists(self, node, &callback)
+    def exists?(env, node, &callback)
+      @conversation.protocol.send_request_exists(self, env, node, &callback)
     end
 
     # convenience method
diff --git a/lib/cyborghood/cyborg/botnet/dsl.rb b/lib/cyborghood/cyborg/botnet/dsl.rb
index 7655005..1975121 100644
--- a/lib/cyborghood/cyborg/botnet/dsl.rb
+++ b/lib/cyborghood/cyborg/botnet/dsl.rb
@@ -23,8 +23,11 @@ module CyborgHood
       def ask(peer, key, cmd, *args)
         action_name = ['ask', cmd, *args].hash
 
-        _add_subtask_using_peer(action_name, peer) do |subtask, conv_thread|
-          conv_thread.call(cmd, *args) do |reply|
+        _add_subtask_using_peer(action_name, peer) do |subtask, conv_thread, env|
+          cmd = cmd.call if cmd.is_a? Proc
+          args.collect!{|a| a.is_a?(Proc) ? a.call : a }
+
+          conv_thread.call(env, cmd, *args) do |reply|
             case reply[:status]
             when :ok
               subtask.results = {key => reply[:result]}
@@ -42,8 +45,10 @@ module CyborgHood
       def know?(peer, key, cmd)
         action_name = ['know', cmd].hash
 
-        _add_subtask_using_peer(action_name, peer) do |subtask, conv_thread|
-          conv_thread.exists?(cmd) do |reply|
+        _add_subtask_using_peer(action_name, peer) do |subtask, conv_thread, env|
+          cmd = cmd.call if cmd.is_a? Proc
+
+          conv_thread.exists?(env, cmd) do |reply|
             case reply[:status]
             when :ok
               subtask.results = {key => reply[:result]}
@@ -79,6 +84,12 @@ module CyborgHood
             registered_resources[subtask_name] = defuse_peer_action_cb
           end
 
+          # catch current environment to transmit it
+          env = {
+            :preferred_locales  => @preferred_locales,
+            :user               => @user
+          }
+
           @bot.contact_peer(peer) do |conv|
             if conv
               logger.debug "Task '#{@name}': subtask '#{subtask.name}': peer '#{peer}' contacted, starting conversation"
@@ -88,7 +99,7 @@ module CyborgHood
               # don't use the block call to leave the conversation thread open
               conv_thread = conv.thread(@notification_name)
 
-              yield(subtask, conv_thread)
+              yield(subtask, conv_thread, env)
             else
               logger.debug "Task '#{@name}': Could not contact peer '#{peer}'"
               subtask.errors << CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': could not contact peer '#{peer}'")
diff --git a/lib/cyborghood/cyborg/botnet/interface.rb b/lib/cyborghood/cyborg/botnet/interface.rb
index bd77f3b..4c19bef 100644
--- a/lib/cyborghood/cyborg/botnet/interface.rb
+++ b/lib/cyborghood/cyborg/botnet/interface.rb
@@ -101,7 +101,7 @@ module CyborgHood
         node '?', :hidden => true do
           on_request do |request|
             if request.args.empty?
-              request.reply.results = lookup_node.__send__(:visible_nodes_names)
+              request.reply.results = lookup_node.__send__(:visible_nodes_names, request)
             else
               request.reply.results = list.nil? ? search_among_children(request, lookup_node) :
                 search_in_list(request, lookup_node, list)
@@ -115,20 +115,22 @@ module CyborgHood
         @request_cb = cb
       end
 
-      def _is_node?(session, node_path)
-        node = __send__(:find_node, session, node_path)
+      def _is_node?(session, env, node_path)
+        node = __send__(:find_node, session, env, node_path)
         not node.nil?
       end
 
-      def _call(session, node_path, args = nil, &send_result_cb)
+      def _call(session, env, node_path, args = nil, &send_result_cb)
         args ||= []
         raise CyberError.new(:unrecoverable, 'api/cyborghood', "wrong format for arguments when calling node '#{node_path}'") unless args.is_a? Array
 
-        node = find_node(session, node_path)
+        node = find_node(session, env, node_path)
         raise CyberError.new(:unrecoverable, 'api/cyborghood', "unknown node '#{node_path}'") if node.nil?
-
         logger.debug "[Server API] Node '#{node_path}' found"
-        node.__send__(:request, session, args, &send_result_cb)
+
+        # TODO: validate environment and setup things (choosen locale for example)
+
+        node.__send__(:request, session, env, args, &send_result_cb)
       end
 
       protected
@@ -158,7 +160,7 @@ module CyborgHood
         end
       end
 
-      def find_node(session, node_path)
+      def find_node(session, env, node_path)
         # node_path is a string argument when interface root node is called, but is a list of node elements later on
         if root?
           logger.debug "[Server API] Looking for node '#{node_path}'"
@@ -174,17 +176,17 @@ module CyborgHood
         if node_element.nil?
           return self
         else
-          next_node = find_child_node(node_element)
+          next_node = find_child_node(session, env, node_element)
           if next_node
             next_node.__send__(:load, node_element)
-            return next_node.__send__(:find_node, session, node_path)
+            return next_node.__send__(:find_node, session, env, node_path)
           else
             return
           end
         end
       end
 
-      def find_child_node(child_node)
+      def find_child_node(session, env, child_node)
         return @nodes[child_node] if @nodes.has_key? child_node
 
         @nodes.each_pair do |match, node|
@@ -193,7 +195,7 @@ module CyborgHood
                    elsif match.is_a? Regexp
                      child_node =~ Regexp.new(match)
                    elsif match.is_a? Proc
-                     match.call.include? child_node
+                     match.call(session, env).include? child_node
                    end
           return node if found
         end
@@ -202,10 +204,11 @@ module CyborgHood
       end
 
       class Request
-        attr_reader :session, :args, :reply
+        attr_reader :session, :env, :args, :reply
 
-        def initialize(session, args = [], &send_result_cb)
+        def initialize(session, env, args = [], &send_result_cb)
           @session = session
+          @env = env.to_ostruct
           @args = args
           @send_result_cb = send_result_cb
 
@@ -222,8 +225,8 @@ module CyborgHood
         end
       end
 
-      def request(session, args = [], &send_result_cb)
-        request = Request.new(session, args, &send_result_cb)
+      def request(session, env, args = [], &send_result_cb)
+        request = Request.new(session, env, args, &send_result_cb)
 
         if @request_cb
           begin
@@ -234,7 +237,7 @@ module CyborgHood
             raise CyberError.new(:unrecoverable, 'api/cyborghood', "call failed on node '#{node_path}': " + $!)
           end
         else
-          request.reply.results = visible_nodes_names
+          request.reply.results = visible_nodes_names(request)
           request.send_reply
         end
       end
@@ -243,19 +246,19 @@ module CyborgHood
         Hash[@nodes.select{|match, node| not node.hidden? }]
       end
 
-      def node_match_to_name(match)
+      def node_match_to_name(request, match)
         if match.is_a? String
           match
         elsif match.is_a? Regexp
           '/' + match.to_s + '/'
         elsif match.is_a? Proc
-          match.call
+          match.call(request.session, request.env)
         end
       end
 
-      def visible_nodes_names
+      def visible_nodes_names(request)
         visible_nodes.keys.collect do |match|
-          node_match_to_name(match)
+          node_match_to_name(request, match)
         end.compact.flatten
       end
 
@@ -297,14 +300,15 @@ module CyborgHood
           if match.is_a? String
             match_list = [match]
           elsif match.is_a? Proc
-            match_list = match.call
+            match_list = match.call(request.session, request.env)
           else
             next
           end
 
+          # TODO: filter by auth token
           match_list.each do |child_node_name|
             node.__send__(:load, child_node_name)
-            result = node.__send__(:request, request.session)
+            result = node.__send__(:request, request.session, request.env)
             next unless result.respond_to? :to_hash
 
             child_node_attrs = result.to_hash
@@ -323,7 +327,7 @@ module CyborgHood
         return {} unless criterias.is_a? Hash
 
         if list.is_a? Proc
-          data = list.call(criterias)
+          data = list.call(request.session, request.env, criterias)
 
           return data[:list] unless data[:post_filter]
         end
diff --git a/lib/cyborghood/cyborg/botnet/protocol.rb b/lib/cyborghood/cyborg/botnet/protocol.rb
index 80984b4..3a07503 100644
--- a/lib/cyborghood/cyborg/botnet/protocol.rb
+++ b/lib/cyborghood/cyborg/botnet/protocol.rb
@@ -116,9 +116,10 @@ module CyborgHood
       @conversation.bot.schedule_task do
         begin
           @conversation.bot.interface._call(message.conv_thread.session,
-                                                                     message.action_parameters[:node],
-                                                                     message.action_parameters[:parameters],
-                                                                     &process_result_cb)
+                                            message.action_parameters[:environment],
+                                            message.action_parameters[:node],
+                                            message.action_parameters[:parameters],
+                                            &process_result_cb)
         rescue CyberError => e
           process_result_cb.call(:errors => [{
             :category  => e.category,
@@ -143,11 +144,15 @@ module CyborgHood
                                    :message   => "missing parameters"
                                  })
       end
+
       send_reply_ack(message)
+
       @conversation.bot.schedule_task(@@request_callback) do
         {
           :reply_message => message,
-          :results => @conversation.bot.interface._is_node?(message.conv_thread.session, message.action_parameters[:node])
+          :results => @conversation.bot.interface._is_node?(message.conv_thread.session,
+                                                            message.action_parameters[:environment],
+                                                            message.action_parameters[:node])
         }
       end
     end
@@ -246,18 +251,28 @@ module CyborgHood
       @conversation.thread('system').new_message("REQUEST EXISTS", { :node => node }).send
     end
 
-    def send_request_call(conv_thread, node, *parameters, &callback)
-      message = conv_thread.new_message("REQUEST CALL", { :node => node, :parameters => parameters }).send
+    def send_request_call(conv_thread, env, node, *parameters, &callback)
+      message = conv_thread.new_message("REQUEST CALL", {
+                                          :node => node,
+                                          :parameters => parameters,
+                                          :environment => env
+                                        }).send
       message.register_callback(callback)
     end
 
-    def send_request_exists(conv_thread, node, &callback)
-      message = conv_thread.new_message("REQUEST EXISTS", { :node => node }).send
+    def send_request_exists(conv_thread, env, node, &callback)
+      message = conv_thread.new_message("REQUEST EXISTS", {
+                                          :node => node,
+                                          :environment => env
+                                        }).send
       message.register_callback(callback)
     end
 
-    def send_request_describe(conv_thread, node)
-      message = conv_thread.new_message("REQUEST DESCRIBE", { :node => node }).send
+    def send_request_describe(conv_thread, env, node)
+      message = conv_thread.new_message("REQUEST DESCRIBE", {
+                                          :node => node,
+                                          :environment => env
+                                        }).send
       message.register_callback(callback)
     end
 
diff --git a/lib/cyborghood/cyborg/botnet/session.rb b/lib/cyborghood/cyborg/botnet/session.rb
index 3bed6f9..cb9bfb9 100644
--- a/lib/cyborghood/cyborg/botnet/session.rb
+++ b/lib/cyborghood/cyborg/botnet/session.rb
@@ -41,6 +41,10 @@ module CyborgHood
         end
         obj
       end
+
+      def to_hash
+        Hash[self]
+      end
     end
   end
 end
