app-info 2.0.0.beta3 → 2.0.0.beta4

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.
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zip'
4
+ require 'pngdefry'
5
+ require 'fileutils'
6
+ require 'securerandom'
7
+ require 'cfpropertylist'
8
+ require 'app_info/util'
9
+ require 'app_info/core_ext/object/try'
10
+
11
+ module AppInfo
12
+ # IPA parser
13
+ class IPA
14
+ attr_reader :file
15
+
16
+ # iOS Export types
17
+ module ExportType
18
+ DEBUG = 'Debug'
19
+ ADHOC = 'AdHoc'
20
+ INHOUSE = 'inHouse'
21
+ RELEASE = 'Release'
22
+ UNKOWN = nil
23
+ end
24
+
25
+ def initialize(file)
26
+ @file = file
27
+ end
28
+
29
+ def size(humanable = false)
30
+ AppInfo::Util.file_size(@file, humanable)
31
+ end
32
+
33
+ def os
34
+ AppInfo::Platform::IOS
35
+ end
36
+ alias file_type os
37
+
38
+ def iphone?
39
+ info.iphone?
40
+ end
41
+
42
+ def ipad?
43
+ info.ipad?
44
+ end
45
+
46
+ def universal?
47
+ info.universal?
48
+ end
49
+
50
+ def build_version
51
+ info.build_version
52
+ end
53
+
54
+ def release_version
55
+ info.release_version
56
+ end
57
+
58
+ def identifier
59
+ info.identifier
60
+ end
61
+
62
+ def name
63
+ display_name || bundle_name
64
+ end
65
+
66
+ def display_name
67
+ info.display_name
68
+ end
69
+
70
+ def bundle_name
71
+ info.bundle_name
72
+ end
73
+
74
+ def icons
75
+ info.icons
76
+ end
77
+
78
+ #
79
+ # Return the minimum OS version for the given application
80
+ #
81
+ def min_sdk_version
82
+ info.min_sdk_version
83
+ end
84
+
85
+ def device_type
86
+ info.device_type
87
+ end
88
+
89
+ def devices
90
+ mobileprovision.devices
91
+ end
92
+
93
+ def team_name
94
+ mobileprovision.team_name
95
+ end
96
+
97
+ def team_identifier
98
+ mobileprovision.team_identifier
99
+ end
100
+
101
+ def profile_name
102
+ mobileprovision.profile_name
103
+ end
104
+
105
+ def expired_date
106
+ mobileprovision.expired_date
107
+ end
108
+
109
+ def distribution_name
110
+ "#{profile_name} - #{team_name}" if profile_name && team_name
111
+ end
112
+
113
+ def release_type
114
+ if stored?
115
+ ExportType::RELEASE
116
+ else
117
+ build_type
118
+ end
119
+ end
120
+
121
+ def build_type
122
+ if mobileprovision?
123
+ if devices
124
+ ExportType::ADHOC
125
+ else
126
+ ExportType::INHOUSE
127
+ end
128
+ else
129
+ ExportType::DEBUG
130
+ end
131
+ end
132
+
133
+ def stored?
134
+ metadata? ? true : false
135
+ end
136
+
137
+ def hide_developer_certificates
138
+ mobileprovision.delete('DeveloperCertificates') if mobileprovision?
139
+ end
140
+
141
+ def mobileprovision
142
+ return unless mobileprovision?
143
+ return @mobileprovision if @mobileprovision
144
+
145
+ @mobileprovision = MobileProvision.new(mobileprovision_path)
146
+ end
147
+
148
+ def mobileprovision?
149
+ File.exist?mobileprovision_path
150
+ end
151
+
152
+ def mobileprovision_path
153
+ filename = 'embedded.mobileprovision'
154
+ @mobileprovision_path ||= File.join(@file, filename)
155
+ unless File.exist?(@mobileprovision_path)
156
+ @mobileprovision_path = File.join(app_path, filename)
157
+ end
158
+
159
+ @mobileprovision_path
160
+ end
161
+
162
+ def metadata
163
+ return unless metadata?
164
+
165
+ @metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
166
+ end
167
+
168
+ def metadata?
169
+ File.exist?(metadata_path)
170
+ end
171
+
172
+ def metadata_path
173
+ @metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
174
+ end
175
+
176
+ def info
177
+ @info ||= InfoPlist.new(app_path)
178
+ end
179
+
180
+ def app_path
181
+ @app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
182
+ end
183
+
184
+ def cleanup!
185
+ return unless @contents
186
+
187
+ FileUtils.rm_rf(@contents)
188
+
189
+ @contents = nil
190
+ @icons = nil
191
+ @app_path = nil
192
+ @metadata = nil
193
+ @metadata_path = nil
194
+ @info = nil
195
+ end
196
+
197
+ alias bundle_id identifier
198
+
199
+ private
200
+
201
+ def contents
202
+ # source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
203
+ unless @contents
204
+ @contents = "#{Dir.mktmpdir}/AppInfo-ios-#{SecureRandom.hex}"
205
+ Zip::File.open(@file) do |zip_file|
206
+ zip_file.each do |f|
207
+ f_path = File.join(@contents, f.name)
208
+ zip_file.extract(f, f_path) unless File.exist?(f_path)
209
+ end
210
+ end
211
+ end
212
+
213
+ @contents
214
+ end
215
+
216
+ def icons_root_path
217
+ iphone = 'CFBundleIcons'
218
+ ipad = 'CFBundleIcons~ipad'
219
+
220
+ case device_type
221
+ when 'iPhone'
222
+ [iphone]
223
+ when 'iPad'
224
+ [ipad]
225
+ when 'Universal'
226
+ [iphone, ipad]
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cfpropertylist'
4
+
5
+ module AppInfo
6
+ # iOS Info.plist parser
7
+ class InfoPlist
8
+ def initialize(app_path)
9
+ @app_path = app_path
10
+ end
11
+
12
+ def build_version
13
+ info.try(:[], 'CFBundleVersion')
14
+ end
15
+
16
+ def release_version
17
+ info.try(:[], 'CFBundleShortVersionString')
18
+ end
19
+
20
+ def identifier
21
+ info.try(:[], 'CFBundleIdentifier')
22
+ end
23
+ alias bundle_id identifier
24
+
25
+ def name
26
+ display_name || bundle_name
27
+ end
28
+
29
+ def display_name
30
+ info.try(:[], 'CFBundleDisplayName')
31
+ end
32
+
33
+ def bundle_name
34
+ info.try(:[], 'CFBundleName')
35
+ end
36
+
37
+ #
38
+ # Extract the Minimum OS Version from the Info.plist
39
+ #
40
+ def min_sdk_version
41
+ info.try(:[], 'MinimumOSVersion')
42
+ end
43
+
44
+ def icons
45
+ return @icons if @icons
46
+
47
+ @icons = []
48
+ icons_root_path.each do |name|
49
+ icon_array = info.try(:[], name)
50
+ .try(:[], 'CFBundlePrimaryIcon')
51
+ .try(:[], 'CFBundleIconFiles')
52
+
53
+ next if icon_array.nil? || icon_array.zero?
54
+
55
+ icon_array.each do |items|
56
+ Dir.glob(File.join(@app_path, "#{items}*")).find_all.each do |file|
57
+ dict = {
58
+ name: File.basename(file),
59
+ file: file,
60
+ dimensions: Pngdefry.dimensions(file)
61
+ }
62
+
63
+ @icons.push(dict)
64
+ end
65
+ end
66
+ end
67
+
68
+ @icons
69
+ end
70
+
71
+ def device_type
72
+ device_family = info.try(:[], 'UIDeviceFamily')
73
+ if device_family.length == 1
74
+ case device_family
75
+ when [1]
76
+ 'iPhone'
77
+ when [2]
78
+ 'iPad'
79
+ end
80
+ elsif device_family.length == 2 && device_family == [1, 2]
81
+ 'Universal'
82
+ end
83
+ end
84
+
85
+ def iphone?
86
+ device_type == 'iPhone'
87
+ end
88
+
89
+ def ipad?
90
+ device_type == 'iPad'
91
+ end
92
+
93
+ def universal?
94
+ device_type == 'Universal'
95
+ end
96
+
97
+ def release_type
98
+ if stored?
99
+ 'Store'
100
+ else
101
+ build_type
102
+ end
103
+ end
104
+
105
+ def [](key)
106
+ info.try(:[], key.to_s)
107
+ end
108
+
109
+ private
110
+
111
+ def info
112
+ @info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
113
+ end
114
+
115
+ def info_path
116
+ File.join(@app_path, 'Info.plist')
117
+ end
118
+
119
+ IPHONE_KEY = 'CFBundleIcons'
120
+ IPAD_KEY = 'CFBundleIcons~ipad'
121
+
122
+ def icons_root_path
123
+ case device_type
124
+ when 'iPhone'
125
+ [IPHONE_KEY]
126
+ when 'iPad'
127
+ [IPAD_KEY]
128
+ when 'Universal'
129
+ [IPHONE_KEY, IPAD_KEY]
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cfpropertylist'
4
+
5
+ module AppInfo
6
+ # .mobileprovision file parser
7
+ class MobileProvision
8
+ def initialize(path)
9
+ @path = path
10
+ end
11
+
12
+ def name
13
+ mobileprovision.try(:[], 'Name')
14
+ end
15
+
16
+ def app_name
17
+ mobileprovision.try(:[], 'AppIDName')
18
+ end
19
+
20
+ def devices
21
+ mobileprovision.try(:[], 'ProvisionedDevices')
22
+ end
23
+
24
+ def team_identifier
25
+ mobileprovision.try(:[], 'TeamIdentifier')
26
+ end
27
+
28
+ def team_name
29
+ mobileprovision.try(:[], 'TeamName')
30
+ end
31
+
32
+ def profile_name
33
+ mobileprovision.try(:[], 'Name')
34
+ end
35
+
36
+ def created_date
37
+ mobileprovision.try(:[], 'CreationDate')
38
+ end
39
+
40
+ def expired_date
41
+ mobileprovision.try(:[], 'ExpirationDate')
42
+ end
43
+
44
+ def entitlements
45
+ mobileprovision.try(:[], 'Entitlements')
46
+ end
47
+
48
+ def method_missing(method_name, *args, &block)
49
+ key = if method_name.to_s.include?('_')
50
+ method_name.to_s
51
+ .split('_')
52
+ .map(&:capitalize)
53
+ .join('')
54
+ else
55
+ method_name.to_s
56
+ end
57
+
58
+ mobileprovision.try(:[], key)
59
+ end
60
+
61
+ def empty?
62
+ mobileprovision.nil?
63
+ end
64
+
65
+ def mobileprovision
66
+ return @mobileprovision = nil unless File.exist?(@path)
67
+
68
+ data = File.read(@path)
69
+ data = strip_plist_wrapper(data) unless bplist?(data)
70
+ list = CFPropertyList::List.new(data: data).value
71
+ @mobileprovision = CFPropertyList.native_types(list)
72
+ rescue CFFormatError
73
+ @mobileprovision = nil
74
+ end
75
+
76
+ private
77
+
78
+ def bplist?(raw)
79
+ raw[0..5] == 'bplist'
80
+ end
81
+
82
+ def strip_plist_wrapper(raw)
83
+ end_tag = '</plist>'
84
+ start_point = raw.index('<?xml version=')
85
+ end_point = raw.index(end_tag) + end_tag.size - 1
86
+ raw[start_point..end_point]
87
+ end
88
+ end
89
+ end