blur 2.1.3 → 2.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/executables/blur +16 -15
- data/library/blur/callbacks.rb +12 -16
- data/library/blur/channel.rb +11 -8
- data/library/blur/client.rb +36 -43
- data/library/blur/handling.rb +140 -113
- data/library/blur/network/connection.rb +24 -28
- data/library/blur/network/isupport.rb +38 -32
- data/library/blur/network.rb +87 -14
- data/library/blur/script.rb +21 -12
- data/library/blur/script_cache.rb +12 -10
- data/library/blur/user.rb +43 -26
- data/library/blur/version.rb +3 -3
- data/library/blur.rb +16 -14
- metadata +8 -10
- data/library/blur/enhancements.rb +0 -39
- data/library/blur/logging.rb +0 -41
data/library/blur/handling.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Blur
|
4
4
|
class Client
|
@@ -24,85 +24,98 @@ module Blur
|
|
24
24
|
# # RPL_WHOISUSER
|
25
25
|
# # <nick> <user> <host> * :<real name>
|
26
26
|
# def got_whois_user network, message
|
27
|
-
# puts "nick: #{message.parameters[0]} user: #{message.parameters[1]} host: #{message.parameters[2]}
|
27
|
+
# puts "nick: #{message.parameters[0]} user: #{message.parameters[1]} host: #{message.parameters[2]} ..."
|
28
28
|
# end
|
29
|
-
#
|
29
|
+
#
|
30
30
|
# @see http://www.irchelp.org/irchelp/rfc/chapter6.html
|
31
31
|
module Handling
|
32
|
-
|
33
32
|
# Called when the MOTD was received, which also means it is ready.
|
34
33
|
#
|
35
34
|
# == Callbacks:
|
36
35
|
# Emits +:connection_ready+ with the parameter +network+.
|
37
36
|
#
|
38
37
|
# Automatically joins the channels specified in +:channels+.
|
39
|
-
def got_end_of_motd network,
|
38
|
+
def got_end_of_motd network, _message
|
40
39
|
emit :connection_ready, network
|
41
|
-
|
40
|
+
|
42
41
|
network.options['channels'].each do |channel|
|
43
42
|
network.join channel
|
44
43
|
end
|
45
44
|
end
|
46
|
-
|
45
|
+
|
47
46
|
# Called when the namelist of a channel was received.
|
48
47
|
def got_name_reply network, message
|
49
48
|
name = message.parameters[2] # Channel name.
|
50
49
|
nicks = message.parameters[3].split.map do |nick|
|
51
50
|
# Slice the nick if the first character is a user mode prefix.
|
52
|
-
if network.user_prefixes.include? nick.chr
|
53
|
-
nick.slice! 0
|
54
|
-
end
|
51
|
+
nick.slice! 0 if network.user_prefixes.include? nick.chr
|
55
52
|
|
56
53
|
nick
|
57
54
|
end
|
58
|
-
|
59
|
-
if channel = find_or_create_channel(name, network)
|
60
|
-
users = nicks.map{|nick| find_or_create_user nick, network }
|
61
|
-
users.each do |user|
|
62
|
-
user.channels << channel
|
63
|
-
channel.users << user unless channel.users.include? user
|
64
|
-
end
|
65
55
|
|
66
|
-
|
56
|
+
return unless (channel = find_or_create_channel(name, network))
|
57
|
+
|
58
|
+
users = nicks.map { |nick| find_or_create_user nick, network }
|
59
|
+
users.each do |user|
|
60
|
+
user.channels << channel
|
61
|
+
channel.users << user unless channel.users.include? user
|
67
62
|
end
|
63
|
+
|
64
|
+
emit :channel_who_reply, channel
|
68
65
|
end
|
69
|
-
|
66
|
+
|
70
67
|
# Called when a channel topic was changed.
|
71
68
|
#
|
72
69
|
# == Callbacks:
|
73
70
|
# Emits :topic_change with the parameters +channel+ and +topic+.
|
74
71
|
def got_channel_topic network, message
|
75
72
|
_, channel_name, topic = message.parameters
|
76
|
-
|
77
|
-
if channel = find_or_create_channel(channel_name, network)
|
78
|
-
emit :channel_topic, channel, topic
|
79
73
|
|
80
|
-
|
81
|
-
|
74
|
+
return unless (channel = find_or_create_channel(channel_name, network))
|
75
|
+
|
76
|
+
emit :channel_topic, channel, topic
|
77
|
+
|
78
|
+
channel.topic = topic
|
82
79
|
end
|
83
|
-
|
80
|
+
|
84
81
|
# Called when the server needs to verify that we're alive.
|
85
82
|
def got_ping network, message
|
83
|
+
network.last_pong_time = Time.now
|
86
84
|
network.transmit :PONG, message.parameters[0]
|
87
85
|
|
88
|
-
emit :network_ping, message.parameters[0]
|
86
|
+
emit :network_ping, network, message.parameters[0]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Called when the server reponds to our periodic PINGs.
|
90
|
+
def got_pong network, message
|
91
|
+
network.last_pong_time = Time.now
|
92
|
+
|
93
|
+
emit :network_pong, network, message.parameters[0]
|
89
94
|
end
|
90
|
-
|
95
|
+
|
91
96
|
# Called when a user changed nickname.
|
92
97
|
#
|
93
98
|
# == Callbacks:
|
94
99
|
# Emits :user_rename with the parameters +channel+, +user+ and +new_nick+
|
95
100
|
def got_nick network, message
|
96
101
|
old_nick = message.prefix.nick
|
97
|
-
|
98
|
-
if
|
102
|
+
|
103
|
+
# Update or own nickname if has been changed by the server
|
104
|
+
if network.nickname == old_nick
|
99
105
|
new_nick = message.parameters[0]
|
100
|
-
|
101
|
-
|
102
|
-
network.
|
106
|
+
|
107
|
+
emit :nick, new_nick
|
108
|
+
network.nickname = new_nick
|
103
109
|
end
|
110
|
+
|
111
|
+
return unless (user = network.users.delete(old_nick))
|
112
|
+
|
113
|
+
new_nick = message.parameters[0]
|
114
|
+
emit :user_rename, user, new_nick
|
115
|
+
user.nick = new_nick
|
116
|
+
network.users[new_nick] = user
|
104
117
|
end
|
105
|
-
|
118
|
+
|
106
119
|
# Called when a message was received (both channel and private messages).
|
107
120
|
#
|
108
121
|
# == Callbacks:
|
@@ -114,10 +127,11 @@ module Blur
|
|
114
127
|
# @note Messages are contained as strings.
|
115
128
|
def got_privmsg network, message
|
116
129
|
return unless message.prefix.nick # Ignore all server privmsgs
|
130
|
+
|
117
131
|
name, msg = message.parameters
|
118
132
|
|
119
|
-
if channel = network.channels[name]
|
120
|
-
unless user = network.users[message.prefix.nick]
|
133
|
+
if (channel = network.channels[name])
|
134
|
+
unless (user = network.users[message.prefix.nick])
|
121
135
|
user = User.new message.prefix.nick, network
|
122
136
|
end
|
123
137
|
|
@@ -126,7 +140,7 @@ module Blur
|
|
126
140
|
|
127
141
|
emit :message, user, channel, msg
|
128
142
|
else # This is a private message
|
129
|
-
unless user = network.users[message.prefix.nick]
|
143
|
+
unless (user = network.users[message.prefix.nick])
|
130
144
|
user = User.new message.prefix.nick, network
|
131
145
|
user.name = message.prefix.user
|
132
146
|
user.host = message.prefix.host
|
@@ -135,7 +149,7 @@ module Blur
|
|
135
149
|
emit :private_message, user, msg
|
136
150
|
end
|
137
151
|
end
|
138
|
-
|
152
|
+
|
139
153
|
# Called when a user joined a channel.
|
140
154
|
#
|
141
155
|
# == Callbacks:
|
@@ -146,30 +160,29 @@ module Blur
|
|
146
160
|
user = find_or_create_user message.prefix.nick, network
|
147
161
|
user.name = message.prefix.user
|
148
162
|
user.host = message.prefix.host
|
149
|
-
|
150
|
-
if channel = find_or_create_channel(channel_name, network)
|
151
|
-
_user_join_channel user, channel
|
152
163
|
|
153
|
-
|
154
|
-
|
164
|
+
return unless (channel = find_or_create_channel(channel_name, network))
|
165
|
+
|
166
|
+
_user_join_channel user, channel
|
167
|
+
|
168
|
+
emit :user_entered, channel, user
|
155
169
|
end
|
156
|
-
|
170
|
+
|
157
171
|
# Called when a user left a channel.
|
158
172
|
#
|
159
173
|
# == Callbacks:
|
160
174
|
# Emits +:user_left+ with the parameters +channel+ and +user+.
|
161
175
|
def got_part network, message
|
162
176
|
channel_name = message.parameters[0]
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
177
|
+
|
178
|
+
return unless (channel = network.channels[channel_name])
|
179
|
+
return unless (user = network.users[message.prefix.nick])
|
180
|
+
|
181
|
+
_user_part_channel user, channel
|
182
|
+
|
183
|
+
emit :user_left, channel, user
|
171
184
|
end
|
172
|
-
|
185
|
+
|
173
186
|
# Called when a user disconnected from a network.
|
174
187
|
#
|
175
188
|
# == Callbacks:
|
@@ -177,17 +190,17 @@ module Blur
|
|
177
190
|
def got_quit network, message
|
178
191
|
nick = message.prefix.nick
|
179
192
|
reason = message.parameters[2]
|
180
|
-
|
181
|
-
if user = network.users[nick]
|
182
|
-
user.channels.each do |channel|
|
183
|
-
channel.users.delete user
|
184
|
-
end
|
185
193
|
|
186
|
-
|
187
|
-
|
194
|
+
return unless (user = network.users[nick])
|
195
|
+
|
196
|
+
user.channels.each do |channel|
|
197
|
+
channel.users.delete user
|
188
198
|
end
|
199
|
+
|
200
|
+
emit :user_quit, user, reason
|
201
|
+
network.users.delete nick
|
189
202
|
end
|
190
|
-
|
203
|
+
|
191
204
|
# Called when a user was kicked from a channel.
|
192
205
|
#
|
193
206
|
# == Callbacks:
|
@@ -197,32 +210,30 @@ module Blur
|
|
197
210
|
# +kicker+ is the user that kicked +kickee+.
|
198
211
|
def got_kick network, message
|
199
212
|
name, target, reason = message.parameters
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
end
|
209
|
-
end
|
213
|
+
|
214
|
+
return unless (channel = network.channels[name])
|
215
|
+
return unless (kicker = network.users[message.prefix.nick])
|
216
|
+
return unless (kickee = network.users[target])
|
217
|
+
|
218
|
+
_user_part_channel kickee, channel
|
219
|
+
|
220
|
+
emit :user_kicked, kicker, channel, kickee, reason
|
210
221
|
end
|
211
|
-
|
222
|
+
|
212
223
|
# Called when a topic was changed for a channel.
|
213
224
|
#
|
214
225
|
# == Callbacks:
|
215
226
|
# Emits :topic with the parameters +user+, +channel+ and +topic+.
|
216
227
|
def got_topic network, message
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
channel.topic = topic
|
228
|
+
_channel_name, topic = message.parameters
|
229
|
+
|
230
|
+
return unless (channel = network.channels[channel.name])
|
231
|
+
|
232
|
+
if (user = network.users[message.prefix.nick])
|
233
|
+
emit :topic, user, channel, topic
|
225
234
|
end
|
235
|
+
|
236
|
+
channel.topic = topic
|
226
237
|
end
|
227
238
|
|
228
239
|
# Called when a channel or a users flags was altered.
|
@@ -233,56 +244,69 @@ module Blur
|
|
233
244
|
# === When it's user modes:
|
234
245
|
# Emits +:user_mode+ with the parameters +user+ and +modes+.
|
235
246
|
def got_mode network, message
|
236
|
-
name,
|
247
|
+
name, _modes, _limit, _nick, _mask = message.parameters
|
237
248
|
|
238
|
-
|
239
|
-
# FIXME
|
240
|
-
end
|
249
|
+
return unless (_channel = network.channels[name])
|
241
250
|
end
|
242
251
|
|
243
252
|
# Called when the network announces its ISUPPORT parameters.
|
244
253
|
def got_005 network, message
|
245
254
|
params = message.parameters[1..-2]
|
246
255
|
|
247
|
-
network.isupport.parse
|
256
|
+
network.isupport.parse(*params)
|
248
257
|
end
|
249
258
|
|
250
259
|
# Received when the server supports capability negotiation.
|
260
|
+
#
|
261
|
+
# CAP * LS :multi-prefix sasl
|
251
262
|
def got_cap network, message
|
252
|
-
|
263
|
+
_id, command = message.parameters[0..1]
|
253
264
|
|
254
265
|
case command
|
266
|
+
when 'LS'
|
267
|
+
capabilities = message.parameters[2]&.split
|
268
|
+
req = []
|
269
|
+
|
270
|
+
req << 'sasl' if network.sasl? && capabilities.include?('sasl')
|
271
|
+
req << 'account-tag' if capabilities.include?('account-tag')
|
272
|
+
|
273
|
+
network.transmit :CAP, 'REQ', req.join(' ') if req.any?
|
274
|
+
|
275
|
+
capabilities.each { |name| network.capabilities.push name }
|
276
|
+
|
277
|
+
emit :network_capabilities, network, capabilities
|
278
|
+
|
255
279
|
when 'ACK'
|
256
280
|
capabilities = message.parameters[2]&.split
|
257
281
|
|
258
|
-
if capabilities&.include?
|
259
|
-
|
260
|
-
|
261
|
-
network.cap_end
|
262
|
-
end
|
282
|
+
network.transmit :AUTHENTICATE, 'PLAIN' if capabilities&.include?('sasl') && network.sasl?
|
283
|
+
|
284
|
+
network.cap_end if network.waiting_for_cap
|
263
285
|
when 'NAK'
|
264
286
|
capabilities = message.parameters[2]&.split
|
265
287
|
|
266
|
-
if capabilities&.include?
|
288
|
+
if capabilities&.include?('sasl') && network.sasl?
|
267
289
|
puts "The server does not support SASL, but you've configured it " \
|
268
|
-
|
290
|
+
'as such! Disconnecting!'
|
269
291
|
|
270
292
|
network.disconnect
|
271
293
|
end
|
272
|
-
|
273
294
|
end
|
274
295
|
end
|
275
296
|
|
276
297
|
def got_001 network, message
|
277
|
-
if network.waiting_for_cap
|
278
|
-
|
279
|
-
|
298
|
+
network.abort_cap_neg if network.waiting_for_cap
|
299
|
+
|
300
|
+
# Get our nickname from the server
|
301
|
+
nickname = message.parameters[0]
|
302
|
+
network.nickname = nickname
|
280
303
|
end
|
281
304
|
|
282
305
|
def got_authenticate network, message
|
283
306
|
case message.parameters[0]
|
284
307
|
when '+'
|
285
308
|
return unless network.sasl?
|
309
|
+
|
286
310
|
sasl = network.options['sasl']
|
287
311
|
|
288
312
|
response = "#{sasl['username']}\x00#{sasl['username']}\x00#{sasl['password']}"
|
@@ -292,37 +316,41 @@ module Blur
|
|
292
316
|
|
293
317
|
# :server 900 <nick> <nick>!<ident>@<host> <account> :You are now logged in as <user>
|
294
318
|
# RPL_LOGGEDIN SASL
|
295
|
-
def got_900 network,
|
296
|
-
if network.waiting_for_cap
|
297
|
-
network.cap_end
|
298
|
-
end
|
319
|
+
def got_900 network, _message
|
320
|
+
network.cap_end if network.waiting_for_cap
|
299
321
|
end
|
300
322
|
|
301
323
|
# :server 904 <nick> :SASL authentication failed
|
302
324
|
# ERR_SASLFAIL
|
303
325
|
def got_904 network, message
|
304
|
-
|
326
|
+
_nick, _message = message.parameters
|
305
327
|
|
306
|
-
puts
|
328
|
+
puts 'SASL authentication failed! Disconnecting!'
|
307
329
|
|
308
330
|
network.disconnect
|
309
331
|
end
|
310
332
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
333
|
+
def got_nickname_in_use network, message
|
334
|
+
nickname = network.options['nickname']
|
335
|
+
network.transmit :NICK, "#{nickname}_"
|
336
|
+
end
|
337
|
+
|
338
|
+
alias got_353 got_name_reply
|
339
|
+
alias got_422 got_end_of_motd
|
340
|
+
alias got_376 got_end_of_motd
|
341
|
+
alias got_332 got_channel_topic
|
342
|
+
alias got_433 got_nickname_in_use
|
315
343
|
|
316
|
-
|
344
|
+
private
|
317
345
|
|
318
346
|
def _user_part_channel user, channel
|
319
347
|
user.channels.delete channel
|
320
348
|
channel.users.delete user
|
321
349
|
|
322
350
|
# Forget the user if we no longer share any channels.
|
323
|
-
|
324
|
-
|
325
|
-
|
351
|
+
return unless user.channels.empty?
|
352
|
+
|
353
|
+
user.network.users.delete user.nick
|
326
354
|
end
|
327
355
|
|
328
356
|
def _user_join_channel user, channel
|
@@ -331,7 +359,7 @@ module Blur
|
|
331
359
|
end
|
332
360
|
|
333
361
|
def find_or_create_user nick, network
|
334
|
-
unless user = network.users[nick]
|
362
|
+
unless (user = network.users[nick])
|
335
363
|
user = User.new nick, network
|
336
364
|
network.users[nick] = user
|
337
365
|
emit :user_created, user
|
@@ -341,7 +369,7 @@ module Blur
|
|
341
369
|
end
|
342
370
|
|
343
371
|
def find_or_create_channel name, network
|
344
|
-
unless channel = network.channels[name]
|
372
|
+
unless (channel = network.channels[name])
|
345
373
|
channel = Channel.new name, network
|
346
374
|
network.channels[name] = channel
|
347
375
|
emit :channel_created, channel
|
@@ -350,6 +378,5 @@ module Blur
|
|
350
378
|
channel
|
351
379
|
end
|
352
380
|
end
|
353
|
-
|
354
381
|
end
|
355
382
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Blur
|
4
4
|
class Network
|
@@ -13,14 +13,23 @@ module Blur
|
|
13
13
|
class Connection < EM::Protocols::LineAndTextProtocol
|
14
14
|
SSLValidationError = Class.new StandardError
|
15
15
|
|
16
|
+
# @return [Float] the default connection timeout interval in seconds.
|
17
|
+
DEFAULT_CONNECT_TIMEOUT_INTERVAL = 30
|
18
|
+
|
16
19
|
# Check whether or not connection is established.
|
17
|
-
def established
|
20
|
+
def established?
|
21
|
+
@connected == true
|
22
|
+
end
|
18
23
|
|
19
24
|
# EventMachine instantiates this class, and then sends event messages to
|
20
25
|
# that instance.
|
21
26
|
def initialize network
|
22
27
|
@network = network
|
23
28
|
@connected = false
|
29
|
+
connect_timeout = network.options.fetch 'connect_timeout',
|
30
|
+
DEFAULT_CONNECT_TIMEOUT_INTERVAL
|
31
|
+
|
32
|
+
self.pending_connect_timeout = connect_timeout
|
24
33
|
|
25
34
|
super
|
26
35
|
end
|
@@ -28,11 +37,10 @@ module Blur
|
|
28
37
|
# Called when a new connection is being set up, all we're going to use
|
29
38
|
# it for is to enable SSL/TLS on our connection.
|
30
39
|
def post_init
|
31
|
-
|
32
|
-
verify_peer = (@network.options[:ssl_no_verify] ? false : true)
|
40
|
+
return unless @network.secure?
|
33
41
|
|
34
|
-
|
35
|
-
|
42
|
+
verify_peer = (@network.options[:ssl_no_verify] ? false : true)
|
43
|
+
start_tls verify_peer: verify_peer
|
36
44
|
end
|
37
45
|
|
38
46
|
# Called when a line was received, the connection sends it to the network
|
@@ -61,12 +69,8 @@ module Blur
|
|
61
69
|
ssl_cert_file = @network.options[:ssl_cert_file]
|
62
70
|
peer_certificate = OpenSSL::X509::Certificate.new peer_cert
|
63
71
|
|
64
|
-
if ssl_cert_file
|
65
|
-
|
66
|
-
raise SSLValidationError, "Could not read the CA certificate file."
|
67
|
-
|
68
|
-
return false
|
69
|
-
end
|
72
|
+
if ssl_cert_file && !File.readable?(ssl_cert_file)
|
73
|
+
raise SSLValidationError, 'Could not read the CA certificate file.'
|
70
74
|
end
|
71
75
|
|
72
76
|
if fingerprint_verification?
|
@@ -75,9 +79,7 @@ module Blur
|
|
75
79
|
|
76
80
|
if fingerprint != peer_fingerprint
|
77
81
|
raise SSLValidationError,
|
78
|
-
|
79
|
-
|
80
|
-
return false
|
82
|
+
"Expected fingerprint '#{fingerprint}', but got '#{peer_fingerprint}'"
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
@@ -85,11 +87,7 @@ module Blur
|
|
85
87
|
ca_certificate = OpenSSL::X509::Certificate.new File.read ssl_cert_file
|
86
88
|
valid_signature = peer_certificate.verify ca_certificate.public_key
|
87
89
|
|
88
|
-
|
89
|
-
raise SSLValidationError, "Certificate verify failed"
|
90
|
-
|
91
|
-
return false
|
92
|
-
end
|
90
|
+
raise SSLValidationError, 'Certificate verify failed' unless valid_signature
|
93
91
|
end
|
94
92
|
|
95
93
|
true
|
@@ -98,9 +96,7 @@ module Blur
|
|
98
96
|
# Called once the connection is finally established.
|
99
97
|
def connection_completed
|
100
98
|
# We aren't completely connected yet if the connection is encrypted.
|
101
|
-
unless @network.secure?
|
102
|
-
connected!
|
103
|
-
end
|
99
|
+
connected! unless @network.secure?
|
104
100
|
end
|
105
101
|
|
106
102
|
# Called just as the connection is being terminated, either by remote or
|
@@ -112,7 +108,8 @@ module Blur
|
|
112
108
|
super
|
113
109
|
end
|
114
110
|
|
115
|
-
|
111
|
+
private
|
112
|
+
|
116
113
|
# Called when connection has been established.
|
117
114
|
def connected!
|
118
115
|
@connected = true
|
@@ -122,12 +119,12 @@ module Blur
|
|
122
119
|
|
123
120
|
# Returns true if we're expected to verify the certificate fingerprint.
|
124
121
|
def fingerprint_verification?
|
125
|
-
|
122
|
+
!@network.options[:ssl_fingerprint].nil?
|
126
123
|
end
|
127
124
|
|
128
125
|
# Returns true if we should verify the peer certificate.
|
129
126
|
def certificate_verification?
|
130
|
-
|
127
|
+
!@network.options[:ssl_cert_file].nil?
|
131
128
|
end
|
132
129
|
|
133
130
|
# Get the hexadecimal representation of the certificates public key.
|
@@ -143,9 +140,8 @@ module Blur
|
|
143
140
|
fingerprint = @network.options[:ssl_fingerprint]
|
144
141
|
|
145
142
|
raise SSLValidationError,
|
146
|
-
|
143
|
+
"Expected fingerprint '#{fingerprint}' but got '#{peer_fingerprint}'"
|
147
144
|
end
|
148
|
-
|
149
145
|
end
|
150
146
|
end
|
151
147
|
end
|