ruggby 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.rdoc +2 -0
  2. data/Gemfile +2 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +38 -0
  5. data/README.md +187 -0
  6. data/Rakefile +12 -0
  7. data/init.rb +1 -0
  8. data/lib/ruggby.rb +72 -0
  9. data/lib/ruggby/action/base.rb +16 -0
  10. data/lib/ruggby/action/change_status.rb +30 -0
  11. data/lib/ruggby/action/create_message.rb +27 -0
  12. data/lib/ruggby/action/login.rb +40 -0
  13. data/lib/ruggby/action/mark.rb +27 -0
  14. data/lib/ruggby/action/new_message.rb +28 -0
  15. data/lib/ruggby/action/ping.rb +40 -0
  16. data/lib/ruggby/action/read.rb +49 -0
  17. data/lib/ruggby/callback.rb +22 -0
  18. data/lib/ruggby/client.rb +95 -0
  19. data/lib/ruggby/converter.rb +57 -0
  20. data/lib/ruggby/logger.rb +67 -0
  21. data/lib/ruggby/packet/factory.rb +39 -0
  22. data/lib/ruggby/packet/incoming/base.rb +31 -0
  23. data/lib/ruggby/packet/incoming/login_status.rb +28 -0
  24. data/lib/ruggby/packet/incoming/message.rb +35 -0
  25. data/lib/ruggby/packet/incoming/welcome.rb +33 -0
  26. data/lib/ruggby/packet/outgoing/base.rb +67 -0
  27. data/lib/ruggby/packet/outgoing/change_status.rb +57 -0
  28. data/lib/ruggby/packet/outgoing/login.rb +75 -0
  29. data/lib/ruggby/packet/outgoing/mark.rb +28 -0
  30. data/lib/ruggby/packet/outgoing/message.rb +47 -0
  31. data/lib/ruggby/packet/outgoing/ping.rb +27 -0
  32. data/lib/ruggby/password.rb +19 -0
  33. data/lib/ruggby/socket.rb +67 -0
  34. data/lib/ruggby/string_encoder.rb +42 -0
  35. data/lib/ruggby/threader.rb +21 -0
  36. data/lib/ruggby/version.rb +5 -0
  37. data/ruggby.gemspec +35 -0
  38. data/spec/callback_spec.rb +40 -0
  39. data/spec/spec_helper.rb +7 -0
  40. metadata +142 -0
@@ -0,0 +1,28 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Incoming
6
+
7
+ # Message telling as whether or not the loginj process was successfull
8
+ # It is a bit different from others because it gets one parameter on which
9
+ # we assume that the login was successful or unsuccessful
10
+ class LoginStatus
11
+
12
+ TYPE = 0x00035
13
+
14
+ def initialize(valid)
15
+ @valid = valid
16
+ end
17
+
18
+ def successful?
19
+ @valid
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,35 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Incoming
6
+
7
+ # Packet containing and incoming message
8
+ # Message packet contains not formatted data (not parsed, etc) so
9
+ class Message < RuGGby::Packet::Incoming::Base
10
+
11
+ TYPE = 0x002e
12
+ PATTERN = 'LLLLa*'
13
+
14
+ attr_reader :uin, :message, :data, :created_at
15
+
16
+ def initialize(data)
17
+ super
18
+ @uin = @data[0]
19
+ @created_at = @data[2]
20
+ @message = @data[4]
21
+ end
22
+
23
+ private
24
+
25
+ def pattern
26
+ PATTERN
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,33 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Incoming
6
+
7
+ # Welcome packet - sent by GG when we connect to their socket
8
+ # Contains a seed used to mix with password before authorization
9
+ class Welcome < RuGGby::Packet::Incoming::Base
10
+
11
+ TYPE = 0x0001
12
+ PATTERN = 'I'
13
+
14
+ attr_reader :seed
15
+
16
+ def initialize(data)
17
+ super
18
+ @seed = @data[0]
19
+ end
20
+
21
+ private
22
+
23
+ def pattern
24
+ PATTERN
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,67 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Base class for all the outgoing messages
8
+ # Contains all the methods needed to prepare the data to be send via
9
+ # TCPSocket
10
+ class Base
11
+
12
+ class << self
13
+
14
+ attr_reader :send_only_header
15
+
16
+ @send_only_header = false
17
+
18
+ # Tell us that, the package will be send without body
19
+ def only_header
20
+ @send_only_header = true
21
+ end
22
+
23
+ end
24
+
25
+ class NotImplemented < Exception; end
26
+
27
+ def initialize(params = {})
28
+ params.each do |key, value|
29
+ self.send(:"#{key}=", value)
30
+ end
31
+ end
32
+
33
+ # Pack data set according to given pattern
34
+ def pack
35
+ if self.class.send_only_header
36
+ rawbody=[].pack('')
37
+ else
38
+ rawbody = body.pack(pattern)
39
+ end
40
+
41
+ [type, rawbody.length].pack('LL') + rawbody
42
+ end
43
+
44
+ private
45
+
46
+ # Body array
47
+ def body
48
+ raise NotImplemented
49
+ end
50
+
51
+ # Packet type
52
+ def type
53
+ raise NotImplemented
54
+ end
55
+
56
+ # Pack pattern
57
+ def pattern
58
+ raise NotImplemented
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,57 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Packet send when we want to change status
8
+ # Params:
9
+ # => new_status
10
+ # => description
11
+ class ChangeStatus < RuGGby::Packet::Outgoing::Base
12
+
13
+ TYPE = 0x0038
14
+ PATTERN = 'LLL'
15
+
16
+ attr_accessor :status, :description
17
+
18
+ def initialize(status, description)
19
+ @description = description || ''
20
+
21
+ if @description.length > 0
22
+ @status = RuGGby::Converter.status_description(status)
23
+ else
24
+ @status = RuGGby::Converter.status(status)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def body
31
+ [
32
+ @status, # L
33
+ 0, # L
34
+ @description.length, # L
35
+ @description # a
36
+ ]
37
+ end
38
+
39
+ def type
40
+ TYPE
41
+ end
42
+
43
+ def pattern
44
+ if @description.length > 0
45
+ PATTERN+"a#{@description.length}"
46
+ else
47
+ PATTERN
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,75 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Packet send when we want to login into our GG account
8
+ # There is a lot of data that needs to be send here, but basically
9
+ # We need to provide:
10
+ # => :uin - gg number
11
+ # => :hash - hashed password
12
+ # We can also provide a gg status:
13
+ # => :status
14
+ class Login < RuGGby::Packet::Outgoing::Base
15
+
16
+ TYPE = 0x0031
17
+ PATTERN = 'La2Ca64LLLLSLSCCLa35L'
18
+ GG_VERSION = 'Gadu-Gadu Client build 10.0.0.10450'
19
+
20
+ attr_accessor :uin, :hash, :status, :description
21
+
22
+ def initialize(params = {})
23
+ super
24
+ @description = params[:description] || ''
25
+
26
+ if @description.length > 0
27
+ @status = RuGGby::Converter.status_description(@status)
28
+ else
29
+ @status = RuGGby::Converter.status(@status)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def body
36
+ [
37
+ @uin.to_i, # L
38
+ 'pl', # a2
39
+ 0x02, # C
40
+ @hash, # a64
41
+ @status, # L
42
+ 0, # L
43
+ 0x00000007, # L
44
+ 0, # L
45
+ 0, # S
46
+ 0, # L
47
+ 0, # S
48
+ 64, # C
49
+ 0x64, # C
50
+ GG_VERSION.length, # L
51
+ GG_VERSION, #a35
52
+ @description.length, #L,
53
+ @description # Nothing?
54
+ ]
55
+ end
56
+
57
+ def type
58
+ TYPE
59
+ end
60
+
61
+ def pattern
62
+ if @description.length > 0
63
+ PATTERN+"a#{@description.length}"
64
+ else
65
+ PATTERN
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,28 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Mark instance needs to be send to GG if we don't have (and don't want)
8
+ # any user list - if we don't send this after valid loggin process
9
+ # the GG will not communicate with us
10
+ class Mark < RuGGby::Packet::Outgoing::Base
11
+
12
+ TYPE = 0x12
13
+
14
+ only_header
15
+
16
+ private
17
+
18
+ def type
19
+ TYPE
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,47 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Out going message class
8
+ # When we want to send a message to a GG user, we need to provide
9
+ # a GG number an a message
10
+ class Message < RuGGby::Packet::Outgoing::Base
11
+
12
+ TYPE = 0x0b
13
+ PATTERN = 'LLLa*C'
14
+
15
+ attr_accessor :message, :uin
16
+
17
+ # uin => GG nr
18
+ # msg => message we want to send
19
+ def initialize(uin, msg)
20
+ # Type uin (gg number) to integer, just to be sure
21
+ @uin = uin.to_i
22
+ # Convert incoming message into ISO-8859-2 just to be sure that
23
+ # polish letters will be sent properly
24
+ @message = StringEncoder.to_output(msg)
25
+ end
26
+
27
+ private
28
+
29
+ def body
30
+ [@uin, rand(2**32), 0x08, @message, 0]
31
+ end
32
+
33
+ def pattern
34
+ PATTERN
35
+ end
36
+
37
+ def type
38
+ TYPE
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,27 @@
1
+ module RuGGby
2
+
3
+ module Packet
4
+
5
+ module Outgoing
6
+
7
+ # Ping packet is used to upkeep the TCP connection with GG
8
+ # Needs to be send every 3-5 minutes to tell GG that we are alive
9
+ class Ping < RuGGby::Packet::Outgoing::Base
10
+
11
+ TYPE = 0x08
12
+
13
+ only_header
14
+
15
+ private
16
+
17
+ def type
18
+ TYPE
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'digest/sha1'
2
+
3
+
4
+ module RuGGby
5
+
6
+ # Password encryption method
7
+ class Password
8
+
9
+ # SHA1
10
+ HASH_TYPE = 0x02
11
+
12
+ # Hash password with seed to a format accepted by GG server
13
+ def self.hash(password, seed)
14
+ Digest::SHA1.digest(password+[seed].pack('L'))
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,67 @@
1
+ require 'socket'
2
+ require 'open-uri'
3
+
4
+ module RuGGby
5
+
6
+ # TCP socket proxy
7
+ class Socket
8
+ # GG version that we send to the URL
9
+ VERSION = '10.0.0.7669'
10
+ # URL with 'real' server address
11
+ URL = 'http://appmsg.gadu-gadu.pl'
12
+
13
+ class NotOperating < Exception; end
14
+
15
+ attr_reader :host, :port
16
+
17
+ # We need to provide a GG number because different GG numbers might get
18
+ # different 'real' servers
19
+ def initialize(gg_nr)
20
+ conn_params = self.class.connection_params(gg_nr)
21
+ @host = conn_params[:host]
22
+ @port = conn_params[:port]
23
+ ensure_operating
24
+
25
+ socket
26
+ end
27
+
28
+ # Before writing to socket, pack the incoming data
29
+ # We use pack because we assume that only objects from
30
+ # ruGGby::Packet::Outgoing might be send and all of them implemenent this
31
+ # method
32
+ def write(data)
33
+ socket.write(data.pack)
34
+ end
35
+
36
+ private
37
+
38
+ # Send all the unknown methods invokations into TCPSocket
39
+ def method_missing(name, *args, &block)
40
+ socket.public_send(name, *args, &block)
41
+ end
42
+
43
+ # Create a TCPSocket
44
+ def socket
45
+ @socket ||= ::TCPSocket.new(@host , @port)
46
+ end
47
+
48
+ # Get connection params
49
+ def self.connection_params(gg_nr)
50
+ gg_info = open(build_url(gg_nr)).readlines[0].split(/\s/)[2].split(':')
51
+ {:host => gg_info[0], :port => gg_info[1]}
52
+ end
53
+
54
+ # Buduje caly adres ktory bedziemy odpytywac o IP i port serwera do laczenia
55
+ # sie z GG
56
+ def self.build_url(gg_nr)
57
+ @build_url ||= "#{URL}/appsvc/appmsg_ver8.asp?fmnumber=#{gg_nr}&version=#{VERSION}&lastmsg=0"
58
+ end
59
+
60
+ # Raise exception if we will get a not operating 'real' server
61
+ def ensure_operating
62
+ raise NotOperating if @host == 'notoperating'
63
+ end
64
+
65
+ end
66
+
67
+ end