failirc 0.0.1

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.
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