blur 2.1.3 → 2.1.6
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.
- 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
|