app-info 2.8.4 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -6,11 +6,19 @@ require 'app_info/png_uncrush'
6
6
 
7
7
  module AppInfo
8
8
  # iOS Info.plist parser
9
- class InfoPlist
9
+ class InfoPlist < File
10
10
  extend Forwardable
11
11
 
12
- def initialize(file)
13
- @file = file
12
+ # Icon Key
13
+ ICON_KEYS = {
14
+ Device::IPHONE => ['CFBundleIcons'],
15
+ Device::IPAD => ['CFBundleIcons~ipad'],
16
+ Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
17
+ Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
18
+ }.freeze
19
+
20
+ def file_type
21
+ Format::INFOPLIST
14
22
  end
15
23
 
16
24
  def version
@@ -126,7 +134,7 @@ module AppInfo
126
134
  private
127
135
 
128
136
  def info
129
- return unless File.file?(@file)
137
+ return unless ::File.file?(@file)
130
138
 
131
139
  @info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: @file).value)
132
140
  end
@@ -134,9 +142,9 @@ module AppInfo
134
142
  def app_path
135
143
  @app_path ||= case device_type
136
144
  when Device::MACOS
137
- File.dirname(@file)
145
+ ::File.dirname(@file)
138
146
  else
139
- File.expand_path('../', @file)
147
+ ::File.expand_path('../', @file)
140
148
  end
141
149
  end
142
150
  end
@@ -8,7 +8,7 @@ module AppInfo
8
8
  extend Forwardable
9
9
 
10
10
  def self.parse(path, name = 'Frameworks')
11
- files = Dir.glob(File.join(path, name.to_s, '*'))
11
+ files = Dir.glob(::File.join(path, name.to_s, '*'))
12
12
  return [] if files.empty?
13
13
 
14
14
  files.sort.each_with_object([]) do |file, obj|
@@ -26,7 +26,7 @@ module AppInfo
26
26
  end
27
27
 
28
28
  def name
29
- File.basename(file)
29
+ ::File.basename(file)
30
30
  end
31
31
 
32
32
  def macho
@@ -37,11 +37,11 @@ module AppInfo
37
37
  end
38
38
 
39
39
  def lib?
40
- File.file?(file)
40
+ ::File.file?(file)
41
41
  end
42
42
 
43
43
  def info
44
- @info ||= InfoPlist.new(File.join(file, 'Info.plist'))
44
+ @info ||= InfoPlist.new(::File.join(file, 'Info.plist'))
45
45
  end
46
46
 
47
47
  def to_s
data/lib/app_info/ipa.rb CHANGED
@@ -7,7 +7,7 @@ require 'cfpropertylist'
7
7
 
8
8
  module AppInfo
9
9
  # IPA parser
10
- class IPA
10
+ class IPA < File
11
11
  include Helper::HumanFileSize
12
12
  include Helper::Archive
13
13
  extend Forwardable
@@ -25,18 +25,26 @@ module AppInfo
25
25
  INHOUSE = 'Enterprise' # Rename and Alias to enterprise
26
26
  end
27
27
 
28
- def initialize(file)
29
- @file = file
30
- end
31
-
28
+ # return file size
29
+ # @example Read file size in integer
30
+ # aab.size # => 3618865
31
+ #
32
+ # @example Read file size in human readabale
33
+ # aab.size(human_size: true) # => '3.45 MB'
34
+ #
35
+ # @param [Boolean] human_size Convert integer value to human readable.
36
+ # @return [Integer, String]
32
37
  def size(human_size: false)
33
38
  file_to_human_size(@file, human_size: human_size)
34
39
  end
35
40
 
36
- def os
41
+ def file_type
42
+ Format::IPA
43
+ end
44
+
45
+ def platform
37
46
  Platform::IOS
38
47
  end
39
- alias file_type os
40
48
 
41
49
  def_delegators :info, :iphone?, :ipad?, :universal?, :build_version, :name,
42
50
  :release_version, :identifier, :bundle_id, :display_name,
@@ -70,7 +78,7 @@ module AppInfo
70
78
  end
71
79
 
72
80
  def archs
73
- return unless File.exist?(bundle_path)
81
+ return unless ::File.exist?(bundle_path)
74
82
 
75
83
  file = MachO.open(bundle_path)
76
84
  case file
@@ -114,14 +122,14 @@ module AppInfo
114
122
  end
115
123
 
116
124
  def mobileprovision?
117
- File.exist?(mobileprovision_path)
125
+ ::File.exist?(mobileprovision_path)
118
126
  end
119
127
 
120
128
  def mobileprovision_path
121
129
  filename = 'embedded.mobileprovision'
122
- @mobileprovision_path ||= File.join(@file, filename)
123
- unless File.exist?(@mobileprovision_path)
124
- @mobileprovision_path = File.join(app_path, filename)
130
+ @mobileprovision_path ||= ::File.join(@file, filename)
131
+ unless ::File.exist?(@mobileprovision_path)
132
+ @mobileprovision_path = ::File.join(app_path, filename)
125
133
  end
126
134
 
127
135
  @mobileprovision_path
@@ -134,15 +142,15 @@ module AppInfo
134
142
  end
135
143
 
136
144
  def metadata?
137
- File.exist?(metadata_path)
145
+ ::File.exist?(metadata_path)
138
146
  end
139
147
 
140
148
  def metadata_path
141
- @metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
149
+ @metadata_path ||= ::File.join(contents, 'iTunesMetadata.plist')
142
150
  end
143
151
 
144
152
  def bundle_path
145
- @bundle_path ||= File.join(app_path, info.bundle_name)
153
+ @bundle_path ||= ::File.join(app_path, info.bundle_name)
146
154
  end
147
155
 
148
156
  def info
@@ -150,35 +158,32 @@ module AppInfo
150
158
  end
151
159
 
152
160
  def info_path
153
- @info_path ||= File.join(app_path, 'Info.plist')
161
+ @info_path ||= ::File.join(app_path, 'Info.plist')
154
162
  end
155
163
 
156
164
  def app_path
157
- @app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
165
+ @app_path ||= Dir.glob(::File.join(contents, 'Payload', '*.app')).first
158
166
  end
159
167
 
160
168
  IPHONE_KEY = 'CFBundleIcons'
161
169
  IPAD_KEY = 'CFBundleIcons~ipad'
162
170
 
163
171
  def icons_path
164
- return @icons_path if @icons_path
165
-
166
- @icons_path = []
167
- icon_keys.each do |name|
168
- filenames = info.try(:[], name)
169
- .try(:[], 'CFBundlePrimaryIcon')
170
- .try(:[], 'CFBundleIconFiles')
171
-
172
- next if filenames.nil? || filenames.empty?
173
-
174
- filenames.each do |filename|
175
- Dir.glob(File.join(app_path, "#{filename}*")).find_all.each do |file|
176
- @icons_path << file
172
+ @icons_path ||= lambda {
173
+ icon_keys.each_with_object([]) do |name, icons|
174
+ filenames = info.try(:[], name)
175
+ .try(:[], 'CFBundlePrimaryIcon')
176
+ .try(:[], 'CFBundleIconFiles')
177
+
178
+ next if filenames.nil? || filenames.empty?
179
+
180
+ filenames.each do |filename|
181
+ Dir.glob(::File.join(app_path, "#{filename}*")).find_all.each do |file|
182
+ icons << file
183
+ end
177
184
  end
178
185
  end
179
- end
180
-
181
- @icons_path
186
+ }.call
182
187
  end
183
188
 
184
189
  def clear!
@@ -197,7 +202,7 @@ module AppInfo
197
202
  end
198
203
 
199
204
  def contents
200
- @contents ||= unarchive(@file, path: 'ios')
205
+ @contents ||= unarchive(@file, prefix: 'ios')
201
206
  end
202
207
 
203
208
  private
@@ -206,7 +211,7 @@ module AppInfo
206
211
  uncrushed_file = uncrush ? uncrush_png(file) : nil
207
212
 
208
213
  {
209
- name: File.basename(file),
214
+ name: ::File.basename(file),
210
215
  file: file,
211
216
  uncrushed_file: uncrushed_file,
212
217
  dimensions: PngUncrush.dimensions(file)
@@ -217,7 +222,7 @@ module AppInfo
217
222
  def uncrush_png(src_file)
218
223
  dest_file = tempdir(src_file, prefix: 'uncrushed')
219
224
  PngUncrush.decompress(src_file, dest_file)
220
- File.exist?(dest_file) ? dest_file : nil
225
+ ::File.exist?(dest_file) ? dest_file : nil
221
226
  end
222
227
 
223
228
  def icon_keys
@@ -7,7 +7,7 @@ require 'cfpropertylist'
7
7
 
8
8
  module AppInfo
9
9
  # MacOS App parser
10
- class Macos
10
+ class Macos < File
11
11
  include Helper::HumanFileSize
12
12
  include Helper::Archive
13
13
  extend Forwardable
@@ -21,18 +21,26 @@ module AppInfo
21
21
  APPSTORE = 'AppStore'
22
22
  end
23
23
 
24
- def initialize(file)
25
- @file = file
26
- end
27
-
24
+ # return file size
25
+ # @example Read file size in integer
26
+ # aab.size # => 3618865
27
+ #
28
+ # @example Read file size in human readabale
29
+ # aab.size(human_size: true) # => '3.45 MB'
30
+ #
31
+ # @param [Boolean] human_size Convert integer value to human readable.
32
+ # @return [Integer, String]
28
33
  def size(human_size: false)
29
34
  file_to_human_size(@file, human_size: human_size)
30
35
  end
31
36
 
32
- def os
37
+ def file_type
38
+ Format::MACOS
39
+ end
40
+
41
+ def platform
33
42
  Platform::MACOS
34
43
  end
35
- alias file_type os
36
44
 
37
45
  def_delegators :info, :macos?, :iphone?, :ipad?, :universal?, :build_version, :name,
38
46
  :release_version, :identifier, :bundle_id, :display_name,
@@ -56,14 +64,14 @@ module AppInfo
56
64
  end
57
65
 
58
66
  def stored?
59
- File.exist?(store_path)
67
+ ::File.exist?(store_path)
60
68
  end
61
69
 
62
70
  def icons(convert: true)
63
71
  return unless icon_file
64
72
 
65
73
  data = {
66
- name: File.basename(icon_file),
74
+ name: ::File.basename(icon_file),
67
75
  file: icon_file
68
76
  }
69
77
 
@@ -72,7 +80,7 @@ module AppInfo
72
80
  end
73
81
 
74
82
  def archs
75
- return unless File.exist?(binary_path)
83
+ return unless ::File.exist?(binary_path)
76
84
 
77
85
  file = MachO.open(binary_path)
78
86
  case file
@@ -97,25 +105,25 @@ module AppInfo
97
105
  end
98
106
 
99
107
  def mobileprovision?
100
- File.exist?(mobileprovision_path)
108
+ ::File.exist?(mobileprovision_path)
101
109
  end
102
110
 
103
111
  def mobileprovision_path
104
- @mobileprovision_path ||= File.join(app_path, 'Contents', 'embedded.provisionprofile')
112
+ @mobileprovision_path ||= ::File.join(app_path, 'Contents', 'embedded.provisionprofile')
105
113
  end
106
114
 
107
115
  def store_path
108
- @store_path ||= File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
116
+ @store_path ||= ::File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
109
117
  end
110
118
 
111
119
  def binary_path
112
120
  return @binary_path if @binary_path
113
121
 
114
- base_path = File.join(app_path, 'Contents', 'MacOS')
122
+ base_path = ::File.join(app_path, 'Contents', 'MacOS')
115
123
  binary = info['CFBundleExecutable']
116
- return File.join(base_path, binary) if binary
124
+ return ::File.join(base_path, binary) if binary
117
125
 
118
- @binary_path ||= Dir.glob(File.join(base_path, '*')).first
126
+ @binary_path ||= Dir.glob(::File.join(base_path, '*')).first
119
127
  end
120
128
 
121
129
  def info
@@ -123,11 +131,11 @@ module AppInfo
123
131
  end
124
132
 
125
133
  def info_path
126
- @info_path ||= File.join(app_path, 'Contents', 'Info.plist')
134
+ @info_path ||= ::File.join(app_path, 'Contents', 'Info.plist')
127
135
  end
128
136
 
129
137
  def app_path
130
- @app_path ||= Dir.glob(File.join(contents, '*.app')).first
138
+ @app_path ||= Dir.glob(::File.join(contents, '*.app')).first
131
139
  end
132
140
 
133
141
  def clear!
@@ -137,14 +145,14 @@ module AppInfo
137
145
 
138
146
  @contents = nil
139
147
  @app_path = nil
140
- @binrary_path = nil
148
+ @binary_path = nil
141
149
  @info_path = nil
142
150
  @info = nil
143
151
  @icons = nil
144
152
  end
145
153
 
146
154
  def contents
147
- @contents ||= unarchive(@file, path: 'macos')
155
+ @contents ||= unarchive(@file, prefix: 'macos')
148
156
  end
149
157
 
150
158
  private
@@ -155,8 +163,8 @@ module AppInfo
155
163
  info.icons.each do |key|
156
164
  next unless value = info[key]
157
165
 
158
- file = File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
159
- next unless File.file?(file)
166
+ file = ::File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
167
+ next unless ::File.file?(file)
160
168
 
161
169
  return @icon_file = file
162
170
  end
@@ -173,14 +181,14 @@ module AppInfo
173
181
  file = data[:file]
174
182
  reader = Icns::Reader.new(file)
175
183
  Icns::SIZE_TO_TYPE.each do |size, _|
176
- dest_filename = "#{File.basename(file, '.icns')}_#{size}x#{size}.png"
177
- dest_file = tempdir(File.join(File.dirname(file), dest_filename), prefix: 'converted')
184
+ dest_filename = "#{::File.basename(file, '.icns')}_#{size}x#{size}.png"
185
+ dest_file = tempdir(::File.join(::File.dirname(file), dest_filename), prefix: 'converted')
178
186
  next unless icon_data = reader.image(size: size)
179
187
 
180
- File.write(dest_file, icon_data, encoding: Encoding::BINARY)
188
+ ::File.write(dest_file, icon_data, encoding: Encoding::BINARY)
181
189
 
182
190
  data[:sets] << {
183
- name: File.basename(dest_filename),
191
+ name: ::File.basename(dest_filename),
184
192
  file: dest_file,
185
193
  dimensions: ImageSize.path(dest_file).size
186
194
  }
@@ -5,9 +5,9 @@ require 'cfpropertylist'
5
5
 
6
6
  module AppInfo
7
7
  # .mobileprovision file parser
8
- class MobileProvision
9
- def initialize(path)
10
- @path = path
8
+ class MobileProvision < File
9
+ def file_type
10
+ Format::MOBILEPROVISION
11
11
  end
12
12
 
13
13
  def name
@@ -66,12 +66,22 @@ module AppInfo
66
66
  mobileprovision.try(:[], 'Entitlements')
67
67
  end
68
68
 
69
+ # return developer certificates.
70
+ #
71
+ # @deprecated Use {#certificates} instead of this method.
69
72
  def developer_certs
73
+ certificates
74
+ end
75
+
76
+ # return developer certificates.
77
+ #
78
+ # @return [Array<Certificate>]
79
+ def certificates
70
80
  certs = mobileprovision.try(:[], 'DeveloperCertificates')
71
81
  return if certs.empty?
72
82
 
73
- certs.each_with_object([]) do |cert, obj|
74
- obj << DeveloperCertificate.new(cert)
83
+ certs.each_with_object([]) do |cert_data, obj|
84
+ obj << Certificate.parse(cert_data)
75
85
  end
76
86
  end
77
87
 
@@ -142,10 +152,10 @@ module AppInfo
142
152
  when 'com.apple.developer.networking.vpn.api'
143
153
  capabilities << 'Personal VPN'
144
154
  when 'com.apple.developer.healthkit',
145
- 'com.apple.developer.healthkit.access'
155
+ 'com.apple.developer.healthkit.access'
146
156
  capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
147
157
  when 'com.apple.developer.icloud-services',
148
- 'com.apple.developer.icloud-container-identifiers'
158
+ 'com.apple.developer.icloud-container-identifiers'
149
159
  capabilities << 'iCloud' unless capabilities.include?('iCloud')
150
160
  when 'com.apple.developer.in-app-payments'
151
161
  capabilities << 'Apple Pay'
@@ -201,9 +211,9 @@ module AppInfo
201
211
  end
202
212
 
203
213
  def mobileprovision
204
- return @mobileprovision = nil unless File.exist?(@path)
214
+ return @mobileprovision = nil unless ::File.exist?(@file)
205
215
 
206
- data = File.read(@path)
216
+ data = ::File.read(@file)
207
217
  data = strip_plist_wrapper(data) unless bplist?(data)
208
218
  list = CFPropertyList::List.new(data: data).value
209
219
  @mobileprovision = CFPropertyList.native_types(list)
@@ -235,26 +245,5 @@ module AppInfo
235
245
  end_point = raw.index(end_tag) + end_tag.size - 1
236
246
  raw[start_point..end_point]
237
247
  end
238
-
239
- # Developer Certificate
240
- class DeveloperCertificate
241
- attr_reader :raw
242
-
243
- def initialize(data)
244
- @raw = OpenSSL::X509::Certificate.new(data)
245
- end
246
-
247
- def name
248
- @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
249
- end
250
-
251
- def created_date
252
- @raw.not_after
253
- end
254
-
255
- def expired_date
256
- @raw.not_before
257
- end
258
- end
259
248
  end
260
249
  end