app-info 3.1.2 → 3.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +27 -6
- data/CHANGELOG.md +15 -1
- data/app_info.gemspec +2 -2
- data/lib/app_info/android.rb +5 -5
- data/lib/app_info/apk.rb +1 -1
- data/lib/app_info/const.rb +58 -28
- data/lib/app_info/hap.rb +73 -0
- data/lib/app_info/happ.rb +46 -0
- data/lib/app_info/harmonyos.rb +65 -0
- data/lib/app_info/helper/file_type_detection.rb +111 -0
- data/lib/app_info/helper.rb +1 -0
- data/lib/app_info/info_plist.rb +19 -19
- data/lib/app_info/ipa.rb +3 -3
- data/lib/app_info/mobile_provision.rb +7 -1
- data/lib/app_info/pack_info.rb +51 -0
- data/lib/app_info/pe.rb +2 -2
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +10 -80
- metadata +19 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f6e47f362a58abf973195167f69726c0772ea2b4448527d6ee28c10c5c10922
|
4
|
+
data.tar.gz: 43adc9aae496b0658f4d27a993a0f89edb4c8d8deb5e5e1bee371588efa2aa10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0cfb7fb41c9e3ea9562913278fc7a4309ead0bb4bb497252a73c3008ef7c969b3169929722d9db7ce504f61882881f0635f274d7ae76a2116cc60fd5c933b14
|
7
|
+
data.tar.gz: '018f96706c906dfbf30d53dd415d0f3c669d2b84848938d8e3c45fa1c56bb52855032c8b100f67dd6e0cab423db4266ba7d82967105517e7c4d3ad40aee190da'
|
data/.github/workflows/ci.yml
CHANGED
@@ -8,15 +8,35 @@ on:
|
|
8
8
|
pull_request:
|
9
9
|
|
10
10
|
jobs:
|
11
|
+
linting:
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v4
|
16
|
+
|
17
|
+
- name: Setup Ruby 3.x
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: 3
|
21
|
+
bundler-cache: true
|
22
|
+
|
23
|
+
- name: Rubocop
|
24
|
+
run: bundle exec rubocop --format progress
|
25
|
+
|
11
26
|
test:
|
27
|
+
needs: [ linting ]
|
28
|
+
name: test ${{ matrix.ruby }}
|
29
|
+
runs-on: ubuntu-latest
|
12
30
|
strategy:
|
13
31
|
fail-fast: false
|
14
32
|
matrix:
|
15
33
|
ruby: [ ruby-3.1, ruby-3.2, ruby-3.3 ]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
34
|
+
experimental: [false]
|
35
|
+
include:
|
36
|
+
- ruby: head
|
37
|
+
experimental: true
|
38
|
+
- ruby: truffleruby-head
|
39
|
+
experimental: true
|
20
40
|
steps:
|
21
41
|
- uses: actions/checkout@v4
|
22
42
|
|
@@ -25,5 +45,6 @@ jobs:
|
|
25
45
|
ruby-version: ${{ matrix.ruby }}
|
26
46
|
bundler-cache: true
|
27
47
|
|
28
|
-
- name:
|
29
|
-
|
48
|
+
- name: RSpec
|
49
|
+
continue-on-error: ${{ matrix.experimental }}
|
50
|
+
run: bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
9
9
|
|
10
10
|
> List all changes before release a new version.
|
11
11
|
|
12
|
+
## [3.1.4] (2024-06-27)
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- Android(apk): Add fetch locales support.
|
17
|
+
- Android(apk): Add architectures support.
|
18
|
+
- Android(apk): Add detect universal apk.
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
- Android(apk): Fix Unknown chunk type 0x0203. #[icyleaf/android_parser#6](https://github.com/icyleaf/android_parser/issues/6)
|
23
|
+
- Android(apk): Fix 3bits of lang and country in locales.
|
24
|
+
|
12
25
|
## [3.1.2] (2024-06-25)
|
13
26
|
|
14
27
|
### Fixed
|
@@ -342,7 +355,8 @@ Dropped Ruby 2.5 ~ 3.0 support (no changes required.).
|
|
342
355
|
|
343
356
|
- Updated dependency of CFPropertly list be a range between 2.3.4. (thanks @[cschroed](https://github.com/cschroed))
|
344
357
|
|
345
|
-
[Unreleased]: https://github.com/icyleaf/app-info/compare/v3.1.
|
358
|
+
[Unreleased]: https://github.com/icyleaf/app-info/compare/v3.1.4..HEAD
|
359
|
+
[3.1.4]: https://github.com/icyleaf/app-info/compare/v3.1.2...v3.1.4
|
346
360
|
[3.1.2]: https://github.com/icyleaf/app-info/compare/v3.1.0...v3.1.2
|
347
361
|
[3.1.0]: https://github.com/icyleaf/app-info/compare/v3.0.0...v3.1.0
|
348
362
|
[3.0.0]: https://github.com/icyleaf/app-info/compare/v2.8.5...v3.0.0
|
data/app_info.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_dependency 'base64', '~> 0.2.0'
|
26
26
|
spec.add_dependency 'image_size', '>= 1.5', '< 3.5'
|
27
27
|
spec.add_dependency 'ruby-macho', '>= 1.4', '< 5'
|
28
|
-
spec.add_dependency 'android_parser', '
|
28
|
+
spec.add_dependency 'android_parser', '>= 2.7', '< 3.0'
|
29
29
|
spec.add_dependency 'rubyzip', '>= 1.2', '< 3.0'
|
30
30
|
spec.add_dependency 'uuidtools', '>= 2.1.5', '< 2.3.0'
|
31
31
|
spec.add_dependency 'icns', '~> 0.2.0'
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_development_dependency 'rake', '>= 10.0'
|
37
37
|
|
38
38
|
spec.post_install_message = <<~ENDBANNER
|
39
|
-
AppInfo 3.0
|
39
|
+
AppInfo 3.0 was out!
|
40
40
|
**********************
|
41
41
|
The public API of some AppInfo classes has been changed.
|
42
42
|
|
data/lib/app_info/android.rb
CHANGED
@@ -36,15 +36,15 @@ module AppInfo
|
|
36
36
|
# @return [Symbol] {Device}
|
37
37
|
def device
|
38
38
|
if watch?
|
39
|
-
Device::WATCH
|
39
|
+
Device::Google::WATCH
|
40
40
|
elsif television?
|
41
|
-
Device::TELEVISION
|
41
|
+
Device::Google::TELEVISION
|
42
42
|
elsif automotive?
|
43
|
-
Device::AUTOMOTIVE
|
43
|
+
Device::Google::AUTOMOTIVE
|
44
44
|
elsif tablet?
|
45
|
-
Device::TABLET
|
45
|
+
Device::Google::TABLET
|
46
46
|
else
|
47
|
-
Device::PHONE
|
47
|
+
Device::Google::PHONE
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
data/lib/app_info/apk.rb
CHANGED
data/lib/app_info/const.rb
CHANGED
@@ -23,6 +23,11 @@ module AppInfo
|
|
23
23
|
AAB = :aab
|
24
24
|
PROGUARD = :proguard
|
25
25
|
|
26
|
+
# HarmonyOS
|
27
|
+
|
28
|
+
HAP = :hap
|
29
|
+
HAPP = :app
|
30
|
+
|
26
31
|
# Windows
|
27
32
|
|
28
33
|
PE = :pe
|
@@ -35,6 +40,7 @@ module AppInfo
|
|
35
40
|
APPLE = :apple
|
36
41
|
GOOGLE = :google
|
37
42
|
MICROSOFT = :microsoft
|
43
|
+
HUAWEI = :huawei
|
38
44
|
end
|
39
45
|
|
40
46
|
# Platform
|
@@ -44,36 +50,60 @@ module AppInfo
|
|
44
50
|
ANDROID = :android
|
45
51
|
APPLETV = :appletv
|
46
52
|
WINDOWS = :windows
|
53
|
+
HARMONYOS = :harmonyos
|
47
54
|
end
|
48
55
|
|
49
|
-
#
|
56
|
+
# Device Type
|
50
57
|
module Device
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
58
|
+
module Apple
|
59
|
+
# macOS
|
60
|
+
MACOS = :macos
|
61
|
+
|
62
|
+
# Apple iPhone
|
63
|
+
IPHONE = :iphone
|
64
|
+
# Apple iPad
|
65
|
+
IPAD = :ipad
|
66
|
+
# Apple Universal (iPhone and iPad)
|
67
|
+
UNIVERSAL = :universal
|
68
|
+
# Apple TV
|
69
|
+
APPLETV = :appletv
|
70
|
+
# Apple Watch (TODO: not implemented yet)
|
71
|
+
IWATCH = :iwatch
|
72
|
+
end
|
73
|
+
|
74
|
+
module Google
|
75
|
+
# Android Phone
|
76
|
+
PHONE = :phone
|
77
|
+
# Android Tablet (TODO: not implemented yet)
|
78
|
+
TABLET = :tablet
|
79
|
+
# Android Watch
|
80
|
+
WATCH = :watch
|
81
|
+
# Android TV
|
82
|
+
TELEVISION = :television
|
83
|
+
# Android Car Automotive
|
84
|
+
AUTOMOTIVE = :automotive
|
85
|
+
end
|
86
|
+
|
87
|
+
module Huawei
|
88
|
+
# HarmonyOS Default
|
89
|
+
DEFAULT = :default
|
90
|
+
# HarmonyOS Phone
|
91
|
+
PHONE = :phone
|
92
|
+
# HarmonyOS Tablet
|
93
|
+
TABLET = :tablet
|
94
|
+
# HarmonyOS TV
|
95
|
+
TV = :tv
|
96
|
+
# HarmonyOS wearable
|
97
|
+
WEARABLE = :wearable
|
98
|
+
# HarmonyOS Car
|
99
|
+
CAR = :car
|
100
|
+
# HarmonyOS 2-in-1 tablet and laptop
|
101
|
+
TWO_IN_ONE = :two_in_one
|
102
|
+
end
|
103
|
+
|
104
|
+
module Microsoft
|
105
|
+
# Windows
|
106
|
+
WINDOWS = :windows
|
107
|
+
end
|
78
108
|
end
|
79
109
|
end
|
data/lib/app_info/hap.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# Parse HAP file parser
|
5
|
+
class HAP < HarmonyOS
|
6
|
+
# Full icons metadata
|
7
|
+
# @example
|
8
|
+
# ipa.icons
|
9
|
+
# # => [
|
10
|
+
# # {
|
11
|
+
# # name: 'icon.png',
|
12
|
+
# # file: '/path/to/icon.png',
|
13
|
+
# # uncrushed_file: '/path/to/uncrushed_icon.png',
|
14
|
+
# # dimensions: [64, 64]
|
15
|
+
# # },
|
16
|
+
# # {
|
17
|
+
# # name: 'icon1.png',
|
18
|
+
# # file: '/path/to/icon1.png',
|
19
|
+
# # uncrushed_file: '/path/to/uncrushed_icon1.png',
|
20
|
+
# # dimensions: [120, 120]
|
21
|
+
# # }
|
22
|
+
# # ]
|
23
|
+
# @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
|
24
|
+
def icons
|
25
|
+
@icons ||= icons_path.each_with_object([]) do |file, obj|
|
26
|
+
obj << {
|
27
|
+
name: ::File.basename(file),
|
28
|
+
file: file,
|
29
|
+
uncrushed_file: file,
|
30
|
+
dimensions: ImageSize.path(file).size
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<String>]
|
36
|
+
def icons_path
|
37
|
+
@icons_path ||= [::File.join(contents, 'resources', 'base', 'media', 'app_icon.png')]
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [JSON]
|
41
|
+
def module_info
|
42
|
+
@module_info ||= JSON.parse(::File.read(module_info_path))
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String]
|
46
|
+
def module_info_path
|
47
|
+
@module_info_path ||= ::File.join(contents, 'module.json')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
def name
|
52
|
+
# TODO: The application display name should be determined by looking up
|
53
|
+
# the value of the variable named in the "label" field of the "module.json"
|
54
|
+
# file within the "resources.index" file.
|
55
|
+
pack_info.bundle_name
|
56
|
+
end
|
57
|
+
|
58
|
+
def clear!
|
59
|
+
return unless @contents
|
60
|
+
|
61
|
+
FileUtils.rm_rf(@contents)
|
62
|
+
|
63
|
+
@pack_info = nil
|
64
|
+
@info_path = nil
|
65
|
+
@contents = nil
|
66
|
+
|
67
|
+
@module_info_path = nil
|
68
|
+
@module_info = nil
|
69
|
+
@icons_path = nil
|
70
|
+
@icons = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# parser for HarmonyOS .APP file
|
5
|
+
class HAPP < HarmonyOS
|
6
|
+
def_delegators :default_entry, :icons
|
7
|
+
# @return [HAP]
|
8
|
+
def default_entry
|
9
|
+
hap_path = ::File.join(contents, "#{default_entry_name}.hap")
|
10
|
+
@default_entry ||= HAP.new(hap_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def default_entry_name
|
15
|
+
return @default_entry_name if @default_entry_name
|
16
|
+
|
17
|
+
pack_info.packages.each do |package|
|
18
|
+
if package['moduleType'] == 'entry' && package['deliveryWithInstall']
|
19
|
+
@default_entry_name ||= package['name']
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@default_entry_name
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
def name
|
28
|
+
default_entry.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear!
|
32
|
+
return unless @contents
|
33
|
+
|
34
|
+
FileUtils.rm_rf(@contents)
|
35
|
+
|
36
|
+
@pack_info = nil
|
37
|
+
@info_path = nil
|
38
|
+
@contents = nil
|
39
|
+
|
40
|
+
@default_entry_name = nil
|
41
|
+
@default_entry&.clear!
|
42
|
+
|
43
|
+
@default_entry = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# HarmonyOS base parser for hap and app file
|
5
|
+
class HarmonyOS < File
|
6
|
+
extend Forwardable
|
7
|
+
include Helper::HumanFileSize
|
8
|
+
include Helper::Archive
|
9
|
+
|
10
|
+
def_delegators :pack_info, :build_version, :release_version, :bundle_id
|
11
|
+
|
12
|
+
# return file size
|
13
|
+
# @example Read file size in integer
|
14
|
+
# aab.size # => 3618865
|
15
|
+
#
|
16
|
+
# @example Read file size in human readabale
|
17
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
18
|
+
#
|
19
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
20
|
+
# @return [Integer, String]
|
21
|
+
def size(human_size: false)
|
22
|
+
file_to_human_size(@file, human_size: human_size)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Symbol] {Manufacturer}
|
26
|
+
def manufacturer
|
27
|
+
Manufacturer::HUAWEI
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Symbol] {Platform}
|
31
|
+
def platform
|
32
|
+
Platform::HARMONYOS
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Symbol] {Device}
|
36
|
+
def device
|
37
|
+
Device::Huawei::DEFAULT
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [PackInfo]
|
41
|
+
def pack_info
|
42
|
+
@pack_info ||= PackInfo.new(info_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String]
|
46
|
+
def info_path
|
47
|
+
@info_path ||= ::File.join(contents, 'pack.info')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] unzipped file path
|
51
|
+
def contents
|
52
|
+
@contents ||= unarchive(@file, prefix: format.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @abstract Subclass and override {#name} to implement.
|
56
|
+
def name
|
57
|
+
not_implemented_error!(__method__)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @abstract Subclass and override {#clear!} to implement.
|
61
|
+
def clear!
|
62
|
+
not_implemented_error!(__method__)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo::Helper
|
4
|
+
module FileTypeDetection
|
5
|
+
# Detect file type by reading file header
|
6
|
+
#
|
7
|
+
# TODO: This can be better solution, if anyone knows, tell me please.
|
8
|
+
def file_type(file)
|
9
|
+
header_hex = ::File.read(file, 100)
|
10
|
+
case header_hex
|
11
|
+
when ZIP_RETGEX
|
12
|
+
detect_zip_file(file)
|
13
|
+
when PE_REGEX
|
14
|
+
AppInfo::Format::PE
|
15
|
+
when PLIST_REGEX, BPLIST_REGEX
|
16
|
+
AppInfo::Format::MOBILEPROVISION
|
17
|
+
else
|
18
|
+
AppInfo::Format::UNKNOWN
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Detect file type for zip files
|
25
|
+
def detect_zip_file(file)
|
26
|
+
Zip.warn_invalid_date = false
|
27
|
+
zip_file = Zip::File.open(file)
|
28
|
+
|
29
|
+
return AppInfo::Format::PROGUARD if proguard_clues?(zip_file)
|
30
|
+
return AppInfo::Format::APK if apk_clues?(zip_file)
|
31
|
+
return AppInfo::Format::AAB if aab_clues?(zip_file)
|
32
|
+
return AppInfo::Format::MACOS if macos_clues?(zip_file)
|
33
|
+
return AppInfo::Format::PE if pe_clues?(zip_file)
|
34
|
+
return AppInfo::Format::HAP if hap_clues?(zip_file)
|
35
|
+
return AppInfo::Format::HAPP if happ_clues?(zip_file)
|
36
|
+
return AppInfo::Format::UNKNOWN unless clue = other_clues?(zip_file)
|
37
|
+
|
38
|
+
clue
|
39
|
+
ensure
|
40
|
+
zip_file.close
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check for Proguard clues in zip file
|
44
|
+
def proguard_clues?(zip_file)
|
45
|
+
!zip_file.glob('*mapping*.txt').empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check for APK clues in zip file
|
49
|
+
def apk_clues?(zip_file)
|
50
|
+
!zip_file.find_entry('AndroidManifest.xml').nil? &&
|
51
|
+
!zip_file.find_entry('classes.dex').nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check for AAB clues in zip file
|
55
|
+
def aab_clues?(zip_file)
|
56
|
+
!zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
|
57
|
+
!zip_file.find_entry('BundleConfig.pb').nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Check for macOS clues in zip file
|
61
|
+
def macos_clues?(zip_file)
|
62
|
+
!zip_file.glob('*/Contents/MacOS/*').empty? &&
|
63
|
+
!zip_file.glob('*/Contents/Info.plist').empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check for PE clues in zip file
|
67
|
+
def pe_clues?(zip_file)
|
68
|
+
!zip_file.glob('*.exe').empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check for HAP clues in zip file
|
72
|
+
def hap_clues?(zip_file)
|
73
|
+
!zip_file.find_entry('pack.info').nil? && !zip_file.find_entry('module.json').nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Check for HAPP clues in zip file
|
77
|
+
def happ_clues?(zip_file)
|
78
|
+
pack_info_count = 0
|
79
|
+
hap_count = 0
|
80
|
+
|
81
|
+
zip_file.each do |f|
|
82
|
+
path = f.name
|
83
|
+
|
84
|
+
if path == 'pack.info'
|
85
|
+
pack_info_count += 1
|
86
|
+
elsif path.end_with?('.hap')
|
87
|
+
hap_count += 1
|
88
|
+
else
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
pack_info_count == 1 && hap_count >= 1
|
94
|
+
end
|
95
|
+
|
96
|
+
# Check for other clues in zip file
|
97
|
+
def other_clues?(zip_file)
|
98
|
+
zip_file.each do |f|
|
99
|
+
path = f.name
|
100
|
+
|
101
|
+
return AppInfo::Format::IPA if path.include?('Payload/') && path.end_with?('Info.plist')
|
102
|
+
return AppInfo::Format::DSYM if path.include?('Contents/Resources/DWARF/')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
ZIP_RETGEX = /^\x50\x4b\x03\x04/.freeze
|
107
|
+
PE_REGEX = /^MZ/.freeze
|
108
|
+
PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
|
109
|
+
BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
|
110
|
+
end
|
111
|
+
end
|
data/lib/app_info/helper.rb
CHANGED
data/lib/app_info/info_plist.rb
CHANGED
@@ -11,10 +11,10 @@ module AppInfo
|
|
11
11
|
|
12
12
|
# Icon Key
|
13
13
|
ICON_KEYS = {
|
14
|
-
Device::IPHONE => ['CFBundleIcons'],
|
15
|
-
Device::IPAD => ['CFBundleIcons~ipad'],
|
16
|
-
Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
-
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
14
|
+
Device::Apple::IPHONE => ['CFBundleIcons'],
|
15
|
+
Device::Apple::IPAD => ['CFBundleIcons~ipad'],
|
16
|
+
Device::Apple::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
+
Device::Apple::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
# @return [Symbol] {Manufacturer}
|
@@ -25,11 +25,11 @@ module AppInfo
|
|
25
25
|
# @return [Symbol] {Platform}
|
26
26
|
def platform
|
27
27
|
case device
|
28
|
-
when Device::MACOS
|
28
|
+
when Device::Apple::MACOS
|
29
29
|
Platform::MACOS
|
30
|
-
when Device::IPHONE, Device::IPAD, Device::UNIVERSAL
|
30
|
+
when Device::Apple::IPHONE, Device::Apple::IPAD, Device::Apple::UNIVERSAL
|
31
31
|
Platform::IOS
|
32
|
-
when Device::APPLETV
|
32
|
+
when Device::Apple::APPLETV
|
33
33
|
Platform::APPLETV
|
34
34
|
end
|
35
35
|
end
|
@@ -37,17 +37,17 @@ module AppInfo
|
|
37
37
|
# @return [Symbol] {Device}
|
38
38
|
def device
|
39
39
|
if device_family == [1]
|
40
|
-
Device::IPHONE
|
40
|
+
Device::Apple::IPHONE
|
41
41
|
elsif device_family == [2]
|
42
|
-
Device::IPAD
|
42
|
+
Device::Apple::IPAD
|
43
43
|
elsif device_family == [1, 2]
|
44
|
-
Device::UNIVERSAL
|
44
|
+
Device::Apple::UNIVERSAL
|
45
45
|
elsif device_family == [3]
|
46
|
-
Device::APPLETV
|
46
|
+
Device::Apple::APPLETV
|
47
47
|
elsif device_family == [6]
|
48
|
-
Device::APPMACOSLETV
|
48
|
+
Device::Apple::APPMACOSLETV
|
49
49
|
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTManufacturerName').nil?
|
50
|
-
Device::MACOS
|
50
|
+
Device::Apple::MACOS
|
51
51
|
else
|
52
52
|
raise NotImplementedError, "Unkonwn device: #{device_family}"
|
53
53
|
end
|
@@ -113,27 +113,27 @@ module AppInfo
|
|
113
113
|
|
114
114
|
# @return [Boolean]
|
115
115
|
def iphone?
|
116
|
-
device == Device::IPHONE
|
116
|
+
device == Device::Apple::IPHONE
|
117
117
|
end
|
118
118
|
|
119
119
|
# @return [Boolean]
|
120
120
|
def ipad?
|
121
|
-
device == Device::IPAD
|
121
|
+
device == Device::Apple::IPAD
|
122
122
|
end
|
123
123
|
|
124
124
|
# @return [Boolean]
|
125
125
|
def universal?
|
126
|
-
device == Device::UNIVERSAL
|
126
|
+
device == Device::Apple::UNIVERSAL
|
127
127
|
end
|
128
128
|
|
129
129
|
# @return [Boolean]
|
130
130
|
def macos?
|
131
|
-
device == Device::MACOS
|
131
|
+
device == Device::Apple::MACOS
|
132
132
|
end
|
133
133
|
|
134
134
|
# @return [Boolean]
|
135
135
|
def appletv?
|
136
|
-
device == Device::APPLETV
|
136
|
+
device == Device::Apple::APPLETV
|
137
137
|
end
|
138
138
|
|
139
139
|
# @return [Array<String>]
|
@@ -212,7 +212,7 @@ module AppInfo
|
|
212
212
|
|
213
213
|
def app_path
|
214
214
|
@app_path ||= case device
|
215
|
-
when Device::MACOS
|
215
|
+
when Device::Apple::MACOS
|
216
216
|
::File.dirname(@file)
|
217
217
|
else
|
218
218
|
::File.expand_path('../', @file)
|
data/lib/app_info/ipa.rb
CHANGED
@@ -149,11 +149,11 @@ module AppInfo
|
|
149
149
|
|
150
150
|
def icon_keys
|
151
151
|
@icon_keys ||= case device
|
152
|
-
when Device::IPHONE, Device::APPLETV
|
152
|
+
when Device::Apple::IPHONE, Device::Apple::APPLETV
|
153
153
|
[IPHONE_KEY]
|
154
|
-
when Device::IPAD
|
154
|
+
when Device::Apple::IPAD
|
155
155
|
[IPAD_KEY]
|
156
|
-
when Device::UNIVERSAL
|
156
|
+
when Device::Apple::UNIVERSAL
|
157
157
|
[IPHONE_KEY, IPAD_KEY]
|
158
158
|
end
|
159
159
|
end
|
@@ -185,7 +185,9 @@ module AppInfo
|
|
185
185
|
capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
|
186
186
|
when 'com.apple.developer.icloud-services',
|
187
187
|
'com.apple.developer.icloud-container-identifiers'
|
188
|
-
capabilities << 'iCloud' unless capabilities.include?('iCloud')
|
188
|
+
capabilities << 'iCloud: CloudKit' unless capabilities.include?('iCloud: CloudKit')
|
189
|
+
when 'com.apple.developer.ubiquity-kvstore-identifier'
|
190
|
+
capabilities << 'iCloud: iCloud key-value storage'
|
189
191
|
when 'com.apple.developer.in-app-payments'
|
190
192
|
capabilities << 'Apple Pay'
|
191
193
|
when 'com.apple.developer.homekit'
|
@@ -220,6 +222,10 @@ module AppInfo
|
|
220
222
|
capabilities << 'MDM Managed Associated Domains'
|
221
223
|
when 'keychain-access-groups'
|
222
224
|
capabilities << 'Keychain Sharing'
|
225
|
+
when 'com.apple.developer.usernotifications.time-sensitive'
|
226
|
+
capabilities << 'Time Sensitive Notifications'
|
227
|
+
when 'com.apple.developer.game-center'
|
228
|
+
capabilities << 'Game Center'
|
223
229
|
# macOS Only
|
224
230
|
when 'com.apple.developer.maps'
|
225
231
|
capabilities << 'Maps'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module AppInfo
|
6
|
+
# HarmonyOS pack.info parser
|
7
|
+
class PackInfo < File
|
8
|
+
# @return [String]
|
9
|
+
def version_code
|
10
|
+
app['version']['code']
|
11
|
+
end
|
12
|
+
alias build_version version_code
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
def version_name
|
16
|
+
app['version']['name']
|
17
|
+
end
|
18
|
+
alias release_version version_name
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def bundle_name
|
22
|
+
app['bundleName']
|
23
|
+
end
|
24
|
+
alias bundle_id bundle_name
|
25
|
+
|
26
|
+
# @return [JSON]
|
27
|
+
def app
|
28
|
+
@app ||= summary['app']
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Array<JSON>]
|
32
|
+
def modules
|
33
|
+
@modules ||= summary['modules']
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [JSON]
|
37
|
+
def summary
|
38
|
+
@summary ||= content['summary']
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<JSON>]
|
42
|
+
def packages
|
43
|
+
@packages ||= content['packages']
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [JSON]
|
47
|
+
def content
|
48
|
+
JSON.parse(::File.read(@file))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/app_info/pe.rb
CHANGED
@@ -36,7 +36,7 @@ module AppInfo
|
|
36
36
|
|
37
37
|
# @return [Symbol] {Device}
|
38
38
|
def device
|
39
|
-
Device::WINDOWS
|
39
|
+
Device::Microsoft::WINDOWS
|
40
40
|
end
|
41
41
|
|
42
42
|
# return file size
|
@@ -189,7 +189,7 @@ module AppInfo
|
|
189
189
|
def binary_file
|
190
190
|
@binary_file ||= lambda {
|
191
191
|
file_io = ::File.open(@file, 'rb')
|
192
|
-
return @file unless file_io.read(100) =~
|
192
|
+
return @file unless file_io.read(100) =~ Helper::FileTypeDetection::ZIP_RETGEX
|
193
193
|
|
194
194
|
zip_file = Zip::File.open(@file)
|
195
195
|
zip_entry = zip_file.glob('*.exe').first
|
data/lib/app_info/version.rb
CHANGED
data/lib/app_info.rb
CHANGED
@@ -10,6 +10,7 @@ require 'app_info/error'
|
|
10
10
|
require 'app_info/file'
|
11
11
|
require 'app_info/info_plist'
|
12
12
|
require 'app_info/mobile_provision'
|
13
|
+
require 'app_info/pack_info'
|
13
14
|
|
14
15
|
require 'app_info/apple'
|
15
16
|
require 'app_info/macos'
|
@@ -21,16 +22,22 @@ require 'app_info/android'
|
|
21
22
|
require 'app_info/apk'
|
22
23
|
require 'app_info/aab'
|
23
24
|
|
25
|
+
require 'app_info/harmonyos'
|
26
|
+
require 'app_info/happ'
|
27
|
+
require 'app_info/hap'
|
28
|
+
|
24
29
|
require 'app_info/proguard'
|
25
30
|
require 'app_info/dsym'
|
26
31
|
|
27
32
|
require 'app_info/pe'
|
28
33
|
|
29
|
-
# fix
|
34
|
+
# fix invalid date format warnings
|
30
35
|
Zip.warn_invalid_date = false
|
31
36
|
|
32
37
|
# AppInfo Module
|
33
38
|
module AppInfo
|
39
|
+
extend Helper::FileTypeDetection
|
40
|
+
|
34
41
|
class << self
|
35
42
|
# Get a new parser for automatic
|
36
43
|
def parse(file)
|
@@ -40,6 +47,8 @@ module AppInfo
|
|
40
47
|
when Format::IPA then IPA.new(file)
|
41
48
|
when Format::APK then APK.new(file)
|
42
49
|
when Format::AAB then AAB.new(file)
|
50
|
+
when Format::HAP then HAP.new(file)
|
51
|
+
when Format::HAPP then HAPP.new(file)
|
43
52
|
when Format::MOBILEPROVISION then MobileProvision.new(file)
|
44
53
|
when Format::DSYM then DSYM.new(file)
|
45
54
|
when Format::PROGUARD then Proguard.new(file)
|
@@ -61,89 +70,10 @@ module AppInfo
|
|
61
70
|
file_type(file) != Format::UNKNOWN
|
62
71
|
end
|
63
72
|
|
64
|
-
# Detect file type by read file header
|
65
|
-
#
|
66
|
-
# TODO: This can be better solution, if anyone knows, tell me please.
|
67
|
-
def file_type(file)
|
68
|
-
header_hex = ::File.read(file, 100)
|
69
|
-
case header_hex
|
70
|
-
when ZIP_RETGEX
|
71
|
-
detect_zip_file(file)
|
72
|
-
when PE_REGEX
|
73
|
-
Format::PE
|
74
|
-
when PLIST_REGEX, BPLIST_REGEX
|
75
|
-
Format::MOBILEPROVISION
|
76
|
-
else
|
77
|
-
Format::UNKNOWN
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
73
|
def logger
|
82
74
|
@logger ||= Logger.new($stdout, level: :warn)
|
83
75
|
end
|
84
76
|
|
85
77
|
attr_writer :logger
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
# :nodoc:
|
90
|
-
def detect_zip_file(file)
|
91
|
-
Zip.warn_invalid_date = false
|
92
|
-
zip_file = Zip::File.open(file)
|
93
|
-
|
94
|
-
return Format::PROGUARD if proguard_clues?(zip_file)
|
95
|
-
return Format::APK if apk_clues?(zip_file)
|
96
|
-
return Format::AAB if aab_clues?(zip_file)
|
97
|
-
return Format::MACOS if macos_clues?(zip_file)
|
98
|
-
return Format::PE if pe_clues?(zip_file)
|
99
|
-
return Format::UNKNOWN unless clue = other_clues?(zip_file)
|
100
|
-
|
101
|
-
clue
|
102
|
-
ensure
|
103
|
-
zip_file.close
|
104
|
-
end
|
105
|
-
|
106
|
-
# :nodoc:
|
107
|
-
def proguard_clues?(zip_file)
|
108
|
-
!zip_file.glob('*mapping*.txt').empty?
|
109
|
-
end
|
110
|
-
|
111
|
-
# :nodoc:
|
112
|
-
def apk_clues?(zip_file)
|
113
|
-
!zip_file.find_entry('AndroidManifest.xml').nil? &&
|
114
|
-
!zip_file.find_entry('classes.dex').nil?
|
115
|
-
end
|
116
|
-
|
117
|
-
# :nodoc:
|
118
|
-
def aab_clues?(zip_file)
|
119
|
-
!zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
|
120
|
-
!zip_file.find_entry('BundleConfig.pb').nil?
|
121
|
-
end
|
122
|
-
|
123
|
-
# :nodoc:
|
124
|
-
def macos_clues?(zip_file)
|
125
|
-
!zip_file.glob('*/Contents/MacOS/*').empty? &&
|
126
|
-
!zip_file.glob('*/Contents/Info.plist').empty?
|
127
|
-
end
|
128
|
-
|
129
|
-
# :nodoc:
|
130
|
-
def pe_clues?(zip_file)
|
131
|
-
!zip_file.glob('*.exe').empty?
|
132
|
-
end
|
133
|
-
|
134
|
-
# :nodoc:
|
135
|
-
def other_clues?(zip_file)
|
136
|
-
zip_file.each do |f|
|
137
|
-
path = f.name
|
138
|
-
|
139
|
-
return Format::IPA if path.include?('Payload/') && path.end_with?('Info.plist')
|
140
|
-
return Format::DSYM if path.include?('Contents/Resources/DWARF/')
|
141
|
-
end
|
142
|
-
end
|
143
78
|
end
|
144
|
-
|
145
|
-
ZIP_RETGEX = /^\x50\x4b\x03\x04/.freeze
|
146
|
-
PE_REGEX = /^MZ/.freeze
|
147
|
-
PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
|
148
|
-
BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
|
149
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: app-info
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icyleaf
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: CFPropertyList
|
@@ -102,16 +102,22 @@ dependencies:
|
|
102
102
|
name: android_parser
|
103
103
|
requirement: !ruby/object:Gem::Requirement
|
104
104
|
requirements:
|
105
|
-
- - "
|
105
|
+
- - ">="
|
106
106
|
- !ruby/object:Gem::Version
|
107
|
-
version: 2.
|
107
|
+
version: '2.7'
|
108
|
+
- - "<"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
108
111
|
type: :runtime
|
109
112
|
prerelease: false
|
110
113
|
version_requirements: !ruby/object:Gem::Requirement
|
111
114
|
requirements:
|
112
|
-
- - "
|
115
|
+
- - ">="
|
113
116
|
- !ruby/object:Gem::Version
|
114
|
-
version: 2.
|
117
|
+
version: '2.7'
|
118
|
+
- - "<"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '3.0'
|
115
121
|
- !ruby/object:Gem::Dependency
|
116
122
|
name: rubyzip
|
117
123
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,9 +280,13 @@ files:
|
|
274
280
|
- lib/app_info/dsym/macho.rb
|
275
281
|
- lib/app_info/error.rb
|
276
282
|
- lib/app_info/file.rb
|
283
|
+
- lib/app_info/hap.rb
|
284
|
+
- lib/app_info/happ.rb
|
285
|
+
- lib/app_info/harmonyos.rb
|
277
286
|
- lib/app_info/helper.rb
|
278
287
|
- lib/app_info/helper/archive.rb
|
279
288
|
- lib/app_info/helper/file_size.rb
|
289
|
+
- lib/app_info/helper/file_type_detection.rb
|
280
290
|
- lib/app_info/helper/generate_class.rb
|
281
291
|
- lib/app_info/helper/protobuf.rb
|
282
292
|
- lib/app_info/helper/signatures.rb
|
@@ -286,6 +296,7 @@ files:
|
|
286
296
|
- lib/app_info/ipa/plugin.rb
|
287
297
|
- lib/app_info/macos.rb
|
288
298
|
- lib/app_info/mobile_provision.rb
|
299
|
+
- lib/app_info/pack_info.rb
|
289
300
|
- lib/app_info/pe.rb
|
290
301
|
- lib/app_info/png_uncrush.rb
|
291
302
|
- lib/app_info/proguard.rb
|
@@ -303,7 +314,7 @@ licenses:
|
|
303
314
|
- MIT
|
304
315
|
metadata: {}
|
305
316
|
post_install_message: |
|
306
|
-
AppInfo 3.0
|
317
|
+
AppInfo 3.0 was out!
|
307
318
|
**********************
|
308
319
|
The public API of some AppInfo classes has been changed.
|
309
320
|
|
@@ -324,7 +335,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
324
335
|
- !ruby/object:Gem::Version
|
325
336
|
version: '0'
|
326
337
|
requirements: []
|
327
|
-
rubygems_version: 3.5.
|
338
|
+
rubygems_version: 3.5.7
|
328
339
|
signing_key:
|
329
340
|
specification_version: 4
|
330
341
|
summary: Teardown tool for mobile app(ipa/apk) and dSYM file, analysis metedata like
|