XLiveServices 0.0.2 → 0.1.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.
@@ -12,6 +12,10 @@ module XLiveServices
12
12
  global :convert_response_tags_to, :camelcase
13
13
  global :soap_version, 2
14
14
  global :namespaces, { 'xmlns:a' => 'http://www.w3.org/2005/08/addressing' }
15
+ global :open_timeout, 3
16
+ global :ssl_version, :TLSv1
17
+ global :ssl_verify_mode, :none if LiveIdentity.isAvailable?
18
+ global :ssl_ca_cert_file, XLiveServices::CERT_FILE
15
19
 
16
20
  ConfigurationName = 'IMarketplacePublic'
17
21
 
@@ -22,11 +26,13 @@ module XLiveServices
22
26
  end
23
27
 
24
28
  def initialize(endpoint, wgxService)
29
+ raise 'Invalid WgxService Token!' if wgxService.nil? or wgxService.Token.nil? or wgxService.Token.empty?
25
30
  @WgxService = wgxService
26
31
  client.globals[:endpoint] = endpoint
27
32
  end
28
33
 
29
34
  def self.BuildOfferGUID(offerID, titleID=nil)
35
+ return offerID.to_s if offerID.to_s.match(/^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/i)
30
36
  titleID = offerID >> 32 if titleID.nil?
31
37
  offerID &= 0xffffffff
32
38
  "%08x-0000-4000-8000-0000%08x" % [offerID, titleID]
@@ -42,12 +48,12 @@ module XLiveServices
42
48
 
43
49
  def GetPurchaseHistory(locale, pageNum = 1, orderBy = SortField::Title)
44
50
  client.globals[:soap_header] = GetHeader(__callee__)
45
- client.call __callee__, message: { locale: locale, pageNum: pageNum, orderBy: Utils::Serialization::Serialize('enum', orderBy) }
51
+ client.call __callee__, message: { locale: locale, pageNum: pageNum, orderBy: Utils::Serialization::Serialize(orderBy, 'enum') }
46
52
  end
47
53
 
48
54
  def ReadUserSettings(titleID, settings)
49
55
  client.globals[:soap_header] = GetHeader(__callee__)
50
- client.call __callee__, message: { titleID: titleID, settings: Utils::Serialization::Serialize('uint[]', settings) }
56
+ client.call __callee__, message: { titleID: titleID, settings: Utils::Serialization::Serialize(settings, 'uint[]') }
51
57
  end
52
58
 
53
59
  def GetOfferDetailsPublic(locale, offerGUID)
@@ -72,7 +78,7 @@ module XLiveServices
72
78
 
73
79
  def GetMediaUrls(urls, offerGUID)
74
80
  client.globals[:soap_header] = GetHeader(__callee__)
75
- client.call __callee__, message: { urls: Utils::Serialization::Serialize('string[]', urls), offerID: offerGUID }
81
+ client.call __callee__, message: { urls: Utils::Serialization::Serialize(urls, 'string[]'), offerID: offerGUID }
76
82
  end
77
83
 
78
84
  end
@@ -0,0 +1,95 @@
1
+ require 'tempfile'
2
+ require 'libmspack'
3
+ require 'multi_xml'
4
+
5
+ module XLiveServices
6
+ module Media
7
+ class DownloadError < RuntimeError; end
8
+ class DownloadForbidden < DownloadError; end
9
+
10
+ def self.Download(urls, target, overwrite = false, keep = false)
11
+ uris = []
12
+ urls.each do |url|
13
+ uris << [URI.parse(url), url]
14
+ end
15
+ uris.each do |uri|
16
+ path = File::expand_path(File.basename(uri.first.path), target)
17
+ request = HTTPI::Request.new(uri.last)
18
+ response = nil
19
+ file = nil
20
+ begin
21
+ if overwrite or not File.exist?(path) or File::size(path).zero?
22
+ response = nil
23
+ file = File.open(path, 'wb')
24
+ request.on_body do |data|
25
+ file.write(data)
26
+ end
27
+ yield(request, path)
28
+ response = HTTPI.get(request)
29
+ if response.error?
30
+ file.truncate(0)
31
+ if response.code == 403
32
+ raise DownloadForbidden.new(response.body)
33
+ else
34
+ raise DownloadError.new(response.code)
35
+ end
36
+ end
37
+ file.close
38
+ file = nil
39
+ else
40
+ yield(nil, path)
41
+ end
42
+ urls.delete(uri.last)
43
+ rescue Exception => e
44
+ file.close if file
45
+ FileUtils.remove_file(path, true) unless keep
46
+ raise e
47
+ end
48
+ end
49
+ end
50
+
51
+ def self.IsManifest?(path)
52
+ parts = path.split('.')
53
+ return false if parts.length < 2
54
+ parts[-2].end_with?('_manifest')
55
+ end
56
+
57
+ def self.IsSupportedManifest?(path)
58
+ path.end_with?('.cab')
59
+ end
60
+
61
+ def self.GetManifestLinks(manifest_cab)
62
+ links = []
63
+ decompressor = LibMsPack::CabDecompressor.new
64
+ decompressor.setParam(LibMsPack::MSCABD_PARAM_FIXMSZIP, 1)
65
+ cab = decompressor.open(manifest_cab)
66
+ begin
67
+ file = cab.files
68
+ begin
69
+ if file.getFilename.casecmp('Content\OfferManifest.xml').zero?
70
+ xmlFile = Tempfile.new('manifest_xml')
71
+ decompressor.extract(file, xmlFile.path)
72
+ begin
73
+ xmlFile.open
74
+ data = MultiXml.parse(xmlFile)
75
+ items = data['OfferManifest']['Items']['Item']
76
+ items = [items] unless items.is_a?(Array)
77
+ items.each do |item|
78
+ links << item['Link']['Url']
79
+ end
80
+ ensure
81
+ xmlFile.close
82
+ xmlFile.unlink
83
+ end
84
+ break
85
+ end
86
+ end until (file = file.next).nil?
87
+ ensure
88
+ decompressor.close(cab)
89
+ decompressor.destroy
90
+ end
91
+ links
92
+ end
93
+
94
+ end
95
+ end
@@ -18,8 +18,19 @@ module XLiveServices
18
18
  namespace + configurationName + '/' + name
19
19
  end
20
20
 
21
+ def self.CleanURLs(urls)
22
+ clean = []
23
+ urls.each do |url|
24
+ uri = URI.parse(url)
25
+ uri.query = nil
26
+ uri.fragment = nil
27
+ clean << uri.to_s
28
+ end
29
+ clean
30
+ end
31
+
21
32
  class Serialization
22
- def self.Serialize(type, data)
33
+ def self.Serialize(data, type)
23
34
  serialized = {}
24
35
  case type
25
36
  when 'enum'
@@ -33,6 +44,17 @@ module XLiveServices
33
44
  end
34
45
  serialized
35
46
  end
47
+
48
+ def self.Deserialize(data, type)
49
+ unserialized = nil
50
+ case type
51
+ when 'string[]'
52
+ unserialized = data['string']
53
+ unserialized = [] unless unserialized
54
+ unserialized = [unserialized] unless unserialized.is_a?(Array)
55
+ end
56
+ unserialized
57
+ end
36
58
  end
37
59
  end
38
60
  end
@@ -1,3 +1,4 @@
1
1
  module XLiveServices
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
4
+
@@ -11,9 +11,14 @@ module XLiveServices
11
11
  @Locale = 'en-US'
12
12
  @Locale = locale if locale
13
13
  @Config = XLiveServices.GetLcwConfig(@Locale)
14
- @Live = LiveIdentity.new(LiveIdGUID, LiveIdVersion, :NO_UI, { :IDCRL_OPTION_ENVIRONMENT => 'Production' })
14
+ @Live = nil
15
+ @Identity = nil
15
16
 
16
- if username.nil?
17
+ if LiveIdentity.isAvailable?
18
+ @Live = LiveIdentity.new(LiveIdGUID, LiveIdVersion, :NO_UI, { :IDCRL_OPTION_ENVIRONMENT => 'Production' })
19
+ end
20
+
21
+ if username.nil? and @Live
17
22
  identities = @Live.GetIdentities(LiveIdentity::PPCRL_CREDTYPE_PASSWORD)
18
23
  @Username = identities.GetNextIdentityName
19
24
  if !@Username
@@ -25,53 +30,77 @@ module XLiveServices
25
30
  @Username = username
26
31
  end
27
32
 
28
- @Identity = @Live.GetIdentity(@Username, :IDENTITY_SHARE_ALL)
29
- if !@Identity.HasPersistedCredential?(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY)
30
- @Identity.SetCredential(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY, @Username)
33
+ if @Live
34
+ @Identity = @Live.GetIdentity(@Username, :IDENTITY_SHARE_ALL)
35
+ if !@Identity.HasPersistedCredential?(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY)
36
+ @Identity.SetCredential(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY, @Username)
37
+ end
31
38
  end
32
39
 
33
- if password
34
- @Identity.SetCredential(LiveIdentity::PPCRL_CREDTYPE_PASSWORD, password)
35
- elsif !@Identity.HasPersistedCredential?(LiveIdentity::PPCRL_CREDTYPE_PASSWORD)
36
- raise XLiveServicesError.new("No Password for #{@Username}!")
40
+ if @Identity
41
+ if password
42
+ @Identity.SetCredential(LiveIdentity::PPCRL_CREDTYPE_PASSWORD, password)
43
+ elsif !@Identity.HasPersistedCredential?(LiveIdentity::PPCRL_CREDTYPE_PASSWORD)
44
+ raise XLiveServicesError.new("No Password for #{@Username}!")
45
+ end
37
46
  end
38
47
  end
39
48
 
40
49
  def PersistCredentials
50
+ return false unless @Identity
41
51
  @Identity.PersistCredential(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY)
42
52
  @Identity.PersistCredential(LiveIdentity::PPCRL_CREDTYPE_PASSWORD)
43
53
  end
44
54
 
45
55
  def RemovePersistedCredentials
56
+ return false unless @Identity
46
57
  @Identity.RemovePersistedCredential(LiveIdentity::PPCRL_CREDTYPE_PASSWORD)
47
58
  @Identity.RemovePersistedCredential(LiveIdentity::PPCRL_CREDTYPE_MEMBERNAMEONLY)
48
59
  end
49
60
 
50
61
  def IsAuthenticated?
51
- @Identity.IsAuthenticated?
62
+ if @Identity
63
+ @Identity.IsAuthenticated?
64
+ else
65
+ nil
66
+ end
52
67
  end
53
68
 
54
69
  def Authenticate
55
- return if IsAuthenticated?()
70
+ return true if IsAuthenticated?()
71
+ return false unless @Identity
56
72
  @Identity.Authenticate(nil, :LOGONIDENTITY_ALLOW_PERSISTENT_COOKIES)
57
73
  end
58
74
 
59
- def GetUserAuthService
75
+ def SetUserAuthService(service)
76
+ @GetUserAuthService = service
77
+ end
78
+
79
+ def GetUserAuthService()
60
80
  @GetUserAuthService ||= XLiveServices.GetUserAuthService(@Identity, @Config)
61
81
  end
62
82
 
63
- def GetUserAuthorizationInfo
64
- userAuthorization = XLiveServices.GetUserAuthorization(@Config[:URL][:GetUserAuth].first, GetUserAuthService())
83
+ def GetUserAuthorizationInfo(authURL = nil)
84
+ authURL = @Config[:URL][:GetUserAuth].first if authURL.nil?
85
+ userAuthorization = XLiveServices.GetUserAuthorization(authURL, GetUserAuthService())
65
86
  userAuthorization['GetUserAuthorizationInfo']
66
87
  end
67
88
 
89
+ def SetWgxService(service)
90
+ @WgxService = service
91
+ end
92
+
68
93
  def GetWgxService
69
94
  @WgxService ||= XLiveServices.GetWgxService(@Identity, @Config)
70
95
  end
71
96
 
72
- def GetMarketplace
97
+ def GetMarketplace(host = nil, path = nil)
98
+ # Main service url https://services.gamesforwindows.com/SecurePublic/MarketPlacePublic.svc
73
99
  # Alternative service url https://services.gamesforwindows.com/SecurePublic/MarketplaceRestSecure.svc
74
- XLiveServices::MarketplacePublic.new(@Config[:URL][:WgxService].first, GetWgxService())
100
+ uri = URI(@Config[:URL][:WgxService].first)
101
+ uri.host = host if host # eg. services.xboxlive.com
102
+ uri.path = path if path
103
+ XLiveServices::MarketplacePublic.new(uri.to_s, GetWgxService())
75
104
  end
76
105
  end
77
106
  end
@@ -3,11 +3,19 @@ require 'multi_xml'
3
3
  require 'live_identity'
4
4
 
5
5
  module XLiveServices
6
- class XLiveServicesError < RuntimeError; end
6
+ class XLiveServicesError < RuntimeError
7
+ attr_accessor :Code
8
+ attr_accessor :Body
9
+ end
10
+ class XLiveServicesUnauthorized < XLiveServicesError; end
7
11
 
8
12
  def self.GetLcwConfig(locale = 'en-US')
9
13
  raise XLiveServicesError.new('Invalid Locale!') if locale.nil? or locale.empty?
10
- response = HTTPI.get(HTTPI::Request.new("https://live.xbox.com/#{locale}/GetLcwConfig.ashx"))
14
+ request = HTTPI::Request.new("https://live.xbox.com/#{locale}/GetLcwConfig.ashx")
15
+ request.auth.ssl.ca_cert_file = CERT_FILE
16
+ request.auth.ssl.ssl_version = :TLSv1_2
17
+ request.auth.ssl.verify_mode = :none if LiveIdentity.isAvailable?
18
+ response = HTTPI.get(request)
11
19
  raise "Error! HTTP Status Code: #{response.code}" if response.error?
12
20
  XLiveServices.ParseConfig(MultiXml.parse(response.body))
13
21
  end
@@ -24,6 +32,7 @@ module XLiveServices
24
32
  end
25
33
 
26
34
  def self.DoAuth(identity, serviceName, policy)
35
+ return nil unless identity
27
36
  identity.GetService(serviceName, policy)
28
37
  end
29
38
 
@@ -38,13 +47,24 @@ module XLiveServices
38
47
  end
39
48
 
40
49
  def self.GetUserAuthorization(url, userAuthService)
41
- raise XLiveServicesError.new('Invalid AuthService Token!') if userAuthService.Token.nil? or userAuthService.Token.empty?
50
+ raise XLiveServicesError.new('Invalid AuthService Token!') if userAuthService.nil? or userAuthService.Token.nil? or userAuthService.Token.empty?
42
51
  request = HTTPI::Request.new(url)
43
52
  request.body = { :serviceType => 1, :titleId => 0 }
53
+ request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
44
54
  request.headers['Authorization'] = "WLID1.0 #{userAuthService.Token}"
45
55
  request.headers['X-ClientType'] = 'panorama'
56
+ request.auth.ssl.ca_cert_file = CERT_FILE
57
+ request.auth.ssl.verify_mode = :none if LiveIdentity.isAvailable?
46
58
  response = HTTPI.post(request)
47
- raise "Error! HTTP Status Code: #{response.code} #{response.body}" if response.error?
59
+ if response.error?
60
+ message = "Error! HTTP Status Code: #{response.code}"
61
+ error_class = XLiveServicesError
62
+ error_class = XLiveServicesUnauthorized if response.code == 401
63
+ error_exception = error_class.new(message)
64
+ error_exception.Code = response.code
65
+ error_exception.Body = response.body
66
+ raise error_exception
67
+ end
48
68
  MultiXml.parse(response.body)
49
69
  end
50
70
 
metadata CHANGED
@@ -1,153 +1,167 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: XLiveServices
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dāvis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-31 00:00:00.000000000 Z
11
+ date: 2016-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpi
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: multi_xml
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: LiveIdentity
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.0.3
47
+ version: 0.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.0.3
54
+ version: 0.1.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: savon
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: builder
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: libmspack
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - ~>
101
+ - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '1.6'
103
+ version: '1.12'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - ~>
108
+ - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '1.6'
110
+ version: '1.12'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rake
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - '>='
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - '>='
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - '>='
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
131
  version: '0'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - '>='
136
+ - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: simplecov
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - '>='
143
+ - - ">="
130
144
  - !ruby/object:Gem::Version
131
145
  version: '0'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - '>='
150
+ - - ">="
137
151
  - !ruby/object:Gem::Version
138
152
  version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: yard
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - '>='
157
+ - - ">="
144
158
  - !ruby/object:Gem::Version
145
159
  version: '0'
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - '>='
164
+ - - ">="
151
165
  - !ruby/object:Gem::Version
152
166
  version: '0'
153
167
  description: A library to consume Xbox LIVE and Games for Windows LIVE services.
@@ -157,9 +171,9 @@ executables: []
157
171
  extensions: []
158
172
  extra_rdoc_files: []
159
173
  files:
160
- - .gitignore
161
- - .travis.yml
162
- - .yardopts
174
+ - ".gitignore"
175
+ - ".travis.yml"
176
+ - ".yardopts"
163
177
  - Gemfile
164
178
  - README.md
165
179
  - Rakefile
@@ -169,6 +183,7 @@ files:
169
183
  - lib/xlive_services.rb
170
184
  - lib/xlive_services/hresult.rb
171
185
  - lib/xlive_services/marketplace_public.rb
186
+ - lib/xlive_services/media.rb
172
187
  - lib/xlive_services/utils.rb
173
188
  - lib/xlive_services/version.rb
174
189
  - lib/xlive_services/xlive.rb
@@ -185,17 +200,17 @@ require_paths:
185
200
  - lib
186
201
  required_ruby_version: !ruby/object:Gem::Requirement
187
202
  requirements:
188
- - - '>='
203
+ - - ">="
189
204
  - !ruby/object:Gem::Version
190
205
  version: '0'
191
206
  required_rubygems_version: !ruby/object:Gem::Requirement
192
207
  requirements:
193
- - - '>='
208
+ - - ">="
194
209
  - !ruby/object:Gem::Version
195
210
  version: '0'
196
211
  requirements: []
197
212
  rubyforge_project:
198
- rubygems_version: 2.2.2
213
+ rubygems_version: 2.6.4
199
214
  signing_key:
200
215
  specification_version: 4
201
216
  summary: Interact with Xbox LIVE and Games for Windows LIVE services.