rgadu 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/gg/dccserver.rb +3 -1
- data/lib/gg.rb +124 -93
- metadata +2 -2
data/lib/gg/dccserver.rb
CHANGED
@@ -28,6 +28,7 @@ class DCCServer
|
|
28
28
|
type=packet_raw.unpack('L')[0]
|
29
29
|
if type==0x03
|
30
30
|
if filename=@queue[client_uin]
|
31
|
+
@queue.delete(client_uin)
|
31
32
|
init_send(client_uin, session, filename)
|
32
33
|
end
|
33
34
|
elsif type==0x02
|
@@ -36,6 +37,7 @@ class DCCServer
|
|
36
37
|
elsif @queue[packet_raw]
|
37
38
|
code=packet_raw
|
38
39
|
direction, filename, filesize, offset = @queue[code]
|
40
|
+
@queue.delete(code)
|
39
41
|
if direction == :out
|
40
42
|
session.write(code)
|
41
43
|
begin
|
@@ -110,7 +112,7 @@ class DCCServer
|
|
110
112
|
if type==0x01
|
111
113
|
packet_raw = session.read(332) or return
|
112
114
|
packet_body = packet_raw.unpack('LLLLLLLLLLLLLLa262a14')
|
113
|
-
filename = packet_body[14].sub(/\x00
|
115
|
+
filename = packet_body[14].sub(/\x00.*/m, '').sub(%r{.*/}, '')
|
114
116
|
filesize = packet_body[11]
|
115
117
|
if @prepare_recv and local_filename=@prepare_recv.call(client_uin, filename, filesize)
|
116
118
|
if local_filename.class == Array
|
data/lib/gg.rb
CHANGED
@@ -21,13 +21,15 @@ class GG
|
|
21
21
|
:invisible => 0x16,
|
22
22
|
:notavail => 0x15}
|
23
23
|
VERSION = {0x18 => '5.0', 0x19 => '5.0', 0x1b => '5.0', 0x1c => '5.7', 0x1e => '5.7',
|
24
|
-
0x20 => '6.0', 0x21 => '6.0', 0x22 => '6.0',
|
25
|
-
0x25 => '7.0', 0x26 => '7.0', 0x27 => '7.0', 0x28 => '7.5', 0x29 => '7.6', 0x2a => '7.7'}
|
24
|
+
0x20 => '6.0', 0x21 => '6.0', 0x22 => '6.0',
|
25
|
+
0x24 => '7.0', 0x25 => '7.0', 0x26 => '7.0', 0x27 => '7.0', 0x28 => '7.5', 0x29 => '7.6', 0x2a => '7.7'}
|
26
26
|
TO_GENDER = {:male => '2', :female => '1'}
|
27
27
|
PATTERN = {0x02 => 'LLa*',
|
28
28
|
0x0f => 'LCLSCCCa*',
|
29
|
+
0x17 => 'LCLSCCCa*',
|
29
30
|
0x0c => 'LCLSLSa*',
|
30
31
|
0x11 => 'LCLSCCCCa*',
|
32
|
+
0x18 => 'LCLSCCCCa*',
|
31
33
|
0x0e => 'CLa*',
|
32
34
|
0x0a => 'LLLLa*',
|
33
35
|
0x23 => 'La8',
|
@@ -36,42 +38,64 @@ class GG
|
|
36
38
|
0x1f => 'LLa8a21a43'}
|
37
39
|
|
38
40
|
=begin rdoc
|
39
|
-
Tworzy nowy obiekt GG i loguje sie na serwerze
|
41
|
+
Tworzy nowy obiekt GG i loguje sie na serwerze uzywajac numeru *uin* i hasla *password*. Dodatkowe parametry to: :server => serwer GG, :port => port na serwerze GG, :dccport => port dla polaczen bezposrednich, :contacts => tablica z lista kontaktow, :version => wersja protokolu.
|
40
42
|
=end
|
41
|
-
def initialize(uin, password,
|
43
|
+
def initialize(uin, password, params={})
|
44
|
+
server = params[:server] || '217.17.45.146'
|
45
|
+
port = params[:port] || 8074
|
46
|
+
dccport = params[:dccport]
|
47
|
+
contacts = params[:contacts] || []
|
48
|
+
version = (params[:version] || 6.0).to_f
|
42
49
|
@uin=uin
|
43
50
|
@dccport=dccport
|
44
51
|
@socket=TCPSocket.new(server, port)
|
45
52
|
type, length = read_header
|
46
53
|
if type!=0x01
|
47
54
|
raise "Packet not recognized"
|
48
|
-
end
|
55
|
+
end
|
49
56
|
seed=read_body(length, 'L')[0]
|
50
|
-
|
51
|
-
if dccport>0
|
57
|
+
if dccport
|
52
58
|
@dcc = DCCServer.new(dccport, uin)
|
53
59
|
localip=@socket.addr[3].split('.').collect {|x| x.to_i }.pack('C4').unpack('L')[0]
|
54
60
|
else
|
55
61
|
localip=0
|
56
62
|
end
|
57
|
-
|
63
|
+
if version >= 7.7
|
64
|
+
protocol = 0x2a
|
65
|
+
elsif version >= 7.6
|
66
|
+
protocol = 0x29
|
67
|
+
elsif version >= 7
|
68
|
+
protocol = 0x24
|
69
|
+
else
|
70
|
+
protocol = 0x20
|
71
|
+
end
|
72
|
+
if version >= 7
|
73
|
+
hash = Digest::SHA1.digest(password + [seed].pack('L'))
|
74
|
+
write(0x19, 'LCa64LLCLSLSCC', uin, 0x02, hash, 0x02, protocol, 0x00, localip, dccport, localip, dccport, 0x00, 0xbe)
|
75
|
+
else
|
76
|
+
hash = gg_login_hash(password, seed)
|
77
|
+
write(0x15, 'LLLLCLSLSCC', uin, hash, 0x02, 0x20, 0x00, localip, dccport, localip, dccport, 0x00, 0xbe)
|
78
|
+
end
|
58
79
|
type, length = read_header
|
59
80
|
read_body(length)
|
60
81
|
unless type==0x03 or type==0x14
|
61
82
|
raise "Authorization failed"
|
62
83
|
end
|
63
|
-
if contacts
|
64
|
-
|
84
|
+
if contacts.length > 0
|
85
|
+
contactpacket = []
|
86
|
+
contacts.each {|contact| contactpacket.push(contact, 0x03) }
|
87
|
+
write(0x10, 'LC'*contacts.length, *contactpacket)
|
65
88
|
else
|
66
89
|
write(0x12, '')
|
67
90
|
end
|
68
91
|
@action, @status, @search_reply, @dcc_code, @dcc_action, @dcc_client_action = {}, {}, {}, [], {}, {}
|
92
|
+
contacts.each {|contact| @status[contact] = {:status => :notavail} }
|
69
93
|
@action_thread, @dcc_client, @dcc_client_thread = [], [], []
|
70
94
|
@action[0x0a] = lambda {|sender, seq, time, cl, message| msg_received(sender, seq, time, cl, message) }
|
71
95
|
@action[0x02] = lambda {|uin, status, description| status_changed(uin, status, description) }
|
72
|
-
@action[0x0f] = lambda {|uin, status, ip, port, version, x, y, description| status_changed(uin, status, description, ip, port, version) }
|
96
|
+
@action[0x0f] = @action[0x17] = lambda {|uin, status, ip, port, version, x, y, description| status_changed(uin, status, description, ip, port, version) }
|
73
97
|
@action[0x0c] = lambda {|uin, status, ip, port, version, x, description| status_changed(uin, status, description, ip, port, version) }
|
74
|
-
@action[0x11] = lambda {|uin, status, ip, port, version, imgsize, x, descsize, description| status_changed(uin, status, description, ip, port, version) }
|
98
|
+
@action[0x11] = @action[0x18] = lambda {|uin, status, ip, port, version, imgsize, x, descsize, description| status_changed(uin, status, description, ip, port, version) }
|
75
99
|
@action[0x0e] = lambda {|type, seq, response| @search_reply[seq] = response }
|
76
100
|
@action[0x23] = lambda {|type, code| @dcc_code << code }
|
77
101
|
@action[0x21] = lambda {|uin, code, offset, x| @dcc_action[code].call(offset) if @dcc_action[code] }
|
@@ -182,80 +206,59 @@ Wyszukiwanie w katalogu publicznym. *params* moze byc numerem GG lub tablica aso
|
|
182
206
|
end
|
183
207
|
|
184
208
|
=begin rdoc
|
185
|
-
Wysyla plik *filename* do *uin*.
|
209
|
+
Wysyla plik *filename* do *uin*. Jesli dolaczono blok kodu, blok ten bedzie wywolywany podczas wysylania pliku. Blok przyjmuje jeden parametr - aktualn� pozycj� pliku w bajtach, symbol :rejected je�li po��czenie zosta�o odrzucone lub :aborted w przypadku przerwania transferu. Funkcja w zaleznosci od wersji protokolu DCC zwraca kod transferu lub numer GG odbiorcy.
|
186
210
|
=end
|
187
211
|
def dcc_request(uin, filename, hash=nil, &callback)
|
188
|
-
raise "
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
if
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if @socket.addr[3] =~ /^(10\.|172\.16\.|192\.168\.)/
|
215
|
-
@dcc_client_action[code] = lambda do |ip, port|
|
216
|
-
@dcc_client_thread << Thread.new(ip, port, @uin, uin, code, filename, filesize, offset, callback) do |ip, port, client_uin, server_uin, code, filename, filesize, offset, callback|
|
217
|
-
begin
|
218
|
-
client = DCCClient.new(ip, port, client_uin, server_uin, code)
|
219
|
-
@dcc_client << client
|
220
|
-
if callback
|
221
|
-
client.client_send7(filename, filesize, offset) {|pos| callback.call(pos) }
|
222
|
-
else
|
223
|
-
client.client_send7(filename, filesize, offset)
|
224
|
-
end
|
225
|
-
rescue
|
226
|
-
warn "Error: #{$!}"
|
227
|
-
end
|
212
|
+
raise "dcc_request: no DCC server" unless @dcc
|
213
|
+
raise "dcc_request: file #{filename} not found" unless File.exist?(filename)
|
214
|
+
raise "dcc_request: #{uin} is not on the contact list" unless contact?(uin)
|
215
|
+
warn "dcc_request: optional parameter 'hash' is ignored and will be removed" if hash
|
216
|
+
return nil if get_status(uin) == :notavail
|
217
|
+
if version = get_version(uin) and version.to_f >= 7.6
|
218
|
+
write(0x23, 'L', 0x04)
|
219
|
+
sleep 0.1 until code = @dcc_code.shift
|
220
|
+
filesize=File.size(filename)
|
221
|
+
hash = filehash(filename, version)
|
222
|
+
write(0x20, 'a8LLLa226a29LLa20', code, @uin, uin, 0x04, File.basename(filename), '', filesize, 0, hash)
|
223
|
+
@dcc_action[code] = lambda do |offset|
|
224
|
+
if offset == -1
|
225
|
+
callback.call(:rejected) if callback
|
226
|
+
else
|
227
|
+
@dcc.add_send_code(code, filename, filesize, offset, &callback)
|
228
|
+
write(0x1f, 'LLa8a21a43', uin, 0x01, code, @socket.addr[3]+' '+@dccport.to_s, '')
|
229
|
+
if @socket.addr[3] =~ /^(10\.|172\.16\.|192\.168\.)/
|
230
|
+
@dcc_client_action[code] = lambda do |ip, port|
|
231
|
+
@dcc_client_thread << Thread.new(ip, port, @uin, uin, code, filename, filesize, offset, callback) do |ip, port, client_uin, server_uin, code, filename, filesize, offset, callback|
|
232
|
+
begin
|
233
|
+
client = DCCClient.new(ip, port, client_uin, server_uin, code)
|
234
|
+
@dcc_client << client
|
235
|
+
client.client_send7(filename, filesize, offset, &callback)
|
236
|
+
rescue
|
237
|
+
warn "Error: #{$!}"
|
228
238
|
end
|
229
239
|
end
|
230
240
|
end
|
231
241
|
end
|
232
242
|
end
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
rescue
|
246
|
-
warn "Error: #{$!}"
|
247
|
-
end
|
248
|
-
end
|
249
|
-
else
|
250
|
-
if callback
|
251
|
-
@dcc.add_send(uin, filename) {|pos| callback.call(pos) }
|
252
|
-
else
|
253
|
-
@dcc.add_send(uin, filename)
|
243
|
+
end
|
244
|
+
code
|
245
|
+
else
|
246
|
+
return nil if get_port(uin) == 2 or get_port(uin) == 0
|
247
|
+
if @socket.addr[3] =~ /^(10\.|172\.16\.|192\.168\.)/ and ip = get_ip(uin) and port = get_port(uin) and port > 2
|
248
|
+
@dcc_client_thread << Thread.new(ip, port, @uin, uin, filename, callback) do |ip, port, client_uin, server_uin, filename, callback|
|
249
|
+
begin
|
250
|
+
client = DCCClient.new(ip, port, client_uin, server_uin)
|
251
|
+
@dcc_client << client
|
252
|
+
client.client_send(filename, &callback)
|
253
|
+
rescue
|
254
|
+
warn "Error: #{$!}"
|
254
255
|
end
|
255
|
-
write(0x0b, 'LLLa*C', uin, rand(2**32), 0x10, "\x02", 0)
|
256
256
|
end
|
257
|
-
|
257
|
+
else
|
258
|
+
@dcc.add_send(uin, filename, &callback)
|
259
|
+
write(0x0b, 'LLLa*C', uin, rand(2**32), 0x10, "\x02", 0)
|
258
260
|
end
|
261
|
+
uin
|
259
262
|
end
|
260
263
|
end
|
261
264
|
|
@@ -279,12 +282,13 @@ Ustawia blok kodu jako zdarzenie wywolywane, gdy ktos zechce wyslac do nas plik.
|
|
279
282
|
def on_dcc_recv(&action)
|
280
283
|
raise "No DCC server" unless @dcc
|
281
284
|
@dcc_recv_action = action
|
282
|
-
@dcc.on_recv
|
285
|
+
@dcc.on_recv(&action)
|
283
286
|
@action[0x20] = lambda do |code, sender_uin, recv_uin, type, filename, x, filesize, y, hash|
|
284
287
|
if type==0x04
|
285
288
|
filename.sub!(/\x00.*/m, '')
|
289
|
+
filename.sub!(%r{.*/}, '')
|
286
290
|
if local_filename=action.call(sender_uin, filename, filesize)
|
287
|
-
if local_filename.
|
291
|
+
if local_filename.respond_to?(:to_ary)
|
288
292
|
local_filename, callback = local_filename
|
289
293
|
end
|
290
294
|
if File.exist?(local_filename)
|
@@ -292,11 +296,7 @@ Ustawia blok kodu jako zdarzenie wywolywane, gdy ktos zechce wyslac do nas plik.
|
|
292
296
|
else
|
293
297
|
offset = 0
|
294
298
|
end
|
295
|
-
|
296
|
-
@dcc.add_recv_code(code, local_filename, filesize, offset) {|pos| callback.call(pos) }
|
297
|
-
else
|
298
|
-
@dcc.add_recv_code(code, local_filename, filesize, offset)
|
299
|
-
end
|
299
|
+
@dcc.add_recv_code(code, local_filename, filesize, offset, &callback)
|
300
300
|
write(0x21, 'La8LL', sender_uin, code, offset, 0)
|
301
301
|
write(0x1f, 'LLa8a21a43', sender_uin, 0x01, code, @socket.addr[3]+' '+@dccport.to_s, '')
|
302
302
|
@dcc_client_action[code] = lambda do |ip, port|
|
@@ -304,11 +304,7 @@ Ustawia blok kodu jako zdarzenie wywolywane, gdy ktos zechce wyslac do nas plik.
|
|
304
304
|
begin
|
305
305
|
client = DCCClient.new(ip, port, client_uin, server_uin, code)
|
306
306
|
@dcc_client << client
|
307
|
-
|
308
|
-
client.client_recv7(filename, filesize, offset) {|pos| callback.call(pos) }
|
309
|
-
else
|
310
|
-
client.client_recv7(filename, filesize, offset)
|
311
|
-
end
|
307
|
+
client.client_recv7(filename, filesize, offset, &callback)
|
312
308
|
rescue
|
313
309
|
warn "Error: #{$!}"
|
314
310
|
end
|
@@ -321,39 +317,46 @@ Ustawia blok kodu jako zdarzenie wywolywane, gdy ktos zechce wyslac do nas plik.
|
|
321
317
|
end
|
322
318
|
end
|
323
319
|
|
320
|
+
=begin rdoc
|
321
|
+
Sprawdza czy *uin* jest na liscie kontaktow. Zwraca true lub false.
|
322
|
+
=end
|
323
|
+
def contact?(uin)
|
324
|
+
@status[uin] ? true : false
|
325
|
+
end
|
326
|
+
|
324
327
|
=begin rdoc
|
325
328
|
Zwraca status *uin* jesli jest on na liscie kontaktow. Status moze byc jednym z symboli: :avail, :busy, :notavail.
|
326
329
|
=end
|
327
330
|
def get_status(uin)
|
328
|
-
@status[uin][:status]
|
331
|
+
@status[uin][:status] if @status[uin]
|
329
332
|
end
|
330
333
|
|
331
334
|
=begin rdoc
|
332
335
|
Zwraca opis *uin* jesli jest on na liscie kontaktow.
|
333
336
|
=end
|
334
337
|
def get_description(uin)
|
335
|
-
@status[uin][:description]
|
338
|
+
@status[uin][:description] if @status[uin]
|
336
339
|
end
|
337
340
|
|
338
341
|
=begin rdoc
|
339
342
|
Zwraca adres IP *uin* jako lancuch znakow w postaci 4 liczb oddzielonych kropkami.
|
340
343
|
=end
|
341
344
|
def get_ip(uin)
|
342
|
-
@status[uin][:ip]
|
345
|
+
@status[uin][:ip] if @status[uin]
|
343
346
|
end
|
344
347
|
|
345
348
|
=begin rdoc
|
346
349
|
Zwraca port, na ktorym *uin* ma serwer DCC.
|
347
350
|
=end
|
348
351
|
def get_port(uin)
|
349
|
-
@status[uin][:port]
|
352
|
+
@status[uin][:port] if @status[uin]
|
350
353
|
end
|
351
354
|
|
352
355
|
=begin rdoc
|
353
356
|
Zwraca wersje klienta *uin* jako liczbe Float lub nil jesli nie rozpoznano wersji.
|
354
357
|
=end
|
355
358
|
def get_version(uin)
|
356
|
-
@status[uin][:version]
|
359
|
+
@status[uin][:version] if @status[uin]
|
357
360
|
end
|
358
361
|
|
359
362
|
=begin rdoc
|
@@ -380,6 +383,7 @@ Rozlacza sie z serwerem GG, wylacza serwer DCC i zamyka wszystkie polaczenia DCC
|
|
380
383
|
@socket.close
|
381
384
|
@dcc.close if @dcc
|
382
385
|
@dcc_client_thread.each {|t| t.exit }
|
386
|
+
nil
|
383
387
|
end
|
384
388
|
|
385
389
|
def write(type, pattern, *params)
|
@@ -419,6 +423,10 @@ Rozlacza sie z serwerem GG, wylacza serwer DCC i zamyka wszystkie polaczenia DCC
|
|
419
423
|
end
|
420
424
|
end
|
421
425
|
|
426
|
+
def set_action(type, &action)
|
427
|
+
@action[type] = action
|
428
|
+
end
|
429
|
+
|
422
430
|
def ping
|
423
431
|
write(0x08, '')
|
424
432
|
end
|
@@ -441,6 +449,29 @@ Rozlacza sie z serwerem GG, wylacza serwer DCC i zamyka wszystkie polaczenia DCC
|
|
441
449
|
y
|
442
450
|
end
|
443
451
|
|
452
|
+
def filehash(filename, version=7.7)
|
453
|
+
digest = Digest::SHA1.new
|
454
|
+
File.open(filename) do |f|
|
455
|
+
if File.size(filename) <= 10*1024*1024
|
456
|
+
while chunk = f.read(1024*1024)
|
457
|
+
digest << chunk
|
458
|
+
end
|
459
|
+
else
|
460
|
+
if version.to_s == '7.6'
|
461
|
+
chunksize = 0xfe000
|
462
|
+
else
|
463
|
+
chunksize = 0x100000
|
464
|
+
end
|
465
|
+
seekjump = (File.size(filename)-0x100000)/9
|
466
|
+
9.times do |i|
|
467
|
+
f.seek i*seekjump
|
468
|
+
digest << f.read(chunksize)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
digest.digest
|
473
|
+
end
|
474
|
+
|
444
475
|
def msg_received(sender, seq, time, cl, message)
|
445
476
|
if cl == 0x10 and message.chomp(0.chr) == 2.chr
|
446
477
|
if @dcc_recv_action and ip = get_ip(sender) and port = get_port(sender) and port > 1
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rgadu
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-09-23 00:00:00 +02:00
|
8
8
|
summary: A simple pure-Ruby implementation of the Gadu-Gadu protocol.
|
9
9
|
require_paths:
|
10
10
|
- lib
|