lib/fir/util/parser/ipa.rb
# encoding: utf-8
require_relative './common'
module FIR
module Parser
class Ipa
include Parser::Common
def initialize(path)
@path = path
end
def app
@app ||= App.new(app_path, is_stored)
end
def app_path
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
end
def cleanup
return unless @contents
FileUtils.rm_rf(@contents)
@contents = nil
end
def metadata
return unless has_metadata?
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
end
def has_metadata?
File.file?(metadata_path)
end
def metadata_path
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
end
def is_stored
has_metadata? ? true : false
end
def contents
return if @contents
@contents = "#{Dir.tmpdir}/ipa_files-#{Time.now.to_i}"
Zip::File.open(@path) do |zip_file|
zip_file.each do |f|
f_path = File.join(@contents, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
end
end
@contents
end
class App
include Parser::Common
def initialize(path, is_stored = false)
@path = path
@is_stored = is_stored
end
def full_info(options)
if options.fetch(:full_info, false)
basic_info.merge!(icons: tmp_icons)
end
basic_info
end
def basic_info
@basic_info ||= {
type: 'ios',
identifier: identifier,
name: name,
display_name: display_name,
build: version.to_s,
version: short_version.to_s,
devices: devices,
release_type: release_type,
distribution_name: distribution_name
}
end
def info
@info ||= CFPropertyList.native_types(
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
end
def name
info['CFBundleName']
end
def identifier
info['CFBundleIdentifier']
end
def display_name
info['CFBundleDisplayName']
end
def version
info['CFBundleVersion']
end
def short_version
info['CFBundleShortVersionString']
end
def tmp_icons
icons.map { |data| generate_tmp_icon(data, :ipa) }
end
def icons
@icons ||= begin
icons = []
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
icons << get_image(name)
icons << get_image("#{name}@2x")
end
icons.delete_if &:!
rescue NoMethodError
[]
end
end
def mobileprovision
return unless has_mobileprovision?
return @mobileprovision if @mobileprovision
cmd = "security cms -D -i \"#{mobileprovision_path}\""
begin
@mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: `#{cmd}`).value)
rescue CFFormatError
@mobileprovision = {}
end
end
def has_mobileprovision?
File.file? mobileprovision_path
end
def mobileprovision_path
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
end
def hide_developer_certificates
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
end
def devices
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
end
def distribution_name
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
end
def release_type
if @is_stored
'store'
else
if has_mobileprovision?
if devices
'adhoc'
else
'inhouse'
end
end
end
end
private
def get_image(name)
path = File.join(@path, "#{name}.png")
return nil unless File.exist?(path)
path
end
end
end
end
end