app-info 2.8.2 → 3.0.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +12 -7
- data/.github/workflows/ci.yml +7 -5
- data/.github/workflows/create_release.yml +15 -0
- data/.rubocop.yml +33 -11
- data/CHANGELOG.md +107 -1
- data/Gemfile +10 -5
- data/README.md +82 -15
- data/Rakefile +11 -0
- data/app_info.gemspec +14 -5
- data/lib/app_info/aab.rb +76 -110
- data/lib/app_info/android/signature.rb +114 -0
- data/lib/app_info/android/signatures/base.rb +53 -0
- data/lib/app_info/android/signatures/info.rb +158 -0
- data/lib/app_info/android/signatures/v1.rb +63 -0
- data/lib/app_info/android/signatures/v2.rb +121 -0
- data/lib/app_info/android/signatures/v3.rb +131 -0
- data/lib/app_info/android/signatures/v4.rb +18 -0
- data/lib/app_info/android.rb +181 -0
- data/lib/app_info/apk.rb +77 -112
- data/lib/app_info/apple.rb +192 -0
- data/lib/app_info/certificate.rb +176 -0
- data/lib/app_info/const.rb +76 -0
- data/lib/app_info/core_ext/object/try.rb +3 -1
- data/lib/app_info/core_ext/string/inflector.rb +2 -0
- data/lib/app_info/dsym/debug_info.rb +81 -0
- data/lib/app_info/dsym/macho.rb +62 -0
- data/lib/app_info/dsym.rb +35 -135
- data/lib/app_info/error.rb +3 -1
- data/lib/app_info/file.rb +49 -0
- data/lib/app_info/helper/archive.rb +37 -0
- data/lib/app_info/helper/file_size.rb +25 -0
- data/lib/app_info/helper/generate_class.rb +29 -0
- data/lib/app_info/helper/protobuf.rb +12 -0
- data/lib/app_info/helper/signatures.rb +229 -0
- data/lib/app_info/helper.rb +5 -128
- data/lib/app_info/info_plist.rb +66 -29
- data/lib/app_info/ipa/framework.rb +4 -4
- data/lib/app_info/ipa.rb +61 -135
- data/lib/app_info/macos.rb +54 -102
- data/lib/app_info/mobile_provision.rb +66 -48
- data/lib/app_info/pe.rb +322 -0
- data/lib/app_info/png_uncrush.rb +25 -5
- data/lib/app_info/proguard.rb +39 -22
- data/lib/app_info/protobuf/manifest.rb +22 -11
- data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
- data/lib/app_info/protobuf/models/README.md +8 -1
- data/lib/app_info/protobuf/models/Resources.proto +51 -0
- data/lib/app_info/protobuf/models/Resources_pb.rb +42 -0
- data/lib/app_info/protobuf/resources.rb +5 -5
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +93 -43
- metadata +57 -37
data/lib/app_info/pe.rb
ADDED
@@ -0,0 +1,322 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pedump'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module AppInfo
|
8
|
+
# Windows PE parser
|
9
|
+
#
|
10
|
+
# @see https://learn.microsoft.com/zh-cn/windows/win32/debug/pe-format Microsoft PE Format
|
11
|
+
class PE < File
|
12
|
+
include Helper::HumanFileSize
|
13
|
+
include Helper::Archive
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
ARCH = {
|
17
|
+
0x014c => 'x86',
|
18
|
+
0x0200 => 'Intel Itanium',
|
19
|
+
0x8664 => 'x64',
|
20
|
+
0x1c0 => 'arm',
|
21
|
+
0xaa64 => 'arm64',
|
22
|
+
0x5032 => 'RISC-v 32',
|
23
|
+
0x5064 => 'RISC-v 64',
|
24
|
+
0x5128 => 'RISC-v 128'
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# @return [Symbol] {Manufacturer}
|
28
|
+
def manufacturer
|
29
|
+
Manufacturer::MICROSOFT
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Symbol] {Platform}
|
33
|
+
def platform
|
34
|
+
Platform::WINDOWS
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Symbol] {Device}
|
38
|
+
def device
|
39
|
+
Device::WINDOWS
|
40
|
+
end
|
41
|
+
|
42
|
+
# return file size
|
43
|
+
# @example Read file size in integer
|
44
|
+
# aab.size # => 3618865
|
45
|
+
#
|
46
|
+
# @example Read file size in human readabale
|
47
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
48
|
+
#
|
49
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
50
|
+
# @return [Integer, String]
|
51
|
+
def size(human_size: false)
|
52
|
+
file_to_human_size(@file, human_size: human_size)
|
53
|
+
end
|
54
|
+
|
55
|
+
def binary_size(human_size: false)
|
56
|
+
file_to_human_size(binary_file, human_size: human_size)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @!method product_name
|
60
|
+
# @see VersionInfo#product_name
|
61
|
+
# @return [String]
|
62
|
+
# @!method product_version
|
63
|
+
# @see VersionInfo#product_version
|
64
|
+
# @return [String]
|
65
|
+
# @!method company_name
|
66
|
+
# @see VersionInfo#company_name
|
67
|
+
# @return [String]
|
68
|
+
# @!method assembly_version
|
69
|
+
# @see VersionInfo#assembly_version
|
70
|
+
# @return [String]
|
71
|
+
# @!method file_version
|
72
|
+
# @see VersionInfo#file_version
|
73
|
+
# @return [String]
|
74
|
+
# @!method file_description
|
75
|
+
# @see VersionInfo#file_description
|
76
|
+
# @return [String]
|
77
|
+
# @!method copyright
|
78
|
+
# @see VersionInfo#copyright
|
79
|
+
# @return [String]
|
80
|
+
# @return [String]
|
81
|
+
# @!method special_build
|
82
|
+
# @see VersionInfo#special_build
|
83
|
+
# @return [String]
|
84
|
+
# @!method private_build
|
85
|
+
# @see VersionInfo#private_build
|
86
|
+
# @return [String]
|
87
|
+
# @!method original_filename
|
88
|
+
# @see VersionInfo#original_filename
|
89
|
+
# @return [String]
|
90
|
+
# @!method internal_name
|
91
|
+
# @see VersionInfo#internal_name
|
92
|
+
# @return [String]
|
93
|
+
# @!method legal_trademarks
|
94
|
+
# @see VersionInfo#legal_trademarks
|
95
|
+
# @return [String]
|
96
|
+
def_delegators :version_info, :product_name, :product_version, :company_name, :assembly_version,
|
97
|
+
:file_version, :file_description, :copyright, :special_build, :private_build,
|
98
|
+
:original_filename, :internal_name, :legal_trademarks
|
99
|
+
|
100
|
+
alias name product_name
|
101
|
+
|
102
|
+
# Find {#product_version} then fallback to {#file_version}
|
103
|
+
# @return [String, nil]
|
104
|
+
def release_version
|
105
|
+
product_version || file_version
|
106
|
+
end
|
107
|
+
|
108
|
+
# Find {#special_build}, {#private_build} then fallback to {#assembly_version}
|
109
|
+
# @return [String, nil]
|
110
|
+
def build_version
|
111
|
+
special_build || private_build || assembly_version
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [String]
|
115
|
+
def archs
|
116
|
+
ARCH[image_file_header.Machine] || 'unknown'
|
117
|
+
end
|
118
|
+
alias architectures archs
|
119
|
+
|
120
|
+
# @return [Hash{String => String}] imports imports of libraries
|
121
|
+
def imports
|
122
|
+
@imports ||= pe.imports.each_with_object({}) do |import, obj|
|
123
|
+
obj[import.module_name] = import.first_thunk.map(&:name).compact
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Array{String}] icons paths of bmp image icons
|
128
|
+
def icons
|
129
|
+
@icons ||= lambda {
|
130
|
+
# Fetch the largest size icon
|
131
|
+
files = []
|
132
|
+
pe.resources&.find_all do |res|
|
133
|
+
next unless res.type == 'ICON'
|
134
|
+
|
135
|
+
filename = "#{::File.basename(file, '.*')}-#{res.type}-#{res.id}.bmp"
|
136
|
+
icon_file = tempdir(filename, prefix: 'pe', system: true)
|
137
|
+
mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
|
138
|
+
|
139
|
+
begin
|
140
|
+
::File.open(icon_file, 'wb') do |f|
|
141
|
+
f << res.restore_bitmap(io)
|
142
|
+
end
|
143
|
+
|
144
|
+
if res.bitmap_mask(io)
|
145
|
+
mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
|
146
|
+
::File.open(mask_icon_file, 'wb') do |f|
|
147
|
+
f << res.bitmap_mask(io)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
rescue StandardError => e
|
151
|
+
# ignore pedump throws any exception.
|
152
|
+
raise e unless e.backtrace.first.include?('pedump')
|
153
|
+
|
154
|
+
FileUtils.rm_f(icon_file)
|
155
|
+
ensure
|
156
|
+
next unless ::File.exist?(icon_file)
|
157
|
+
|
158
|
+
mask_file = ::File.exist?(mask_icon_file) ? mask_icon_file : nil
|
159
|
+
files << icon_metadata(icon_file, mask_file: mask_file)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
files
|
164
|
+
}.call
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [PEdump]
|
168
|
+
def pe
|
169
|
+
@pe ||= lambda {
|
170
|
+
pe = PEdump.new(io)
|
171
|
+
pe.logger.level = Logger::FATAL # ignore :warn logger output
|
172
|
+
pe
|
173
|
+
}.call
|
174
|
+
end
|
175
|
+
|
176
|
+
# @return [VersionInfo]
|
177
|
+
def version_info
|
178
|
+
@version_info ||= VersionInfo.new(pe.version_info)
|
179
|
+
end
|
180
|
+
|
181
|
+
def clear!
|
182
|
+
@io = nil
|
183
|
+
@pe = nil
|
184
|
+
@icons = nil
|
185
|
+
@imports = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [String] binary_file path
|
189
|
+
def binary_file
|
190
|
+
@binary_file ||= lambda {
|
191
|
+
file_io = ::File.open(@file, 'rb')
|
192
|
+
return @file unless file_io.read(100) =~ AppInfo::ZIP_RETGEX
|
193
|
+
|
194
|
+
zip_file = Zip::File.open(@file)
|
195
|
+
zip_entry = zip_file.glob('*.exe').first
|
196
|
+
raise NotFoundError, 'Not found .exe file in archive file' if zip_entry.nil?
|
197
|
+
|
198
|
+
exe_file = tempdir(zip_entry.name, prefix: 'pe-exe', system: true)
|
199
|
+
zip_entry.extract(exe_file)
|
200
|
+
zip_file.close
|
201
|
+
|
202
|
+
return exe_file
|
203
|
+
}.call
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def image_file_header
|
209
|
+
@image_file_header ||= pe.pe.image_file_header
|
210
|
+
end
|
211
|
+
|
212
|
+
# @return [Hash{Symbol => String}]
|
213
|
+
def icon_metadata(file, mask_file: nil)
|
214
|
+
{
|
215
|
+
name: ::File.basename(file),
|
216
|
+
file: file,
|
217
|
+
mask: mask_file,
|
218
|
+
dimensions: ImageSize.path(file).size
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
# @return [File]
|
223
|
+
def io
|
224
|
+
@io ||= ::File.open(binary_file, 'rb')
|
225
|
+
end
|
226
|
+
|
227
|
+
# VersionInfo class
|
228
|
+
#
|
229
|
+
# @see https://learn.microsoft.com/zh-cn/windows/win32/menurc/versioninfo-resource
|
230
|
+
class VersionInfo
|
231
|
+
def initialize(raw)
|
232
|
+
@raw = raw
|
233
|
+
end
|
234
|
+
|
235
|
+
# @return [String]
|
236
|
+
def company_name
|
237
|
+
@company_name ||= value_of('CompanyName')
|
238
|
+
end
|
239
|
+
|
240
|
+
# @return [String]
|
241
|
+
def product_name
|
242
|
+
@product_name ||= value_of('ProductName')
|
243
|
+
end
|
244
|
+
|
245
|
+
# @return [String]
|
246
|
+
def product_version
|
247
|
+
@product_version ||= value_of('ProductVersion')
|
248
|
+
end
|
249
|
+
|
250
|
+
# @return [String]
|
251
|
+
def assembly_version
|
252
|
+
@assembly_version ||= value_of('Assembly Version')
|
253
|
+
end
|
254
|
+
|
255
|
+
# @return [String]
|
256
|
+
def file_version
|
257
|
+
@file_version ||= value_of('FileVersion')
|
258
|
+
end
|
259
|
+
|
260
|
+
# @return [String, nil]
|
261
|
+
def file_description
|
262
|
+
@file_description ||= value_of('FileDescription')
|
263
|
+
end
|
264
|
+
|
265
|
+
# @return [String, nil]
|
266
|
+
def special_build
|
267
|
+
@special_build ||= value_of('SpecialBuild')
|
268
|
+
end
|
269
|
+
|
270
|
+
# @return [String, nil]
|
271
|
+
def private_build
|
272
|
+
@private_build ||= value_of('PrivateBuild')
|
273
|
+
end
|
274
|
+
|
275
|
+
# @return [String]
|
276
|
+
def original_filename
|
277
|
+
@original_filename ||= value_of('OriginalFilename')
|
278
|
+
end
|
279
|
+
|
280
|
+
# @return [String]
|
281
|
+
def internal_name
|
282
|
+
@internal_name ||= value_of('InternalName')
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [String]
|
286
|
+
def legal_trademarks
|
287
|
+
@legal_trademarks ||= value_of('LegalTrademarks')
|
288
|
+
end
|
289
|
+
|
290
|
+
# @return [String]
|
291
|
+
def copyright
|
292
|
+
@copyright ||= value_of('LegalCopyright')
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
|
297
|
+
def value_of(key)
|
298
|
+
info.each do |v|
|
299
|
+
return v[:Value] if v[:szKey] == key.to_s
|
300
|
+
end
|
301
|
+
|
302
|
+
nil
|
303
|
+
end
|
304
|
+
|
305
|
+
def info
|
306
|
+
return @info if @info
|
307
|
+
|
308
|
+
@raw.each do |item|
|
309
|
+
next unless item.is_a?(PEdump::VS_VERSIONINFO)
|
310
|
+
|
311
|
+
versions = item[:Children].select { |v| v.is_a?(PEdump::StringFileInfo) }
|
312
|
+
next if versions.empty?
|
313
|
+
|
314
|
+
@info = versions[0][:Children][0][:Children]
|
315
|
+
return @info
|
316
|
+
end
|
317
|
+
|
318
|
+
@info = []
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
data/lib/app_info/png_uncrush.rb
CHANGED
@@ -7,9 +7,10 @@ require 'zlib'
|
|
7
7
|
require 'stringio'
|
8
8
|
|
9
9
|
module AppInfo
|
10
|
+
# Decompress iOS Png image file.
|
11
|
+
# @see https://github.com/swcai/iphone-png-normalizer swcai/iphone-png-normalizer
|
12
|
+
# @author Wenwei Cai
|
10
13
|
class PngUncrush
|
11
|
-
class Error < StandardError; end
|
12
|
-
|
13
14
|
class FormatError < Error; end
|
14
15
|
|
15
16
|
class PngReader # :nodoc:
|
@@ -31,20 +32,26 @@ module AppInfo
|
|
31
32
|
@data = String.new
|
32
33
|
end
|
33
34
|
|
35
|
+
# @return [Integer]
|
34
36
|
def size
|
35
37
|
@io.size
|
36
38
|
end
|
37
39
|
|
40
|
+
# @param [String] format
|
41
|
+
# @return [String]
|
42
|
+
# @see IO package data
|
38
43
|
def unpack(format)
|
39
44
|
@io.unpack(format)
|
40
45
|
end
|
41
46
|
|
47
|
+
# @return [String]
|
42
48
|
def header
|
43
49
|
@header ||= self[0, 8]
|
44
50
|
end
|
45
51
|
|
52
|
+
# @return [Boolean]
|
46
53
|
def png?
|
47
|
-
|
54
|
+
header.bytes == PNG_HEADER
|
48
55
|
end
|
49
56
|
|
50
57
|
def [](offset, length)
|
@@ -60,23 +67,36 @@ module AppInfo
|
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
70
|
+
# Decompress crushed png file.
|
71
|
+
# @param [String] input path of png file
|
72
|
+
# @param [String] output path of output file
|
73
|
+
# @return [Boolean] result whether decompress successfully
|
63
74
|
def self.decompress(input, output)
|
64
75
|
new(input).decompress(output)
|
65
76
|
end
|
66
77
|
|
78
|
+
# get png dimensions
|
79
|
+
# @param [String] input path of png file
|
80
|
+
# @return [Array<Integer>] dimensions width, height value of png file
|
67
81
|
def self.dimensions(input)
|
68
82
|
new(input).dimensions
|
69
83
|
end
|
70
84
|
|
71
85
|
def initialize(filename)
|
72
|
-
@io = PngReader.new(File.open(filename))
|
86
|
+
@io = PngReader.new(::File.open(filename))
|
73
87
|
raise FormatError, 'not a png file' unless @io.png?
|
74
88
|
end
|
75
89
|
|
90
|
+
# get png dimensions
|
91
|
+
# @param [String] input path of png file
|
92
|
+
# @return [Array<Integer>] dimensions width, height value of png file
|
76
93
|
def dimensions
|
77
94
|
_dump_sections(dimensions: true)
|
78
95
|
end
|
79
96
|
|
97
|
+
# Decompress crushed png file.
|
98
|
+
# @param [String] output path of output file
|
99
|
+
# @return [Boolean] result whether decompress successfully
|
80
100
|
def decompress(output)
|
81
101
|
content = _remap(_dump_sections)
|
82
102
|
return false unless content
|
@@ -125,7 +145,7 @@ module AppInfo
|
|
125
145
|
end
|
126
146
|
|
127
147
|
def write_file(path, content)
|
128
|
-
File.write(path, content, encoding: Encoding::BINARY)
|
148
|
+
::File.write(path, content, encoding: Encoding::BINARY)
|
129
149
|
true
|
130
150
|
end
|
131
151
|
|
data/lib/app_info/proguard.rb
CHANGED
@@ -5,52 +5,52 @@ require 'rexml/document'
|
|
5
5
|
|
6
6
|
module AppInfo
|
7
7
|
# Proguard parser
|
8
|
-
class Proguard
|
8
|
+
class Proguard < File
|
9
9
|
include Helper::Archive
|
10
10
|
|
11
11
|
NAMESPACE = UUIDTools::UUID.sha1_create(UUIDTools::UUID_DNS_NAMESPACE, 'icyleaf.com')
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@file = file
|
13
|
+
# @return [Symbol] {Manufacturer}
|
14
|
+
def manufacturer
|
15
|
+
Manufacturer::GOOGLE
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
|
-
|
18
|
+
# @return [Symbol] {Platform}
|
19
|
+
def platform
|
20
|
+
Platform::ANDROID
|
21
21
|
end
|
22
22
|
|
23
|
+
# @return [String]
|
23
24
|
def uuid
|
24
25
|
# Similar to https://docs.sentry.io/workflow/debug-files/#proguard-uuids
|
25
|
-
UUIDTools::UUID.sha1_create(NAMESPACE, File.read(mapping_path)).to_s
|
26
|
+
UUIDTools::UUID.sha1_create(NAMESPACE, ::File.read(mapping_path)).to_s
|
26
27
|
end
|
27
28
|
alias debug_id uuid
|
28
29
|
|
30
|
+
# @return [Boolean]
|
29
31
|
def mapping?
|
30
|
-
File.exist?(mapping_path)
|
32
|
+
::File.exist?(mapping_path)
|
31
33
|
end
|
32
34
|
|
35
|
+
# @return [Boolean]
|
33
36
|
def manifest?
|
34
|
-
File.exist?(manifest_path)
|
37
|
+
::File.exist?(manifest_path)
|
35
38
|
end
|
36
39
|
|
40
|
+
# @return [Boolean]
|
37
41
|
def symbol?
|
38
|
-
File.exist?(symbol_path)
|
42
|
+
::File.exist?(symbol_path)
|
39
43
|
end
|
40
44
|
alias resource? symbol?
|
41
45
|
|
46
|
+
# @return [String, nil]
|
42
47
|
def package_name
|
43
48
|
return unless manifest?
|
44
49
|
|
45
50
|
manifest.root.attributes['package']
|
46
51
|
end
|
47
52
|
|
48
|
-
|
49
|
-
return unless manifest?
|
50
|
-
|
51
|
-
manifest.root.attributes['package']
|
52
|
-
end
|
53
|
-
|
53
|
+
# @return [String, nil]
|
54
54
|
def version_name
|
55
55
|
return unless manifest?
|
56
56
|
|
@@ -58,6 +58,7 @@ module AppInfo
|
|
58
58
|
end
|
59
59
|
alias release_version version_name
|
60
60
|
|
61
|
+
# @return [String, nil]
|
61
62
|
def version_code
|
62
63
|
return unless manifest?
|
63
64
|
|
@@ -65,27 +66,43 @@ module AppInfo
|
|
65
66
|
end
|
66
67
|
alias build_version version_code
|
67
68
|
|
69
|
+
def files
|
70
|
+
Dir.children(contents).each_with_object([]) do |filename, obj|
|
71
|
+
path = ::File.join(contents, filename)
|
72
|
+
obj << {
|
73
|
+
name: filename,
|
74
|
+
path: path,
|
75
|
+
size: ::File.size(path)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [REXML::Document]
|
68
81
|
def manifest
|
69
82
|
return unless manifest?
|
70
83
|
|
71
|
-
@manifest ||= REXML::Document.new(File.new(manifest_path))
|
84
|
+
@manifest ||= REXML::Document.new(::File.new(manifest_path))
|
72
85
|
end
|
73
86
|
|
87
|
+
# @return [String]
|
74
88
|
def mapping_path
|
75
|
-
@mapping_path ||= Dir.glob(File.join(contents, '*mapping*.txt')).first
|
89
|
+
@mapping_path ||= Dir.glob(::File.join(contents, '*mapping*.txt')).first
|
76
90
|
end
|
77
91
|
|
92
|
+
# @return [String]
|
78
93
|
def manifest_path
|
79
|
-
@manifest_path ||= File.join(contents, 'AndroidManifest.xml')
|
94
|
+
@manifest_path ||= ::File.join(contents, 'AndroidManifest.xml')
|
80
95
|
end
|
81
96
|
|
97
|
+
# @return [String]
|
82
98
|
def symbol_path
|
83
|
-
@symbol_path ||= File.join(contents, 'R.txt')
|
99
|
+
@symbol_path ||= ::File.join(contents, 'R.txt')
|
84
100
|
end
|
85
101
|
alias resource_path symbol_path
|
86
102
|
|
103
|
+
# @return [String] contents path of contents
|
87
104
|
def contents
|
88
|
-
@contents ||= unarchive(@file,
|
105
|
+
@contents ||= unarchive(@file, prefix: 'proguard')
|
89
106
|
end
|
90
107
|
|
91
108
|
def clear!
|
@@ -6,8 +6,9 @@ require 'app_info/core_ext'
|
|
6
6
|
|
7
7
|
module AppInfo
|
8
8
|
module Protobuf
|
9
|
+
# AAB Protobuf Base class
|
9
10
|
class Base
|
10
|
-
include Helper::
|
11
|
+
include Helper::GenerateClass
|
11
12
|
|
12
13
|
def initialize(doc, resources = nil)
|
13
14
|
@resources = resources
|
@@ -17,10 +18,11 @@ module AppInfo
|
|
17
18
|
private
|
18
19
|
|
19
20
|
def parse(_)
|
20
|
-
raise 'not implemented'
|
21
|
+
raise ProtobufParseError, 'not implemented'
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
# AAB Protobuf Attribute
|
24
26
|
class Attribute < Base
|
25
27
|
attr_reader :namespace, :name, :value, :resource_id
|
26
28
|
|
@@ -47,6 +49,8 @@ module AppInfo
|
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
52
|
+
# AAB Protobuf Node class.
|
53
|
+
# example: manifest,activity, activity-alias, service, receiver, provider, application
|
50
54
|
class Node < Base
|
51
55
|
attr_reader :name, :attributes, :children
|
52
56
|
|
@@ -106,6 +110,7 @@ module AppInfo
|
|
106
110
|
end
|
107
111
|
end
|
108
112
|
|
113
|
+
# AAB Protobuf Manifest
|
109
114
|
class Manifest < Node
|
110
115
|
def self.parse(io, resources = nil)
|
111
116
|
doc = Aapt::Pb::XmlNode.decode(io)
|
@@ -152,8 +157,10 @@ module AppInfo
|
|
152
157
|
|
153
158
|
def intent_filters(search: nil)
|
154
159
|
activities.each_with_object([]) do |activity, obj|
|
160
|
+
next unless activity.respond_to?(:intent_filter)
|
161
|
+
|
155
162
|
intent_filters = activity.intent_filter
|
156
|
-
next if intent_filters&.empty?
|
163
|
+
next if intent_filters.nil? || intent_filters&.empty?
|
157
164
|
|
158
165
|
if search.nil? || search.empty?
|
159
166
|
obj << intent_filters
|
@@ -168,7 +175,6 @@ module AppInfo
|
|
168
175
|
end.flatten.uniq
|
169
176
|
end
|
170
177
|
|
171
|
-
# :nodoc:
|
172
178
|
# Workaround ruby always return true by called `Object.const_defined?(Data)`
|
173
179
|
class Data < Node; end
|
174
180
|
|
@@ -182,6 +188,8 @@ module AppInfo
|
|
182
188
|
CATEGORY_BROWSABLE = 'android.intent.category.BROWSABLE'
|
183
189
|
|
184
190
|
def deep_links?
|
191
|
+
return unless respond_to?(:data)
|
192
|
+
|
185
193
|
browsable? && data.any? { |d| DEEP_LINK_SCHEMES.include?(d.scheme) }
|
186
194
|
end
|
187
195
|
|
@@ -193,6 +201,12 @@ module AppInfo
|
|
193
201
|
.uniq
|
194
202
|
end
|
195
203
|
|
204
|
+
def schemes?
|
205
|
+
return unless respond_to?(:data)
|
206
|
+
|
207
|
+
browsable? && data.any? { |d| !DEEP_LINK_SCHEMES.include?(d.scheme) }
|
208
|
+
end
|
209
|
+
|
196
210
|
def schemes
|
197
211
|
return unless schemes?
|
198
212
|
|
@@ -201,23 +215,20 @@ module AppInfo
|
|
201
215
|
.uniq
|
202
216
|
end
|
203
217
|
|
204
|
-
def schemes?
|
205
|
-
browsable? && data.any? { |d| !DEEP_LINK_SCHEMES.include?(d.scheme) }
|
206
|
-
end
|
207
|
-
|
208
218
|
def browsable?
|
209
219
|
exist?(CATEGORY_BROWSABLE)
|
210
220
|
end
|
211
221
|
|
212
222
|
def exist?(name, type: nil)
|
213
223
|
if type.to_s.empty? && !name.start_with?('android.intent.')
|
214
|
-
raise '
|
224
|
+
raise ProtobufParseError, 'Not found intent type'
|
215
225
|
end
|
216
226
|
|
217
227
|
type ||= name.split('.')[2]
|
218
|
-
raise 'Not found type' unless TYPES.include?(type)
|
228
|
+
raise ProtobufParseError, 'Not found intent type' unless TYPES.include?(type)
|
229
|
+
return false unless intent = send(type.to_sym)
|
219
230
|
|
220
|
-
values =
|
231
|
+
values = intent.select { |e| e.name == name }
|
221
232
|
values.empty? ? false : values
|
222
233
|
end
|
223
234
|
end
|
@@ -3,12 +3,19 @@
|
|
3
3
|
## Convert to ruby model
|
4
4
|
|
5
5
|
```bash
|
6
|
-
cd
|
6
|
+
cd lib/app_info/protobuf/models
|
7
7
|
|
8
8
|
protoc --ruby_out=. Resources.proto
|
9
9
|
protoc --ruby_out=. Configuration.proto
|
10
10
|
```
|
11
11
|
|
12
|
+
## Decode
|
13
|
+
|
14
|
+
```bash
|
15
|
+
protoc --decode=aapt.pb.ResourceTable Resources.proto < aab/base/BundleConfig.pb > BundleConfig.txt
|
16
|
+
protoc --decode=aapt.pb.ResourceTable Resources.proto < aab/base/native.pb > native.txt
|
17
|
+
```
|
18
|
+
|
12
19
|
## Resouces
|
13
20
|
|
14
21
|
`Configuration.proto` and `Resources.proto` can be found in aapt2's github:
|