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,410 @@
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
+ require 'failirc/client/server'
28
+
29
+ module IRC
30
+
31
+ class Client
32
+
33
+ class Dispatcher
34
+
35
+ class ConnectionDispatcher
36
+ class Connections
37
+ attr_reader :client
38
+
39
+ def initialize (client)
40
+ @client = client
41
+
42
+ @data = ThreadSafeHash.new
43
+ @data[:sockets] = []
44
+ @data[:servers] = {
45
+ :bySocket => {},
46
+ :byName => {},
47
+ }
48
+ end
49
+
50
+ def sockets
51
+ @data[:sockets]
52
+ end
53
+
54
+ def servers
55
+ @data[:servers]
56
+ end
57
+
58
+ def empty?
59
+ sockets.empty?
60
+ end
61
+
62
+ def exists? (socket)
63
+ servers[:bySocket][socket] ? true : false
64
+ end
65
+
66
+ def delete (socket)
67
+ if !exists?(socket)
68
+ return
69
+ end
70
+
71
+ @data[:sockets].delete(socket)
72
+
73
+ server = @data[:servers][:bySocket][socket]
74
+ @data[:servers][:bySocket].delete(socket)
75
+ @data[:servers][:byName].delete(server.name)
76
+ end
77
+ end
78
+
79
+ class Data
80
+ attr_reader :client, :dispatcher
81
+
82
+ def initialize (dispatcher)
83
+ @client = dispatcher.client
84
+ @dispatcher = dispatcher
85
+
86
+ @data = ThreadSafeHash.new
87
+ end
88
+
89
+ def [] (socket)
90
+ if socket.is_a?(Server)
91
+ socket = socket.socket
92
+ end
93
+
94
+ if !@data[socket].is_a?(Array)
95
+ @data[socket] = []
96
+ end
97
+
98
+ @data[socket]
99
+ end
100
+
101
+ def push (socket, string)
102
+ if string.is_a?(String)
103
+ string.lstrip!
104
+ end
105
+
106
+ if string == :EOC
107
+ if socket.is_a?(Client) || socket.is_a?(User)
108
+ socket = socket.socket
109
+ end
110
+
111
+ dispatcher.disconnecting.push({ :server => client.server(socket), :output => self[socket] })
112
+ end
113
+
114
+ if (string && !string.empty?) || self[socket].last == :EOC
115
+ self[socket].push(string)
116
+ end
117
+ end
118
+
119
+ def pop (socket)
120
+ self[socket].shift
121
+ end
122
+
123
+ def clear (socket)
124
+ self[socket].clear
125
+ end
126
+
127
+ def delete (socket)
128
+ if socket.is_a?(Server)
129
+ socket = socket.socket
130
+ end
131
+
132
+ @data.delete(socket)
133
+ end
134
+
135
+ def first (socket)
136
+ self[socket].first
137
+ end
138
+
139
+ def last (socket)
140
+ self[socket].last
141
+ end
142
+
143
+ def empty? (socket=nil)
144
+ if socket
145
+ if socket.is_a?(Server)
146
+ socket = socket.socket
147
+ end
148
+
149
+ if @data.has_key?(socket)
150
+ return @data[socket].empty?
151
+ else
152
+ return true
153
+ end
154
+ else
155
+ return @data.empty?
156
+ end
157
+ end
158
+
159
+ def each (&block)
160
+ @data.each_key &block
161
+ end
162
+ end
163
+
164
+ attr_reader :client, :dispatcher, :connections, :input, :output, :disconnecting
165
+
166
+ def initialize (dispatcher)
167
+ @client = dispatcher.client
168
+ @dispatcher = dispatcher
169
+
170
+ @connections = Connections.new(client)
171
+ @input = Data.new(dispatcher)
172
+ @output = Data.new(dispatcher)
173
+ @disconnecting = []
174
+ end
175
+
176
+ def sockets
177
+ @connections.sockets
178
+ end
179
+
180
+ def servers
181
+ @connections.servers
182
+ end
183
+
184
+ def connect (options, config, name=nil)
185
+ socket = nil
186
+ context = nil
187
+
188
+ begin
189
+ socket = TCPSocket.new(options[:host], options[:port])
190
+ rescue Errno::ECONNREFUSED
191
+ self.debug "Could not connect to #{options[:host]}/#{options[:port]}."
192
+ return
193
+ end
194
+
195
+ if options[:ssl] != 'disabled'
196
+ context = SSLUtils::context(options[:ssl_cert], options[:ssl_key])
197
+ end
198
+
199
+ host = socket.peeraddr[2]
200
+ ip = socket.peeraddr[3]
201
+ port = socket.peeraddr[1]
202
+
203
+ self.debug "Connecting to #{host}[#{ip}/#{port}]"
204
+
205
+ begin
206
+ if config.attributes['ssl'] != 'disabled'
207
+ ssl = OpenSSL::SSL::SSLSocket.new socket, context
208
+
209
+ ssl.connect
210
+ socket = ssl
211
+ end
212
+
213
+ @connections.servers[:bySocket][socket] = Server.new(client, socket, config, name)
214
+ @connections.servers[:byName][server(socket).name] = server socket
215
+ @connections.sockets.push(socket)
216
+
217
+ if config.attributes['password']
218
+ server(socket).password = config.attributes['password']
219
+ end
220
+
221
+ @input[socket]
222
+
223
+ dispatcher.execute :connect, @connections.servers[:bySocket][socket]
224
+ rescue OpenSSL::SSL::SSLError
225
+ self.debug "Tried to connect to #{host}[#{ip}/#{port}] with SSL but the handshake failed."
226
+ socket.close rescue nil
227
+ rescue Errno::ECONNRESET
228
+ socket.close rescue nil
229
+ self.debug "#{host}[#{ip}/#{port}] connection reset."
230
+ rescue Exception => e
231
+ socket.close rescue nil
232
+ self.debug e
233
+ end
234
+ end
235
+
236
+ def read (timeout=0.1)
237
+ begin
238
+ reading, = IO::select @connections.sockets, nil, nil, timeout
239
+ rescue IOError
240
+ @connections.sockets.each {|socket|
241
+ if socket.closed?
242
+ kill server socket
243
+ end
244
+ }
245
+ rescue Exception => e
246
+ self.debug e
247
+ end
248
+
249
+ if !reading
250
+ return
251
+ end
252
+
253
+ reading.each {|socket|
254
+ server = self.server socket
255
+
256
+ begin
257
+ input = socket.read_nonblock 2048
258
+
259
+ if !input || input.empty?
260
+ raise Errno::EPIPE
261
+ end
262
+
263
+ input.split(/[\r\n]+/).each {|string|
264
+ @input.push(socket, string)
265
+ }
266
+ rescue IOError
267
+ disconnect server, 'Input/output error'
268
+ rescue Errno::EBADF, Errno::EPIPE, OpenSSL::SSL::SSLError
269
+ disconnect server, 'Client exited'
270
+ rescue Errno::ECONNRESET
271
+ disconnect server, 'Connection reset by peer'
272
+ rescue Errno::ETIMEDOUT
273
+ disconnect server, 'Ping timeout'
274
+ rescue Errno::EHOSTUNREACH
275
+ disconnect server, 'No route to host'
276
+ rescue Errno::EAGAIN, IO::WaitReadable
277
+ rescue Exception => e
278
+ self.debug e
279
+ end
280
+ }
281
+ end
282
+
283
+ def disconnect (server, message)
284
+ @output.push server, :EOC
285
+ @output.push server, message
286
+ end
287
+
288
+ def clean
289
+ @disconnecting.each {|data|
290
+ server = data[:server]
291
+ output = data[:output]
292
+
293
+ if output.first == :EOC
294
+ output.shift
295
+ handleDisconnection server, output.shift
296
+ @disconnecting.delete(data)
297
+ end
298
+ }
299
+ end
300
+
301
+ def handle
302
+ @input.each {|socket|
303
+ if dispatcher.event.handling[socket] || @input.empty?(socket)
304
+ next
305
+ end
306
+
307
+ Thread.new {
308
+ begin
309
+ if string = @input.pop(socket)
310
+ dispatcher.dispatch(:input, server(socket), string)
311
+ end
312
+ rescue Exception => e
313
+ self.debug e
314
+ end
315
+ }
316
+ }
317
+ end
318
+
319
+ def write (timeout=0)
320
+ begin
321
+ none, writing = IO::select nil, @connections.sockets, nil, timeout
322
+ rescue IOError
323
+ @connections.sockets.each {|socket|
324
+ if socket.closed?
325
+ kill server socket
326
+ end
327
+ }
328
+ rescue Exception => e
329
+ self.debug e
330
+ end
331
+
332
+ if !writing
333
+ return
334
+ end
335
+
336
+ writing.each {|socket|
337
+ if @output.empty?(socket)
338
+ next
339
+ end
340
+
341
+ server = self.server socket
342
+
343
+ begin
344
+ while !@output.empty?(socket)
345
+ output = @output.first(socket)
346
+
347
+ if output == :EOC
348
+ @output.delete(socket)
349
+ else
350
+ output.force_encoding 'ASCII-8BIT'
351
+ socket.write_nonblock "#{output}\r\n"
352
+
353
+ @output.pop(socket)
354
+ end
355
+ end
356
+ rescue IOError
357
+ disconnect server, 'Input/output error'
358
+ rescue Errno::EBADF, Errno::EPIPE, OpenSSL::SSL::SSLError
359
+ disconnect server, 'Client exited'
360
+ rescue Errno::ECONNRESET
361
+ disconnect server, 'Connection reset by peer'
362
+ rescue Errno::ETIMEDOUT
363
+ disconnect server, 'Ping timeout'
364
+ rescue Errno::EHOSTUNREACH
365
+ disconnect server, 'No route to host'
366
+ rescue Errno::EAGAIN, IO::WaitWritable
367
+ rescue Exception => e
368
+ self.debug e
369
+ end
370
+ }
371
+ end
372
+
373
+ def handleDisconnection (server, message)
374
+ @dispatcher.execute(:disconnect, server, message) rescue nil
375
+
376
+ @input.delete(server.socket)
377
+ @output.delete(server.socket)
378
+ connections.delete(server.socket)
379
+
380
+ self.debug "Disconnected from #{server}[#{server.ip}/#{server.port}]"
381
+
382
+ server.socket.close rescue nil
383
+ end
384
+
385
+ def finalize
386
+ begin
387
+ @connections.sockets.each {|socket|
388
+ disconnect self.server(socket), disconnecting
389
+ }
390
+ rescue Exception => e
391
+ self.debug e
392
+ end
393
+ end
394
+
395
+ def server (identifier)
396
+ if identifier.is_a?(Server)
397
+ return identifier
398
+ elsif identifier.is_a?(String)
399
+ return @connections.servers[:byName][identifier]
400
+ else
401
+ return @connections.servers[:bySocket][identifier]
402
+ end
403
+ end
404
+ end
405
+
406
+ end
407
+
408
+ end
409
+
410
+ end
@@ -0,0 +1,113 @@
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
+ module IRC
21
+
22
+ class Client
23
+
24
+ class Dispatcher
25
+
26
+ class Event
27
+ class Callback
28
+ attr_reader :method
29
+ attr_accessor :priority
30
+
31
+ def initialize (method, priority=0)
32
+ @method = method
33
+ @priority = priority
34
+ end
35
+
36
+ def call (*args)
37
+ return @method.call(*args)
38
+ end
39
+ end
40
+
41
+ attr_reader :types, :chain, :aliases, :dispatcher, :server, :string
42
+ attr_accessor :special
43
+
44
+ def initialize (dispatcher, chain, server, string)
45
+ @dispatcher = dispatcher
46
+ @chain = chain
47
+ @server = server
48
+ @string = string
49
+ @types = Event.types(dispatcher, chain, string)
50
+ @aliases = Event.aliases(dispatcher, chain, types)
51
+ @callbacks = Event.callbacks(dispatcher, chain, types)
52
+ end
53
+
54
+ def callbacks
55
+ if @callbacks
56
+ return @callbacks
57
+ else
58
+ tmp = Event.callbacks(@dispatcher, @chain, @type)
59
+
60
+ if tmp
61
+ return @callbacks = tmp
62
+ else
63
+ return []
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.types (dispatcher, chain, string)
69
+ types = []
70
+
71
+ dispatcher.events[chain].each_key {|key|
72
+ if key.class == Regexp && key.match(string)
73
+ types.push key
74
+ end
75
+ }
76
+
77
+ return types
78
+ end
79
+
80
+ def self.aliases (dispatcher, chain, types)
81
+ aliases = []
82
+
83
+ dispatcher.aliases[chain].each {|key, value|
84
+ if types.include?(value)
85
+ aliases.push key
86
+ end
87
+ }
88
+
89
+ return aliases
90
+ end
91
+
92
+ def self.callbacks (dispatcher, chain, types)
93
+ callbacks = []
94
+
95
+ if chain == :pre || chain == :post || chain == :default
96
+ callbacks.insert(-1, *dispatcher.events[chain])
97
+ else
98
+ types.each {|type|
99
+ callbacks.insert(-1, *dispatcher.events[chain][type])
100
+ }
101
+ end
102
+
103
+ return callbacks
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ Event = Dispatcher::Event
110
+
111
+ end
112
+
113
+ end