rgadu 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/gg/dccserver.rb +3 -1
  2. data/lib/gg.rb +124 -93
  3. 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', 0x24 => '6.1',
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. Opcjonalnie uruchamia serwer DCC na porcie *dccport* i przesy�a serwerowi tablic� z list� kontakt�w.
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, dccport=0, contacts=nil, server='217.17.45.146', port=8074)
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
- hash=gg_login_hash(password, seed)
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
- write(0x15, 'LLLLCLSLSCC', uin, hash, 0x02, 0x20, 0x00, localip, dccport, localip, dccport, 0x00, 0xbe)
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 and contacts.length > 0
64
- write(0x10, 'LC'*contacts.length, *(contacts.map {|contact| [contact, 0x03] }.flatten))
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*. Mo�na poda� hash SHA1 pliku w postaci ci�gu bajt�w, w przeciwnym razie hash jest obliczany w razie potrzeby. 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.
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 "No DCC server" unless @dcc
189
- if File.exist?(filename)
190
- if @status[uin] and version=@status[uin][:version] and version.to_f >= 7.6
191
- write(0x23, 'L', 0x04)
192
- sleep 0.1 until code = @dcc_code.shift
193
- filesize=File.size(filename)
194
- unless hash
195
- checksum = Digest::SHA1.new
196
- file = File.open(filename, 'rb')
197
- while chunk = file.read(1048576)
198
- checksum << chunk
199
- end
200
- file.close
201
- hash = checksum.digest
202
- end
203
- write(0x20, 'a8LLLa226a29LLa20', code, @uin, uin, 0x04, File.basename(filename), '', filesize, 0, hash)
204
- @dcc_action[code] = lambda do |offset|
205
- if offset == -1
206
- callback.call(:rejected)
207
- else
208
- if callback
209
- @dcc.add_send_code(code, filename, filesize, offset) {|pos| callback.call(pos) }
210
- else
211
- @dcc.add_send_code(code, filename, filesize, offset)
212
- end
213
- write(0x1f, 'LLa8a21a43', uin, 0x01, code, @socket.addr[3]+' '+@dccport.to_s, '')
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
- code
234
- else
235
- if @socket.addr[3] =~ /^(10\.|172\.16\.|192\.168\.)/ and ip = get_ip(uin) and port = get_port(uin) and port > 1
236
- @dcc_client_thread << Thread.new(ip, port, @uin, uin, filename, callback) do |ip, port, client_uin, server_uin, filename, callback|
237
- begin
238
- client = DCCClient.new(ip, port, client_uin, server_uin)
239
- @dcc_client << client
240
- if callback
241
- client.client_send(filename) {|pos| callback.call(pos) }
242
- else
243
- client.client_send(filename)
244
- end
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
- uin
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 {|uin, filename, filesize| action.call(uin, filename, filesize) }
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.class == Array
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
- if callback
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
- if callback
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.1.1
7
- date: 2007-08-22 00:00:00 +02:00
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