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,203 @@
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/client/dispatcher/event'
22
+
23
+ module IRC
24
+
25
+ class Client
26
+
27
+ class Dispatcher
28
+
29
+ class EventDispatcher
30
+ attr_reader :client, :dispatcher, :handling, :aliases, :events
31
+
32
+ def initialize (dispatcher)
33
+ @client = dispatcher.client
34
+ @dispatcher = dispatcher
35
+ @handling = ThreadSafeHash.new
36
+
37
+ @aliases = newAliases
38
+ @events = newEvents
39
+ end
40
+
41
+ def newAliases
42
+ Hash[
43
+ :input => {},
44
+ :output => {},
45
+ ]
46
+ end
47
+
48
+ def newEvents
49
+ Hash[
50
+ :pre => [],
51
+ :post => [],
52
+ :default => [],
53
+
54
+ :custom => {},
55
+
56
+ :input => {},
57
+ :output => {}
58
+ ]
59
+ end
60
+
61
+ def handle (what, chain, deep, server)
62
+ if chain != :input || deep
63
+ return
64
+ end
65
+
66
+ if what == :start
67
+ @handling[server.socket] = true
68
+ else
69
+ @handling.delete(server.socket)
70
+ end
71
+ end
72
+
73
+ def dispatch (chain, server, string, deep=false)
74
+ if !server
75
+ return
76
+ end
77
+
78
+ handle(:start, chain, deep, server)
79
+
80
+ event = Event.new(self, chain, server, string)
81
+ result = string
82
+
83
+ @events[:pre].each {|callback|
84
+ event.special = :pre
85
+
86
+ if callback.call(event, server, string) == false
87
+ handle(:stop, chain, deep, server)
88
+ return false
89
+ end
90
+ }
91
+
92
+ if !event.types.empty?
93
+ event.special = nil
94
+
95
+ event.callbacks.each {|callback|
96
+ begin
97
+ if callback.call(server, string) == false
98
+ handle(:stop, chain, deep, server)
99
+ return false
100
+ end
101
+ rescue Exception => e
102
+ self.debug e
103
+ end
104
+ }
105
+ elsif chain == :input
106
+ @events[:default].each {|callback|
107
+ event.special = :default
108
+
109
+ if callback.call(event, server, string) == false
110
+ handle(:stop, chain, deep, server)
111
+ return false
112
+ end
113
+ }
114
+ end
115
+
116
+ @events[:post].each {|callback|
117
+ event.special = :post
118
+
119
+ if callback.call(event, server, string) == false
120
+ handle(:stop, chain, deep, server)
121
+ return false
122
+ end
123
+ }
124
+
125
+ handle(:stop, chain, deep, server)
126
+
127
+ return result
128
+ end
129
+
130
+ def execute (event, *args)
131
+ if @events[:custom][event]
132
+ @events[:custom][event].each {|callback|
133
+ begin
134
+ if callback.method.call(*args) == false
135
+ return false
136
+ end
137
+ rescue Exception => e
138
+ self.debug e
139
+ end
140
+ }
141
+ end
142
+ end
143
+
144
+ def alias (chain, symbol, regex)
145
+ if !regex
146
+ @aliases[chain].delete(symbol)
147
+ elsif !regex.class == Regexp
148
+ raise 'You have to alias to a Regexp.'
149
+ else
150
+ @aliases[chain][symbol] = regex
151
+ end
152
+ end
153
+
154
+ def register (chain, type, callback, priority=0)
155
+ if !type
156
+ events = @events[chain]
157
+
158
+ if !events
159
+ events = @events[chain] = []
160
+ end
161
+ else
162
+ if @aliases[chain]
163
+ if @aliases[chain][type]
164
+ type = @aliases[chain][type]
165
+ end
166
+ end
167
+
168
+ if !@events[chain]
169
+ @events[chain] = {}
170
+ end
171
+
172
+ events = @events[chain][type]
173
+
174
+ if !events
175
+ events = @events[chain][type] = []
176
+ end
177
+ end
178
+
179
+ if !callback
180
+ events.clear
181
+ elsif callback.is_a?(Array)
182
+ callback.each {|callback|
183
+ register(chain, type, callback)
184
+ }
185
+ else
186
+ if callback.is_a?(Event::Callback)
187
+ events.push(callback)
188
+ else
189
+ events.push(Event::Callback.new(callback, priority))
190
+ end
191
+ end
192
+
193
+ events.sort! {|a, b|
194
+ a.priority <=> b.priority
195
+ }
196
+ end
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ end
@@ -0,0 +1,103 @@
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
+
22
+ module IRC
23
+
24
+ class Client
25
+
26
+ class Module
27
+ attr_reader :client
28
+
29
+ def initialize (client)
30
+ @client = client
31
+
32
+ if @aliases
33
+ if @aliases[:input]
34
+ @aliases[:input].each {|key, value|
35
+ @client.dispatcher.alias(:input, key, value)
36
+ }
37
+ end
38
+
39
+ if @aliases[:output]
40
+ @aliases[:output].each {|key, value|
41
+ @client.dispatcher.alias(:output, key, value)
42
+ }
43
+ end
44
+ end
45
+
46
+ if @events
47
+ if @events[:pre]
48
+ @client.dispatcher.register(:pre, nil, @events[:pre])
49
+ end
50
+
51
+ if @events[:post]
52
+ @client.dispatcher.register(:post, nil, @events[:post])
53
+ end
54
+
55
+ if @events[:default]
56
+ @client.dispatcher.register(:default, nil, @events[:default])
57
+ end
58
+
59
+ if @events[:custom]
60
+ @events[:custom].each {|key, value|
61
+ @client.dispatcher.register(:custom, key, value)
62
+ }
63
+ end
64
+
65
+ if @events[:input]
66
+ @events[:input].each {|key, value|
67
+ @client.dispatcher.register(:input, key, value)
68
+ }
69
+ end
70
+
71
+ if @events[:output]
72
+ @events[:output].each {|key, value|
73
+ @client.dispatcher.register(:output, key, value)
74
+ }
75
+ end
76
+ end
77
+
78
+ begin
79
+ rehash
80
+ rescue NameError
81
+ rescue Exception => e
82
+ self.debug e
83
+ end
84
+ end
85
+
86
+ def finalize
87
+ if @aliases
88
+ @aliases.each_key {|key|
89
+ @client.dispatcher.alias(key, nil)
90
+ }
91
+ end
92
+
93
+ if @events
94
+ @events.each_key {|key|
95
+ @client.dispatcher.register(key, nil)
96
+ }
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,361 @@
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/mask'
22
+ require 'failirc/errors'
23
+ require 'failirc/responses'
24
+
25
+ require 'failirc/client/module'
26
+
27
+ require 'failirc/client/channel'
28
+ require 'failirc/client/user'
29
+ require 'failirc/client/client'
30
+
31
+ module IRC
32
+
33
+ class Client
34
+
35
+ module Modules
36
+
37
+ class Base < Module
38
+ @@version = '0.0.1'
39
+
40
+ @@supported = {}
41
+
42
+ def self.version
43
+ @@version
44
+ end
45
+
46
+ def self.supported
47
+ @@supported
48
+ end
49
+
50
+ def description
51
+ "Base-#{Base.version}"
52
+ end
53
+
54
+ def initialize (client)
55
+ @aliases = {
56
+ :input => {
57
+ :NUMERIC => /^:([^ ]+)\s+(\d{3})\s+(.+)/,
58
+
59
+ :PING => /^PING( |$)/i,
60
+
61
+ :JOIN => /^:.+?\s+JOIN\s+:./i,
62
+
63
+ :PRIVMSG => /^:.+?\s+PRIVMSG\s+.+?\s+:/i,
64
+ },
65
+ }
66
+
67
+ @events = {
68
+ :custom => {
69
+ :connect => self.method(:connect),
70
+ :numeric => self.method(:received_numeric),
71
+
72
+ :join => self.method(:join),
73
+ :who => self.method(:who),
74
+
75
+ :message => self.method(:send_message),
76
+
77
+ :quit => self.method(:do_quit),
78
+ },
79
+
80
+ :input => {
81
+ :NUMERIC => self.method(:numeric),
82
+
83
+ :PING => self.method(:pong),
84
+
85
+ :JOIN => self.method(:joined),
86
+
87
+ :PRIVMSG => self.method(:message),
88
+ },
89
+ }
90
+
91
+ super(client)
92
+ end
93
+
94
+ module Utils
95
+ module Server
96
+ @@defaultSupportedModes = [
97
+ { :mode => 'o', :status => '@' },
98
+ { :mode => 'h', :status => '%' },
99
+ { :mode => 'v', :status => '+' }
100
+ ]
101
+
102
+ def self.supportedModes
103
+ if !Base.supported['PREFIX']
104
+ return @@defaultSupportedModes
105
+ else
106
+ match = Base.supported['PREFIX'].match(/\((.*?)\)(.*)$/)
107
+
108
+ if !match
109
+ return @@defaultSupportedModes
110
+ else
111
+ modes = match[1].split
112
+ statuses = match[2].split
113
+ result = []
114
+
115
+ 1.upto(modes.length) {|f|
116
+ result.push({
117
+ :mode => modes.shift,
118
+ :status => statuses.shift,
119
+ })
120
+ }
121
+
122
+ return result
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ module Channel
129
+ def self.isValid (string)
130
+ string.match(/^[&#+!][^ ,:\a]{0,50}$/) ? true : false
131
+ end
132
+ end
133
+ end
134
+
135
+ def connect (server)
136
+ if server.password
137
+ server.send :raw, "PASS #{server.password}"
138
+ end
139
+
140
+ server.send :raw, "NICK #{client.nick}"
141
+ server.send :raw, "USER #{client.user} * * :#{client.realName}"
142
+ end
143
+
144
+ def pong (server, string)
145
+ match = string.match(/PING\s+(.*)$/)
146
+
147
+ if match
148
+ server.send :raw, "PONG #{match[1]}"
149
+ end
150
+ end
151
+
152
+ def numeric (server, string)
153
+ match = string.match(/^:[^ ]+\s+(\d{3})\s+.+?(\s+(.*))?$/)
154
+
155
+ if match
156
+ client.dispatcher.execute :numeric, server, match[1].to_i, match[3]
157
+ end
158
+ end
159
+
160
+ def received_numeric (server, number, message)
161
+ case number
162
+
163
+ # stuff supported by the server
164
+ when 5
165
+ message.split(/\s+/).each {|support|
166
+ support = support.split(/=/)
167
+
168
+ Base.supported[support[0]] = support[1]
169
+ }
170
+
171
+ # end of MOTD or no MOTD, hence concluded connection.
172
+ when 376, 422
173
+ client.dispatcher.execute :connected, server
174
+
175
+ # WHO reply
176
+ when 352
177
+ match = message.match(/(.+?)\s+(.+?)\s+(.+?)\s+.+?\s+(.+?)\s+.+?\s+:\d+\s+(.+)$/)
178
+
179
+ if !match
180
+ return
181
+ end
182
+
183
+ channel = match[1]
184
+ mask = Mask.new(match[4], match[2], match[3])
185
+ realName = match[5]
186
+
187
+ if !server.channels[channel]
188
+ server.channels[channel] = Channel.new(server, channel)
189
+ end
190
+
191
+ channel = server.channels[channel]
192
+
193
+ if mask.nick == server.nick
194
+ return
195
+ end
196
+
197
+ if !server.clients[mask.nick] || server.clients[mask.nick].mask != mask
198
+ server.clients[mask.nick] = Client.new(server, mask)
199
+ server.clients[mask.nick].realName = realName
200
+ end
201
+
202
+ channel.add(server.clients[mask.nick])
203
+
204
+ end
205
+ end
206
+
207
+ def join (server, channel, password=nil)
208
+ text = "JOIN #{channel}"
209
+
210
+ if password
211
+ text << " #{password}"
212
+ end
213
+
214
+ server.send :raw, text
215
+ client.execute :who, server, channel
216
+ end
217
+
218
+ def joined (server, string)
219
+ match = string.match(/^:(.+?)\s+JOIN\s+:(.+)$/i)
220
+
221
+ if !match
222
+ return
223
+ end
224
+
225
+ mask = Mask.parse(match[1])
226
+ channel = match[2]
227
+
228
+ if mask.nick == server.nick
229
+ if !server.channels[channel]
230
+ server.channels[channel] = Channel.new(server, channel)
231
+ end
232
+ else
233
+ if !server.clients[mask.nick] || server.clients[mask.nick].mask != mask
234
+ server.clients[mask.nick] = Client.new(server, mask)
235
+ end
236
+
237
+ if server.channels[channel]
238
+ server.channels[channel].add(server.clients[mask.nick])
239
+ end
240
+ end
241
+ end
242
+
243
+ def who (server, channel)
244
+ server.send :raw, "WHO #{channel}"
245
+ end
246
+
247
+ def message (server, string)
248
+ match = string.match(/:(.+?)\s+PRIVMSG\s+(.+)\s+:(.*)$/i)
249
+
250
+ if !match
251
+ return
252
+ end
253
+
254
+ from = Mask.parse(match[1])
255
+
256
+ if !server.clients[from.nick] || server.clients[from.nick] != from
257
+ from = server.clients[from.nick] = Client.new(server, from)
258
+ else
259
+ from = server.clients[from.nick]
260
+ end
261
+
262
+ to = match[2]
263
+
264
+ if Utils::Channel::isValid(to)
265
+ to = server.channels[to]
266
+ from = to.user from
267
+ else
268
+ to = client
269
+ end
270
+
271
+ message = match[3]
272
+
273
+ if match = message.match(/^\x01([^ ]*)( (.*?))?\x01$/)
274
+ client.execute :ctcp, server, :message, from, to, match[1], match[3]
275
+ else
276
+ client.execute :message, server, :input, from, to, message
277
+ end
278
+ end
279
+
280
+ def send_message (server, chain, from, to, message)
281
+ if chain != :output
282
+ return
283
+ end
284
+
285
+ if to.is_a?(Channel)
286
+ name = to.name
287
+ elsif to.is_a?(Client)
288
+ name = to.nick
289
+ else
290
+ name = to.to_s
291
+ end
292
+
293
+ server.send :raw, "PRIVMSG #{name} :#{message}"
294
+ end
295
+
296
+ def notice (server, string)
297
+ match = string.match(/:(.+?)\s+NOTICE\s+(.+)\s+:(.*)$/i)
298
+
299
+ if !match
300
+ return
301
+ end
302
+
303
+ from = Mask.parse(match[1])
304
+
305
+ if !server.clients[from.nick] || server.clients[from.nick] != from
306
+ from = server.clients[from.nick] = Client.new(server, from)
307
+ else
308
+ from = server.clients[from.nick]
309
+ end
310
+
311
+ to = match[2]
312
+
313
+ if Utils::Channel::isValid(to)
314
+ to = server.channels[to]
315
+ from = to.user from
316
+ else
317
+ to = client
318
+ end
319
+
320
+ message = match[3]
321
+
322
+ if match = message.match(/^\x01([^ ]*)( (.*?))?\x01$/)
323
+ client.execute :ctcp, server, :notice, from, to, match[1], match[3]
324
+ else
325
+ client.execute :message, server, :input, from, to, message
326
+ end
327
+ end
328
+
329
+
330
+ def send_notice (server, chain, from, to, message, level=nil)
331
+ if chain != :output
332
+ return
333
+ end
334
+
335
+ if to.is_a?(Channel)
336
+ name = "#{level}#{to.name}"
337
+ elsif to.is_a?(Client)
338
+ name = to.nick
339
+ end
340
+
341
+ server.send :raw, "NOTICE #{name} :#{message}"
342
+ end
343
+
344
+ def send_ctcp (server, kind, chain, from, to, type, message, level=nil)
345
+
346
+ end
347
+
348
+ def do_quit (server, message)
349
+ if message
350
+ server.send :raw, "QUIT #{message}"
351
+ else
352
+ server.send :raw, "QUIT"
353
+ end
354
+ end
355
+ end
356
+
357
+ end
358
+
359
+ end
360
+
361
+ end