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