failirc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/failbot +162 -0
- data/bin/failircd +61 -0
- data/lib/failirc.rb +25 -0
- data/lib/failirc/client.rb +227 -0
- data/lib/failirc/client/channel.rb +98 -0
- data/lib/failirc/client/channels.rb +85 -0
- data/lib/failirc/client/client.rb +59 -0
- data/lib/failirc/client/clients.rb +43 -0
- data/lib/failirc/client/dispatcher.rb +209 -0
- data/lib/failirc/client/dispatcher/connectiondispatcher.rb +410 -0
- data/lib/failirc/client/dispatcher/event.rb +113 -0
- data/lib/failirc/client/dispatcher/eventdispatcher.rb +203 -0
- data/lib/failirc/client/module.rb +103 -0
- data/lib/failirc/client/modules/Base.rb +361 -0
- data/lib/failirc/client/modules/Logger.rb +89 -0
- data/lib/failirc/client/server.rb +89 -0
- data/lib/failirc/client/user.rb +66 -0
- data/lib/failirc/client/users.rb +100 -0
- data/lib/failirc/errors.rb +339 -0
- data/lib/failirc/extensions.rb +124 -0
- data/lib/failirc/mask.rb +117 -0
- data/lib/failirc/modes.rb +54 -0
- data/lib/failirc/responses.rb +360 -0
- data/lib/failirc/server.rb +266 -0
- data/lib/failirc/server/channel.rb +122 -0
- data/lib/failirc/server/channels.rb +84 -0
- data/lib/failirc/server/client.rb +100 -0
- data/lib/failirc/server/clients.rb +54 -0
- data/lib/failirc/server/dispatcher.rb +219 -0
- data/lib/failirc/server/dispatcher/connectiondispatcher.rb +477 -0
- data/lib/failirc/server/dispatcher/event.rb +113 -0
- data/lib/failirc/server/dispatcher/eventdispatcher.rb +196 -0
- data/lib/failirc/server/link.rb +50 -0
- data/lib/failirc/server/links.rb +49 -0
- data/lib/failirc/server/module.rb +103 -0
- data/lib/failirc/server/modules/Base.rb +2545 -0
- data/lib/failirc/server/modules/Cloaking.rb +170 -0
- data/lib/failirc/server/modules/Firewall.rb +104 -0
- data/lib/failirc/server/modules/Netlog.rb +67 -0
- data/lib/failirc/server/modules/Roulette.rb +78 -0
- data/lib/failirc/server/modules/TinyURL.rb +98 -0
- data/lib/failirc/server/modules/Translate.rb +62 -0
- data/lib/failirc/server/modules/WordFilter.rb +144 -0
- data/lib/failirc/server/user.rb +72 -0
- data/lib/failirc/server/users.rb +103 -0
- data/lib/failirc/sslutils.rb +74 -0
- data/lib/failirc/utils.rb +53 -0
- metadata +107 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
# failirc, a fail IRC library.
|
2
|
+
#
|
3
|
+
# Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
|
4
|
+
#
|
5
|
+
# This file is part of failirc.
|
6
|
+
#
|
7
|
+
# failirc is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# failirc is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Affero General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with failirc. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'failirc/utils'
|
21
|
+
require 'failirc/modes'
|
22
|
+
require 'failirc/mask'
|
23
|
+
|
24
|
+
require 'failirc/server/channels'
|
25
|
+
|
26
|
+
module IRC
|
27
|
+
|
28
|
+
class Server
|
29
|
+
|
30
|
+
class Client
|
31
|
+
attr_reader :server, :socket, :listen, :ip, :port, :channels, :modes, :mask, :nick, :user, :host, :connectedOn
|
32
|
+
attr_accessor :password, :realName
|
33
|
+
|
34
|
+
def initialize (server, socket, listen=nil)
|
35
|
+
@server = server
|
36
|
+
@socket = socket
|
37
|
+
@listen = listen
|
38
|
+
|
39
|
+
@registered = false
|
40
|
+
|
41
|
+
@channels = Channels.new(@server)
|
42
|
+
@modes = Modes.new
|
43
|
+
|
44
|
+
if socket.is_a?(Mask)
|
45
|
+
@mask = socket
|
46
|
+
else
|
47
|
+
@mask = Mask.new
|
48
|
+
self.host = socket.peeraddr[2]
|
49
|
+
@ip = socket.peeraddr[3]
|
50
|
+
@port = socket.addr[1]
|
51
|
+
|
52
|
+
if socket.is_a?(OpenSSL::SSL::SSLSocket)
|
53
|
+
@modes[:ssl] = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@connectedOn = Time.now
|
58
|
+
end
|
59
|
+
|
60
|
+
def nick= (value)
|
61
|
+
@mask.nick = @nick = value
|
62
|
+
end
|
63
|
+
|
64
|
+
def user= (value)
|
65
|
+
@mask.user = @user = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def host= (value)
|
69
|
+
@mask.host = @host = value
|
70
|
+
end
|
71
|
+
|
72
|
+
def send (symbol, *args)
|
73
|
+
begin
|
74
|
+
self.method(symbol).call(*args)
|
75
|
+
rescue Exception => e
|
76
|
+
self.debug e
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def raw (text)
|
81
|
+
@server.dispatcher.dispatch :output, self, text
|
82
|
+
@server.dispatcher.connection.output.push @socket, text
|
83
|
+
end
|
84
|
+
|
85
|
+
def numeric (response, value=nil)
|
86
|
+
raw ":#{server.host} #{'%03d' % response[:code]} #{nick || 'faggot'} #{eval(response[:text])}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
mask.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
return "#<Client: #{mask} #{modes}#{(modes[:registered]) ? ' registered' : ''}>"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# failirc, a fail IRC library.
|
2
|
+
#
|
3
|
+
# Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
|
4
|
+
#
|
5
|
+
# This file is part of failirc.
|
6
|
+
#
|
7
|
+
# failirc is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# failirc is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Affero General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with failirc. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'failirc/server/client'
|
21
|
+
|
22
|
+
module IRC
|
23
|
+
|
24
|
+
class Server
|
25
|
+
|
26
|
+
class Clients < Hash
|
27
|
+
attr_reader :server
|
28
|
+
|
29
|
+
def initialize (server, *args)
|
30
|
+
@server = server
|
31
|
+
|
32
|
+
super(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def send (*args)
|
36
|
+
each_value {|user|
|
37
|
+
user.send(*args)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
result = ""
|
43
|
+
|
44
|
+
each_value {|client|
|
45
|
+
result << " #{client.inspect}"
|
46
|
+
}
|
47
|
+
|
48
|
+
return result[1, result.length]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# failirc, a fail IRC library.
|
2
|
+
#
|
3
|
+
# Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
|
4
|
+
#
|
5
|
+
# This file is part of failirc.
|
6
|
+
#
|
7
|
+
# failirc is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# failirc is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Affero General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with failirc. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'failirc/server/dispatcher/connectiondispatcher'
|
21
|
+
require 'failirc/server/dispatcher/eventdispatcher'
|
22
|
+
|
23
|
+
module IRC
|
24
|
+
|
25
|
+
class Server
|
26
|
+
|
27
|
+
class Dispatcher
|
28
|
+
attr_reader :server, :connection, :event
|
29
|
+
|
30
|
+
def initialize (server)
|
31
|
+
@server = server
|
32
|
+
|
33
|
+
@connection = ConnectionDispatcher.new(self)
|
34
|
+
@event = EventDispatcher.new(self)
|
35
|
+
|
36
|
+
@intervals = {}
|
37
|
+
@timeouts = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def start
|
41
|
+
@started = true
|
42
|
+
|
43
|
+
@listening = Fiber.new {
|
44
|
+
while true
|
45
|
+
if !@connection.connections.empty?
|
46
|
+
timeout = 0
|
47
|
+
else
|
48
|
+
timeout = 2
|
49
|
+
end
|
50
|
+
|
51
|
+
@connection.accept timeout
|
52
|
+
|
53
|
+
Fiber.yield
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
@reading = Fiber.new {
|
58
|
+
while true
|
59
|
+
@connection.read
|
60
|
+
|
61
|
+
Fiber.yield
|
62
|
+
end
|
63
|
+
}
|
64
|
+
|
65
|
+
@cleaning = Fiber.new {
|
66
|
+
while true
|
67
|
+
@connection.clean
|
68
|
+
|
69
|
+
Fiber.yield
|
70
|
+
end
|
71
|
+
}
|
72
|
+
|
73
|
+
@handling = Fiber.new {
|
74
|
+
while true
|
75
|
+
@connection.handle
|
76
|
+
|
77
|
+
Fiber.yield
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
@writing = Fiber.new {
|
82
|
+
while true
|
83
|
+
@connection.write
|
84
|
+
|
85
|
+
Fiber.yield
|
86
|
+
end
|
87
|
+
}
|
88
|
+
|
89
|
+
@defaults = [@listening, @cleaning, @reading, @handling, @writing]
|
90
|
+
|
91
|
+
self.loop
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop
|
95
|
+
if !@started
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
@started = false
|
100
|
+
@stopping = true
|
101
|
+
|
102
|
+
@event.finalize
|
103
|
+
@connection.finalize
|
104
|
+
|
105
|
+
@stopping = false
|
106
|
+
end
|
107
|
+
|
108
|
+
def loop
|
109
|
+
while true
|
110
|
+
@defaults.each {|fiber|
|
111
|
+
begin
|
112
|
+
fiber.resume
|
113
|
+
rescue FiberError
|
114
|
+
self.debug 'Something went deeply wrong in the dispatcher, aborting.'
|
115
|
+
Process::exit 23
|
116
|
+
rescue Exception => e
|
117
|
+
self.debug e
|
118
|
+
end
|
119
|
+
}
|
120
|
+
|
121
|
+
@intervals.each {|fiber, meta|
|
122
|
+
begin
|
123
|
+
if !@intervals[fiber]
|
124
|
+
raise FiberError
|
125
|
+
end
|
126
|
+
|
127
|
+
if meta[:at] <= Time.now
|
128
|
+
fiber.resume
|
129
|
+
|
130
|
+
meta[:at] += meta[:offset]
|
131
|
+
end
|
132
|
+
rescue FiberError
|
133
|
+
clearInterval meta
|
134
|
+
rescue Exception => e
|
135
|
+
self.debug e
|
136
|
+
end
|
137
|
+
}
|
138
|
+
|
139
|
+
@timeouts.each {|fiber, meta|
|
140
|
+
begin
|
141
|
+
if !@timeouts[fiber]
|
142
|
+
raise FiberError
|
143
|
+
end
|
144
|
+
|
145
|
+
if meta[:at] <= Time.now
|
146
|
+
fiber.resume
|
147
|
+
|
148
|
+
clearTimeout meta
|
149
|
+
end
|
150
|
+
rescue FiberError
|
151
|
+
clearTimeout meta
|
152
|
+
rescue Exception => e
|
153
|
+
self.debug e
|
154
|
+
end
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def setTimeout (fiber, time)
|
160
|
+
@timeouts[fiber] = {
|
161
|
+
:fiber => fiber,
|
162
|
+
:at => Time.now + time,
|
163
|
+
:on => Time.now,
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
def clearTimeout (timeout)
|
168
|
+
@timeouts.delete(timeout[:fiber])
|
169
|
+
end
|
170
|
+
|
171
|
+
def setInterval (fiber, time)
|
172
|
+
@intervals[fiber] = {
|
173
|
+
:fiber => fiber,
|
174
|
+
:offset => time,
|
175
|
+
:at => Time.now + time,
|
176
|
+
:on => Time.now,
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
def clearInterval (interval)
|
181
|
+
@intervals.delete(interval[:fiber])
|
182
|
+
end
|
183
|
+
|
184
|
+
def connections
|
185
|
+
@connection.connections
|
186
|
+
end
|
187
|
+
|
188
|
+
def input
|
189
|
+
@connection.input
|
190
|
+
end
|
191
|
+
|
192
|
+
def output
|
193
|
+
@connection.output
|
194
|
+
end
|
195
|
+
|
196
|
+
def disconnecting
|
197
|
+
@connection.disconnecting
|
198
|
+
end
|
199
|
+
|
200
|
+
def alias (*args)
|
201
|
+
@event.alias(*args)
|
202
|
+
end
|
203
|
+
|
204
|
+
def register (*args)
|
205
|
+
@event.register(*args)
|
206
|
+
end
|
207
|
+
|
208
|
+
def dispatch (*args)
|
209
|
+
@event.dispatch(*args)
|
210
|
+
end
|
211
|
+
|
212
|
+
def execute (*args)
|
213
|
+
@event.execute(*args)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
@@ -0,0 +1,477 @@
|
|
1
|
+
# failirc, a fail IRC library.
|
2
|
+
#
|
3
|
+
# Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
|
4
|
+
#
|
5
|
+
# This file is part of failirc.
|
6
|
+
#
|
7
|
+
# failirc is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# failirc is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Affero General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with failirc. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'thread'
|
21
|
+
require 'socket'
|
22
|
+
require 'openssl/nonblock'
|
23
|
+
|
24
|
+
require 'failirc/utils'
|
25
|
+
require 'failirc/sslutils'
|
26
|
+
|
27
|
+
module IRC
|
28
|
+
|
29
|
+
class Server
|
30
|
+
|
31
|
+
class Dispatcher
|
32
|
+
|
33
|
+
class ConnectionDispatcher
|
34
|
+
class Connections
|
35
|
+
attr_reader :server
|
36
|
+
|
37
|
+
def initialize (server)
|
38
|
+
@server = server
|
39
|
+
|
40
|
+
@data = ThreadSafeHash.new
|
41
|
+
|
42
|
+
@data[:listening] = {
|
43
|
+
:sockets => [],
|
44
|
+
:data => {},
|
45
|
+
}
|
46
|
+
|
47
|
+
@data[:sockets] = []
|
48
|
+
@data[:things] = {}
|
49
|
+
@data[:clients] = CaseInsensitiveHash.new
|
50
|
+
@data[:links] = CaseInsensitiveHash.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def listening
|
54
|
+
@data[:listening]
|
55
|
+
end
|
56
|
+
|
57
|
+
def sockets
|
58
|
+
@data[:sockets]
|
59
|
+
end
|
60
|
+
|
61
|
+
def things
|
62
|
+
@data[:things]
|
63
|
+
end
|
64
|
+
|
65
|
+
def clients
|
66
|
+
@data[:clients]
|
67
|
+
end
|
68
|
+
|
69
|
+
def links
|
70
|
+
@data[:links]
|
71
|
+
end
|
72
|
+
|
73
|
+
def empty?
|
74
|
+
sockets.empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
def exists? (socket)
|
78
|
+
things[socket] ? true : false
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete (socket)
|
82
|
+
if !exists?(socket)
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
thing = @data[:things][socket]
|
87
|
+
|
88
|
+
if thing.is_a?(Client)
|
89
|
+
@data[:clients].delete(thing.nick)
|
90
|
+
@data[:clients].delete(socket)
|
91
|
+
elsif thing.is_a?(Link)
|
92
|
+
@data[:links].delete(thing.host)
|
93
|
+
@data[:links].delete(socket)
|
94
|
+
end
|
95
|
+
|
96
|
+
@data[:sockets].delete(socket)
|
97
|
+
@data[:things].delete(socket)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Data
|
102
|
+
attr_reader :server, :dispatcher
|
103
|
+
|
104
|
+
def initialize (dispatcher)
|
105
|
+
@server = dispatcher.server
|
106
|
+
@dispatcher = dispatcher
|
107
|
+
|
108
|
+
@data = ThreadSafeHash.new
|
109
|
+
end
|
110
|
+
|
111
|
+
def [] (socket)
|
112
|
+
if socket.is_a?(Client) || socket.is_a?(User)
|
113
|
+
socket = socket.socket
|
114
|
+
end
|
115
|
+
|
116
|
+
if !@data[socket].is_a?(Array)
|
117
|
+
@data[socket] = []
|
118
|
+
end
|
119
|
+
|
120
|
+
@data[socket]
|
121
|
+
end
|
122
|
+
|
123
|
+
def push (socket, string)
|
124
|
+
if string.is_a?(String)
|
125
|
+
string.lstrip!
|
126
|
+
end
|
127
|
+
|
128
|
+
if string == :EOC
|
129
|
+
if socket.is_a?(Client) || socket.is_a?(User)
|
130
|
+
socket = socket.socket
|
131
|
+
end
|
132
|
+
|
133
|
+
dispatcher.disconnecting.push({ :thing => dispatcher.connections.things[socket], :output => self[socket] })
|
134
|
+
end
|
135
|
+
|
136
|
+
if (string && !string.empty?) || self[socket].last == :EOC
|
137
|
+
self[socket].push(string)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def pop (socket)
|
142
|
+
self[socket].shift
|
143
|
+
end
|
144
|
+
|
145
|
+
def clear (socket)
|
146
|
+
self[socket].clear
|
147
|
+
end
|
148
|
+
|
149
|
+
def delete (socket)
|
150
|
+
if socket.is_a?(Client) || socket.is_a?(User)
|
151
|
+
socket = socket.socket
|
152
|
+
end
|
153
|
+
|
154
|
+
@data.delete(socket)
|
155
|
+
end
|
156
|
+
|
157
|
+
def first (socket)
|
158
|
+
self[socket].first
|
159
|
+
end
|
160
|
+
|
161
|
+
def last (socket)
|
162
|
+
self[socket].last
|
163
|
+
end
|
164
|
+
|
165
|
+
def empty? (socket=nil)
|
166
|
+
if socket.is_a?(Client) || socket.is_a?(User)
|
167
|
+
socket = socket.socket
|
168
|
+
end
|
169
|
+
|
170
|
+
if socket
|
171
|
+
if @data.has_key?(socket)
|
172
|
+
return @data[socket].empty?
|
173
|
+
else
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
else
|
177
|
+
return @data.empty?
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def each (&block)
|
182
|
+
@data.each_key &block
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
attr_reader :server, :dispatcher, :connections, :input, :output, :disconnecting
|
187
|
+
|
188
|
+
def initialize (dispatcher)
|
189
|
+
@server = dispatcher.server
|
190
|
+
@dispatcher = dispatcher
|
191
|
+
|
192
|
+
@connections = Connections.new(server)
|
193
|
+
@input = Data.new(dispatcher)
|
194
|
+
@output = Data.new(dispatcher)
|
195
|
+
@disconnecting = []
|
196
|
+
end
|
197
|
+
|
198
|
+
def sockets
|
199
|
+
@connections.sockets
|
200
|
+
end
|
201
|
+
|
202
|
+
def clients
|
203
|
+
@connections.clients
|
204
|
+
end
|
205
|
+
|
206
|
+
def links
|
207
|
+
@connections.links
|
208
|
+
end
|
209
|
+
|
210
|
+
def listen (options, listen)
|
211
|
+
server = TCPServer.new(options[:bind], options[:port])
|
212
|
+
context = nil
|
213
|
+
|
214
|
+
if options[:ssl] != 'disabled'
|
215
|
+
context = SSLUtils::context(options[:ssl_cert], options[:ssl_key])
|
216
|
+
end
|
217
|
+
|
218
|
+
@connections.listening[:sockets].push(server)
|
219
|
+
@connections.listening[:data][server] = { :listen => listen, :context => context }
|
220
|
+
end
|
221
|
+
|
222
|
+
def accept (timeout=0)
|
223
|
+
begin
|
224
|
+
listening, = IO::select @connections.listening[:sockets], nil, nil, timeout
|
225
|
+
|
226
|
+
if listening
|
227
|
+
listening.each {|server|
|
228
|
+
begin
|
229
|
+
socket, = server.accept_nonblock
|
230
|
+
|
231
|
+
if socket
|
232
|
+
newConnection socket, @connections.listening[:data][server][:listen], @connections.listening[:data][server][:context]
|
233
|
+
end
|
234
|
+
rescue Errno::EAGAIN
|
235
|
+
rescue Exception => e
|
236
|
+
self.debug e
|
237
|
+
end
|
238
|
+
}
|
239
|
+
end
|
240
|
+
rescue IOError
|
241
|
+
@connections.listening[:sockets].each {|socket|
|
242
|
+
if socket.closed?
|
243
|
+
@connections.listening[:sockets].delete(socket)
|
244
|
+
@connections.listening[:data].delete(socket @connections.listening[:data].delete(socket))
|
245
|
+
end
|
246
|
+
}
|
247
|
+
rescue
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Executed with each incoming connection
|
252
|
+
def newConnection (socket, listen, context=nil)
|
253
|
+
# here, somehow we should check if the incoming peer is a linked server or a real client
|
254
|
+
|
255
|
+
host = socket.peeraddr[2]
|
256
|
+
ip = socket.peeraddr[3]
|
257
|
+
port = socket.addr[1]
|
258
|
+
|
259
|
+
self.debug "#{host}[#{ip}/#{port}] connecting."
|
260
|
+
|
261
|
+
Thread.new {
|
262
|
+
begin
|
263
|
+
if listen.attributes['ssl'] != 'disabled'
|
264
|
+
ssl = OpenSSL::SSL::SSLSocket.new socket, context
|
265
|
+
|
266
|
+
ssl.accept
|
267
|
+
socket = ssl
|
268
|
+
end
|
269
|
+
|
270
|
+
@connections.things[socket] = @connections.clients[socket] = Server::Client.new(server, socket, listen)
|
271
|
+
@connections.sockets.push(socket)
|
272
|
+
|
273
|
+
@input[socket]
|
274
|
+
rescue OpenSSL::SSL::SSLError
|
275
|
+
socket.write_nonblock "This is a SSL connection, faggot.\r\n" rescue nil
|
276
|
+
self.debug "#{host}[#{ip}/#{port}] tried to connect to a SSL connection and failed the handshake."
|
277
|
+
socket.close rescue nil
|
278
|
+
rescue Errno::ECONNRESET
|
279
|
+
socket.close rescue nil
|
280
|
+
self.debug "#{host}[#{ip}/#{port}] connection reset."
|
281
|
+
rescue Exception => e
|
282
|
+
socket.close rescue nil
|
283
|
+
self.debug(e)
|
284
|
+
end
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
def read (timeout=0.1)
|
289
|
+
begin
|
290
|
+
reading, = IO::select @connections.sockets, nil, nil, timeout
|
291
|
+
rescue IOError
|
292
|
+
@connections.sockets.each {|socket|
|
293
|
+
if socket.closed?
|
294
|
+
server.kill socket
|
295
|
+
end
|
296
|
+
}
|
297
|
+
rescue Exception => e
|
298
|
+
self.debug e
|
299
|
+
end
|
300
|
+
|
301
|
+
if !reading
|
302
|
+
return
|
303
|
+
end
|
304
|
+
|
305
|
+
reading.each {|socket|
|
306
|
+
thing = thing socket
|
307
|
+
|
308
|
+
begin
|
309
|
+
input = socket.read_nonblock 2048
|
310
|
+
|
311
|
+
if !input || input.empty?
|
312
|
+
raise Errno::EPIPE
|
313
|
+
end
|
314
|
+
|
315
|
+
input.split(/[\r\n]+/).each {|string|
|
316
|
+
@input.push(socket, string)
|
317
|
+
}
|
318
|
+
rescue IOError
|
319
|
+
server.kill thing, 'Input/output error', true
|
320
|
+
rescue Errno::EBADF, Errno::EPIPE, OpenSSL::SSL::SSLError
|
321
|
+
server.kill thing, 'Client exited', true
|
322
|
+
rescue Errno::ECONNRESET
|
323
|
+
server.kill thing, 'Connection reset by peer', true
|
324
|
+
rescue Errno::ETIMEDOUT
|
325
|
+
server.kill thing, 'Ping timeout', true
|
326
|
+
rescue Errno::EHOSTUNREACH
|
327
|
+
server.kill thing, 'No route to host', true
|
328
|
+
rescue Errno::EAGAIN, IO::WaitReadable
|
329
|
+
rescue Exception => e
|
330
|
+
self.debug e
|
331
|
+
end
|
332
|
+
}
|
333
|
+
end
|
334
|
+
|
335
|
+
def clean
|
336
|
+
@disconnecting.each {|data|
|
337
|
+
thing = data[:thing]
|
338
|
+
output = data[:output]
|
339
|
+
|
340
|
+
if output.first == :EOC
|
341
|
+
output.shift
|
342
|
+
handleDisconnection thing, output.shift
|
343
|
+
@disconnecting.delete(data)
|
344
|
+
end
|
345
|
+
}
|
346
|
+
end
|
347
|
+
|
348
|
+
def handle
|
349
|
+
@input.each {|socket|
|
350
|
+
if dispatcher.event.handling[socket] || @input.empty?(socket)
|
351
|
+
next
|
352
|
+
end
|
353
|
+
|
354
|
+
Thread.new {
|
355
|
+
begin
|
356
|
+
if string = @input.pop(socket)
|
357
|
+
dispatcher.dispatch(:input, thing(socket), string)
|
358
|
+
end
|
359
|
+
rescue Exception => e
|
360
|
+
self.debug e
|
361
|
+
end
|
362
|
+
}
|
363
|
+
}
|
364
|
+
end
|
365
|
+
|
366
|
+
def write (timeout=0)
|
367
|
+
begin
|
368
|
+
none, writing, erroring = IO::select nil, @connections.sockets, nil, timeout
|
369
|
+
rescue IOError
|
370
|
+
@connections.sockets.each {|socket|
|
371
|
+
if socket.closed?
|
372
|
+
server.kill thing socket, 'Client exited'
|
373
|
+
end
|
374
|
+
}
|
375
|
+
rescue Exception => e
|
376
|
+
self.debug e
|
377
|
+
end
|
378
|
+
|
379
|
+
if !writing
|
380
|
+
return
|
381
|
+
end
|
382
|
+
|
383
|
+
writing.each {|socket|
|
384
|
+
if @output.empty?(socket)
|
385
|
+
next
|
386
|
+
end
|
387
|
+
|
388
|
+
thing = thing socket
|
389
|
+
|
390
|
+
begin
|
391
|
+
while !@output.empty?(socket)
|
392
|
+
output = @output.first(socket)
|
393
|
+
|
394
|
+
if output == :EOC
|
395
|
+
@output.delete(socket)
|
396
|
+
else
|
397
|
+
output.force_encoding 'ASCII-8BIT'
|
398
|
+
socket.write_nonblock "#{output}\r\n"
|
399
|
+
|
400
|
+
@output.pop(socket)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
rescue IOError
|
404
|
+
server.kill thing, 'Input/output error', true
|
405
|
+
rescue Errno::EBADF, Errno::EPIPE, OpenSSL::SSL::SSLError
|
406
|
+
server.kill thing, 'Client exited', true
|
407
|
+
rescue Errno::ECONNRESET
|
408
|
+
server.kill thing, 'Connection reset by peer', true
|
409
|
+
rescue Errno::ETIMEDOUT
|
410
|
+
server.kill thing, 'Ping timeout', true
|
411
|
+
rescue Errno::EHOSTUNREACH
|
412
|
+
server.kill thing, 'No route to host', true
|
413
|
+
rescue Errno::EAGAIN, IO::WaitWritable
|
414
|
+
rescue Exception => e
|
415
|
+
self.debug e
|
416
|
+
end
|
417
|
+
}
|
418
|
+
end
|
419
|
+
|
420
|
+
def handleDisconnection (thing, message)
|
421
|
+
@dispatcher.execute(:kill, thing, message) rescue nil
|
422
|
+
|
423
|
+
if thing.is_a?(Client)
|
424
|
+
thing.modes[:quitting] = true
|
425
|
+
|
426
|
+
if thing.modes[:registered]
|
427
|
+
thing.channels.each_value {|channel|
|
428
|
+
channel.users.delete(thing.nick)
|
429
|
+
}
|
430
|
+
end
|
431
|
+
elsif thing.is_a?(Link)
|
432
|
+
# wat
|
433
|
+
end
|
434
|
+
|
435
|
+
@input.delete(thing.socket)
|
436
|
+
@output.delete(thing.socket)
|
437
|
+
connections.delete(thing.socket)
|
438
|
+
|
439
|
+
self.debug "#{thing.mask}[#{thing.ip}/#{thing.port}] disconnected."
|
440
|
+
|
441
|
+
thing.socket.close rescue nil
|
442
|
+
end
|
443
|
+
|
444
|
+
def finalize
|
445
|
+
begin
|
446
|
+
@connections.listening[:sockets].each {|server|
|
447
|
+
server.close
|
448
|
+
}
|
449
|
+
|
450
|
+
@clients.each {|key, client|
|
451
|
+
kill client, 'Good night sweet prince.'
|
452
|
+
}
|
453
|
+
|
454
|
+
@links.each {|key, link|
|
455
|
+
kill client, 'Good night sweet prince.'
|
456
|
+
}
|
457
|
+
rescue Exception => e
|
458
|
+
self.debug e
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
def thing (identifier)
|
463
|
+
if identifier.is_a?(Client) || identifier.is_a?(Link)
|
464
|
+
return identifier
|
465
|
+
elsif identifier.is_a?(User)
|
466
|
+
return identifier.client
|
467
|
+
else
|
468
|
+
return @connections.things[identifier]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
end
|
474
|
+
|
475
|
+
end
|
476
|
+
|
477
|
+
end
|