tkellem 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/README.md +28 -6
- data/Rakefile +14 -5
- data/bin/tkellem +2 -107
- data/debian/changelog +5 -0
- data/debian/compat +1 -0
- data/debian/control +18 -0
- data/debian/copyright +42 -0
- data/debian/docs +1 -0
- data/debian/examples +1 -0
- data/debian/install +3 -0
- data/debian/manpages +1 -0
- data/debian/rules +13 -0
- data/debian/source/format +1 -0
- data/debian/tkellem.1 +11 -0
- data/examples/config.yml +2 -29
- data/lib/tkellem/bouncer.rb +196 -31
- data/lib/tkellem/bouncer_connection.rb +90 -85
- data/lib/tkellem/daemon.rb +155 -0
- data/lib/tkellem/irc_message.rb +58 -0
- data/lib/tkellem/irc_server.rb +8 -121
- data/lib/tkellem/migrations/001_init_db.rb +33 -0
- data/lib/tkellem/models/host.rb +11 -0
- data/lib/tkellem/models/listen_address.rb +12 -0
- data/lib/tkellem/models/network.rb +15 -0
- data/lib/tkellem/models/network_user.rb +12 -0
- data/lib/tkellem/models/user.rb +56 -0
- data/lib/tkellem/plugins/backlog.rb +160 -0
- data/lib/tkellem/plugins/push_service.rb +152 -0
- data/lib/tkellem/socket_server.rb +29 -0
- data/lib/tkellem/tkellem_bot.rb +318 -0
- data/lib/tkellem/tkellem_server.rb +114 -0
- data/lib/tkellem/version.rb +3 -0
- data/lib/tkellem.rb +7 -10
- data/resources/bot_command_descriptions.yml +36 -0
- data/spec/irc_message_spec.rb +47 -0
- data/spec/irc_server_spec.rb +60 -0
- data/spec/spec_helper.rb +0 -2
- data/tkellem.gemspec +20 -47
- metadata +118 -22
- data/VERSION +0 -1
- data/lib/tkellem/backlog.rb +0 -85
- data/lib/tkellem/irc_line.rb +0 -58
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
1
3
|
require 'eventmachine'
|
2
|
-
require 'tkellem/
|
3
|
-
require 'tkellem/backlog'
|
4
|
+
require 'tkellem/irc_message'
|
4
5
|
|
5
6
|
module Tkellem
|
6
7
|
|
@@ -8,37 +9,36 @@ module BouncerConnection
|
|
8
9
|
include EM::Protocols::LineText2
|
9
10
|
include Tkellem::EasyLogger
|
10
11
|
|
11
|
-
def initialize(
|
12
|
+
def initialize(tkellem_server, do_ssl)
|
12
13
|
set_delimiter "\r\n"
|
13
14
|
|
14
15
|
@ssl = do_ssl
|
15
|
-
@
|
16
|
+
@tkellem = tkellem_server
|
16
17
|
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@conn_name = nil
|
21
|
-
@name = nil
|
18
|
+
@state = :auth
|
19
|
+
@name = 'new-conn'
|
20
|
+
@data = {}
|
22
21
|
end
|
23
|
-
attr_reader :ssl, :
|
22
|
+
attr_reader :ssl, :bouncer, :name, :device_name
|
23
|
+
alias_method :log_name, :name
|
24
24
|
|
25
|
-
def
|
26
|
-
|
25
|
+
def nick
|
26
|
+
@bouncer ? @bouncer.nick : @nick
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
@
|
29
|
+
def data(key)
|
30
|
+
@data[key] ||= {}
|
31
31
|
end
|
32
32
|
|
33
33
|
def post_init
|
34
34
|
if ssl
|
35
|
-
debug "starting TLS"
|
36
35
|
start_tls :verify_peer => false
|
36
|
+
else
|
37
|
+
ssl_handshake_completed
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
41
|
def ssl_handshake_completed
|
41
|
-
debug "TLS complete"
|
42
42
|
end
|
43
43
|
|
44
44
|
def error!(msg)
|
@@ -47,89 +47,98 @@ module BouncerConnection
|
|
47
47
|
close_connection(true)
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
51
|
-
@
|
52
|
-
|
53
|
-
|
54
|
-
return
|
55
|
-
end
|
56
|
-
|
57
|
-
unless bouncer.do_auth(@nick, @password, irc_server)
|
58
|
-
error!("bad auth, please check your password")
|
59
|
-
@irc_server = @conn_name = @name = @backlog = nil
|
60
|
-
return
|
61
|
-
end
|
62
|
-
|
63
|
-
@conn_name = conn_name
|
64
|
-
@name = client_name
|
65
|
-
@backlog = irc_server.bouncer_connect(self)
|
66
|
-
unless backlog
|
67
|
-
error!("unknown client #{client_name}")
|
68
|
-
@irc_server = @conn_name = @name = nil
|
69
|
-
return
|
70
|
-
end
|
71
|
-
|
50
|
+
def connect_to_irc_server
|
51
|
+
@bouncer = @tkellem.find_bouncer(@user, @conn_name)
|
52
|
+
return error!("Unknown connection: #{@conn_name}") unless @bouncer
|
53
|
+
@state = :connected
|
72
54
|
info "connected"
|
73
|
-
|
74
|
-
irc_server.send_welcome(self)
|
75
|
-
backlog.send_backlog(self)
|
76
|
-
irc_server.rooms.each { |room| simulate_join(room) }
|
55
|
+
@bouncer.connect_client(self)
|
77
56
|
end
|
78
57
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
send_msg(":tkellem!tkellem@tkellem PRIVMSG #{nick} :Unknown tkellem command #{msg.args.first}")
|
58
|
+
def msg_tkellem(msg)
|
59
|
+
TkellemBot.run_command(msg.args.join(' '), @user) do |response|
|
60
|
+
say_as_tkellem(response)
|
84
61
|
end
|
85
62
|
end
|
86
63
|
|
64
|
+
def say_as_tkellem(message)
|
65
|
+
send_msg(":-tkellem!~tkellem@tkellem PRIVMSG #{nick} :#{message}")
|
66
|
+
end
|
67
|
+
|
87
68
|
def receive_line(line)
|
88
69
|
trace "from client: #{line}"
|
89
|
-
msg =
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
if
|
99
|
-
|
100
|
-
else
|
101
|
-
@nick = msg.last
|
102
|
-
connect(@conn_name, @client_name, @password)
|
70
|
+
msg = IrcMessage.parse(line)
|
71
|
+
|
72
|
+
command = msg.command
|
73
|
+
if @user && command == 'PRIVMSG' && msg.args.first == '-tkellem'
|
74
|
+
msg_tkellem(IrcMessage.new(nil, 'TKELLEM', [msg.args.last]))
|
75
|
+
elsif command == 'TKELLEM'
|
76
|
+
msg_tkellem(msg)
|
77
|
+
elsif command == 'CAP'
|
78
|
+
# TODO: full support for CAP -- this just gets mobile colloquy connecting
|
79
|
+
if msg.args.first =~ /req/i
|
80
|
+
send_msg("CAP NAK")
|
103
81
|
end
|
104
|
-
|
105
|
-
|
82
|
+
elsif command == 'PASS'
|
83
|
+
unless @password
|
84
|
+
@password = msg.args.first
|
85
|
+
end
|
86
|
+
elsif command == 'NICK' && @state == :auth
|
87
|
+
@nick = msg.args.first
|
88
|
+
elsif command == 'QUIT'
|
106
89
|
close_connection
|
107
|
-
|
108
|
-
|
90
|
+
elsif command == 'USER'
|
91
|
+
msg_user(msg)
|
92
|
+
elsif @state == :auth
|
93
|
+
error!("Protocol error. You must authenticate first.")
|
94
|
+
elsif @state == :connected
|
95
|
+
@bouncer.client_msg(self, msg)
|
109
96
|
else
|
110
|
-
|
111
|
-
|
97
|
+
say_as_tkellem("You must connect to an irc network to do that.")
|
98
|
+
end
|
99
|
+
|
100
|
+
rescue => e
|
101
|
+
error "Error handling message: {#{msg}} #{e}"
|
102
|
+
e.backtrace.each { |l| error l }
|
103
|
+
begin
|
104
|
+
error! "Internal Tkellem error."
|
105
|
+
rescue
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def msg_user(msg)
|
110
|
+
unless @user
|
111
|
+
@username, rest = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
112
|
+
@name = @username
|
113
|
+
@user = User.authenticate(@username, @password)
|
114
|
+
return error!("Unknown username: #{@username} or bad password.") unless @user
|
115
|
+
|
116
|
+
if rest && !rest.empty?
|
117
|
+
@conn_name, @device_name = rest.split(':', 2)
|
118
|
+
# 'default' or missing device_name to use the default backlog
|
119
|
+
# pass a device_name to have device-independent backlogs
|
120
|
+
@device_name = @device_name.presence || 'default'
|
121
|
+
@name = "#{@username}-#{@conn_name}"
|
122
|
+
@name += "-#{@device_name}" if @device_name
|
123
|
+
connect_to_irc_server
|
112
124
|
else
|
113
|
-
|
114
|
-
|
125
|
+
@name = "#{@username}-console"
|
126
|
+
connect_to_tkellem_console
|
115
127
|
end
|
116
128
|
end
|
117
129
|
end
|
118
130
|
|
131
|
+
def connect_to_tkellem_console
|
132
|
+
send_msg(":tkellem 001 #{nick} :Welcome to the Tkellem admin console")
|
133
|
+
send_msg(":tkellem 376 #{nick} :End")
|
134
|
+
@state = :console
|
135
|
+
end
|
136
|
+
|
119
137
|
def simulate_join(room)
|
120
|
-
send_msg(":#{
|
138
|
+
send_msg(":#{nick}!#{name}@tkellem JOIN #{room}")
|
121
139
|
# TODO: intercept the NAMES response so that only this bouncer gets it
|
122
140
|
# Otherwise other clients might show an "in this room" line.
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
def transient_response(msg)
|
127
|
-
send_msg(msg)
|
128
|
-
if msg.command == "366"
|
129
|
-
# finished joining this room, let's backlog it
|
130
|
-
debug "got final NAMES for #{msg.args[1]}, sending backlog"
|
131
|
-
backlog.send_backlog(self, msg.args[1])
|
132
|
-
end
|
141
|
+
@bouncer.send_msg("NAMES #{room}\r\n")
|
133
142
|
end
|
134
143
|
|
135
144
|
def send_msg(msg)
|
@@ -137,12 +146,8 @@ module BouncerConnection
|
|
137
146
|
send_data("#{msg}\r\n")
|
138
147
|
end
|
139
148
|
|
140
|
-
def log_name
|
141
|
-
"#{@conn_name}-#{name}"
|
142
|
-
end
|
143
|
-
|
144
149
|
def unbind
|
145
|
-
|
150
|
+
@bouncer.disconnect_client(self) if @bouncer
|
146
151
|
end
|
147
152
|
end
|
148
153
|
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require 'tkellem'
|
5
|
+
require 'tkellem/socket_server'
|
6
|
+
|
7
|
+
class Tkellem::Daemon
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
@args = args
|
12
|
+
@options = {
|
13
|
+
:path => File.expand_path("~/.tkellem/"),
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage #{$0} <command> <options>"
|
20
|
+
opts.separator %{\nWhere <command> is one of:
|
21
|
+
start start the jobs daemon
|
22
|
+
stop stop the jobs daemon
|
23
|
+
run start and run in the foreground
|
24
|
+
restart stop and then start the jobs daemon
|
25
|
+
status show daemon status
|
26
|
+
admin run admin commands as if connected to the tkellem console
|
27
|
+
}
|
28
|
+
|
29
|
+
opts.separator "\n<options>"
|
30
|
+
opts.on("-p", "--path", "Use alternate folder for tkellem data (default #{options[:path]})") { |p| options[:path] = p }
|
31
|
+
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
|
32
|
+
end.parse!(@args)
|
33
|
+
|
34
|
+
FileUtils.mkdir_p(path)
|
35
|
+
File.chmod(0700, path)
|
36
|
+
command = @args.shift
|
37
|
+
case command
|
38
|
+
when 'start'
|
39
|
+
daemonize
|
40
|
+
start
|
41
|
+
when 'stop'
|
42
|
+
stop
|
43
|
+
when 'run'
|
44
|
+
start
|
45
|
+
when 'status'
|
46
|
+
exit(status ? 0 : 1)
|
47
|
+
when 'restart'
|
48
|
+
stop if status(false)
|
49
|
+
while status(false)
|
50
|
+
print "."
|
51
|
+
sleep(0.5)
|
52
|
+
end
|
53
|
+
daemonize
|
54
|
+
start
|
55
|
+
when 'admin'
|
56
|
+
admin
|
57
|
+
else
|
58
|
+
raise("Unknown command: #{command.inspect}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def admin
|
65
|
+
require 'socket'
|
66
|
+
socket = UNIXSocket.new(socket_file)
|
67
|
+
line = @args.join(' ').strip
|
68
|
+
if line.empty?
|
69
|
+
require 'readline'
|
70
|
+
while line = Readline.readline('> ', true)
|
71
|
+
admin_command(line, socket)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
admin_command(line, socket)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def admin_command(line, socket)
|
79
|
+
socket.puts(line)
|
80
|
+
loop do
|
81
|
+
line = socket.readline("\n").chomp
|
82
|
+
puts line
|
83
|
+
if line == "\0"
|
84
|
+
break
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def start
|
90
|
+
trap("INT") { EM.stop }
|
91
|
+
EM.run do
|
92
|
+
@admin = EM.start_unix_domain_server(socket_file, Tkellem::SocketServer)
|
93
|
+
Tkellem::TkellemServer.new
|
94
|
+
end
|
95
|
+
ensure
|
96
|
+
remove_files
|
97
|
+
end
|
98
|
+
|
99
|
+
def daemonize
|
100
|
+
puts "Daemonizing..."
|
101
|
+
exit if fork
|
102
|
+
Process.setsid
|
103
|
+
exit if fork
|
104
|
+
@daemon = true
|
105
|
+
File.open(pid_file, 'wb') { |f| f.write(Process.pid.to_s) }
|
106
|
+
|
107
|
+
# TODO: set up logging
|
108
|
+
STDIN.reopen("/dev/null")
|
109
|
+
STDOUT.reopen("/dev/null")
|
110
|
+
STDERR.reopen(STDOUT)
|
111
|
+
# STDOUT.sync = STDERR.sync = true
|
112
|
+
end
|
113
|
+
|
114
|
+
def stop
|
115
|
+
pid = File.read(pid_file) if File.file?(pid_file)
|
116
|
+
if pid.to_i > 0
|
117
|
+
puts "Stopping tkellem #{pid}..."
|
118
|
+
Process.kill('INT', pid.to_i)
|
119
|
+
else
|
120
|
+
status
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def status(print = true)
|
125
|
+
pid = File.read(pid_file) if File.file?(pid_file)
|
126
|
+
if pid
|
127
|
+
puts "tkellem running, pid: #{pid}" if print
|
128
|
+
else
|
129
|
+
puts "tkellem not running" if print
|
130
|
+
end
|
131
|
+
pid.to_i > 0 ? pid.to_i : nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def remove_files
|
135
|
+
FileUtils.rm(socket_file)
|
136
|
+
return unless @daemon
|
137
|
+
pid = File.read(pid_file) if File.file?(pid_file)
|
138
|
+
if pid.to_i == Process.pid
|
139
|
+
FileUtils.rm(pid_file)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def path
|
144
|
+
options[:path]
|
145
|
+
end
|
146
|
+
|
147
|
+
def pid_file
|
148
|
+
File.join(path, 'tkellem.pid')
|
149
|
+
end
|
150
|
+
|
151
|
+
def socket_file
|
152
|
+
File.join(path, 'tkellem.socket')
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Tkellem
|
2
|
+
|
3
|
+
class IrcMessage < Struct.new(:prefix, :command, :args)
|
4
|
+
RE = %r{(:[^ ]+ )?([^ ]*)(.*)}i
|
5
|
+
|
6
|
+
def self.parse(line)
|
7
|
+
md = RE.match(line) or raise("invalid input: #{line.inspect}")
|
8
|
+
|
9
|
+
prefix = md[1] && md[1][1..-1].strip
|
10
|
+
command = md[2].upcase
|
11
|
+
args = md[3]
|
12
|
+
|
13
|
+
args.strip!
|
14
|
+
idx = args.index(":")
|
15
|
+
if idx && (idx == 0 || args[idx-1] == " "[0])
|
16
|
+
args = args[0...idx].split(' ') + [args[idx+1..-1]]
|
17
|
+
else
|
18
|
+
args = args.split(' ')
|
19
|
+
end
|
20
|
+
|
21
|
+
self.new(prefix, command, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def command?(cmd)
|
25
|
+
@command.downcase == cmd.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def replay
|
29
|
+
line = []
|
30
|
+
line << ":#{prefix}" unless prefix.nil?
|
31
|
+
line << command
|
32
|
+
ext_arg = args.last if args.last && args.last.match(%r{\s})
|
33
|
+
line += ext_arg ? args[0...-1] : args
|
34
|
+
line << ":#{ext_arg}" unless ext_arg.nil?
|
35
|
+
line.join ' '
|
36
|
+
end
|
37
|
+
alias_method :to_s, :replay
|
38
|
+
|
39
|
+
def target_user
|
40
|
+
if prefix && md = %r{^([^!]+)}.match(prefix)
|
41
|
+
md[1]
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def with_timestamp(timestamp)
|
48
|
+
args = self.args
|
49
|
+
if args && args[-1]
|
50
|
+
args = args.dup
|
51
|
+
args[-1] = "#{timestamp.strftime("%H:%M:%S")}> #{args[-1]}"
|
52
|
+
end
|
53
|
+
IrcMessage.new(prefix, command, args)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/tkellem/irc_server.rb
CHANGED
@@ -1,47 +1,23 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'eventmachine'
|
3
|
-
require 'tkellem/
|
3
|
+
require 'tkellem/irc_message'
|
4
4
|
require 'tkellem/bouncer_connection'
|
5
|
-
require 'tkellem/backlog'
|
6
5
|
|
7
6
|
module Tkellem
|
8
7
|
|
9
|
-
module
|
8
|
+
module IrcServerConnection
|
10
9
|
include EM::Protocols::LineText2
|
11
10
|
include Tkellem::EasyLogger
|
12
11
|
|
13
|
-
def initialize(bouncer,
|
12
|
+
def initialize(bouncer, do_ssl)
|
14
13
|
set_delimiter "\r\n"
|
15
|
-
|
16
14
|
@bouncer = bouncer
|
17
|
-
@name = name
|
18
15
|
@ssl = do_ssl
|
19
|
-
@nick = nick
|
20
|
-
|
21
|
-
@max_backlog = nil
|
22
|
-
@connected = false
|
23
|
-
@welcomes = []
|
24
|
-
@rooms = Set.new
|
25
|
-
@backlogs = {}
|
26
|
-
@active_conns = []
|
27
|
-
@joined_rooms = false
|
28
|
-
@pending_rooms = []
|
29
|
-
end
|
30
|
-
attr_reader :name, :backlogs, :welcomes, :rooms, :nick, :active_conns
|
31
|
-
alias_method :log_name, :name
|
32
|
-
|
33
|
-
def connected?
|
34
|
-
@connected
|
35
|
-
end
|
36
|
-
|
37
|
-
def set_max_backlog(max_backlog)
|
38
|
-
@max_backlog = max_backlog
|
39
|
-
backlogs.each { |name, backlog| backlog.max_backlog = max_backlog }
|
40
16
|
end
|
41
17
|
|
42
18
|
def post_init
|
43
19
|
if @ssl
|
44
|
-
debug "starting TLS"
|
20
|
+
@bouncer.debug "starting TLS"
|
45
21
|
# TODO: support strict cert checks
|
46
22
|
start_tls :verify_peer => false
|
47
23
|
else
|
@@ -50,106 +26,17 @@ module IrcServer
|
|
50
26
|
end
|
51
27
|
|
52
28
|
def ssl_handshake_completed
|
53
|
-
|
54
|
-
send_msg("USER #{nick} localhost blah :#{nick}")
|
55
|
-
change_nick(nick, true)
|
29
|
+
EM.next_tick { @bouncer.connection_established }
|
56
30
|
end
|
57
31
|
|
58
32
|
def receive_line(line)
|
59
33
|
trace "from server: #{line}"
|
60
|
-
msg =
|
61
|
-
|
62
|
-
case msg.command
|
63
|
-
when /0\d\d/, /2[56]\d/, /37[256]/
|
64
|
-
welcomes << msg
|
65
|
-
got_welcome if msg.command == "376" # end of MOTD
|
66
|
-
when /join/i
|
67
|
-
debug "#{msg.target_user} joined #{msg.last}"
|
68
|
-
rooms << msg.last if msg.target_user == nick
|
69
|
-
when /part/i
|
70
|
-
debug "#{msg.target_user} left #{msg.last}"
|
71
|
-
rooms.delete(msg.last) if msg.target_user == nick
|
72
|
-
when /ping/i
|
73
|
-
send_msg("PONG #{nick}!tkellem #{msg.args.first}")
|
74
|
-
when /pong/i
|
75
|
-
# swallow it, we handle ping-pong from clients separately, in
|
76
|
-
# BouncerConnection
|
77
|
-
else
|
78
|
-
end
|
79
|
-
|
80
|
-
backlogs.each { |name, backlog| backlog.handle_message(msg) }
|
81
|
-
end
|
82
|
-
|
83
|
-
def got_welcome
|
84
|
-
return if @joined_rooms
|
85
|
-
@joined_rooms = true
|
86
|
-
@pending_rooms.each do |room|
|
87
|
-
join_room(room)
|
88
|
-
end
|
89
|
-
@pending_rooms.clear
|
90
|
-
|
91
|
-
# We're all initialized, allow connections
|
92
|
-
@connected = true
|
93
|
-
end
|
94
|
-
|
95
|
-
def change_nick(new_nick, force = false)
|
96
|
-
return if !force && new_nick == @nick
|
97
|
-
@nick = new_nick
|
98
|
-
send_msg("NICK #{new_nick}")
|
99
|
-
end
|
100
|
-
|
101
|
-
def join_room(room_name)
|
102
|
-
if @joined_rooms
|
103
|
-
send_msg("JOIN #{room_name}")
|
104
|
-
else
|
105
|
-
@pending_rooms << room_name
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def add_client(name)
|
110
|
-
return if backlogs[name]
|
111
|
-
backlog = Backlog.new(name, @max_backlog)
|
112
|
-
backlogs[name] = backlog
|
113
|
-
end
|
114
|
-
|
115
|
-
def remove_client(name)
|
116
|
-
backlog = backlogs.delete(name)
|
117
|
-
if backlog
|
118
|
-
backlog.active_conns.each do |conn|
|
119
|
-
conn.error!("client removed")
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def send_msg(msg)
|
125
|
-
trace "to server: #{msg}"
|
126
|
-
send_data("#{msg}\r\n")
|
127
|
-
end
|
128
|
-
|
129
|
-
def send_welcome(bouncer_conn)
|
130
|
-
welcomes.each { |msg| bouncer_conn.send_msg(msg) }
|
34
|
+
msg = IrcMessage.parse(line)
|
35
|
+
@bouncer.server_msg(msg)
|
131
36
|
end
|
132
37
|
|
133
38
|
def unbind
|
134
|
-
|
135
|
-
# TODO: reconnect if desired. but not if this server was explicitly shut
|
136
|
-
# down or removed.
|
137
|
-
backlogs.keys.each { |name| remove_client(name) }
|
138
|
-
end
|
139
|
-
|
140
|
-
def bouncer_connect(bouncer_conn)
|
141
|
-
return nil unless backlogs[bouncer_conn.name]
|
142
|
-
|
143
|
-
active_conns << bouncer_conn
|
144
|
-
backlogs[bouncer_conn.name].add_conn(bouncer_conn)
|
145
|
-
backlogs[bouncer_conn.name]
|
146
|
-
end
|
147
|
-
|
148
|
-
def bouncer_disconnect(bouncer_conn)
|
149
|
-
return nil unless backlogs[bouncer_conn.name]
|
150
|
-
|
151
|
-
backlogs[bouncer_conn.name].remove_conn(bouncer_conn)
|
152
|
-
active_conns.delete(bouncer_conn)
|
39
|
+
@bouncer.disconnected!
|
153
40
|
end
|
154
41
|
end
|
155
42
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class InitDb < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table 'listen_addresses' do |t|
|
4
|
+
t.string 'address', :null => false
|
5
|
+
t.integer 'port', :null => false
|
6
|
+
t.boolean 'ssl', :null => false, :default => false
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table 'users' do |t|
|
10
|
+
t.string 'username', :null => false
|
11
|
+
t.string 'password'
|
12
|
+
t.string 'role', :null => false, :default => 'user'
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table 'networks' do |t|
|
16
|
+
t.belongs_to 'user'
|
17
|
+
t.string 'name', :null => false
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table 'hosts' do |t|
|
21
|
+
t.belongs_to 'network'
|
22
|
+
t.string 'address', :null => false
|
23
|
+
t.integer 'port', :null => false
|
24
|
+
t.boolean 'ssl', :null => false, :default => false
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table 'network_users' do |t|
|
28
|
+
t.belongs_to 'user'
|
29
|
+
t.belongs_to 'network'
|
30
|
+
t.string 'nick'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Tkellem
|
2
|
+
|
3
|
+
class Network < ActiveRecord::Base
|
4
|
+
has_many :hosts, :dependent => :destroy
|
5
|
+
has_many :network_users, :dependent => :destroy
|
6
|
+
# networks either belong to a specific user, or they are public and any user
|
7
|
+
# can join them.
|
8
|
+
belongs_to :user
|
9
|
+
|
10
|
+
def public?
|
11
|
+
!user
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|