diff --git a/ipa_reader.gemspec b/ipa_reader.gemspec index e5a9d1d..018a0f1 100644 --- a/ipa_reader.gemspec +++ b/ipa_reader.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |s| s.description = "I am using this gem to get version to build the over the air iPhone Ad Hoc distribution plist file." s.email = "schlueter@gmail.com" s.executables = ["ipa_reader"] - s.files = ["History.txt", "README.md", "Rakefile", "bin/ipa_reader", "lib/ipa_reader.rb", "lib/ipa_reader/ipa_file.rb", "lib/ipa_reader/plist_binary.rb", "lib/ipa_reader/png_file.rb", "spec/ipa_reader_spec.rb", "spec/spec_helper.rb", "test/MultiG.ipa", "test/test_ipa_reader.rb", "version.txt"] + s.files = ["History.txt", "README.md", "Rakefile", "bin/ipa_reader", "lib/ipa_reader.rb", "lib/ipa_reader/ipa_file.rb", "lib/ipa_reader/plist_binary.rb", "lib/ipa_reader/png_file.rb", "spec/ipa_reader_spec.rb", "spec/spec_helper.rb", "test/Find iPhone.ipa", "test/test_ipa_reader.rb", "version.txt"] s.homepage = "http://github.com/schlu/Ipa-Reader" s.require_paths = ["lib"] s.rubyforge_project = "ipa_reader" diff --git a/lib/ipa_reader/ipa_file.rb b/lib/ipa_reader/ipa_file.rb index 78a784e..bd628df 100644 --- a/lib/ipa_reader/ipa_file.rb +++ b/lib/ipa_reader/ipa_file.rb @@ -5,16 +5,28 @@ require 'zip' end +module DeviceFamily + IPhone = 1 + IPad = 2 +end + module IpaReader class IpaFile - attr_accessor :plist, :file_path + attr_accessor :plist, :file_path, :meta_plist def initialize(file_path) self.file_path = file_path info_plist_file = nil regex = /Payload\/[^\/]+.app\/Info.plist/ - Zip::ZipFile.foreach(file_path) { |f| info_plist_file = f if f.name.match(regex) } - cf_plist = CFPropertyList::List.new(:data => self.read_file(regex), :format => CFPropertyList::List::FORMAT_BINARY) + cf_plist = CFPropertyList::List.new(:data => self.read_file(regex), :format => CFPropertyList::List::FORMAT_AUTO) self.plist = cf_plist.value.to_rb + + meta_data = self.read_file(/iTunesMetadata.plist/) + if meta_data + meta_data.chomp! "\u0000" + self.meta_plist = CFPropertyList::List.new(:data => meta_data, :format => CFPropertyList::List::FORMAT_AUTO).value.to_rb + else + self.meta_plist = {} + end end def version @@ -39,22 +51,25 @@ def minimum_os_version def url_schemes if plist["CFBundleURLTypes"] && plist["CFBundleURLTypes"][0] && plist["CFBundleURLTypes"][0]["CFBundleURLSchemes"] - plist["CFBundleURLTypes"][0]["CFBundleURLSchemes"] + plist["CFBundleURLTypes"][0]["CFBundleURLSchemes"].value.map { |schema| schema.value } else [] end end def icon_file + file = nil if plist["CFBundleIconFiles"] - data = read_file(Regexp.new("#{plist["CFBundleIconFiles"][0]}$")) + file = file_name(Regexp.new("#{plist["CFBundleIconFiles"][-1]}$")) elsif plist["CFBundleIconFile"] - data = read_file(Regexp.new("#{plist["CFBundleIconFile"]}$")) - end - if data - IpaReader::PngFile.normalize_png(data) + file = file_name(Regexp.new("#{plist["CFBundleIconFile"]}$")) + elsif plist['CFBundleIcons'] + primary_icon = plist["CFBundleIcons"]["CFBundlePrimaryIcon"].value + icon_files = primary_icon['CFBundleIconFiles'].value + icon_file = icon_files[-1].value + file = file_name(Regexp.new("#{icon_file}")) else - nil + file = file_name(Regexp.new("Icon@2x")) end end @@ -69,11 +84,66 @@ def bundle_identifier def icon_prerendered plist["UIPrerenderedIcon"] == true end + + def app_id + self.meta_plist["itemId"].to_s + end + + def localized_names + names = {} + regex = /Payload\/[^\/]+.app\/(.+)\.lproj\/InfoPlist.strings/ + Zip::ZipFile.foreach(self.file_path) do |f| + if f.name.match(regex) + file = f + cf_plist = CFPropertyList::List.new(:data => file.get_input_stream.read, :format => CFPropertyList::List::FORMAT_BINARY).value.to_rb + names[$1.to_sym] = cf_plist['CFBundleDisplayName'] + end + end + names + end + + def excutable_file + plist['CFBundleExecutable'] + end + + def genre + meta_plist['genre'] + end + + def genre_id + meta_plist['genreId'].to_s + end + + def artist_id + meta_plist['artistId'].to_s + end + + def artist_name + meta_plist['artistName'] + end + + def release_date + Date.parse meta_plist['releaseDate'] + end + + def device_family + plist['UIDeviceFamily'] + end def read_file(regex) file = nil Zip::ZipFile.foreach(self.file_path) { |f| file = f if f.name.match(regex) } - file.get_input_stream.read + if file + file.get_input_stream.read + end + end + + def file_name(regex) + file = nil + Zip::ZipFile.foreach(self.file_path) { |f| file = f if f.name.match(regex) } + if file + file.name + end end end end diff --git a/test/Find iPhone.ipa b/test/Find iPhone.ipa new file mode 100644 index 0000000..ce426ff Binary files /dev/null and b/test/Find iPhone.ipa differ diff --git a/test/MultiG.ipa b/test/MultiG.ipa deleted file mode 100644 index 39de321..0000000 Binary files a/test/MultiG.ipa and /dev/null differ diff --git a/test/test_ipa_reader.rb b/test/test_ipa_reader.rb index 0b998b3..4e81575 100644 --- a/test/test_ipa_reader.rb +++ b/test/test_ipa_reader.rb @@ -3,7 +3,7 @@ class IpaReaderTest < Test::Unit::TestCase def setup - @ipa_file = IpaReader::IpaFile.new(File.dirname(__FILE__) + '/MultiG.ipa') + @ipa_file = IpaReader::IpaFile.new(File.dirname(__FILE__) + '/Find iPhone.ipa') end def test_parse @@ -11,37 +11,109 @@ def test_parse end def test_version - assert_equal(@ipa_file.version, "1.2.2.4") + assert_equal(@ipa_file.version, "376") end def test_short_version # asserting nil because the test file doesn't have this key - assert_nil(@ipa_file.short_version) + assert_equal(@ipa_file.short_version, "3.0") end def test_name - assert_equal(@ipa_file.name, "MultiG") + assert_equal(@ipa_file.name, "FindMyiPhone") end def test_target_os_version - assert_equal(@ipa_file.target_os_version, "4.1") + assert_equal(@ipa_file.target_os_version, "7.0") end def test_minimum_os_version - assert_equal(@ipa_file.minimum_os_version, "3.1") + assert_equal(@ipa_file.minimum_os_version, "7.0") end def test_url_schemes - assert_equal(@ipa_file.url_schemes, []) + assert_equal(@ipa_file.url_schemes, ['fmip1']) + end + + def test_icon_file + assert_equal(@ipa_file.icon_file, 'Payload/FindMyiPhone.app/AppIcon60x60@2x.png') end def test_bundle_identifier - assert_equal("com.dcrails.multig", @ipa_file.bundle_identifier) + assert_equal("com.apple.mobileme.fmip1", @ipa_file.bundle_identifier) end def test_icon_prerendered - assert_equal(false, @ipa_file.icon_prerendered) + assert_equal(true, @ipa_file.icon_prerendered) end + def test_app_id + assert_equal('376101648', @ipa_file.app_id) + end + + def test_localized_names + assert_equal({:ar=>"عثور iPhone", + :ca=>"Buscar", + :cs=>"Najít iPhone", + :da=>"Find iPhone", + :de=>"Mein iPhone", + :el=>"Εύρεση", + :en=>"Find iPhone", + :en_GB=>"Find iPhone", + :es=>"Buscar", + :fi=>"Etsi iPhone", + :fr=>"Localiser", + :he=>"מצא iPhone", + :hr=>"Nađi iPhone", + :hu=>"Keresés", + :id=>"Cari iPhone", + :it=>"Trova iPhone", + :ja=>"Find iPhone", + :ko=>"iPhone 찾기", + :ms=>"Cari iPhone", + :nl=>"Zoek iPhone", + :no=>"Finn iPhone", + :pl=>"Znajdź", + :pt=>"Buscar", + :pt_PT=>"Encontrar", + :ro=>"Găsire iPhone", + :ru=>"Найти iPhone", + :sk=>"Nájsť iPhone", + :sv=>"Hitta iPhone", + :th=>"ค้นหา iPhone", + :tr=>"iPhone'u Bul", + :uk=>"Де iPhone", + :vi=>"Tìm iPhone", + :zh_CN=>"查找 iPhone", + :zh_TW=>"尋找 iPhone"}, @ipa_file.localized_names) + end + + def test_excutable_file + assert_equal(@ipa_file.excutable_file, "FindMyiPhone") + end + + def test_genre + assert_equal(@ipa_file.genre, "Utilities") + end + + def test_genre_id + assert_equal(@ipa_file.genre_id, "6002") + end + + def test_artist_id + assert_equal(@ipa_file.artist_id, "284417353") + end + + def test_artist_name + assert_equal(@ipa_file.artist_name, "Apple") + end + + def test_release_date + assert_equal(@ipa_file.release_date.to_s, "2010-06-18") + end + + def test_device_family + assert_equal(@ipa_file.device_family, [DeviceFamily::IPhone, DeviceFamily::IPad]) + end end \ No newline at end of file