app-info 2.8.4 → 3.0.0.beta1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +3 -1
  4. data/.rubocop.yml +31 -11
  5. data/CHANGELOG.md +30 -2
  6. data/Gemfile +7 -1
  7. data/README.md +74 -9
  8. data/Rakefile +11 -0
  9. data/app_info.gemspec +12 -3
  10. data/lib/app_info/aab.rb +58 -39
  11. data/lib/app_info/android/signature.rb +114 -0
  12. data/lib/app_info/android/signatures/base.rb +49 -0
  13. data/lib/app_info/android/signatures/info.rb +152 -0
  14. data/lib/app_info/android/signatures/v1.rb +59 -0
  15. data/lib/app_info/android/signatures/v2.rb +117 -0
  16. data/lib/app_info/android/signatures/v3.rb +127 -0
  17. data/lib/app_info/android/signatures/v4.rb +14 -0
  18. data/lib/app_info/apk.rb +43 -46
  19. data/lib/app_info/certificate.rb +181 -0
  20. data/lib/app_info/const.rb +41 -0
  21. data/lib/app_info/core_ext/object/try.rb +3 -1
  22. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  23. data/lib/app_info/dsym/debug_info.rb +72 -0
  24. data/lib/app_info/dsym/macho.rb +55 -0
  25. data/lib/app_info/dsym.rb +27 -134
  26. data/lib/app_info/error.rb +8 -0
  27. data/lib/app_info/file.rb +23 -0
  28. data/lib/app_info/helper/archive.rb +37 -0
  29. data/lib/app_info/helper/file_size.rb +25 -0
  30. data/lib/app_info/helper/generate_class.rb +29 -0
  31. data/lib/app_info/helper/protobuf.rb +12 -0
  32. data/lib/app_info/helper/signatures.rb +229 -0
  33. data/lib/app_info/helper.rb +5 -126
  34. data/lib/app_info/info_plist.rb +14 -6
  35. data/lib/app_info/ipa/framework.rb +4 -4
  36. data/lib/app_info/ipa.rb +41 -36
  37. data/lib/app_info/macos.rb +34 -26
  38. data/lib/app_info/mobile_provision.rb +19 -30
  39. data/lib/app_info/pe.rb +226 -0
  40. data/lib/app_info/png_uncrush.rb +5 -4
  41. data/lib/app_info/proguard.rb +11 -17
  42. data/lib/app_info/protobuf/manifest.rb +16 -9
  43. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  44. data/lib/app_info/protobuf/models/README.md +7 -0
  45. data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
  46. data/lib/app_info/protobuf/resources.rb +5 -5
  47. data/lib/app_info/version.rb +1 -1
  48. data/lib/app_info.rb +91 -42
  49. metadata +46 -35
@@ -0,0 +1,226 @@
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
+ def file_type
28
+ Format::PE
29
+ end
30
+
31
+ def platform
32
+ Platform::WINDOWS
33
+ end
34
+
35
+ # return file size
36
+ # @example Read file size in integer
37
+ # aab.size # => 3618865
38
+ #
39
+ # @example Read file size in human readabale
40
+ # aab.size(human_size: true) # => '3.45 MB'
41
+ #
42
+ # @param [Boolean] human_size Convert integer value to human readable.
43
+ # @return [Integer, String]
44
+ def size(human_size: false)
45
+ file_to_human_size(@file, human_size: human_size)
46
+ end
47
+
48
+ def binary_size(human_size: false)
49
+ file_to_human_size(binary_file, human_size: human_size)
50
+ end
51
+
52
+ def_delegators :version_info, :product_name, :product_version, :company_name, :assembly_version
53
+
54
+ alias name product_name
55
+ alias release_version product_version
56
+ alias build_version assembly_version
57
+
58
+ def archs
59
+ ARCH[image_file_header.Machine] || 'unknown'
60
+ end
61
+ alias architectures archs
62
+
63
+ def imports
64
+ @imports ||= pe.imports.each_with_object({}) do |import, obj|
65
+ obj[import.module_name] = import.first_thunk.map(&:name).compact
66
+ end
67
+ end
68
+
69
+ def icons
70
+ @icons ||= lambda {
71
+ # Fetch the largest size icon
72
+ files = []
73
+ pe.resources&.find_all do |res|
74
+ next unless res.type == 'ICON'
75
+
76
+ filename = "#{::File.basename(file, '.*')}-#{res.type}-#{res.id}.bmp"
77
+ icon_file = tempdir(filename, prefix: 'pe', system: true)
78
+ mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
79
+
80
+ begin
81
+ ::File.open(icon_file, 'wb') do |f|
82
+ f << res.restore_bitmap(io)
83
+ end
84
+
85
+ if res.bitmap_mask(io)
86
+ mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
87
+ ::File.open(mask_icon_file, 'wb') do |f|
88
+ f << res.bitmap_mask(io)
89
+ end
90
+ end
91
+ rescue StandardError => e
92
+ # ignore pedump throws any exception.
93
+ raise e unless e.backtrace.first.include?('pedump')
94
+
95
+ FileUtils.rm_f(icon_file)
96
+ ensure
97
+ next unless ::File.exist?(icon_file)
98
+
99
+ mask_file = ::File.exist?(mask_icon_file) ? mask_icon_file : nil
100
+ files << icon_metadata(icon_file, mask_file: mask_file)
101
+ end
102
+ end
103
+
104
+ files
105
+ }.call
106
+ end
107
+
108
+ def pe
109
+ @pe ||= lambda {
110
+ pe = PEdump.new(io)
111
+ pe.logger.level = Logger::FATAL # ignore :warn logger output
112
+ pe
113
+ }.call
114
+ end
115
+
116
+ def version_info
117
+ @version_info ||= VersionInfo.new(pe.version_info)
118
+ end
119
+
120
+ def clear!
121
+ @io = nil
122
+ @pe = nil
123
+ @icons = nil
124
+ @imports = nil
125
+ end
126
+
127
+ def binary_file
128
+ @binary_file ||= lambda {
129
+ file_io = ::File.open(@file, 'rb')
130
+ return @file unless file_io.read(100) =~ AppInfo::ZIP_RETGEX
131
+
132
+ zip_file = Zip::File.open(@file)
133
+ zip_entry = zip_file.glob('*.exe').first
134
+ raise NotFoundWinBinraryError, 'Not found .exe file in archive file' if zip_entry.nil?
135
+
136
+ exe_file = tempdir(zip_entry.name, prefix: 'pe-exe', system: true)
137
+ zip_entry.extract(exe_file)
138
+ zip_file.close
139
+
140
+ return exe_file
141
+ }.call
142
+ end
143
+
144
+ private
145
+
146
+ def image_file_header
147
+ @image_file_header ||= pe.pe.image_file_header
148
+ end
149
+
150
+ def icon_metadata(file, mask_file: nil)
151
+ {
152
+ name: ::File.basename(file),
153
+ file: file,
154
+ mask: mask_file,
155
+ dimensions: ImageSize.path(file).size
156
+ }
157
+ end
158
+
159
+ def io
160
+ @io ||= ::File.open(binary_file, 'rb')
161
+ end
162
+
163
+ # VersionInfo class
164
+ #
165
+ # Ref: https://learn.microsoft.com/zh-cn/windows/win32/menurc/versioninfo-resource
166
+ class VersionInfo
167
+ def initialize(raw)
168
+ @raw = raw
169
+ end
170
+
171
+ def company_name
172
+ @company_name ||= value_of('CompanyName')
173
+ end
174
+
175
+ def product_name
176
+ @product_name ||= value_of('ProductName')
177
+ end
178
+
179
+ def product_version
180
+ @product_version ||= value_of('ProductVersion')
181
+ end
182
+
183
+ def assembly_version
184
+ @assembly_version ||= value_of('Assembly Version')
185
+ end
186
+
187
+ def file_version
188
+ @file_version ||= value_of('FileVersion')
189
+ end
190
+
191
+ def file_description
192
+ @file_description ||= value_of('FileDescription')
193
+ end
194
+
195
+ def copyright
196
+ @copyright ||= value_of('LegalCopyright')
197
+ end
198
+
199
+ private
200
+
201
+ def value_of(key)
202
+ info.each do |v|
203
+ return v[:Value] if v[:szKey] == key.to_s
204
+ end
205
+
206
+ nil
207
+ end
208
+
209
+ def info
210
+ return @info if @info
211
+
212
+ @raw.each do |item|
213
+ next unless item.is_a?(PEdump::VS_VERSIONINFO)
214
+
215
+ versions = item[:Children].select { |v| v.is_a?(PEdump::StringFileInfo) }
216
+ next if versions.empty?
217
+
218
+ @info = versions[0][:Children][0][:Children]
219
+ return @info
220
+ end
221
+
222
+ @info = []
223
+ end
224
+ end
225
+ end
226
+ end
@@ -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:
@@ -69,7 +70,7 @@ module AppInfo
69
70
  end
70
71
 
71
72
  def initialize(filename)
72
- @io = PngReader.new(File.open(filename))
73
+ @io = PngReader.new(::File.open(filename))
73
74
  raise FormatError, 'not a png file' unless @io.png?
74
75
  end
75
76
 
@@ -125,7 +126,7 @@ module AppInfo
125
126
  end
126
127
 
127
128
  def write_file(path, content)
128
- File.write(path, content, encoding: Encoding::BINARY)
129
+ ::File.write(path, content, encoding: Encoding::BINARY)
129
130
  true
130
131
  end
131
132
 
@@ -5,37 +5,31 @@ 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
- attr_reader :file
14
-
15
- def initialize(file)
16
- @file = file
17
- end
18
-
19
13
  def file_type
20
- Platform::PROGUARD
14
+ Format::PROGUARD
21
15
  end
22
16
 
23
17
  def uuid
24
18
  # Similar to https://docs.sentry.io/workflow/debug-files/#proguard-uuids
25
- UUIDTools::UUID.sha1_create(NAMESPACE, File.read(mapping_path)).to_s
19
+ UUIDTools::UUID.sha1_create(NAMESPACE, ::File.read(mapping_path)).to_s
26
20
  end
27
21
  alias debug_id uuid
28
22
 
29
23
  def mapping?
30
- File.exist?(mapping_path)
24
+ ::File.exist?(mapping_path)
31
25
  end
32
26
 
33
27
  def manifest?
34
- File.exist?(manifest_path)
28
+ ::File.exist?(manifest_path)
35
29
  end
36
30
 
37
31
  def symbol?
38
- File.exist?(symbol_path)
32
+ ::File.exist?(symbol_path)
39
33
  end
40
34
  alias resource? symbol?
41
35
 
@@ -68,24 +62,24 @@ module AppInfo
68
62
  def manifest
69
63
  return unless manifest?
70
64
 
71
- @manifest ||= REXML::Document.new(File.new(manifest_path))
65
+ @manifest ||= REXML::Document.new(::File.new(manifest_path))
72
66
  end
73
67
 
74
68
  def mapping_path
75
- @mapping_path ||= Dir.glob(File.join(contents, '*mapping*.txt')).first
69
+ @mapping_path ||= Dir.glob(::File.join(contents, '*mapping*.txt')).first
76
70
  end
77
71
 
78
72
  def manifest_path
79
- @manifest_path ||= File.join(contents, 'AndroidManifest.xml')
73
+ @manifest_path ||= ::File.join(contents, 'AndroidManifest.xml')
80
74
  end
81
75
 
82
76
  def symbol_path
83
- @symbol_path ||= File.join(contents, 'R.txt')
77
+ @symbol_path ||= ::File.join(contents, 'R.txt')
84
78
  end
85
79
  alias resource_path symbol_path
86
80
 
87
81
  def contents
88
- @contents ||= unarchive(@file, path: 'proguard')
82
+ @contents ||= unarchive(@file, prefix: 'proguard')
89
83
  end
90
84
 
91
85
  def clear!
@@ -7,7 +7,7 @@ require 'app_info/core_ext'
7
7
  module AppInfo
8
8
  module Protobuf
9
9
  class Base
10
- include Helper::Defines
10
+ include Helper::GenerateClass
11
11
 
12
12
  def initialize(doc, resources = nil)
13
13
  @resources = resources
@@ -17,7 +17,7 @@ module AppInfo
17
17
  private
18
18
 
19
19
  def parse(_)
20
- raise 'not implemented'
20
+ raise ProtobufParseError, 'not implemented'
21
21
  end
22
22
  end
23
23
 
@@ -152,6 +152,8 @@ module AppInfo
152
152
 
153
153
  def intent_filters(search: nil)
154
154
  activities.each_with_object([]) do |activity, obj|
155
+ next unless activity.respond_to?(:intent_filter)
156
+
155
157
  intent_filters = activity.intent_filter
156
158
  next if intent_filters.nil? || intent_filters&.empty?
157
159
 
@@ -182,6 +184,8 @@ module AppInfo
182
184
  CATEGORY_BROWSABLE = 'android.intent.category.BROWSABLE'
183
185
 
184
186
  def deep_links?
187
+ return unless respond_to?(:data)
188
+
185
189
  browsable? && data.any? { |d| DEEP_LINK_SCHEMES.include?(d.scheme) }
186
190
  end
187
191
 
@@ -193,6 +197,12 @@ module AppInfo
193
197
  .uniq
194
198
  end
195
199
 
200
+ def schemes?
201
+ return unless respond_to?(:data)
202
+
203
+ browsable? && data.any? { |d| !DEEP_LINK_SCHEMES.include?(d.scheme) }
204
+ end
205
+
196
206
  def schemes
197
207
  return unless schemes?
198
208
 
@@ -201,23 +211,20 @@ module AppInfo
201
211
  .uniq
202
212
  end
203
213
 
204
- def schemes?
205
- browsable? && data.any? { |d| !DEEP_LINK_SCHEMES.include?(d.scheme) }
206
- end
207
-
208
214
  def browsable?
209
215
  exist?(CATEGORY_BROWSABLE)
210
216
  end
211
217
 
212
218
  def exist?(name, type: nil)
213
219
  if type.to_s.empty? && !name.start_with?('android.intent.')
214
- raise 'Fill type or use correct name'
220
+ raise ProtobufParseError, 'Not found intent type'
215
221
  end
216
222
 
217
223
  type ||= name.split('.')[2]
218
- raise 'Not found type' unless TYPES.include?(type)
224
+ raise ProtobufParseError, 'Not found intent type' unless TYPES.include?(type)
225
+ return false unless intent = send(type.to_sym)
219
226
 
220
- values = send(type.to_sym).select { |e| e.name == name }
227
+ values = intent.select { |e| e.name == name }
221
228
  values.empty? ? false : values
222
229
  end
223
230
  end
@@ -118,6 +118,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
118
118
  end
119
119
  end
120
120
 
121
+ # @!visibility private
121
122
  module Aapt
122
123
  module Pb
123
124
  Configuration = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("aapt.pb.Configuration").msgclass
@@ -9,6 +9,13 @@ 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:
@@ -322,7 +322,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
322
322
  end
323
323
  end
324
324
 
325
+ # @!visibility private
325
326
  module Aapt
327
+
326
328
  module Pb
327
329
  StringPool = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("aapt.pb.StringPool").msgclass
328
330
  SourcePosition = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("aapt.pb.SourcePosition").msgclass
@@ -12,7 +12,7 @@ module AppInfo
12
12
  new(doc)
13
13
  end
14
14
 
15
- include Helper::ReferenceParser
15
+ include Helper::Protobuf
16
16
 
17
17
  attr_reader :packages, :tool_fingerprint
18
18
 
@@ -53,7 +53,7 @@ module AppInfo
53
53
  end
54
54
 
55
55
  class Package
56
- include Helper::Defines
56
+ include Helper::GenerateClass
57
57
 
58
58
  attr_reader :name, :types
59
59
 
@@ -113,14 +113,14 @@ module AppInfo
113
113
  end
114
114
 
115
115
  class Entry
116
+ include Helper::GenerateClass
117
+
116
118
  def self.parse_from(type, package)
117
119
  type.entry.each_with_object([]) do |entry, obj|
118
120
  obj << Entry.new(entry, package)
119
121
  end
120
122
  end
121
123
 
122
- include Helper::Defines
123
-
124
124
  attr_reader :name, :values
125
125
 
126
126
  def initialize(doc, package)
@@ -155,7 +155,7 @@ module AppInfo
155
155
  end
156
156
 
157
157
  class Value
158
- include Helper::ReferenceParser
158
+ include Helper::Protobuf
159
159
  extend Forwardable
160
160
 
161
161
  attr_reader :locale, :config, :original_value, :value, :type
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppInfo
4
- VERSION = '2.8.4'
4
+ VERSION = '3.0.0.beta1'
5
5
  end
data/lib/app_info.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'app_info/version'
4
- require 'app_info/error'
5
4
  require 'app_info/core_ext'
5
+ require 'app_info/const'
6
+ require 'app_info/certificate'
6
7
  require 'app_info/helper'
8
+ require 'app_info/error'
7
9
 
10
+ require 'app_info/file'
8
11
  require 'app_info/info_plist'
9
12
  require 'app_info/mobile_provision'
10
13
 
@@ -12,6 +15,7 @@ require 'app_info/ipa'
12
15
  require 'app_info/ipa/plugin'
13
16
  require 'app_info/ipa/framework'
14
17
 
18
+ require 'app_info/android/signature'
15
19
  require 'app_info/apk'
16
20
  require 'app_info/aab'
17
21
 
@@ -19,6 +23,7 @@ require 'app_info/proguard'
19
23
  require 'app_info/dsym'
20
24
 
21
25
  require 'app_info/macos'
26
+ require 'app_info/pe'
22
27
 
23
28
  # fix invaild date format warnings
24
29
  Zip.warn_invalid_date = false
@@ -28,36 +33,56 @@ module AppInfo
28
33
  class << self
29
34
  # Get a new parser for automatic
30
35
  def parse(file)
31
- raise NotFoundError, file unless File.exist?(file)
32
-
33
- case file_type(file)
34
- when :ipa then IPA.new(file)
35
- when :apk then APK.new(file)
36
- when :aab then AAB.new(file)
37
- when :mobileprovision then MobileProvision.new(file)
38
- when :dsym then DSYM.new(file)
39
- when :proguard then Proguard.new(file)
40
- when :macos then Macos.new(file)
41
- else
42
- raise UnkownFileTypeError, "Do not detect file type: #{file}"
43
- end
36
+ raise NotFoundError, file unless ::File.exist?(file)
37
+
38
+ parser = case file_type(file)
39
+ when Format::IPA then IPA.new(file)
40
+ when Format::APK then APK.new(file)
41
+ when Format::AAB then AAB.new(file)
42
+ when Format::MOBILEPROVISION then MobileProvision.new(file)
43
+ when Format::DSYM then DSYM.new(file)
44
+ when Format::PROGUARD then Proguard.new(file)
45
+ when Format::MACOS then Macos.new(file)
46
+ when Format::PE then PE.new(file)
47
+ else
48
+ raise UnknownFileTypeError, "Do not detect file type: #{file}"
49
+ end
50
+
51
+ return parser unless block_given?
52
+
53
+ # call block and clear!
54
+ yield parser
55
+ parser.clear!
44
56
  end
45
57
  alias dump parse
46
58
 
59
+ def parse?(file)
60
+ file_type(file) != Format::UNKNOWN
61
+ end
62
+
47
63
  # Detect file type by read file header
48
64
  #
49
65
  # TODO: This can be better solution, if anyone knows, tell me please.
50
66
  def file_type(file)
51
- header_hex = File.read(file, 100)
52
- type = if header_hex =~ /^\x50\x4b\x03\x04/
53
- detect_zip_file(file)
54
- else
55
- detect_mobileprovision(header_hex)
56
- end
57
-
58
- type || :unkown
67
+ header_hex = ::File.read(file, 100)
68
+ case header_hex
69
+ when ZIP_RETGEX
70
+ detect_zip_file(file)
71
+ when PE_REGEX
72
+ Format::PE
73
+ when PLIST_REGEX, BPLIST_REGEX
74
+ Format::MOBILEPROVISION
75
+ else
76
+ Format::UNKNOWN
77
+ end
59
78
  end
60
79
 
80
+ def logger
81
+ @logger ||= Logger.new($stdout, level: :warn)
82
+ end
83
+
84
+ attr_writer :logger
85
+
61
86
  private
62
87
 
63
88
  # :nodoc:
@@ -65,35 +90,59 @@ module AppInfo
65
90
  Zip.warn_invalid_date = false
66
91
  zip_file = Zip::File.open(file)
67
92
 
68
- return :proguard unless zip_file.glob('*mapping*.txt').empty?
69
- return :apk if !zip_file.find_entry('AndroidManifest.xml').nil? &&
70
- !zip_file.find_entry('classes.dex').nil?
93
+ return Format::PROGUARD if proguard_clues?(zip_file)
94
+ return Format::APK if apk_clues?(zip_file)
95
+ return Format::AAB if aab_clues?(zip_file)
96
+ return Format::MACOS if macos_clues?(zip_file)
97
+ return Format::PE if pe_clues?(zip_file)
98
+ return Format::UNKNOWN unless clue = other_clues?(zip_file)
71
99
 
72
- return :aab if !zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
73
- !zip_file.find_entry('BundleConfig.pb').nil?
100
+ clue
101
+ ensure
102
+ zip_file.close
103
+ end
74
104
 
75
- return :macos if !zip_file.glob('*/Contents/MacOS/*').empty? &&
76
- !zip_file.glob('*/Contents/Info.plist').empty?
105
+ # :nodoc:
106
+ def proguard_clues?(zip_file)
107
+ !zip_file.glob('*mapping*.txt').empty?
108
+ end
77
109
 
78
- zip_file.each do |f|
79
- path = f.name
110
+ # :nodoc:
111
+ def apk_clues?(zip_file)
112
+ !zip_file.find_entry('AndroidManifest.xml').nil? &&
113
+ !zip_file.find_entry('classes.dex').nil?
114
+ end
80
115
 
81
- return :ipa if path.include?('Payload/') && path.end_with?('Info.plist')
82
- return :dsym if path.include?('Contents/Resources/DWARF/')
83
- end
84
- ensure
85
- zip_file.close
116
+ # :nodoc:
117
+ def aab_clues?(zip_file)
118
+ !zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
119
+ !zip_file.find_entry('BundleConfig.pb').nil?
86
120
  end
87
121
 
88
- PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
89
- BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
122
+ # :nodoc:
123
+ def macos_clues?(zip_file)
124
+ !zip_file.glob('*/Contents/MacOS/*').empty? &&
125
+ !zip_file.glob('*/Contents/Info.plist').empty?
126
+ end
90
127
 
91
128
  # :nodoc:
92
- def detect_mobileprovision(hex)
93
- case hex
94
- when PLIST_REGEX, BPLIST_REGEX
95
- :mobileprovision
129
+ def pe_clues?(zip_file)
130
+ !zip_file.glob('*.exe').empty?
131
+ end
132
+
133
+ # :nodoc:
134
+ def other_clues?(zip_file)
135
+ zip_file.each do |f|
136
+ path = f.name
137
+
138
+ return Format::IPA if path.include?('Payload/') && path.end_with?('Info.plist')
139
+ return Format::DSYM if path.include?('Contents/Resources/DWARF/')
96
140
  end
97
141
  end
98
142
  end
143
+
144
+ ZIP_RETGEX = /^\x50\x4b\x03\x04/.freeze
145
+ PE_REGEX = /^MZ/.freeze
146
+ PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
147
+ BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
99
148
  end