torchat 0.0.1.rc.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.
- data/README.md +43 -0
- data/bin/torchatd +537 -0
- data/doc/protocol/broadcast.md +26 -0
- data/doc/protocol/groupchat.md +140 -0
- data/doc/protocol/latency.md +30 -0
- data/doc/protocol/standard.md +279 -0
- data/doc/protocol/typing.md +41 -0
- data/etc/torchat.yml +12 -0
- data/lib/torchat.rb +239 -0
- data/lib/torchat/protocol.rb +256 -0
- data/lib/torchat/protocol/broadcast.rb +40 -0
- data/lib/torchat/protocol/groupchat.rb +197 -0
- data/lib/torchat/protocol/latency.rb +44 -0
- data/lib/torchat/protocol/standard.rb +367 -0
- data/lib/torchat/protocol/typing.rb +36 -0
- data/lib/torchat/session.rb +603 -0
- data/lib/torchat/session/broadcast/message.rb +50 -0
- data/lib/torchat/session/broadcasts.rb +72 -0
- data/lib/torchat/session/buddies.rb +152 -0
- data/lib/torchat/session/buddy.rb +343 -0
- data/lib/torchat/session/buddy/joined_group_chat.rb +42 -0
- data/lib/torchat/session/buddy/joined_group_chats.rb +46 -0
- data/lib/torchat/session/buddy/latency.rb +54 -0
- data/lib/torchat/session/event.rb +79 -0
- data/lib/torchat/session/file_transfer.rb +173 -0
- data/lib/torchat/session/file_transfer/block.rb +51 -0
- data/lib/torchat/session/file_transfers.rb +89 -0
- data/lib/torchat/session/group_chat.rb +123 -0
- data/lib/torchat/session/group_chat/participant.rb +38 -0
- data/lib/torchat/session/group_chat/participants.rb +52 -0
- data/lib/torchat/session/group_chats.rb +74 -0
- data/lib/torchat/session/incoming.rb +187 -0
- data/lib/torchat/session/outgoing.rb +102 -0
- data/lib/torchat/tor.rb +107 -0
- data/lib/torchat/utils.rb +87 -0
- data/lib/torchat/version.rb +24 -0
- data/test/file_transfer/receiver.rb +41 -0
- data/test/file_transfer/sender.rb +45 -0
- data/test/group_chat/a.rb +37 -0
- data/test/group_chat/b.rb +37 -0
- data/test/group_chat/c.rb +57 -0
- data/torchat.gemspec +21 -0
- metadata +140 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
3
|
+
#
|
4
|
+
# This file is part of torchat for ruby.
|
5
|
+
#
|
6
|
+
# torchat for ruby is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# torchat for ruby is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
class Torchat; module Protocol
|
21
|
+
|
22
|
+
define_extension :typing do
|
23
|
+
define_packet :start do
|
24
|
+
define_unpacker_for 0
|
25
|
+
end
|
26
|
+
|
27
|
+
define_packet :thinking do
|
28
|
+
define_unpacker_for 0
|
29
|
+
end
|
30
|
+
|
31
|
+
define_packet :stop do
|
32
|
+
define_unpacker_for 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end; end
|
@@ -0,0 +1,603 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
3
|
+
#
|
4
|
+
# This file is part of torchat for ruby.
|
5
|
+
#
|
6
|
+
# torchat for ruby is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# torchat for ruby is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with torchat for ruby. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'eventmachine'
|
21
|
+
|
22
|
+
require 'torchat/session/event'
|
23
|
+
require 'torchat/session/buddies'
|
24
|
+
require 'torchat/session/file_transfers'
|
25
|
+
require 'torchat/session/group_chats'
|
26
|
+
require 'torchat/session/broadcasts'
|
27
|
+
|
28
|
+
class Torchat
|
29
|
+
|
30
|
+
class Session
|
31
|
+
attr_reader :config, :id, :name, :description, :status, :buddies, :file_transfers, :group_chats, :broadcasts
|
32
|
+
attr_writer :client, :version
|
33
|
+
attr_accessor :connection_timeout
|
34
|
+
|
35
|
+
def initialize (config)
|
36
|
+
@config = config
|
37
|
+
|
38
|
+
@status = :offline
|
39
|
+
|
40
|
+
@id = @config['id'][/^(.*?)(\.onion)?$/, 1]
|
41
|
+
@name = config['name']
|
42
|
+
@description = config['description']
|
43
|
+
|
44
|
+
@buddies = Buddies.new(self)
|
45
|
+
@file_transfers = FileTransfers.new(self)
|
46
|
+
@group_chats = GroupChats.new(self)
|
47
|
+
@broadcasts = Broadcasts.new(self)
|
48
|
+
|
49
|
+
@callbacks = Hash.new { |h, k| h[k] = [] }
|
50
|
+
@before = Hash.new { |h, k| h[k] = [] }
|
51
|
+
@after = Hash.new { |h, k| h[k] = [] }
|
52
|
+
@timers = []
|
53
|
+
|
54
|
+
@connection_timeout = 60
|
55
|
+
|
56
|
+
on :unknown do |e|
|
57
|
+
e.buddy.send_packet :not_implemented, e.line.split(' ').first
|
58
|
+
end
|
59
|
+
|
60
|
+
on :verify do |e|
|
61
|
+
# this actually gets executed only if the buddy doesn't exist
|
62
|
+
# so we can still check if the buddy is permanent below
|
63
|
+
buddies.add_temporary e.buddy
|
64
|
+
|
65
|
+
e.buddy.send_packet :client, client
|
66
|
+
e.buddy.send_packet :version, version
|
67
|
+
e.buddy.send_packet :supports, Protocol.extensions.map(&:name)
|
68
|
+
|
69
|
+
e.buddy.send_packet :profile_name, name if name
|
70
|
+
e.buddy.send_packet :profile_text, description if description
|
71
|
+
|
72
|
+
if e.buddy.permanent?
|
73
|
+
e.buddy.send_packet :add_me
|
74
|
+
end
|
75
|
+
|
76
|
+
e.buddy.send_packet :status, status
|
77
|
+
end
|
78
|
+
|
79
|
+
on_packet :supports do |e|
|
80
|
+
e.buddy.supports *e.packet.to_a
|
81
|
+
end
|
82
|
+
|
83
|
+
on_packet :status do |e|
|
84
|
+
next if e.buddy.ready?
|
85
|
+
|
86
|
+
e.buddy.ready!
|
87
|
+
|
88
|
+
fire :ready, buddy: e.buddy
|
89
|
+
end
|
90
|
+
|
91
|
+
on_packet :add_me do |e|
|
92
|
+
e.buddy.permanent!
|
93
|
+
end
|
94
|
+
|
95
|
+
on_packet :remove_me do |e|
|
96
|
+
buddies.remove e.buddy
|
97
|
+
|
98
|
+
e.buddy.disconnect
|
99
|
+
end
|
100
|
+
|
101
|
+
on_packet :client do |e|
|
102
|
+
e.buddy.client.name = e.packet.to_str
|
103
|
+
end
|
104
|
+
|
105
|
+
on_packet :version do |e|
|
106
|
+
e.buddy.client.version = e.packet.to_str
|
107
|
+
end
|
108
|
+
|
109
|
+
on_packet :status do |e|
|
110
|
+
old = e.buddy.status
|
111
|
+
|
112
|
+
if old != e.packet.to_sym
|
113
|
+
e.buddy.status = e.packet.to_sym
|
114
|
+
|
115
|
+
fire :status_change, buddy: e.buddy, old: old, new: e.packet.to_sym
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
on_packet :profile_name do |e|
|
120
|
+
e.buddy.name = e.packet.to_str
|
121
|
+
|
122
|
+
fire :profile_change, buddy: e.buddy, changed: :name
|
123
|
+
end
|
124
|
+
|
125
|
+
on_packet :profile_text do |e|
|
126
|
+
e.buddy.description = e.packet.to_str
|
127
|
+
|
128
|
+
fire :profile_change, buddy: e.buddy, changed: :description
|
129
|
+
end
|
130
|
+
|
131
|
+
on_packet :profile_avatar_alpha do |e|
|
132
|
+
e.buddy.avatar.alpha = e.packet.data
|
133
|
+
end
|
134
|
+
|
135
|
+
on_packet :profile_avatar do |e|
|
136
|
+
e.buddy.avatar.rgb = e.packet.data
|
137
|
+
|
138
|
+
fire :profile_change, buddy: e.buddy, changed: :avatar
|
139
|
+
end
|
140
|
+
|
141
|
+
on_packet :message do |e|
|
142
|
+
fire :message, buddy: e.buddy, message: e.packet.to_str
|
143
|
+
end
|
144
|
+
|
145
|
+
on_packet :filename do |e|
|
146
|
+
file_transfers.receive(e.packet.id, e.packet.name, e.packet.size, e.buddy)
|
147
|
+
end
|
148
|
+
|
149
|
+
on_packet :filedata do |e|
|
150
|
+
next unless file_transfer = file_transfers[e.packet.id]
|
151
|
+
|
152
|
+
file_transfer.add_block(e.packet.offset, e.packet.data, e.packet.md5).valid?
|
153
|
+
end
|
154
|
+
|
155
|
+
on_packet :filedata_ok do |e|
|
156
|
+
next unless file_transfer = file_transfers[e.packet.id]
|
157
|
+
|
158
|
+
file_transfer.send_next_block
|
159
|
+
end
|
160
|
+
|
161
|
+
on_packet :filedata_error do |e|
|
162
|
+
next unless file_transfer = file_transfers[e.packet.id]
|
163
|
+
|
164
|
+
file_transfer.send_last_block
|
165
|
+
end
|
166
|
+
|
167
|
+
on_packet :file_stop_sending do |e|
|
168
|
+
next unless file_transfer = file_transfers[e.packet.id]
|
169
|
+
|
170
|
+
file_transfer.stop(true)
|
171
|
+
end
|
172
|
+
|
173
|
+
on_packet :file_stop_receiving do |e|
|
174
|
+
next unless file_transfer = file_transfers[e.packet.id]
|
175
|
+
|
176
|
+
file_transfer.stop(true)
|
177
|
+
end
|
178
|
+
|
179
|
+
set_interval 120 do
|
180
|
+
next unless online?
|
181
|
+
|
182
|
+
buddies.each_value {|buddy|
|
183
|
+
next unless buddy.online?
|
184
|
+
|
185
|
+
if (Time.new.to_i - buddy.last_received.at.to_i) >= 360
|
186
|
+
buddy.disconnect
|
187
|
+
else
|
188
|
+
buddy.send_packet :status, status
|
189
|
+
end
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
set_interval 10 do
|
194
|
+
next unless online?
|
195
|
+
|
196
|
+
buddies.each_value {|buddy|
|
197
|
+
next if buddy.online? || buddy.blocked?
|
198
|
+
|
199
|
+
next if (Time.new.to_i - buddy.last_try.to_i) < ((buddy.tries > 36 ? 36 : buddy.tries) * 10)
|
200
|
+
|
201
|
+
buddy.connect
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
# typing extension support
|
206
|
+
on_packet :typing_start do |e|
|
207
|
+
e.buddy.typing!
|
208
|
+
end
|
209
|
+
|
210
|
+
on_packet :typing_thinking do |e|
|
211
|
+
e.buddy.thinking!
|
212
|
+
end
|
213
|
+
|
214
|
+
on_packet :typing_stop do |e|
|
215
|
+
e.buddy.not_typing!
|
216
|
+
end
|
217
|
+
|
218
|
+
on_packet :message do |e|
|
219
|
+
e.buddy.not_typing!
|
220
|
+
end
|
221
|
+
|
222
|
+
# groupchat extension support
|
223
|
+
on_packet :groupchat, :invite do |e|
|
224
|
+
next if group_chats.has_key? e.packet.id
|
225
|
+
|
226
|
+
group_chat = group_chats.create(e.packet.id, false)
|
227
|
+
group_chat.invited_by e.buddy
|
228
|
+
group_chat.add e.buddy
|
229
|
+
|
230
|
+
fire :group_chat_invite, group_chat: group_chat, buddy: e.buddy
|
231
|
+
end
|
232
|
+
|
233
|
+
on_packet :groupchat, :is_participating do |e|
|
234
|
+
if group_chats[e.packet.id]
|
235
|
+
e.buddy.send_packet [:groupchat, :participating]
|
236
|
+
else
|
237
|
+
e.buddy.send_packet [:groupchat, :not_participating]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
on_packet :groupchat, :participants do |e|
|
242
|
+
next unless group_chat = group_chats[e.packet.id] and !group_chat.joining?
|
243
|
+
|
244
|
+
participants = group_chat.participants.keys - e.packet.to_a
|
245
|
+
participants.reject! { |p| p == id || p == e.buddy.id }
|
246
|
+
|
247
|
+
e.buddy.send_packet [:groupchat, :participants], group_chat.id, participants
|
248
|
+
end
|
249
|
+
|
250
|
+
on_packet :groupchat, :participants do |e|
|
251
|
+
next unless group_chat = group_chats[e.packet.id] and group_chat.joining? and group_chat.invited_by == e.buddy
|
252
|
+
|
253
|
+
if e.packet.empty?
|
254
|
+
group_chat.joined!
|
255
|
+
|
256
|
+
next
|
257
|
+
end
|
258
|
+
|
259
|
+
if e.packet.any? { |id| buddies.has_key?(id) && buddies[id].blocked? }
|
260
|
+
group_chat.leave
|
261
|
+
|
262
|
+
next
|
263
|
+
end
|
264
|
+
|
265
|
+
if e.packet.all? { |id| buddies.has_key?(id) && buddies[id].online? }
|
266
|
+
e.packet.each {|id|
|
267
|
+
group_chat.add buddies[id]
|
268
|
+
}
|
269
|
+
|
270
|
+
e.buddy.send_packet [:groupchat, :participants], group_chat.id, group_chat.participants.keys
|
271
|
+
|
272
|
+
next
|
273
|
+
end
|
274
|
+
|
275
|
+
e.packet.each {|p|
|
276
|
+
next if (buddy = buddies.add_temporary(p)).online?
|
277
|
+
|
278
|
+
buddy.when :ready do
|
279
|
+
buddy.send_packet [:groupchat, :is_participating], group_chat.id
|
280
|
+
|
281
|
+
participating = buddy.on_packet :groupchat, :participating do |e|
|
282
|
+
next if group_chat.left?
|
283
|
+
|
284
|
+
group_chat.add e.buddy
|
285
|
+
|
286
|
+
if e.packet.all? { |id| buddies[id].online? }
|
287
|
+
e.buddy.send_packet [:groupchat, :participants], group_chat.id, group_chat.participants.keys
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
not_participating = buddy.on_packet :groupchat, :not_participating do |e|
|
292
|
+
group_chat.leave
|
293
|
+
end
|
294
|
+
|
295
|
+
# avoid possible memory leak, I don't do this inside both callbacks
|
296
|
+
# because a bad guy could not send either of those packets and there
|
297
|
+
# would be a leak anyway
|
298
|
+
set_timeout 10 do
|
299
|
+
participating.remove!
|
300
|
+
not_participating.remove!
|
301
|
+
end
|
302
|
+
|
303
|
+
e.remove!
|
304
|
+
end
|
305
|
+
}
|
306
|
+
end
|
307
|
+
|
308
|
+
on_packet :groupchat, :invited do |e|
|
309
|
+
next unless group_chat = group_chats[e.packet.id]
|
310
|
+
|
311
|
+
unless (buddy = buddies[e.packet.to_s]) && buddy.online?
|
312
|
+
buddy = buddies.add_temporary(e.packet.to_s)
|
313
|
+
|
314
|
+
buddy.on :ready do |e|
|
315
|
+
group_chat.add buddy, e.buddy
|
316
|
+
|
317
|
+
fire :group_chat_join, group_chat: group_chat, buddy: buddy, invited_by: e.buddy
|
318
|
+
|
319
|
+
e.remove!
|
320
|
+
end
|
321
|
+
else
|
322
|
+
group_chat.add buddy, e.buddy
|
323
|
+
|
324
|
+
fire :group_chat_join, group_chat: group_chat, buddy: buddy, invited_by: e.buddy
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
on_packet :groupchat, :join do |e|
|
329
|
+
next unless group_chat = group_chats[e.packet.id]
|
330
|
+
|
331
|
+
group_chat.add e.buddy
|
332
|
+
|
333
|
+
group_chat.participants.each_value {|participant|
|
334
|
+
next if participant.id == e.buddy.id
|
335
|
+
|
336
|
+
participant.send_packet [:groupchat, :invited], group_chat.id, e.buddy.id
|
337
|
+
}
|
338
|
+
|
339
|
+
fire :group_chat_join, group_chat: group_chat, buddy: e.buddy
|
340
|
+
end
|
341
|
+
|
342
|
+
on_packet :groupchat, :leave do |e|
|
343
|
+
next unless group_chat = e.buddy.group_chats[e.packet.id]
|
344
|
+
|
345
|
+
group_chat.leave e.packet.reason
|
346
|
+
end
|
347
|
+
|
348
|
+
on_packet :groupchat, :message do |e|
|
349
|
+
next unless group_chat = group_chats[e.packet.id]
|
350
|
+
|
351
|
+
fire :group_chat_message, group_chat: group_chat, buddy: e.buddy, message: e.packet.to_str
|
352
|
+
end
|
353
|
+
|
354
|
+
before :disconnect do |e|
|
355
|
+
e.buddy.group_chats.each_value {|group_chat|
|
356
|
+
group_chat.leave
|
357
|
+
}
|
358
|
+
end
|
359
|
+
|
360
|
+
after :group_chat_leave do |e|
|
361
|
+
if e.group_chat.empty? && group_chats.has_key?(e.group_chat.id)
|
362
|
+
group_chats.destroy e.group_chat.id
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# broadcast support
|
367
|
+
on_packet :broadcast, :message do |e|
|
368
|
+
broadcasts.received e.packet.to_str
|
369
|
+
end
|
370
|
+
|
371
|
+
# latency support
|
372
|
+
on_packet :latency, :ping do |e|
|
373
|
+
e.buddy.send_packet [:latency, :pong], e.packet.id
|
374
|
+
end
|
375
|
+
|
376
|
+
on_packet :latency, :pong do |e|
|
377
|
+
e.buddy.latency.pong(e.packet.id)
|
378
|
+
|
379
|
+
fire :latency, buddy: e.buddy, amount: e.buddy.latency.to_f, id: e.packet.id
|
380
|
+
end
|
381
|
+
|
382
|
+
yield self if block_given?
|
383
|
+
end
|
384
|
+
|
385
|
+
def address
|
386
|
+
"#{id}.onion"
|
387
|
+
end
|
388
|
+
|
389
|
+
def client
|
390
|
+
@client || 'ruby-torchat'
|
391
|
+
end
|
392
|
+
|
393
|
+
def version
|
394
|
+
@version || Torchat.version
|
395
|
+
end
|
396
|
+
|
397
|
+
def tor
|
398
|
+
Struct.new(:host, :port).new(
|
399
|
+
@config['connection']['outgoing']['host'],
|
400
|
+
@config['connection']['outgoing']['port'].to_i
|
401
|
+
)
|
402
|
+
end
|
403
|
+
|
404
|
+
def name= (value)
|
405
|
+
@name = value
|
406
|
+
|
407
|
+
buddies.each_value {|buddy|
|
408
|
+
next unless buddy.online?
|
409
|
+
|
410
|
+
buddy.send_packet :profile_name, value
|
411
|
+
}
|
412
|
+
end
|
413
|
+
|
414
|
+
def description= (value)
|
415
|
+
@description = value
|
416
|
+
|
417
|
+
buddies.each_value {|buddy|
|
418
|
+
next unless buddy.online?
|
419
|
+
|
420
|
+
buddy.send_packet :profile_text, value
|
421
|
+
}
|
422
|
+
end
|
423
|
+
|
424
|
+
def offline?; @status == :offline; end
|
425
|
+
def online?; !offline?; end
|
426
|
+
|
427
|
+
def online!
|
428
|
+
return if online?
|
429
|
+
|
430
|
+
@status = :available
|
431
|
+
|
432
|
+
buddies.each_value {|buddy|
|
433
|
+
buddy.connect
|
434
|
+
}
|
435
|
+
end
|
436
|
+
|
437
|
+
def offline!
|
438
|
+
return if offline?
|
439
|
+
|
440
|
+
@status = :offline
|
441
|
+
|
442
|
+
buddies.each_value {|buddy|
|
443
|
+
buddy.disconnect
|
444
|
+
}
|
445
|
+
end
|
446
|
+
|
447
|
+
def status= (value)
|
448
|
+
if value.to_sym.downcase == :offline
|
449
|
+
offline!; return
|
450
|
+
end
|
451
|
+
|
452
|
+
online! if offline?
|
453
|
+
|
454
|
+
unless Protocol[:status].valid?(value)
|
455
|
+
raise ArgumentError, "#{value} is not a valid status"
|
456
|
+
end
|
457
|
+
|
458
|
+
@status = value.to_sym.downcase
|
459
|
+
|
460
|
+
buddies.each_value {|buddy|
|
461
|
+
next unless buddy.online?
|
462
|
+
|
463
|
+
buddy.send_packet :status, @status
|
464
|
+
}
|
465
|
+
end
|
466
|
+
|
467
|
+
def on (what, &block)
|
468
|
+
what = what.to_sym.downcase
|
469
|
+
|
470
|
+
@callbacks[what] << block
|
471
|
+
|
472
|
+
Event::Removable.new(self, what, &block)
|
473
|
+
end
|
474
|
+
|
475
|
+
def on_packet (*args, &block)
|
476
|
+
if args.length == 2
|
477
|
+
extension, name = args
|
478
|
+
else
|
479
|
+
extension, name = nil, args.first
|
480
|
+
end
|
481
|
+
|
482
|
+
if name
|
483
|
+
on :packet do |e|
|
484
|
+
block.call e if e.packet.type == name && e.packet.extension == extension
|
485
|
+
end
|
486
|
+
else
|
487
|
+
on :packet, &block
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
def before (what = nil, &block)
|
492
|
+
what = what.to_sym.downcase if what
|
493
|
+
|
494
|
+
@before[what] << block
|
495
|
+
|
496
|
+
Event::Removable.new(self, what, :before, &block)
|
497
|
+
end
|
498
|
+
|
499
|
+
def after (what = nil, &block)
|
500
|
+
what = what.to_sym.downcase if what
|
501
|
+
|
502
|
+
@after[what] << block
|
503
|
+
|
504
|
+
Event::Removable.new(self, what, :after, &block)
|
505
|
+
end
|
506
|
+
|
507
|
+
def remove_callback (chain = nil, name = nil, block)
|
508
|
+
if block.is_a? Event::Removable
|
509
|
+
chain = block.chain
|
510
|
+
name = block.name
|
511
|
+
block = block.block
|
512
|
+
end
|
513
|
+
|
514
|
+
if name && chain
|
515
|
+
if chain == :before
|
516
|
+
@before[name]
|
517
|
+
elsif chain == :after
|
518
|
+
@after[name]
|
519
|
+
else
|
520
|
+
@callbacks[name]
|
521
|
+
end.delete(block)
|
522
|
+
else
|
523
|
+
[@before[nil], @before[name], @callbacks[name], @after[name], @after[nil]].each {|callbacks|
|
524
|
+
callbacks.each {|callback|
|
525
|
+
if callback == block
|
526
|
+
callbacks.delete(callback)
|
527
|
+
|
528
|
+
return
|
529
|
+
end
|
530
|
+
}
|
531
|
+
}
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
def received_packet (packet)
|
536
|
+
fire :packet, packet: packet, buddy: packet.from
|
537
|
+
end
|
538
|
+
|
539
|
+
def fire (name, data = nil, &block)
|
540
|
+
name = name.downcase.to_sym
|
541
|
+
event = Event.new(self, name, data, &block)
|
542
|
+
|
543
|
+
[@before[nil], @before[name], @callbacks[name], @after[name], @after[nil]].each {|callbacks|
|
544
|
+
callbacks.each {|callback|
|
545
|
+
begin
|
546
|
+
callback.call event
|
547
|
+
rescue => e
|
548
|
+
Torchat.debug e
|
549
|
+
end
|
550
|
+
|
551
|
+
if event.remove?
|
552
|
+
remove_callback(callback)
|
553
|
+
event.removed!
|
554
|
+
end
|
555
|
+
|
556
|
+
return if event.stopped?
|
557
|
+
}
|
558
|
+
}
|
559
|
+
end
|
560
|
+
|
561
|
+
def start (host = nil, port = nil)
|
562
|
+
host ||= @config['connection']['incoming']['host']
|
563
|
+
port ||= @config['connection']['incoming']['port'].to_i
|
564
|
+
|
565
|
+
@signature = EM.start_server host, port, Incoming do |incoming|
|
566
|
+
incoming.instance_variable_set :@session, self
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def stop
|
571
|
+
EM.stop_server @signature
|
572
|
+
|
573
|
+
@timers.each {|timer|
|
574
|
+
EM.cancel_timer(timer)
|
575
|
+
}
|
576
|
+
end
|
577
|
+
|
578
|
+
def set_timeout (*args, &block)
|
579
|
+
EM.schedule {
|
580
|
+
EM.add_timer(*args, &block).tap {|timer|
|
581
|
+
@timers.push(timer)
|
582
|
+
}
|
583
|
+
}
|
584
|
+
end
|
585
|
+
|
586
|
+
def set_interval (*args, &block)
|
587
|
+
EM.schedule {
|
588
|
+
EM.add_periodic_timer(*args, &block).tap {|timer|
|
589
|
+
@timers.push(timer)
|
590
|
+
}
|
591
|
+
}
|
592
|
+
end
|
593
|
+
|
594
|
+
def clear_timeout (what)
|
595
|
+
EM.schedule {
|
596
|
+
EM.cancel_timer(what)
|
597
|
+
}
|
598
|
+
end
|
599
|
+
|
600
|
+
alias clear_interval clear_timeout
|
601
|
+
end
|
602
|
+
|
603
|
+
end
|