]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/webservice.rb
[webservice] control bot through http interface
[user/henk/code/ruby/rbot.git] / lib / rbot / core / webservice.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Web service for bot
5 #
6 # Author:: Matthias Hecker (apoc@geekosphere.org)
7 #
8 # HTTP(S)/json based web service for remote controlling the bot,
9 # similar to remote but much more portable.
10 #
11 # For more info/documentation:
12 # https://github.com/4poc/rbot/wiki/Web-Service
13 #
14
15 require 'webrick'
16 require 'webrick/https'
17 require 'openssl'
18 require 'cgi'
19
20 class ::WebServiceUser < Irc::User
21   def initialize(str, botuser, opts={})
22     super(str, opts)
23     @botuser = botuser
24     @response = []
25   end
26   attr_reader :botuser
27   attr_accessor :response
28 end
29
30 class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
31   def initialize(server, bot)
32     super server
33     @bot = bot
34   end
35
36   def do_POST(req, res)
37     # NOTE: still wip.
38     uri = req.path_info
39     post = CGI::parse(req.body)
40     ip = req.peeraddr[3]
41
42     command = uri.gsub('/', ' ').strip
43
44     username = post['username'].first
45     password = post['password'].first
46
47     botuser = @bot.auth.get_botuser(username)
48     netmask = '%s!%s@%s' % [botuser.username, botuser.username, ip]
49
50     if not botuser or botuser.password != password
51       raise 'Permission Denied'
52     end
53
54     ws_user = WebServiceUser.new(netmask, botuser)
55     message = Irc::PrivMessage.new(@bot, nil, ws_user, @bot.myself, command)
56
57     @bot.plugins.irc_delegate('privmsg', message)
58
59     res.status = 200
60     res['Content-Type'] = 'text/plain'
61     res.body = ws_user.response.join("\n") + "\n"
62   end
63 end
64
65 class WebServiceModule < CoreBotModule
66
67   Config.register Config::BooleanValue.new('webservice.autostart',
68     :default => false,
69     :requires_rescan => true,
70     :desc => 'Whether the web service should be started automatically')
71
72   Config.register Config::IntegerValue.new('webservice.port',
73     :default => 7260, # that's 'rbot'
74     :requires_rescan => true,
75     :desc => 'Port on which the web service will listen')
76
77   Config.register Config::StringValue.new('webservice.host',
78     :default => '127.0.0.1',
79     :requires_rescan => true,
80     :desc => 'Host the web service will bind on')
81
82   Config.register Config::BooleanValue.new('webservice.ssl',
83     :default => false,
84     :requires_rescan => true,
85     :desc => 'Whether the web server should use SSL (recommended!)')
86
87   Config.register Config::StringValue.new('webservice.ssl_key',
88     :default => '~/.rbot/wskey.pem',
89     :requires_rescan => true,
90     :desc => 'Private key file to use for SSL')
91
92   Config.register Config::StringValue.new('webservice.ssl_cert',
93     :default => '~/.rbot/wscert.pem',
94     :requires_rescan => true,
95     :desc => 'Certificate file to use for SSL')
96
97   def initialize
98     super
99     @port = @bot.config['webservice.port']
100     @host = @bot.config['webservice.host']
101     @server = nil
102     begin
103       start_service if @bot.config['webservice.autostart']
104     rescue => e
105       error "couldn't start web service provider: #{e.inspect}"
106     end
107   end
108
109   def start_service
110     raise "Remote service provider already running" if @server
111     opts = {:BindAddress => @host, :Port => @port}
112     if @bot.config['webservice.ssl']
113       opts.merge! :SSLEnable => true
114       cert = File.expand_path @bot.config['webservice.ssl_cert']
115       key = File.expand_path @bot.config['webservice.ssl_key']
116       if File.exists? cert and File.exists? key
117         debug 'using ssl certificate files'
118         opts.merge!({
119           :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(cert)),
120           :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(key))
121         })
122       else
123         debug 'using on-the-fly generated ssl certs'
124         opts.merge! :SSLCertName => [ %w[CN localhost] ]
125         # the problem with this is that it will always use the same
126         # serial number which makes this feature pretty much useless.
127       end
128     end
129     @server = WEBrick::HTTPServer.new(opts)
130     debug 'webservice started: ' + opts.inspect
131     @server.mount('/dispatch', DispatchServlet, @bot)
132     Thread.new { @server.start }
133   end
134
135   def stop_service
136     @server.shutdown if @server
137     @server = nil
138   end
139
140   def cleanup
141     stop_service
142     super
143   end
144
145   def handle_start(m, params)
146     s = ''
147     if @server
148       s << 'web service already running'
149     else
150       begin
151         start_service
152         s << 'web service started'
153       rescue
154         s << 'unable to start web service, error: ' + $!.to_s
155       end
156     end
157     m.reply s
158   end
159
160 end
161
162 webservice = WebServiceModule.new
163
164 webservice.map 'webservice start',
165   :action => 'handle_start',
166   :auth_path => ':manage:'
167
168 webservice.map 'webservice stop',
169   :action => 'handle_stop',
170   :auth_path => ':manage:'
171
172 webservice.default_auth('*', false)
173