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