app-info 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4133c8a5d6188b8b9d111b8c7f105b49f455a111
4
+ data.tar.gz: db50999794694f324ea5ca1ae34c77ab42e68916
5
+ SHA512:
6
+ metadata.gz: 1fd826502a22607e9a6fea4004bee493e0413bca82f2e2e761fccdc2b2c4fbe6dd34ce49cc7b054321f377657a580eb679b156685338c0defe706b14501b3a49
7
+ data.tar.gz: b97a73a49da60aee6be611f463ada184446e2cfd557bb56336653b0ee47ea913ee51bf6dfae0c6061d828aaed79a8312bdfb736489e4cad3079d931596d95d10
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.5
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at icyleaf.cn@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in app-info.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 icyleaf
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,147 @@
1
+ # app_info
2
+
3
+ Teardown tool for mobile app(ipa/apk), analysis metedata like version, name, icon etc.
4
+
5
+ ## Support
6
+
7
+ - Android apk file
8
+ - iOS ipa file
9
+ - Info.plist file
10
+ - .mobileprovision file
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'app_info'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+ $ gem install app_info
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Initialize
35
+
36
+ ```ruby
37
+ require 'app_info'
38
+
39
+ # Automatic detect file extsion and parse
40
+ parser = AppInfo.parse('iphone.ipa')
41
+ parser = AppInfo.parse('ipad.ipa')
42
+ parser = AppInfo.parse('android.ipa')
43
+ parser = AppInfo.parse('App/Info.plist')
44
+ parser = AppInfo.parse('provisioning_profile/uuid.mobileprovision')
45
+ ```
46
+
47
+ ### iOS
48
+
49
+ Teardown suport iPhone/iPad/Universal.
50
+
51
+ ```ruby
52
+ ipa = AppInfo.parse('iphone.ipa')
53
+
54
+ # get app release version
55
+ ipa.release_version
56
+ # => 1.0
57
+
58
+ # get app bundle id
59
+ ipa.bundle_id
60
+ # => com.icyleaf.AppInfoDemo
61
+
62
+ # get app icons
63
+ ipa.icons
64
+ # => [{:name=>"AppIcon29x29@2x~ipad.png", :file=>"/var/folders/mb/8cm0fz4d499968yss9y1j8bc0000gp/T/d20160728-69669-1xnub30/AppInfo-ios-a5369339399e62046d7d59c52254dac6/Payload/bundle.app/AppIcon29x29@2x~ipad.png", :dimensions=>[58, 58]}, ...]
65
+
66
+ # get provisioning profile devices
67
+ ipa.devices
68
+ # => ['18cf53cddee60c5af9c97b1521e7cbf8342628da']
69
+
70
+ # detect app type
71
+ ipa.ipad?
72
+ ipa.iphone?
73
+ ipa.universal?
74
+
75
+ # detect app release type
76
+ ipa.release_type
77
+ # => 'AdHoc'
78
+
79
+ # get more propety in Info.plist
80
+ ipa.info[:CFBundleDisplayName]
81
+ # => 'AppInfoDemo'
82
+ ```
83
+
84
+ More to check [rspec test](spec/app_info).
85
+
86
+ ### Mobile Provision
87
+
88
+ Extract from IPA parser, it could teardown any .mobileprovision file(Provisioning Profile).
89
+ you can download it from Apple Developer Portal.
90
+
91
+ ```ruby
92
+ profile = AppInfo.parse('~/Library/MobileDevice/Provisioning\ Profiles/6e374bb8-a801-411f-ab28-96a4baa23814.mobileprovision')
93
+
94
+ # get app release version
95
+ profile.team_id
96
+ # => '3J9E73E9XS'
97
+
98
+ # get app package name
99
+ profile.team_name
100
+ # => 'Company/Team Name'
101
+
102
+ # get app icons
103
+ profile.devices
104
+ # => ['18cf53cddee60c5af9c97b1521e7cbf8342628da']
105
+ ```
106
+
107
+ ### Android
108
+
109
+ ```ruby
110
+ apk = AppInfo.parse('android.apk')
111
+
112
+ # get app release version
113
+ apk.release_version
114
+ # => 1.0
115
+
116
+ # get app package name
117
+ apk.package_namebundle_id
118
+ # => com.icyleaf.AppInfoDemo
119
+
120
+ # get app icons
121
+ apk.icons
122
+ # => [{:name=>"ic_launcher.png", :file=> "/var/folders/mb/8cm0fz4d499968yss9y1j8bc0000gp/T/d20160728-70163-10d47fl/AppInfo-android-cccbf89a889eb592c5c6f342d56b9a49/res/mipmap-mdpi-v4/ic_launcher.png/ic_launcher.png", :dimensions=>[48, 48]}, ...]
123
+
124
+ # get app support min sdk version
125
+ apk.min_sdk_version
126
+ # => 13
127
+
128
+ # detect app type (It's difficult to detect phone or tablet)
129
+ ipa.tv?
130
+ ipa.wear?
131
+ ```
132
+
133
+ ## Development
134
+
135
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
136
+
137
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
138
+
139
+ ## Contributing
140
+
141
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/app-info. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
142
+
143
+
144
+ ## License
145
+
146
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
147
+
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path('lib', __FILE__)
2
+ require 'app_info'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'pp'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'app_info/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'app-info'
8
+ spec.version = AppInfo::VERSION
9
+ spec.authors = ['icyleaf']
10
+ spec.email = ['icyleaf.cn@gmail.com']
11
+
12
+ spec.summary = 'Teardown tool for mobile app(ipa/apk), analysis metedata like version, name, icon'
13
+ spec.description = 'Teardown tool for ipa/apk files, even support for info.plist and .mobileprovision files'
14
+ spec.homepage = 'http://github.com/icyleaf/app-info'
15
+ spec.license = 'MIT'
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'CFPropertyList', '~> 2.3.2'
21
+ spec.add_dependency 'pngdefry', '~> 0.1.2'
22
+ spec.add_dependency 'ruby_android', '~> 0.7.7'
23
+ spec.add_dependency 'image_size', '~> 1.4.2'
24
+ spec.add_dependency 'os', '~> 0.9.6'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.12'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/app_info'
@@ -0,0 +1,24 @@
1
+ require 'app_info/version'
2
+ require 'app_info/parser'
3
+
4
+ #
5
+ # AppInfo Module
6
+ module AppInfo
7
+ #
8
+ # Get a new parser for automatic
9
+ def self.parse(file)
10
+ raise NotFoundError, file unless File.exist?(file)
11
+
12
+ case File.extname(file).downcase
13
+ when '.ipa' then Parser::IPA.new(file)
14
+ when '.apk' then Parser::APK.new(file)
15
+ when '.mobileprovision' then Parser::MobileProvision.new(file)
16
+ else
17
+ raise NotAppError, file
18
+ end
19
+ end
20
+ singleton_class.send(:alias_method, :dump, :parse)
21
+
22
+ class NotFoundError < StandardError; end
23
+ class NotAppError < StandardError; end
24
+ end
@@ -0,0 +1,25 @@
1
+ module AppInfo
2
+ # Monkey Patch for Object
3
+ module Tryable
4
+ def try(*a, &b)
5
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
6
+ end
7
+
8
+ def try!(*a, &b)
9
+ if a.empty? && block_given?
10
+ if b.arity.zero?
11
+ instance_eval(&b)
12
+ else
13
+ yield self
14
+ end
15
+ else
16
+ public_send(*a, &b)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # :nodoc:
23
+ class Object
24
+ include AppInfo::Tryable
25
+ end
@@ -0,0 +1,14 @@
1
+ require 'app_info/parser/ipa'
2
+ require 'app_info/parser/ipa/info_plist'
3
+ require 'app_info/parser/ipa/mobile_provision'
4
+ require 'app_info/parser/apk'
5
+
6
+ module AppInfo
7
+ module Parser
8
+ # App Platform
9
+ module Platform
10
+ IOS = 'iOS'.freeze
11
+ ANDROID = 'Android'.freeze
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,119 @@
1
+ require 'ruby_apk'
2
+ require 'image_size'
3
+
4
+ module AppInfo
5
+ module Parser
6
+ # Parse APK file
7
+ class APK
8
+ attr_reader :file, :apk
9
+
10
+ # APK Devices
11
+ module Device
12
+ PHONE = 'Phone'.freeze
13
+ TABLET = 'Tablet'.freeze
14
+ WATCH = 'Watch'.freeze
15
+ TV = 'Television'.freeze
16
+ end
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ @apk = ::Android::Apk.new(file)
21
+ end
22
+
23
+ def os
24
+ Parser::Platform::ANDROID
25
+ end
26
+
27
+ def build_version
28
+ manifest.version_code.to_s
29
+ end
30
+
31
+ def release_version
32
+ manifest.version_name
33
+ end
34
+
35
+ def name
36
+ resource.find('@string/app_name')
37
+ end
38
+
39
+ def bundle_id
40
+ manifest.package_name
41
+ end
42
+
43
+ def device_type
44
+ if wear?
45
+ Device::WATCH
46
+ elsif tv?
47
+ Device::TV
48
+ else
49
+ Device::PHONE
50
+ end
51
+ end
52
+
53
+ # TODO: find a way to detect
54
+ # def tablet?
55
+ # resource
56
+ # end
57
+
58
+ def wear?
59
+ uses_features.include?('android.hardware.type.watch')
60
+ end
61
+
62
+ def tv?
63
+ uses_features.include?('android.software.leanback')
64
+ end
65
+
66
+ def min_sdk_version
67
+ manifest.min_sdk_ver
68
+ end
69
+
70
+ def target_sdk_version
71
+ manifest.doc.elements['/manifest/uses-sdk'].attributes['targetSdkVersion'].to_i
72
+ end
73
+
74
+ def uses_features
75
+ uses_features = []
76
+ manifest.doc.each_element('/manifest/uses-feature') do |elem|
77
+ uses_features << elem.attributes['name']
78
+ end
79
+ uses_features.uniq
80
+ end
81
+
82
+ def manifest
83
+ @apk.manifest
84
+ end
85
+
86
+ def resource
87
+ @apk.resource
88
+ end
89
+
90
+ def icons
91
+ unless @icons
92
+ tmp_path = File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
93
+
94
+ @icons = @apk.icon.each_with_object([]) do |(path, data), obj|
95
+ icon_name = File.basename(path)
96
+ icon_path = File.join(tmp_path, File.path(path))
97
+ icon_file = File.join(icon_path, icon_name)
98
+ FileUtils.mkdir_p icon_path
99
+ File.open(icon_file, 'w') do |f|
100
+ f.write data
101
+ end
102
+
103
+ obj << {
104
+ name: icon_name,
105
+ file: icon_file,
106
+ dimensions: ImageSize.path(icon_file).size
107
+ }
108
+ end
109
+ end
110
+
111
+ @icons
112
+ end
113
+
114
+ alias identifier bundle_id
115
+ alias package_name bundle_id
116
+ alias device_type os
117
+ end # /APK
118
+ end # /Parser
119
+ end # /AppInfo
@@ -0,0 +1,218 @@
1
+ require 'os'
2
+ require 'pngdefry'
3
+ require 'fileutils'
4
+ require 'securerandom'
5
+ require 'cfpropertylist'
6
+ require 'app_info/core_ext/object/try'
7
+
8
+ module AppInfo
9
+ module Parser
10
+ # IPA parser
11
+ class IPA
12
+ attr_reader :file, :app_path
13
+
14
+ # iOS Export types
15
+ module ExportType
16
+ DEBUG = 'Debug'.freeze
17
+ ADHOC = 'AdHoc'.freeze
18
+ INHOUSE = 'inHouse'.freeze
19
+ RELEASE = 'Release'.freeze
20
+ end
21
+
22
+ def initialize(file)
23
+ @file = file
24
+ @app_path = app_path
25
+ end
26
+
27
+ def os
28
+ Parser::Platform::IOS
29
+ end
30
+
31
+ def build_version
32
+ info.build_version
33
+ end
34
+
35
+ def release_version
36
+ info.release_version
37
+ end
38
+
39
+ def identifier
40
+ info.identifier
41
+ end
42
+
43
+ def name
44
+ display_name || bundle_name
45
+ end
46
+
47
+ def display_name
48
+ info.display_name
49
+ end
50
+
51
+ def bundle_name
52
+ info.bundle_name
53
+ end
54
+
55
+ def icons
56
+ info.icons
57
+ end
58
+
59
+ def devices
60
+ mobileprovision.devices
61
+ end
62
+
63
+ def team_name
64
+ mobileprovision.team_name
65
+ end
66
+
67
+ def team_identifier
68
+ mobileprovision.team_identifier
69
+ end
70
+
71
+ def profile_name
72
+ mobileprovision.profile_name
73
+ end
74
+
75
+ def expired_date
76
+ mobileprovision.expired_date
77
+ end
78
+
79
+ def distribution_name
80
+ "#{profile_name} - #{team_name}" if profile_name && team_name
81
+ end
82
+
83
+ def device_type
84
+ info.device_type
85
+ end
86
+
87
+ def iphone?
88
+ info.iphone?
89
+ end
90
+
91
+ def ipad?
92
+ info.ipad?
93
+ end
94
+
95
+ def universal?
96
+ info.universal?
97
+ end
98
+
99
+ def release_type
100
+ if stored?
101
+ ExportType::RELEASE
102
+ else
103
+ build_type
104
+ end
105
+ end
106
+
107
+ def build_type
108
+ if mobileprovision?
109
+ if devices
110
+ ExportType::ADHOC
111
+ else
112
+ ExportType::INHOUSE
113
+ end
114
+ else
115
+ ExportType::DEBUG
116
+ end
117
+ end
118
+
119
+ def stored?
120
+ metadata? ? true : false
121
+ end
122
+
123
+ def hide_developer_certificates
124
+ mobileprovision.delete('DeveloperCertificates') if mobileprovision?
125
+ end
126
+
127
+ def cleanup!
128
+ return unless @contents
129
+ FileUtils.rm_rf(@contents)
130
+
131
+ @contents = nil
132
+ @icons = nil
133
+ @app_path = nil
134
+ @metadata = nil
135
+ @metadata_path = nil
136
+ @info = nil
137
+ end
138
+
139
+ def mobileprovision
140
+ return unless mobileprovision?
141
+ return @mobileprovision if @mobileprovision
142
+
143
+ return MobileProvision.new(nil) unless OS.mac?
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
+ @metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
165
+ end
166
+
167
+ def metadata?
168
+ File.exist?(metadata_path)
169
+ end
170
+
171
+ def metadata_path
172
+ @metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
173
+ end
174
+
175
+ def info
176
+ @info ||= InfoPlist.new(@app_path)
177
+ end
178
+
179
+ def app_path
180
+ @app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
181
+ end
182
+
183
+ alias bundle_id identifier
184
+
185
+ private
186
+
187
+ def contents
188
+ # source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
189
+ unless @contents
190
+ @contents = "#{Dir.mktmpdir}/AppInfo-ios-#{SecureRandom.hex}"
191
+ Zip::File.open(@file) do |zip_file|
192
+ zip_file.each do |f|
193
+ f_path = File.join(@contents, f.name)
194
+ FileUtils.mkdir_p(File.dirname(f_path))
195
+ zip_file.extract(f, f_path) unless File.exist?(f_path)
196
+ end
197
+ end
198
+ end
199
+
200
+ @contents
201
+ end
202
+
203
+ def icons_root_path
204
+ iphone = 'CFBundleIcons'.freeze
205
+ ipad = 'CFBundleIcons~ipad'.freeze
206
+
207
+ case device_type
208
+ when 'iPhone'
209
+ [iphone]
210
+ when 'iPad'
211
+ [ipad]
212
+ when 'Universal'
213
+ [iphone, ipad]
214
+ end
215
+ end
216
+ end # /IPA
217
+ end # /Parser
218
+ end # /AppInfo
@@ -0,0 +1,123 @@
1
+ require 'cfpropertylist'
2
+
3
+ module AppInfo
4
+ module Parser
5
+ # iOS Info.plist parser
6
+ class InfoPlist
7
+ def initialize(app_path)
8
+ @app_path = app_path
9
+ end
10
+
11
+ def build_version
12
+ info.try(:[], 'CFBundleVersion')
13
+ end
14
+
15
+ def release_version
16
+ info.try(:[], 'CFBundleShortVersionString')
17
+ end
18
+
19
+ def identifier
20
+ info.try(:[], 'CFBundleIdentifier')
21
+ end
22
+
23
+ def name
24
+ display_name || bundle_name
25
+ end
26
+
27
+ def display_name
28
+ info.try(:[], 'CFBundleDisplayName')
29
+ end
30
+
31
+ def bundle_name
32
+ info.try(:[], 'CFBundleName')
33
+ end
34
+
35
+ def icons
36
+ return @icons if @icons
37
+
38
+ @icons = []
39
+ icons_root_path.each do |name|
40
+ icon_array = info.try(:[], name)
41
+ .try(:[], 'CFBundlePrimaryIcon')
42
+ .try(:[], 'CFBundleIconFiles')
43
+
44
+ next if icon_array.nil? || icon_array.count == 0
45
+
46
+ icon_array.each do |items|
47
+ Dir.glob(File.join(@app_path, "#{items}*")).find_all.each do |file|
48
+ dict = {
49
+ name: File.basename(file),
50
+ file: file,
51
+ dimensions: Pngdefry.dimensions(file)
52
+ }
53
+
54
+ @icons.push(dict)
55
+ end
56
+ end
57
+ end
58
+
59
+ @icons
60
+ end
61
+
62
+ def device_type
63
+ device_family = info.try(:[], 'UIDeviceFamily')
64
+ if device_family.length == 1
65
+ case device_family
66
+ when [1]
67
+ 'iPhone'
68
+ when [2]
69
+ 'iPad'
70
+ end
71
+ elsif device_family.length == 2 && device_family == [1, 2]
72
+ 'Universal'
73
+ end
74
+ end
75
+
76
+ def iphone?
77
+ device_type == 'iPhone'
78
+ end
79
+
80
+ def ipad?
81
+ device_type == 'iPad'
82
+ end
83
+
84
+ def universal?
85
+ device_type == 'Universal'
86
+ end
87
+
88
+ def release_type
89
+ if stored?
90
+ 'Store'
91
+ else
92
+ build_type
93
+ end
94
+ end
95
+
96
+ def info
97
+ @info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
98
+ end
99
+
100
+ def info_path
101
+ File.join(@app_path, 'Info.plist')
102
+ end
103
+
104
+ alias bundle_id identifier
105
+
106
+ private
107
+
108
+ def icons_root_path
109
+ iphone = 'CFBundleIcons'.freeze
110
+ ipad = 'CFBundleIcons~ipad'.freeze
111
+
112
+ case device_type
113
+ when 'iPhone'
114
+ [iphone]
115
+ when 'iPad'
116
+ [ipad]
117
+ when 'Universal'
118
+ [iphone, ipad]
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,69 @@
1
+ require 'cfpropertylist'
2
+
3
+ module AppInfo
4
+ module Parser
5
+ # .mobileprovision file parser
6
+ class MobileProvision
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+
11
+ def name
12
+ mobileprovision.try(:[], 'Name')
13
+ end
14
+
15
+ def app_name
16
+ mobileprovision.try(:[], 'AppIDName')
17
+ end
18
+
19
+ def devices
20
+ mobileprovision.try(:[], 'ProvisionedDevices')
21
+ end
22
+
23
+ def team_identifier
24
+ mobileprovision.try(:[], 'TeamIdentifier')
25
+ end
26
+
27
+ def team_name
28
+ mobileprovision.try(:[], 'TeamName')
29
+ end
30
+
31
+ def profile_name
32
+ mobileprovision.try(:[], 'Name')
33
+ end
34
+
35
+ def created_date
36
+ mobileprovision.try(:[], 'CreationDate')
37
+ end
38
+
39
+ def expired_date
40
+ mobileprovision.try(:[], 'ExpirationDate')
41
+ end
42
+
43
+ def entitlements
44
+ mobileprovision.try(:[], 'Entitlements')
45
+ end
46
+
47
+ def method_missing(method_name, *args, &block)
48
+ key = if method_name.to_s.include?('_')
49
+ method_name.to_s.split('_').map {|k| k.capitalize }.join('')
50
+ else
51
+ method_name.to_s
52
+ end
53
+
54
+ mobileprovision.try(:[], key)
55
+ end
56
+
57
+ def empty?
58
+ mobileprovision.nil?
59
+ end
60
+
61
+ def mobileprovision
62
+ data = `security cms -D -i "#{@path}"`
63
+ @mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: data).value)
64
+ rescue CFFormatError
65
+ @mobileprovision = nil
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module AppInfo
2
+ VERSION = '1.0.1'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: app-info
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - icyleaf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: CFPropertyList
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: pngdefry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby_android
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.7.7
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.7
55
+ - !ruby/object:Gem::Dependency
56
+ name: image_size
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: os
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.6
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.12'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ description: Teardown tool for ipa/apk files, even support for info.plist and .mobileprovision
126
+ files
127
+ email:
128
+ - icyleaf.cn@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - CODE_OF_CONDUCT.md
137
+ - Gemfile
138
+ - LICENSE.txt
139
+ - README.md
140
+ - Rakefile
141
+ - app_info.gemspec
142
+ - lib/app-info.rb
143
+ - lib/app_info.rb
144
+ - lib/app_info/core_ext/object/try.rb
145
+ - lib/app_info/parser.rb
146
+ - lib/app_info/parser/apk.rb
147
+ - lib/app_info/parser/ipa.rb
148
+ - lib/app_info/parser/ipa/info_plist.rb
149
+ - lib/app_info/parser/ipa/mobile_provision.rb
150
+ - lib/app_info/version.rb
151
+ homepage: http://github.com/icyleaf/app-info
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.6.6
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Teardown tool for mobile app(ipa/apk), analysis metedata like version, name,
175
+ icon
176
+ test_files: []