tkellem 0.8.11 → 0.9.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/lib/tkellem/bouncer.rb +50 -15
- data/lib/tkellem/bouncer_connection.rb +46 -60
- data/lib/tkellem/celluloid_tools.rb +131 -0
- data/lib/tkellem/daemon.rb +3 -7
- data/lib/tkellem/irc_message.rb +1 -1
- data/lib/tkellem/migrations/001_init_db.rb +39 -22
- data/lib/tkellem/migrations/002_at_connect_columns.rb +21 -4
- data/lib/tkellem/migrations/003_settings.rb +18 -9
- data/lib/tkellem/models/host.rb +2 -2
- data/lib/tkellem/models/listen_address.rb +13 -1
- data/lib/tkellem/models/network.rb +11 -7
- data/lib/tkellem/models/network_user.rb +22 -6
- data/lib/tkellem/models/setting.rb +3 -3
- data/lib/tkellem/models/user.rb +13 -6
- data/lib/tkellem/plugins/backlog.rb +45 -27
- data/lib/tkellem/socket_server.rb +15 -7
- data/lib/tkellem/tkellem_bot.rb +11 -19
- data/lib/tkellem/tkellem_server.rb +84 -61
- data/lib/tkellem/version.rb +1 -1
- data/lib/tkellem.rb +1 -10
- data/spec/spec_helper.rb +5 -18
- data/tkellem.gemspec +6 -4
- metadata +56 -28
- data/lib/tkellem/irc_server.rb +0 -124
- data/spec/bouncer_connection_spec.rb +0 -37
- data/spec/irc_server_spec.rb +0 -145
data/Gemfile
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
|
5
|
+
gem 'celluloid', :git => 'git://github.com/celluloid/celluloid'
|
6
|
+
gem 'celluloid-io', :git => 'https://github.com/celluloid/celluloid-io.git'
|
7
|
+
gem 'activesupport', '3.2.10'
|
8
|
+
gem 'sequel', '3.42.0'
|
9
|
+
gem 'sqlite3', '1.3.6'
|
10
|
+
gem 'rspec', '2.5'
|
11
|
+
|
3
12
|
# Specify your gem's dependencies in tkellem.gemspec
|
4
13
|
gemspec
|
data/lib/tkellem/bouncer.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute_accessors'
|
2
|
+
require 'celluloid/io'
|
2
3
|
|
3
|
-
require 'tkellem/irc_server'
|
4
4
|
require 'tkellem/bouncer_connection'
|
5
5
|
|
6
6
|
module Tkellem
|
7
7
|
|
8
8
|
class Bouncer
|
9
|
+
include Celluloid::IO
|
9
10
|
include Tkellem::EasyLogger
|
11
|
+
include Tkellem::CelluloidTools::LineReader
|
10
12
|
|
11
13
|
attr_reader :user, :network, :nick, :network_user, :connected_at
|
12
14
|
cattr_accessor :plugins
|
13
15
|
self.plugins = []
|
14
16
|
|
17
|
+
class Room < Struct.new(:name, :topic, :topic_setter, :topic_time); end
|
18
|
+
|
15
19
|
def initialize(network_user)
|
16
20
|
@network_user = network_user
|
17
21
|
@user = network_user.user
|
@@ -21,7 +25,7 @@ class Bouncer
|
|
21
25
|
# maps { client_conn => state_hash }
|
22
26
|
@active_conns = {}
|
23
27
|
@welcomes = []
|
24
|
-
@rooms =
|
28
|
+
@rooms = {}
|
25
29
|
# maps { client_conn => away_status_or_nil }
|
26
30
|
@away = {}
|
27
31
|
# plugin data
|
@@ -29,7 +33,7 @@ class Bouncer
|
|
29
33
|
# clients waiting for us to connect to the irc server
|
30
34
|
@waiting_clients = []
|
31
35
|
|
32
|
-
connect
|
36
|
+
async.connect
|
33
37
|
end
|
34
38
|
|
35
39
|
def data(key)
|
@@ -62,7 +66,7 @@ class Bouncer
|
|
62
66
|
client.send_msg(":#{client.connecting_nick} NICK #{nick}") if client.connecting_nick != nick
|
63
67
|
send_welcome(client)
|
64
68
|
# make the client join all the rooms that we're in
|
65
|
-
@rooms.
|
69
|
+
@rooms.each_value { |room| client.simulate_join(room) }
|
66
70
|
|
67
71
|
plugins.each { |plugin| plugin.new_client_connected(self, client) }
|
68
72
|
check_away_status
|
@@ -100,6 +104,13 @@ class Bouncer
|
|
100
104
|
end
|
101
105
|
end
|
102
106
|
|
107
|
+
def receive_line(line)
|
108
|
+
trace "from server: #{line}"
|
109
|
+
return if line.blank?
|
110
|
+
msg = IrcMessage.parse(line)
|
111
|
+
server_msg(msg)
|
112
|
+
end
|
113
|
+
|
103
114
|
def server_msg(msg)
|
104
115
|
return if plugins.any? do |plugin|
|
105
116
|
!plugin.server_msg(self, msg)
|
@@ -112,12 +123,25 @@ class Bouncer
|
|
112
123
|
false
|
113
124
|
when 'JOIN'
|
114
125
|
debug "#{msg.target_user} joined #{msg.args.first}"
|
115
|
-
@rooms
|
126
|
+
@rooms[msg.args.first] = Room.new(msg.args.first) if msg.target_user == @nick
|
116
127
|
true
|
117
128
|
when 'PART'
|
118
129
|
debug "#{msg.target_user} left #{msg.args.first}"
|
119
130
|
@rooms.delete(msg.args.first) if msg.target_user == @nick
|
120
131
|
true
|
132
|
+
when 'TOPIC'
|
133
|
+
if room = @rooms[msg.args.first]
|
134
|
+
room.topic = msg.args.last
|
135
|
+
end
|
136
|
+
when '332' # topic replay
|
137
|
+
if room = @rooms[msg.args[1]]
|
138
|
+
room.topic = msg.args.last
|
139
|
+
end
|
140
|
+
when '333' # topic timestamp
|
141
|
+
if room = @rooms[msg.args[1]]
|
142
|
+
room.topic_setter = msg.args[2]
|
143
|
+
room.topic_time = msg.args[3]
|
144
|
+
end
|
121
145
|
when 'PING'
|
122
146
|
send_msg("PONG tkellem!tkellem :#{msg.args.last}")
|
123
147
|
false
|
@@ -165,22 +189,21 @@ class Bouncer
|
|
165
189
|
alias_method :log_name, :name
|
166
190
|
|
167
191
|
def send_msg(msg)
|
168
|
-
return unless @conn
|
169
192
|
trace "to server: #{msg}"
|
170
|
-
@
|
193
|
+
@socket.write("#{msg}\r\n")
|
171
194
|
end
|
172
195
|
|
173
|
-
def connection_established
|
174
|
-
|
196
|
+
def connection_established
|
197
|
+
debug "Connected"
|
175
198
|
# TODO: support sending a real username, realname, etc
|
176
199
|
send_msg("USER #{@user.username} somehost tkellem :#{@user.name}@tkellem")
|
177
200
|
change_nick(@nick, true)
|
178
201
|
@connected_at = Time.now
|
202
|
+
async.run
|
179
203
|
end
|
180
204
|
|
181
205
|
def disconnected!
|
182
206
|
debug "OMG we got disconnected."
|
183
|
-
@conn = nil
|
184
207
|
@connected = false
|
185
208
|
@connected_at = nil
|
186
209
|
@active_conns.each { |c,s| c.close_connection }
|
@@ -199,14 +222,26 @@ class Bouncer
|
|
199
222
|
@welcomes.each { |msg| msg.args[0] = nick; bouncer_conn.send_msg(msg) }
|
200
223
|
end
|
201
224
|
|
202
|
-
def connect
|
203
|
-
|
204
|
-
|
225
|
+
def connect
|
226
|
+
hosts = network.reload.hosts
|
227
|
+
# random rather than round-robin, not totally ideal
|
228
|
+
# Note: Celluloid::TCPSocket also is random rather than round-robin when
|
229
|
+
# picking the DNS IP record to connect to
|
230
|
+
host = hosts[rand(hosts.length)]
|
231
|
+
begin
|
232
|
+
info "Connecting to #{host}"
|
233
|
+
@socket = TCPSocket.new(host.address, host.port)
|
234
|
+
if host.ssl
|
235
|
+
@socket = SSLSocket.new(@socket)
|
236
|
+
@socket.connect
|
237
|
+
end
|
238
|
+
end
|
239
|
+
connection_established
|
205
240
|
end
|
206
241
|
|
207
242
|
def ready!
|
208
|
-
@rooms.
|
209
|
-
send_msg("JOIN #{room}")
|
243
|
+
@rooms.each_key do |room|
|
244
|
+
send_msg("JOIN #{room.name}")
|
210
245
|
end
|
211
246
|
|
212
247
|
check_away_status
|
@@ -1,19 +1,19 @@
|
|
1
1
|
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'celluloid/io'
|
2
3
|
|
3
|
-
require 'eventmachine'
|
4
4
|
require 'tkellem/irc_message'
|
5
|
+
require 'tkellem/celluloid_tools'
|
5
6
|
|
6
7
|
module Tkellem
|
7
8
|
|
8
|
-
|
9
|
-
include
|
9
|
+
class BouncerConnection
|
10
|
+
include Celluloid::IO
|
10
11
|
include Tkellem::EasyLogger
|
12
|
+
include Tkellem::CelluloidTools::LineReader
|
11
13
|
|
12
|
-
def initialize(tkellem_server,
|
13
|
-
set_delimiter "\r\n"
|
14
|
-
|
15
|
-
@ssl = do_ssl
|
14
|
+
def initialize(tkellem_server, socket)
|
16
15
|
@tkellem = tkellem_server
|
16
|
+
@socket = socket
|
17
17
|
|
18
18
|
@state = :auth
|
19
19
|
@name = 'new-conn'
|
@@ -31,24 +31,11 @@ module BouncerConnection
|
|
31
31
|
@data[key] ||= {}
|
32
32
|
end
|
33
33
|
|
34
|
-
def post_init
|
35
|
-
failsafe(:post_init) do
|
36
|
-
if ssl
|
37
|
-
start_tls :verify_peer => false
|
38
|
-
else
|
39
|
-
ssl_handshake_completed
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def ssl_handshake_completed
|
45
|
-
end
|
46
|
-
|
47
34
|
def error!(msg)
|
48
35
|
info("ERROR :#{msg}")
|
49
36
|
say_as_tkellem(msg)
|
50
37
|
send_msg("ERROR :#{msg}")
|
51
|
-
close_connection
|
38
|
+
close_connection
|
52
39
|
end
|
53
40
|
|
54
41
|
def connect_to_irc_server
|
@@ -90,40 +77,38 @@ module BouncerConnection
|
|
90
77
|
end
|
91
78
|
|
92
79
|
def receive_line(line)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
send_msg("CAP NAK")
|
107
|
-
end
|
108
|
-
elsif command == 'PASS' && @state == :auth
|
109
|
-
@password = msg.args.first
|
110
|
-
elsif command == 'NICK' && @state == :auth
|
111
|
-
@connecting_nick = msg.args.first
|
112
|
-
maybe_connect
|
113
|
-
elsif command == 'QUIT'
|
114
|
-
close_connection
|
115
|
-
elsif command == 'USER' && @state == :auth
|
116
|
-
unless @username
|
117
|
-
@username, @conn_info = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
118
|
-
end
|
119
|
-
maybe_connect
|
120
|
-
elsif @state == :auth
|
121
|
-
error!("Protocol error. You must authenticate first.")
|
122
|
-
elsif @state == :connected
|
123
|
-
@bouncer.client_msg(self, msg)
|
124
|
-
else
|
125
|
-
say_as_tkellem("You must connect to an irc network to do that.")
|
80
|
+
trace "from client: #{line}"
|
81
|
+
return if line.blank?
|
82
|
+
msg = IrcMessage.parse(line)
|
83
|
+
|
84
|
+
command = msg.command
|
85
|
+
if @state != :auth && command == 'PRIVMSG' && msg.args.first == '-tkellem'
|
86
|
+
msg_tkellem(IrcMessage.new(nil, 'TKELLEM', [msg.args.last]))
|
87
|
+
elsif command == 'TKELLEM' || command == 'TK'
|
88
|
+
msg_tkellem(msg)
|
89
|
+
elsif command == 'CAP'
|
90
|
+
# TODO: full support for CAP -- this just gets mobile colloquy connecting
|
91
|
+
if msg.args.first =~ /req/i
|
92
|
+
send_msg("CAP NAK")
|
126
93
|
end
|
94
|
+
elsif command == 'PASS' && @state == :auth
|
95
|
+
@password = msg.args.first
|
96
|
+
elsif command == 'NICK' && @state == :auth
|
97
|
+
@connecting_nick = msg.args.first
|
98
|
+
maybe_connect
|
99
|
+
elsif command == 'QUIT'
|
100
|
+
close_connection
|
101
|
+
elsif command == 'USER' && @state == :auth
|
102
|
+
unless @username
|
103
|
+
@username, @conn_info = msg.args.first.strip.split('@', 2).map { |a| a.downcase }
|
104
|
+
end
|
105
|
+
maybe_connect
|
106
|
+
elsif @state == :auth
|
107
|
+
error!("Protocol error. You must authenticate first.")
|
108
|
+
elsif @state == :connected
|
109
|
+
@bouncer.client_msg(self, msg)
|
110
|
+
else
|
111
|
+
say_as_tkellem("You must connect to an irc network to do that.")
|
127
112
|
end
|
128
113
|
end
|
129
114
|
|
@@ -188,21 +173,22 @@ module BouncerConnection
|
|
188
173
|
end
|
189
174
|
|
190
175
|
def simulate_join(room)
|
191
|
-
send_msg(":#{nick} JOIN #{room}")
|
176
|
+
send_msg(":#{nick} JOIN #{room.name}")
|
192
177
|
# TODO: intercept the NAMES response so that only this bouncer gets it
|
193
178
|
# Otherwise other clients might show an "in this room" line.
|
194
|
-
@bouncer.send_msg("NAMES #{room}\r\n")
|
179
|
+
@bouncer.send_msg("NAMES #{room.name}\r\n")
|
180
|
+
send_msg(IrcMessage.new(":tkellem", "332", [nick, room.name, room.topic])) if room.topic
|
181
|
+
send_msg(IrcMessage.new(":tkellem", "333", [nick, room.name, room.topic_setter, room.topic_time])) if room.topic_setter && room.topic_time
|
195
182
|
end
|
196
183
|
|
197
184
|
def send_msg(msg)
|
185
|
+
return if @socket.closed?
|
198
186
|
trace "to client: #{msg}"
|
199
|
-
|
187
|
+
@socket.write("#{msg}\r\n")
|
200
188
|
end
|
201
189
|
|
202
190
|
def unbind
|
203
|
-
|
204
|
-
@bouncer.disconnect_client(self) if @bouncer
|
205
|
-
end
|
191
|
+
@bouncer.try(:disconnect_client, self)
|
206
192
|
end
|
207
193
|
end
|
208
194
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'celluloid/io'
|
3
|
+
|
4
|
+
module Tkellem
|
5
|
+
module CelluloidTools
|
6
|
+
|
7
|
+
# Generates a new SSL context with a new cert and key
|
8
|
+
# Great for easily getting up and running, but not necessarily a good idea for
|
9
|
+
# production use
|
10
|
+
def self.generate_ssl_ctx
|
11
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
12
|
+
|
13
|
+
dn = OpenSSL::X509::Name.parse("/CN=tkellem-auto")
|
14
|
+
cert = OpenSSL::X509::Certificate.new
|
15
|
+
cert.version = 2
|
16
|
+
cert.serial = 1
|
17
|
+
cert.subject = dn
|
18
|
+
cert.issuer = dn
|
19
|
+
cert.public_key = key.public_key
|
20
|
+
cert.not_before = Time.now
|
21
|
+
cert.not_after = Time.now + 94670777 # 3 years
|
22
|
+
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
23
|
+
|
24
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
25
|
+
ctx.key = key
|
26
|
+
ctx.cert = cert
|
27
|
+
|
28
|
+
ctx
|
29
|
+
end
|
30
|
+
|
31
|
+
class BackoffSupervisor < ::Celluloid::SupervisionGroup
|
32
|
+
attr_reader :registry
|
33
|
+
|
34
|
+
def restart_actor(actor, reason)
|
35
|
+
member = @members.find do |member|
|
36
|
+
member.actor == actor
|
37
|
+
end
|
38
|
+
raise "a group member went missing. This shouldn't be!" unless member
|
39
|
+
|
40
|
+
if reason
|
41
|
+
end
|
42
|
+
|
43
|
+
member.restart(reason)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Listener < Struct.new(:server, :callback)
|
48
|
+
include Celluloid::IO
|
49
|
+
|
50
|
+
def self.start(*args, &callback)
|
51
|
+
listener = self.new(*args)
|
52
|
+
listener.callback = callback
|
53
|
+
listener.async.run
|
54
|
+
listener
|
55
|
+
end
|
56
|
+
|
57
|
+
def run
|
58
|
+
loop { handle_connection(server.accept) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_connection(socket)
|
62
|
+
callback.(socket)
|
63
|
+
end
|
64
|
+
|
65
|
+
finalizer :close
|
66
|
+
|
67
|
+
def close
|
68
|
+
server.try(:close) unless server.try(:closed?)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class TCPListener < Listener
|
73
|
+
def initialize(host, port)
|
74
|
+
self.server = TCPServer.new(host, port)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class SSLListener < Listener
|
79
|
+
def initialize(host, port)
|
80
|
+
self.server = SSLServer.new(TCPServer.new(host, port), CelluloidTools.generate_ssl_ctx)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class UnixListener < Listener
|
85
|
+
def initialize(socket_path)
|
86
|
+
self.server = UNIXServer.new(socket_path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module LineReader
|
91
|
+
def self.included(k)
|
92
|
+
k.send :finalizer, :close_connection
|
93
|
+
end
|
94
|
+
|
95
|
+
def readline
|
96
|
+
@delimiter ||= "\r\n"
|
97
|
+
@readline_buffer ||= ''
|
98
|
+
loop do
|
99
|
+
if idx = @readline_buffer.index(@delimiter)
|
100
|
+
postidx = idx + @delimiter.size
|
101
|
+
line = @readline_buffer[0, postidx]
|
102
|
+
@readline_buffer = @readline_buffer[postidx..-1]
|
103
|
+
return line
|
104
|
+
else
|
105
|
+
@socket.readpartial(4096, @readline_buffer)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def close_connection
|
111
|
+
@socket.close if @socket && !@socket.closed?
|
112
|
+
end
|
113
|
+
|
114
|
+
def run
|
115
|
+
loop do
|
116
|
+
line = readline
|
117
|
+
receive_line(line)
|
118
|
+
end
|
119
|
+
rescue EOFError, IOError, OpenSSL::SSL::SSLError
|
120
|
+
unbind
|
121
|
+
end
|
122
|
+
|
123
|
+
def receive_line(line)
|
124
|
+
end
|
125
|
+
|
126
|
+
def unbind
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/lib/tkellem/daemon.rb
CHANGED
@@ -2,7 +2,6 @@ require 'fileutils'
|
|
2
2
|
require 'optparse'
|
3
3
|
|
4
4
|
require 'tkellem'
|
5
|
-
require 'tkellem/socket_server'
|
6
5
|
|
7
6
|
class Tkellem::Daemon
|
8
7
|
attr_reader :options
|
@@ -86,11 +85,8 @@ class Tkellem::Daemon
|
|
86
85
|
end
|
87
86
|
|
88
87
|
def start
|
89
|
-
|
90
|
-
|
91
|
-
@admin = EM.start_unix_domain_server(socket_file, Tkellem::SocketServer)
|
92
|
-
Tkellem::TkellemServer.new
|
93
|
-
end
|
88
|
+
remove_files
|
89
|
+
Tkellem::TkellemServer.new(options).run
|
94
90
|
ensure
|
95
91
|
remove_files
|
96
92
|
end
|
@@ -138,7 +134,7 @@ class Tkellem::Daemon
|
|
138
134
|
end
|
139
135
|
|
140
136
|
def remove_files
|
141
|
-
FileUtils.rm(socket_file)
|
137
|
+
FileUtils.rm(socket_file) if File.exist?(socket_file)
|
142
138
|
return unless @daemon
|
143
139
|
pid = File.read(pid_file) if File.file?(pid_file)
|
144
140
|
if pid.to_i == Process.pid
|
data/lib/tkellem/irc_message.rb
CHANGED
@@ -20,7 +20,7 @@ class IrcMessage < Struct.new(:prefix, :command, :args, :ctcp)
|
|
20
20
|
|
21
21
|
msg = self.new(prefix, command, args)
|
22
22
|
|
23
|
-
if args.last.
|
23
|
+
if args.last && args.last.match(%r{#{"\x01"}([^ ]+)([^\1]*)#{"\x01"}})
|
24
24
|
msg.ctcp = $1.upcase
|
25
25
|
msg.args[-1] = $2.strip
|
26
26
|
end
|
@@ -1,33 +1,50 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
t.integer 'port', :null => false
|
6
|
-
t.boolean 'ssl', :null => false, :default => false
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '1')
|
4
|
+
next
|
7
5
|
end
|
8
6
|
|
9
|
-
create_table '
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
create_table 'listen_addresses' do
|
8
|
+
primary_key :id
|
9
|
+
String 'address', :null => false
|
10
|
+
Integer 'port', :null => false
|
11
|
+
boolean 'ssl', :null => false, :default => false
|
13
12
|
end
|
14
13
|
|
15
|
-
create_table '
|
16
|
-
|
17
|
-
|
14
|
+
create_table 'users' do
|
15
|
+
primary_key :id
|
16
|
+
String 'username', :null => false
|
17
|
+
String 'password'
|
18
|
+
String 'role', :null => false, :default => 'user'
|
18
19
|
end
|
19
20
|
|
20
|
-
create_table '
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
t.boolean 'ssl', :null => false, :default => false
|
21
|
+
create_table 'networks' do
|
22
|
+
primary_key :id
|
23
|
+
foreign_key :user_id, :users
|
24
|
+
String 'name', :null => false
|
25
25
|
end
|
26
26
|
|
27
|
-
create_table '
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
create_table 'hosts' do
|
28
|
+
primary_key :id
|
29
|
+
foreign_key :network_id, :networks
|
30
|
+
String 'address', :null => false
|
31
|
+
Integer 'port', :null => false
|
32
|
+
boolean 'ssl', :null => false, :default => false
|
31
33
|
end
|
34
|
+
|
35
|
+
create_table 'network_users' do
|
36
|
+
primary_key :id
|
37
|
+
foreign_key :user_id, :users
|
38
|
+
foreign_key :network_id, :networks
|
39
|
+
String 'nick'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
down do
|
44
|
+
drop_table 'network_users'
|
45
|
+
drop_table 'hosts'
|
46
|
+
drop_table 'networks'
|
47
|
+
drop_table 'users'
|
48
|
+
drop_table 'listen_addresses'
|
32
49
|
end
|
33
50
|
end
|
@@ -1,7 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '2')
|
4
|
+
next
|
5
|
+
end
|
6
|
+
|
7
|
+
alter_table 'networks' do
|
8
|
+
add_column 'at_connect', String, :text => true
|
9
|
+
end
|
10
|
+
alter_table 'network_users' do
|
11
|
+
add_column 'at_connect', String, :text => true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
down do
|
16
|
+
alter_table 'network_users' do
|
17
|
+
drop_column 'at_connect'
|
18
|
+
end
|
19
|
+
alter_table 'networks' do
|
20
|
+
drop_column 'at_connect'
|
21
|
+
end
|
5
22
|
end
|
6
23
|
end
|
7
24
|
|
@@ -1,13 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
t.string :value, :null => false
|
6
|
-
t.boolean :unchanged, :null => false, :default => true
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
if self.table_exists?(:schema_migrations) && self[:schema_migrations].first(:version => '3')
|
4
|
+
next
|
7
5
|
end
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
create_table 'settings' do
|
8
|
+
primary_key :id
|
9
|
+
String :name, :null => false
|
10
|
+
String :value, :null => false
|
11
|
+
boolean :unchanged, :null => false, :default => true
|
12
|
+
end
|
13
|
+
|
14
|
+
self[:settings].insert(:name => 'user_registration', :value => 'closed')
|
15
|
+
self[:settings].insert(:name => 'recaptcha_api_key', :value => '')
|
16
|
+
self[:settings].insert(:name => 'allow_user_networks', :value => 'false')
|
17
|
+
end
|
18
|
+
|
19
|
+
down do
|
20
|
+
drop_table 'settings'
|
12
21
|
end
|
13
22
|
end
|
data/lib/tkellem/models/host.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
module Tkellem
|
2
2
|
|
3
|
-
class ListenAddress <
|
3
|
+
class ListenAddress < Sequel::Model
|
4
|
+
plugin :validation_class_methods
|
5
|
+
|
4
6
|
validates_uniqueness_of :port, :scope => [:address]
|
5
7
|
validates_presence_of :address, :port
|
6
8
|
|
7
9
|
def to_s
|
8
10
|
"#{ssl ? 'ircs' : 'irc'}://#{address}:#{port}"
|
9
11
|
end
|
12
|
+
|
13
|
+
def after_create
|
14
|
+
super
|
15
|
+
$tkellem_server.try(:after_create, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_destroy
|
19
|
+
super
|
20
|
+
$tkellem_server.try(:after_destroy, self)
|
21
|
+
end
|
10
22
|
end
|
11
23
|
|
12
24
|
end
|