]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/core/webservice.rb
fix: webservice message user type
[user/henk/code/ruby/rbot.git] / lib / rbot / core / webservice.rb
index 42365a6ae4c8a8181a78bf25ee68f5592ab59228..112ec85e7672f859b0daefe69ec8d1bce2183e8d 100644 (file)
@@ -17,21 +17,54 @@ require 'webrick/https'
 require 'openssl'
 require 'cgi'
 require 'json'
+require 'erb'
+require 'ostruct'
 
 module ::Irc
 class Bot
     # A WebMessage is a web request and response object combined with helper methods.
     #
     class WebMessage
-      attr_reader :bot, :method, :bot, :req, :res, :post, :client, :path, :source
+      # Bot instance
+      #
+      attr_reader :bot
+      # HTTP method (POST, GET, etc.)
+      #
+      attr_reader :method
+      # Request object, a instance of WEBrick::HTTPRequest ({http://www.ruby-doc.org/stdlib-2.0/libdoc/webrick/rdoc/WEBrick/HTTPRequest.html docs})
+      #
+      attr_reader :req
+      # Response object, a instance of WEBrick::HTTPResponse ({http://www.ruby-doc.org/stdlib-2.0/libdoc/webrick/rdoc/WEBrick/HTTPResponse.html docs})
+      #
+      attr_reader :res
+      # Parsed post request parameters.
+      #
+      attr_reader :post
+      # Parsed url parameters.
+      #
+      attr_reader :args
+      # Client IP.
+      # 
+      attr_reader :client
+      # URL Path.
+      #
+      attr_reader :path
+      # The bot user issuing the command.
+      #
+      attr_reader :source
       def initialize(bot, req, res)
         @bot = bot
         @req = req
         @res = res
 
         @method = req.request_method
+        @post = {}
         if req.body and not req.body.empty?
-          @post = CGI::parse(req.body)
+          @post = parse_query(req.body)
+        end
+        @args = {}
+        if req.query_string and not req.query_string.empty?
+          @args = parse_query(req.query_string)
         end
         @client = req.peeraddr[3]
 
@@ -42,15 +75,27 @@ class Bot
             if botuser and botuser.password == password
               @source = botuser
               true
+            else
+              false
             end
-            false
           else
             true # no need to request auth at this point
           end
         }
 
         @path = req.path
-        debug '@path = ' + @path.inspect
+
+        @load_path = [File.join(Config::datadir, 'web')]
+        @load_path += @bot.plugins.core_module_dirs
+        @load_path += @bot.plugins.plugin_dirs
+      end
+
+      def parse_query(query)
+        params = CGI::parse(query)
+        params.each_pair do |key, val|
+          params[key] = val.last
+        end
+        params
       end
 
       # The target of a RemoteMessage
@@ -63,12 +108,48 @@ class Bot
         true
       end
 
-      # Sends a plaintext response
-      def send_plaintext(body, status=200)
+      # Sends a response with the specified body, status and content type.
+      def send_response(body, status, type)
         @res.status = status
-        @res['Content-Type'] = 'text/plain'
+        @res['Content-Type'] = type
         @res.body = body
       end
+
+      # Sends a plaintext response
+      def send_plaintext(body, status=200)
+        send_response(body, status, 'text/plain')
+      end
+
+      # Sends a json response
+      def send_json(body, status=200)
+        send_response(body, status, 'application/json')
+      end
+
+      # Sends a html response
+      def send_html(body, status=200)
+        send_response(body, status, 'text/html')
+      end
+
+      # Expands a relative filename to absolute using a list of load paths.
+      def get_load_path(filename)
+        @load_path.each do |path|
+          file = File.join(path, filename) 
+          return file if File.exists?(file)
+        end
+      end
+
+      # Renders a erb template and responds it
+      def render(template, args={})
+        file = get_load_path template
+        if not file
+          raise 'template not found: ' + template
+        end
+
+        tmpl = ERB.new(IO.read(file))
+        ns = OpenStruct.new(args)
+        body = tmpl.result(ns.instance_eval { binding })
+        send_html(body, 200)
+      end
     end
 
     # works similar to a message mapper but for url paths
@@ -162,7 +243,7 @@ class Bot
         tmpl = @templates[index]
         raise "Botmodule #{botmodule.name} tried to unmap #{tmpl.inspect} that was handled by #{tmpl.botmodule}" unless tmpl.botmodule == botmodule.name
         debug "Unmapping #{tmpl.inspect}"
-        @templates[handle] = nil
+        @templates[index] = nil
         @templates.clear unless @templates.compact.size > 0
       end
 
@@ -198,8 +279,8 @@ class Bot
             if m.bot.auth.permit?(m.source || Auth::defaultbotuser, auth, '?')
               debug "template match found and auth'd: #{action.inspect} #{params.inspect}"
               response = botmodule.send(action, m, params)
-              if m.res.sent_size == 0
-                m.send_plaintext(response.to_json)
+              if m.res.sent_size == 0 and m.res.body.empty?
+                m.send_json(response.to_json)
               end
               return true
             end
@@ -262,6 +343,16 @@ class Bot
           web_cleanup
         end
       end
+
+      # And just because I like consistency:
+      #
+      module WebCoreBotModule
+        include WebBotModule
+      end
+
+      module WebPlugin
+        include WebBotModule
+      end
     end
 end # Bot
 end # Irc
@@ -287,6 +378,11 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
     begin
       m = WebMessage.new(@bot, req, res)
       @bot.web_dispatcher.handle m
+    rescue WEBrick::HTTPStatus::Unauthorized
+      res.status = 401
+      res['Content-Type'] = 'text/plain'
+      res.body = 'Authentication Required!'
+      error 'authentication error (wrong password)'
     rescue
       res.status = 500
       res['Content-Type'] = 'text/plain'
@@ -307,7 +403,7 @@ end
 
 class WebServiceModule < CoreBotModule
 
-  include WebBotModule
+  include WebCoreBotModule
 
   Config.register Config::BooleanValue.new('webservice.autostart',
     :default => false,
@@ -324,6 +420,10 @@ class WebServiceModule < CoreBotModule
     :requires_rescan => true,
     :desc => 'Host the web service will bind on')
 
+  Config.register Config::StringValue.new('webservice.url',
+    :default => 'http://127.0.0.1:7268',
+    :desc => 'The public URL of the web service.')
+
   Config.register Config::BooleanValue.new('webservice.ssl',
     :default => false,
     :requires_rescan => true,
@@ -430,20 +530,26 @@ class WebServiceModule < CoreBotModule
       return
     end
 
-    command = m.post['command'][0]
+    command = m.post['command']
     if not m.source
       botuser = Auth::defaultbotuser
     else
-      botuser = m.source.botuser
+      botuser = m.source
     end
     netmask = '%s!%s@%s' % [botuser.username, botuser.username, m.client]
 
+    debug 'dispatch command: ' + command
+
     user = WebServiceUser.new(netmask, botuser)
     message = Irc::PrivMessage.new(@bot, nil, user, @bot.myself, command)
 
     res = @bot.plugins.irc_delegate('privmsg', message)
 
-    { :reply => user.response }
+    if m.req['Accept'] == 'application/json'
+      { :reply => user.response }
+    else
+      m.send_plaintext(user.response.join("\n") + "\n")
+    end
   end
 
 end