diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/rbot/core/remote.rb | 401 |
1 files changed, 0 insertions, 401 deletions
diff --git a/lib/rbot/core/remote.rb b/lib/rbot/core/remote.rb deleted file mode 100644 index d8d43533..00000000 --- a/lib/rbot/core/remote.rb +++ /dev/null @@ -1,401 +0,0 @@ -#-- vim:sw=2:et -#++ -# -# :title: Remote service provider for rbot -# -# Author:: Giuseppe Bilotta (giuseppe.bilotta@gmail.com) -# -# From an idea by halorgium <rbot@spork.in>. -# -# TODO find a way to manage session id (logging out, manually and/or -# automatically) - -require 'drb/drb' - -module ::Irc -class Bot - - module Auth - - # We extend the BotUser class to handle remote logins - # - class BotUser - - # A rather simple method to handle remote logins. Nothing special, just a - # password check. - # - def remote_login(password) - if password == @password - debug "remote login for #{self.inspect} succeeded" - return true - else - return false - end - end - end - - # We extend the ManagerClass to handle remote logins - # - class ManagerClass - - MAX_SESSION_ID = 2**128 - 1 - - # Creates a session id when the given password matches the given - # botusername - # - def remote_login(botusername, pwd) - @remote_users = Hash.new unless defined? @remote_users - n = BotUser.sanitize_username(botusername) - k = n.to_sym - raise "No such BotUser #{n}" unless include?(k) - bu = @allbotusers[k] - if bu.remote_login(pwd) - raise "ran out of session ids!" if @remote_users.length == MAX_SESSION_ID - session_id = rand(MAX_SESSION_ID) - while @remote_users.has_key?(session_id) - session_id = rand(MAX_SESSION_ID) - end - @remote_users[session_id] = bu - return session_id - end - return false - end - - # Returns the botuser associated with the given session id - def remote_user(session_id) - return everyone unless session_id - return nil unless defined? @remote_users - if @remote_users.has_key?(session_id) - return @remote_users[session_id] - else - return nil - end - end - end - - end - - - # A RemoteMessage is similar to a BasicUserMessage - # - class RemoteMessage - # associated bot - attr_reader :bot - - # when the message was received - attr_reader :time - - # remote client that originated the message - attr_reader :source - - # contents of the message - attr_accessor :message - - def initialize(bot, source, message) - @bot = bot - @source = source - @message = message - @time = Time.now - end - - # The target of a RemoteMessage - def target - @bot - end - - # Remote messages are always 'private' - def private? - true - end - end - - # The RemoteDispatcher is a kind of MessageMapper, tuned to handle - # RemoteMessages - # - class RemoteDispatcher < MessageMapper - - # It is initialized by passing it the bot instance - # - def initialize(bot) - super - end - - # The map method for the RemoteDispatcher returns the index of the inserted - # template - # - def map(botmodule, *args) - super - return @templates.length - 1 - end - - # The unmap method for the RemoteDispatcher nils the template at the given index, - # therefore effectively removing the mapping - # - def unmap(botmodule, handle) - tmpl = @templates[handle] - 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.clear unless @templates.compact.size > 0 - end - - # We redefine the handle() method from MessageMapper, taking into account - # that @parent is a bot, and that we don't handle fallbacks. - # - # On failure to dispatch anything, the method returns false. If dispatching - # is successfull, the method returns a Hash. - # - # Presently, the hash returned on success has only one key, :return, whose - # value is the actual return value of the successfull dispatch. - # - # TODO this same kind of mechanism could actually be used in MessageMapper - # itself to be able to handle the case of multiple plugins having the same - # 'first word' ... - # - # - def handle(m) - return false if @templates.empty? - failures = [] - @templates.each do |tmpl| - # Skip this element if it was unmapped - next unless tmpl - botmodule = @parent.plugins[tmpl.botmodule] - options = tmpl.recognize(m) - if options.kind_of? Failure - failures << options - else - action = tmpl.options[:action] - unless botmodule.respond_to?(action) - failures << NoActionFailure.new(tmpl, m) - next - end - auth = tmpl.options[:full_auth_path] - debug "checking auth for #{auth}" - # We check for private permission - if m.bot.auth.permit?(m.source, auth, '?') - debug "template match found and auth'd: #{action.inspect} #{options.inspect}" - return :return => botmodule.send(action, m, options) - end - debug "auth failed for #{auth}" - # if it's just an auth failure but otherwise the match is good, - # don't try any more handlers - return false - end - end - failures.each {|r| - debug "#{r.template.inspect} => #{r}" - } - debug "no handler found" - return false - end - - end - - # The Irc::Bot::RemoteObject class represents and object that will take care - # of interfacing with remote clients - # - # Example client session: - # - # require 'drb' - # rbot = DRbObject.new_with_uri('druby://localhost:7268') - # id = rbot.delegate(nil, 'remote login someuser somepass')[:return] - # rbot.delegate(id, 'some secret command') - # - # Of course, the remote login is only neede for commands which may not be available - # to everyone - # - class RemoteObject - - # We don't want this object to be copied clientside, so we make it undumpable - include DRbUndumped - - # Initialization is simple - def initialize(bot) - @bot = bot - end - - # The delegate method. This is the main method used by remote clients to send - # commands to the bot. Most of the time, the method will be called with only - # two parameters (session id and a String), but we allow more parameters - # for future expansions. - # - # The session_id can be nil, meaning that the remote client wants to work as - # an anoynomus botuser. - # - def delegate(session_id, *pars) - warn "Ignoring extra parameters" if pars.length > 1 - cmd = pars.first - client = @bot.auth.remote_user(session_id) - raise "No such session id #{session_id}" unless client - debug "Trying to dispatch command #{cmd.inspect} from #{client.inspect} authorized by #{session_id.inspect}" - m = RemoteMessage.new(@bot, client, cmd) - @bot.remote_dispatcher.handle(m) - end - - private :instance_variables, :instance_variable_get, :instance_variable_set - end - - # The bot also manages a single (for the moment) remote dispatcher. This method - # makes it accessible to the outside world, creating it if necessary. - # - def remote_dispatcher - if defined? @remote_dispatcher - @remote_dispatcher - else - @remote_dispatcher = RemoteDispatcher.new(self) - end - end - - # The bot also manages a single (for the moment) remote object. This method - # makes it accessible to the outside world, creating it if necessary. - # - def remote_object - if defined? @remote_object - @remote_object - else - @remote_object = RemoteObject.new(self) - end - end - - module Plugins - - # We create a new Ruby module that can be included by BotModules that want to - # provide remote interfaces - # - module RemoteBotModule - - # The remote_map acts just like the BotModule#map method, except that - # the map is registered to the @bot's remote_dispatcher. Also, the remote map handle - # is handled for the cleanup management - # - def remote_map(*args) - @remote_maps = Array.new unless defined? @remote_maps - @remote_maps << @bot.remote_dispatcher.map(self, *args) - end - - # Unregister the remote maps. - # - def remote_cleanup - return unless defined? @remote_maps - @remote_maps.each { |h| - @bot.remote_dispatcher.unmap(self, h) - } - @remote_maps.clear - end - - # Redefine the default cleanup method. - # - def cleanup - super - remote_cleanup - end - end - - # And just because I like consistency: - # - module RemoteCoreBotModule - include RemoteBotModule - end - - module RemotePlugin - include RemoteBotModule - end - - end - -end -end - -class RemoteModule < CoreBotModule - - include RemoteCoreBotModule - - Config.register Config::BooleanValue.new('remote.autostart', - :default => false, - :requires_rescan => true, - :desc => "Whether the remote service provider should be started automatically") - - Config.register Config::IntegerValue.new('remote.port', - :default => 7268, # that's 'rbot' - :requires_rescan => true, - :desc => "Port on which the remote interface will be presented") - - Config.register Config::StringValue.new('remote.host', - :default => '127.0.0.1', - :requires_rescan => true, - :desc => "Host on which the remote interface will be presented") - - def initialize - super - @port = @bot.config['remote.port'] - @host = @bot.config['remote.host'] - @drb = nil - begin - start_service if @bot.config['remote.autostart'] - rescue => e - error "couldn't start remote service provider: #{e.inspect}" - end - end - - def start_service - raise "Remote service provider already running" if @drb - @drb = DRb.start_service("druby://#{@host}:#{@port}", @bot.remote_object) - end - - def stop_service - @drb.stop_service if @drb - @drb = nil - end - - def cleanup - stop_service - super - end - - def handle_start(m, params) - if @drb - rep = "remote service provider already running" - rep << " on port #{@port}" if m.private? - else - begin - start_service(@port) - rep = "remote service provider started" - rep << " on port #{@port}" if m.private? - rescue - rep = "couldn't start remote service provider" - end - end - m.reply rep - end - - def remote_test(m, params) - @bot.say params[:channel], "This is a remote test" - end - - def remote_login(m, params) - id = @bot.auth.remote_login(params[:botuser], params[:password]) - raise "login failed" unless id - return id - end - -end - -remote = RemoteModule.new - -remote.map "remote start", - :action => 'handle_start', - :auth_path => ':manage:' - -remote.map "remote stop", - :action => 'handle_stop', - :auth_path => ':manage:' - -remote.default_auth('*', false) - -remote.remote_map "remote test :channel", - :action => 'remote_test' - -remote.remote_map "remote login :botuser :password", - :action => 'remote_login' - -remote.default_auth('login', true) |