027e552cd99c6bdf9cb26dc4d670e1d7c94f57ae
[riece] / lisp / rubyserv.rb
1 # A simple IPC server for executing arbitrary Ruby program.
2 # The protocol is based on Assuan protocol of GnuPG.
3 # http://www.gnupg.org/(en)/related_software/libassuan/index.html
4
5 require 'thread'
6 require 'stringio'
7
8 class RubyServ
9   def initialize
10     @buf = StringIO.new
11     @que = Queue.new
12   end
13
14   def dispatch(line)
15     case line.chomp
16     when /\AD /
17       return @buf << unescape($')
18     when /\A(\S+)\s*/
19       c = $1
20       r = $'
21       d = "dispatch_#{c.downcase}"
22       if respond_to?(d, true)
23         Thread.start do
24           self.send(d, c, r)
25         end
26       else
27         puts("ERR 103 Unknown command\r\n")
28       end
29     end
30   end
31
32   def dispatch_cancel(c, r)
33     puts("ERR 100 Not implemented\r\n")
34   end
35
36   def dispatch_bye(c, r)
37     puts("ERR 100 Not implemented\r\n")
38   end
39
40   def dispatch_auth(c, r)
41     puts("ERR 100 Not implemented\r\n")
42   end
43
44   def dispatch_reset(c, r)
45     puts("ERR 100 Not implemented\r\n")
46   end
47
48   def dispatch_end(c, r)
49     enq_data
50   end
51
52   def dispatch_help(c, r)
53     puts("ERR 100 Not implemented\r\n")
54   end
55
56   def dispatch_quit(c, r)
57     puts("ERR 100 Not implemented\r\n")
58   end
59
60   def dispatch_eval(c, r)
61     r = deq_data if r.empty?
62     p r
63     open('|-') do |f|
64       if f
65         d = f.read
66         Process.wait
67         send_data(d)
68         if $?.success?
69           puts("OK\r\n")
70         else
71           puts("ERR #{$?.exitstatus}\r\n")
72         end
73       else
74         eval(r)
75         exit
76       end
77     end
78   end
79
80   def escape(s)
81     s.gsub(/[%\r\n]/) {|m| '%%%02X' % m[0]}
82   end
83
84   def unescape(s)
85     s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
86   end
87
88   def send_data(d)
89     begin
90       r = [d.length, 998].min   # 998 = 1000 - CRLF
91       (0 ... r).each do |i|
92         r -= 2 if d[i] =~ /[%\r\n]/
93       end
94       puts("D #{escape(d[0 ... r])}\r\n")
95       d = d[r .. -1]
96     end until d.empty?
97   end
98
99   def enq_data
100     @que.enq(@buf.string)
101   end
102
103   def deq_data
104     @que.deq
105   end
106 end
107
108 if $0 == __FILE__
109   serv = RubyServ.new
110   while gets
111     serv.dispatch($_)
112   end
113 end