diff options
-rw-r--r-- | lib/rbot/botuser.rb | 2 | ||||
-rw-r--r-- | lib/rbot/core/webservice.rb | 173 | ||||
-rw-r--r-- | lib/rbot/ircbot.rb | 6 | ||||
-rw-r--r-- | lib/rbot/messagemapper.rb | 6 |
4 files changed, 185 insertions, 2 deletions
diff --git a/lib/rbot/botuser.rb b/lib/rbot/botuser.rb index 110c078f..ab9a8de3 100644 --- a/lib/rbot/botuser.rb +++ b/lib/rbot/botuser.rb @@ -872,7 +872,7 @@ class Bot if user.class <= BotUser botuser = user else - botuser = irc_to_botuser(user) + botuser = user.botuser end cmd = cmdtxt.to_irc_auth_command diff --git a/lib/rbot/core/webservice.rb b/lib/rbot/core/webservice.rb new file mode 100644 index 00000000..222561de --- /dev/null +++ b/lib/rbot/core/webservice.rb @@ -0,0 +1,173 @@ +#-- vim:sw=2:et +#++ +# +# :title: Web service for bot +# +# Author:: Matthias Hecker (apoc@geekosphere.org) +# +# HTTP(S)/json based web service for remote controlling the bot, +# similar to remote but much more portable. +# +# For more info/documentation: +# https://github.com/4poc/rbot/wiki/Web-Service +# + +require 'webrick' +require 'webrick/https' +require 'openssl' +require 'cgi' + +class ::WebServiceUser < Irc::User + def initialize(str, botuser, opts={}) + super(str, opts) + @botuser = botuser + @response = [] + end + attr_reader :botuser + attr_accessor :response +end + +class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet + def initialize(server, bot) + super server + @bot = bot + end + + def do_POST(req, res) + # NOTE: still wip. + uri = req.path_info + post = CGI::parse(req.body) + ip = req.peeraddr[3] + + command = uri.gsub('/', ' ').strip + + username = post['username'].first + password = post['password'].first + + botuser = @bot.auth.get_botuser(username) + netmask = '%s!%s@%s' % [botuser.username, botuser.username, ip] + + if not botuser or botuser.password != password + raise 'Permission Denied' + end + + ws_user = WebServiceUser.new(netmask, botuser) + message = Irc::PrivMessage.new(@bot, nil, ws_user, @bot.myself, command) + + @bot.plugins.irc_delegate('privmsg', message) + + res.status = 200 + res['Content-Type'] = 'text/plain' + res.body = ws_user.response.join("\n") + "\n" + end +end + +class WebServiceModule < CoreBotModule + + Config.register Config::BooleanValue.new('webservice.autostart', + :default => false, + :requires_rescan => true, + :desc => 'Whether the web service should be started automatically') + + Config.register Config::IntegerValue.new('webservice.port', + :default => 7260, # that's 'rbot' + :requires_rescan => true, + :desc => 'Port on which the web service will listen') + + Config.register Config::StringValue.new('webservice.host', + :default => '127.0.0.1', + :requires_rescan => true, + :desc => 'Host the web service will bind on') + + Config.register Config::BooleanValue.new('webservice.ssl', + :default => false, + :requires_rescan => true, + :desc => 'Whether the web server should use SSL (recommended!)') + + Config.register Config::StringValue.new('webservice.ssl_key', + :default => '~/.rbot/wskey.pem', + :requires_rescan => true, + :desc => 'Private key file to use for SSL') + + Config.register Config::StringValue.new('webservice.ssl_cert', + :default => '~/.rbot/wscert.pem', + :requires_rescan => true, + :desc => 'Certificate file to use for SSL') + + def initialize + super + @port = @bot.config['webservice.port'] + @host = @bot.config['webservice.host'] + @server = nil + begin + start_service if @bot.config['webservice.autostart'] + rescue => e + error "couldn't start web service provider: #{e.inspect}" + end + end + + def start_service + raise "Remote service provider already running" if @server + opts = {:BindAddress => @host, :Port => @port} + if @bot.config['webservice.ssl'] + opts.merge! :SSLEnable => true + cert = File.expand_path @bot.config['webservice.ssl_cert'] + key = File.expand_path @bot.config['webservice.ssl_key'] + if File.exists? cert and File.exists? key + debug 'using ssl certificate files' + opts.merge!({ + :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(cert)), + :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(key)) + }) + else + debug 'using on-the-fly generated ssl certs' + opts.merge! :SSLCertName => [ %w[CN localhost] ] + # the problem with this is that it will always use the same + # serial number which makes this feature pretty much useless. + end + end + @server = WEBrick::HTTPServer.new(opts) + debug 'webservice started: ' + opts.inspect + @server.mount('/dispatch', DispatchServlet, @bot) + Thread.new { @server.start } + end + + def stop_service + @server.shutdown if @server + @server = nil + end + + def cleanup + stop_service + super + end + + def handle_start(m, params) + s = '' + if @server + s << 'web service already running' + else + begin + start_service + s << 'web service started' + rescue + s << 'unable to start web service, error: ' + $!.to_s + end + end + m.reply s + end + +end + +webservice = WebServiceModule.new + +webservice.map 'webservice start', + :action => 'handle_start', + :auth_path => ':manage:' + +webservice.map 'webservice stop', + :action => 'handle_stop', + :auth_path => ':manage:' + +webservice.default_auth('*', false) + diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb index 0a8b1aa2..a342e8c1 100644 --- a/lib/rbot/ircbot.rb +++ b/lib/rbot/ircbot.rb @@ -1128,6 +1128,12 @@ class Bot where = ds[:dest] filtered = ds[:text] + if defined? WebServiceUser and where.instance_of? WebServiceUser + debug 'sendmsg to web service!' + where.response << filtered + return + end + # For starters, set up appropriate queue channels and rings mchan = opts[:queue_channel] mring = opts[:queue_ring] diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb index d85f0b14..e154e395 100644 --- a/lib/rbot/messagemapper.rb +++ b/lib/rbot/messagemapper.rb @@ -258,7 +258,11 @@ class Bot debug "checking auth for #{auth}" if m.bot.auth.allow?(auth, m.source, m.replyto) debug "template match found and auth'd: #{action.inspect} #{options.inspect}" - if !m.in_thread && (tmpl.options[:thread] || tmpl.options[:threaded]) + if !m.in_thread and (tmpl.options[:thread] or tmpl.options[:threaded]) and + (defined? WebServiceUser and not m.source.instance_of? WebServiceUser) + # Web service: requests are handled threaded anyway and we want to + # wait for the responses. + # since the message action is in a separate thread, the message may be # delegated to unreplied() before the thread has a chance to actually # mark it as replied. since threading is used mostly for commands that |