tkellem 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|