failirc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/bin/failbot +162 -0
  2. data/bin/failircd +61 -0
  3. data/lib/failirc.rb +25 -0
  4. data/lib/failirc/client.rb +227 -0
  5. data/lib/failirc/client/channel.rb +98 -0
  6. data/lib/failirc/client/channels.rb +85 -0
  7. data/lib/failirc/client/client.rb +59 -0
  8. data/lib/failirc/client/clients.rb +43 -0
  9. data/lib/failirc/client/dispatcher.rb +209 -0
  10. data/lib/failirc/client/dispatcher/connectiondispatcher.rb +410 -0
  11. data/lib/failirc/client/dispatcher/event.rb +113 -0
  12. data/lib/failirc/client/dispatcher/eventdispatcher.rb +203 -0
  13. data/lib/failirc/client/module.rb +103 -0
  14. data/lib/failirc/client/modules/Base.rb +361 -0
  15. data/lib/failirc/client/modules/Logger.rb +89 -0
  16. data/lib/failirc/client/server.rb +89 -0
  17. data/lib/failirc/client/user.rb +66 -0
  18. data/lib/failirc/client/users.rb +100 -0
  19. data/lib/failirc/errors.rb +339 -0
  20. data/lib/failirc/extensions.rb +124 -0
  21. data/lib/failirc/mask.rb +117 -0
  22. data/lib/failirc/modes.rb +54 -0
  23. data/lib/failirc/responses.rb +360 -0
  24. data/lib/failirc/server.rb +266 -0
  25. data/lib/failirc/server/channel.rb +122 -0
  26. data/lib/failirc/server/channels.rb +84 -0
  27. data/lib/failirc/server/client.rb +100 -0
  28. data/lib/failirc/server/clients.rb +54 -0
  29. data/lib/failirc/server/dispatcher.rb +219 -0
  30. data/lib/failirc/server/dispatcher/connectiondispatcher.rb +477 -0
  31. data/lib/failirc/server/dispatcher/event.rb +113 -0
  32. data/lib/failirc/server/dispatcher/eventdispatcher.rb +196 -0
  33. data/lib/failirc/server/link.rb +50 -0
  34. data/lib/failirc/server/links.rb +49 -0
  35. data/lib/failirc/server/module.rb +103 -0
  36. data/lib/failirc/server/modules/Base.rb +2545 -0
  37. data/lib/failirc/server/modules/Cloaking.rb +170 -0
  38. data/lib/failirc/server/modules/Firewall.rb +104 -0
  39. data/lib/failirc/server/modules/Netlog.rb +67 -0
  40. data/lib/failirc/server/modules/Roulette.rb +78 -0
  41. data/lib/failirc/server/modules/TinyURL.rb +98 -0
  42. data/lib/failirc/server/modules/Translate.rb +62 -0
  43. data/lib/failirc/server/modules/WordFilter.rb +144 -0
  44. data/lib/failirc/server/user.rb +72 -0
  45. data/lib/failirc/server/users.rb +103 -0
  46. data/lib/failirc/sslutils.rb +74 -0
  47. data/lib/failirc/utils.rb +53 -0
  48. 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