ruggby 0.3.0

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.
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