read_ipa 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0aa9fcadc0cb606734c8de9894defea9862e2efe
4
+ data.tar.gz: 5829b9607ef440c25f6cdade87a4c8adcc50ebd5
5
+ SHA512:
6
+ metadata.gz: 11161be4ed6136ef3138966596fff2252e431cbc7a750feea76e66c9a8c770c23ecdbea39ed5673f70ec4e7c1f2cfa6732ab1d94f609a4bf39c4536d467e1890
7
+ data.tar.gz: ee7c5a3960a7bc7728fcd3f5fb34f6f1a016aa48d75e45cca725ed17b48c883b94bed324604c84378a6bd414cf3f578f44848a3a1f13d116cfb873c5412249a1
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # read_ipa [![Build Status](https://travis-ci.org/playtestcloud/read_ipa.png?branch=master)](https://travis-ci.org/playtestcloud/read_ipa) [![Gem Version](https://badge.fury.io/rb/read_ipa.png)](http://badge.fury.io/rb/read_ipa)
2
+
3
+ Reads metadata form iPhone Package Archive Files (ipa).
4
+
5
+ ## USAGE
6
+
7
+ ```bash
8
+ irb > require 'rubygems'
9
+ => true
10
+ irb > require 'read_ipa'
11
+ => true
12
+ irb > ipa_file = ReadIpa::IpaFile.new("/path/to/file.ipa")
13
+ => #<ReadIpa::IpaFile:0x1012a9458>
14
+ irb > ipa_file.version
15
+ => "1.2.2.4"
16
+ irb > ipa_file.name
17
+ => "MultiG"
18
+ irb > ipa_file.target_os_version
19
+ => "4.1"
20
+ irb > ipa_file.minimum_os_version
21
+ => "3.1"
22
+ irb > ipa_file.url_schemes
23
+ => []
24
+ irb > ipa_file.bundle_identifier
25
+ => "com.dcrails.multig"
26
+ irb > ipa_file.icon_prerendered
27
+ => false
28
+ ```
29
+
30
+ ## Supported ruby versions
31
+
32
+ * 1.9 **NOT SUPPORTED**
33
+ * 2.0
34
+ * 2.1
35
+ * 2.2
36
+
37
+ ## INSTALL
38
+
39
+ `gem install read_ipa`
40
+
41
+ ## Contributors
42
+
43
+ * [@schlu](//github.com/schlu)
44
+ * [@yayanet](//github.com/yayanet)
45
+ * [@mwhuss](//github.com/mwhuss)
46
+
47
+ ## LICENSE
48
+
49
+ (The MIT License)
50
+
51
+ Copyright (c) 2010
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ 'Software'), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
67
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
68
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
69
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
70
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task default: :test
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'cfpropertylist'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'cfpropertylist'
6
+ end
7
+
8
+ # Adds to_rb functionality to return native ruby types rather than CFTypes
9
+ module CFPropertyList
10
+ class CFDictionary
11
+ def to_rb
12
+ hash_data = {}
13
+ value.keys.each do |key|
14
+ hash_data[key] = value[key].to_hash
15
+ end
16
+ hash_data
17
+ end
18
+ end
19
+ class CFType
20
+ def to_hash
21
+ self.value
22
+ end
23
+ end
24
+ class CFArray
25
+ def to_hash
26
+ hash_data = []
27
+ value.each do |obj|
28
+ hash_data << obj.to_hash
29
+ end
30
+ hash_data
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,113 @@
1
+ # direct port of python code at https://gist.github.com/4141831#file_ipin.py
2
+ require 'zlib'
3
+
4
+ module ReadIpa
5
+ class PngFile
6
+ attr_accessor :raw_data, :width, :height
7
+
8
+ def initialize(oldPNG)
9
+ # begin
10
+ self.raw_data = extract_apple_png(oldPNG)
11
+ # rescue
12
+ # self.width, self.height = oldPNG[0x10..0x18].unpack('NN')
13
+ # self.raw_data = oldPNG
14
+ # end
15
+ end
16
+
17
+ def extract_apple_png(oldPNG)
18
+ pngheader = "\x89PNG\r\n\x1A\n".b
19
+
20
+ if oldPNG[0...8] != pngheader
21
+ return nil
22
+ end
23
+
24
+ newPNG = oldPNG[0...8]
25
+
26
+ chunkPos = newPNG.length
27
+
28
+ idatAcc = "".b
29
+ breakLoop = false
30
+
31
+ # For each chunk in the PNG file
32
+ while chunkPos < oldPNG.length
33
+ skip = false
34
+
35
+ # Reading chunk
36
+ chunkLength = oldPNG[chunkPos...chunkPos+4]
37
+ chunkLength = chunkLength.unpack("N")[0]
38
+ chunkType = oldPNG[chunkPos+4...chunkPos+8]
39
+ chunkData = oldPNG[chunkPos+8...chunkPos+8+chunkLength]
40
+ chunkCRC = oldPNG[chunkPos+chunkLength+8...chunkPos+chunkLength+12]
41
+ chunkCRC = chunkCRC.unpack("N")[0]
42
+ chunkPos += chunkLength + 12
43
+
44
+ # Parsing the header chunk
45
+ if chunkType == "IHDR".b
46
+ self.width = chunkData[0...4].unpack("N")[0]
47
+ self.height = chunkData[4...8].unpack("N")[0]
48
+ end
49
+
50
+ # Parsing the image chunk
51
+ if chunkType == "IDAT".b
52
+ # Store the chunk data for later decompression
53
+ idatAcc += chunkData
54
+ skip = true
55
+ end
56
+
57
+ # Removing CgBI chunk
58
+ if chunkType == "CgBI".b
59
+ skip = true
60
+ end
61
+
62
+ # Stopping the PNG file parsing
63
+ if chunkType == "IEND".b
64
+ # Uncompressing the image chunk
65
+ inf = Zlib::Inflate.new(-Zlib::MAX_WBITS)
66
+ chunkData = inf.inflate(idatAcc)
67
+ inf.finish
68
+ inf.close
69
+
70
+ chunkType = "IDAT".b
71
+
72
+ # Swapping red & blue bytes for each pixel
73
+ newdata = "".b
74
+
75
+ self.height.times do
76
+ i = newdata.length
77
+ newdata += chunkData[i..i].to_s
78
+ self.width.times do
79
+ i = newdata.length
80
+ newdata += chunkData[i+2..i+2].to_s
81
+ newdata += chunkData[i+1..i+1].to_s
82
+ newdata += chunkData[i+0..i+0].to_s
83
+ newdata += chunkData[i+3..i+3].to_s
84
+ end
85
+ end
86
+
87
+ # Compressing the image chunk
88
+ chunkData = newdata
89
+ chunkData = Zlib::Deflate.deflate( chunkData )
90
+ chunkLength = chunkData.length
91
+ chunkCRC = Zlib.crc32(chunkType)
92
+ chunkCRC = Zlib.crc32(chunkData, chunkCRC)
93
+ chunkCRC = (chunkCRC + 0x100000000) % 0x100000000
94
+ breakLoop = true
95
+ end
96
+
97
+ if !skip
98
+ newPNG += [chunkLength].pack("N")
99
+ newPNG += chunkType
100
+ if chunkLength > 0
101
+ newPNG += chunkData
102
+ end
103
+ newPNG += [chunkCRC].pack("N")
104
+ end
105
+
106
+ break if breakLoop
107
+ end
108
+
109
+ return newPNG
110
+ end
111
+
112
+ end
113
+ end
data/lib/read_ipa.rb ADDED
@@ -0,0 +1,109 @@
1
+ begin
2
+ require 'zip/zip'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'zip/zip'
6
+ end
7
+
8
+ require 'read_ipa/plist_binary'
9
+ require 'read_ipa/png_file'
10
+
11
+ module ReadIpa
12
+ class IpaFile
13
+ attr_accessor :plist, :file_path
14
+ def initialize(file_path)
15
+ self.file_path = file_path
16
+ @app_folder = Zip::ZipFile.foreach(file_path).find { |e| /.*\.app\/$/ =~ e.to_s }.to_s
17
+ @zipfile = Zip::ZipFile.open(file_path)
18
+
19
+ cf_plist = CFPropertyList::List.new(data: @zipfile.read(@app_folder + "Info.plist"), format: CFPropertyList::List::FORMAT_AUTO)
20
+ self.plist = cf_plist.value.to_rb
21
+ end
22
+
23
+ def version
24
+ plist["CFBundleVersion"]
25
+ end
26
+
27
+ def short_version
28
+ plist["CFBundleShortVersionString"]
29
+ end
30
+
31
+ def name
32
+ plist["CFBundleDisplayName"]
33
+ end
34
+
35
+ def target_os_version
36
+ plist["DTPlatformVersion"].match(/[\d\.]*/)[0]
37
+ end
38
+
39
+ def minimum_os_version
40
+ plist["MinimumOSVersion"].match(/[\d\.]*/)[0]
41
+ end
42
+
43
+ def url_schemes
44
+ if plist["CFBundleURLTypes"] && plist["CFBundleURLTypes"][0] && plist["CFBundleURLTypes"][0]["CFBundleURLSchemes"]
45
+ plist["CFBundleURLTypes"][0]["CFBundleURLSchemes"]
46
+ else
47
+ []
48
+ end
49
+ end
50
+
51
+ def icon_file
52
+ if plist["CFBundleIconFiles"]
53
+ highest_res_icon = plist["CFBundleIconFiles"]
54
+ .map{ |icon|
55
+ data = read_file(icon)
56
+ ReadIpa::PngFile.new(data)
57
+ }
58
+ .sort{ |a,b| b.width <=> a.width }
59
+ .first
60
+ highest_res_icon.raw_data
61
+ elsif plist["CFBundleIconFile"]
62
+ data = read_file(plist["CFBundleIconFile"])
63
+ png = ReadIpa::PngFile.new(data)
64
+ png.raw_data
65
+ else
66
+ nil
67
+ end
68
+ end
69
+
70
+ def executable_file_name
71
+ plist["CFBundleExecutable"]
72
+ end
73
+
74
+ def executable_file
75
+ read_file(executable_file_name)
76
+ end
77
+
78
+ def mobile_provision_file
79
+ read_file("embedded.mobileprovision")
80
+ end
81
+
82
+ def bundle_identifier
83
+ plist["CFBundleIdentifier"]
84
+ end
85
+
86
+ def icon_prerendered
87
+ plist["UIPrerenderedIcon"] == true
88
+ end
89
+
90
+ def for_ipad?
91
+ return true if plist["UIDeviceFamily"] && (plist["UIDeviceFamily"] == 2 || plist["UIDeviceFamily"].include?(2))
92
+ return true if plist["UIDeviceFamily"] && (plist["UIDeviceFamily"] == "2" || plist["UIDeviceFamily"].include?("2"))
93
+ return false
94
+ end
95
+
96
+ def for_iphone?
97
+ return true if !plist["UIDeviceFamily"]
98
+ return true if plist["UIDeviceFamily"] == 1 || plist["UIDeviceFamily"].include?(1)
99
+ return true if plist["UIDeviceFamily"] == "1" || plist["UIDeviceFamily"].include?("1")
100
+ return false
101
+ end
102
+
103
+ private
104
+
105
+ def read_file(entry)
106
+ @zipfile.read(@app_folder + entry)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,68 @@
1
+ require 'minitest/autorun'
2
+ require 'digest'
3
+ require 'read_ipa'
4
+
5
+ class ReadIpaTest < Minitest::Test
6
+ def setup
7
+ @ipa_file = ReadIpa::IpaFile.new(File.dirname(__FILE__) + '/MultiG.ipa')
8
+ end
9
+
10
+ def test_parse
11
+ assert(@ipa_file.plist.keys.count > 0)
12
+ end
13
+
14
+ def test_version
15
+ assert_equal(@ipa_file.version, "1.2.2.4")
16
+ end
17
+
18
+ def test_short_version
19
+ # asserting nil because the test file doesn't have this key
20
+ assert_nil(@ipa_file.short_version)
21
+ end
22
+
23
+ def test_name
24
+ assert_equal(@ipa_file.name, "MultiG")
25
+ end
26
+
27
+ def test_target_os_version
28
+ assert_equal(@ipa_file.target_os_version, "4.1")
29
+ end
30
+
31
+ def test_minimum_os_version
32
+ assert_equal(@ipa_file.minimum_os_version, "3.1")
33
+ end
34
+
35
+ def test_url_schemes
36
+ assert_equal(@ipa_file.url_schemes, [])
37
+ end
38
+
39
+ def test_bundle_identifier
40
+ assert_equal("com.dcrails.multig", @ipa_file.bundle_identifier)
41
+ end
42
+
43
+ def test_icon_prerendered
44
+ assert_equal(false, @ipa_file.icon_prerendered)
45
+ end
46
+
47
+ def test_icon
48
+ assert_equal("56b1eecad1cb7046b2e944dcd90fa74b77187f2cb4c766d7bb328ad86c37ca04",
49
+ Digest::SHA256::hexdigest(@ipa_file.icon_file))
50
+ end
51
+
52
+ def test_executable_file_name
53
+ assert_equal("MultiG", @ipa_file.executable_file_name)
54
+ end
55
+
56
+ def test_executable_file
57
+ assert_equal("227e5272684846d7c8193dbe0995a2df62314d11a069608831f5d38d51ee9c7a",
58
+ Digest::SHA256::hexdigest(@ipa_file.executable_file))
59
+ end
60
+
61
+ def test_for_iphone
62
+ assert_equal(true, @ipa_file.for_iphone?)
63
+ end
64
+
65
+ def test_for_ipad
66
+ assert_equal(true, @ipa_file.for_ipad?)
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: read_ipa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Marvin Killing
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubyzip
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.9
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.9
55
+ - !ruby/object:Gem::Dependency
56
+ name: CFPropertyList
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.1.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.1.1
69
+ description: Extract metadata from iOS packages such as the app name, the app icons
70
+ or the binary file. This is a diverging fork of github.com/schlu/Ipa-Reader.
71
+ email: marvinkilling@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - Rakefile
78
+ - lib/read_ipa.rb
79
+ - lib/read_ipa/plist_binary.rb
80
+ - lib/read_ipa/png_file.rb
81
+ - test/test_read_ipa.rb
82
+ homepage: https://github.com/playtestcloud/read_ipa
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '2'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.4.6
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Read metadata from iOS IPA package files
106
+ test_files:
107
+ - test/test_read_ipa.rb