Class: Pliney::IPA

Inherits:
Object
  • Object
show all
Defined in:
lib/pliney/ipa.rb

Defined Under Namespace

Classes: ZipExtractError

Constant Summary collapse

SYSTEM_HAS_UNZIP =
system("which unzip > /dev/null")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(zipfile) ⇒ IPA

Returns a new instance of IPA.



32
33
34
# File 'lib/pliney/ipa.rb', line 32

def initialize(zipfile)
    @zipfile = zipfile
end

Instance Attribute Details

#zipfileObject (readonly)

TODO - creating an ipa from scratch? def self.create(path)

new(Zip::File.open(path, Zip::File::CREATE))

end



30
31
32
# File 'lib/pliney/ipa.rb', line 30

def zipfile
  @zipfile
end

Class Method Details

.from_path(path) ⇒ Object



14
15
16
17
18
19
20
21
22
23
# File 'lib/pliney/ipa.rb', line 14

def self.from_path(path)
    ipa = new(Zip::File.open(path))
    if block_given?
        ret = yield(ipa)
        ipa.close
        return ret
    else
        return ipa
    end
end

Instance Method Details

#appdirObject



36
37
38
# File 'lib/pliney/ipa.rb', line 36

def appdir
    @appdir ||= find_appdir
end

#bundle_identifierObject



58
59
60
# File 'lib/pliney/ipa.rb', line 58

def bundle_identifier
    return info_plist["CFBundleIdentifier"]
end

#bundle_short_versionObject



66
67
68
# File 'lib/pliney/ipa.rb', line 66

def bundle_short_version
    return info_plist["CFBundleShortVersionString"]
end

#bundle_versionObject



62
63
64
# File 'lib/pliney/ipa.rb', line 62

def bundle_version
    return info_plist["CFBundleVersion"]
end

#canonical_nameObject



213
214
215
# File 'lib/pliney/ipa.rb', line 213

def canonical_name
    return "#{self.team_identifier}.#{self.bundle_identifier}.v#{self.bundle_short_version}"
end

#closeObject



82
83
84
# File 'lib/pliney/ipa.rb', line 82

def close
    @zipfile.close
end

#codesignature_for_entry(file_entry) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/pliney/ipa.rb', line 110

def codesignature_for_entry(file_entry)
    _with_tmpdir do |tmp_path|
        tmpf = tmp_path.join("executable")
        file_entry.extract(tmpf.to_s)
        return ::Pliney::AppleCodeSignature.from_path(tmpf.to_s)
    end
end

#each_executable_entryObject



196
197
198
199
200
201
202
203
204
205
# File 'lib/pliney/ipa.rb', line 196

def each_executable_entry
    each_file_entry do |entry|
        next if (zipstream = entry.get_input_stream).nil?
        next if (magicbytes = zipstream.read(4)).nil?
        next if (magic = magicbytes.unpack("N").first).nil?
        if ::Pliney::MachO::is_macho_magic(magic) or ::Pliney::MachO::is_fat_magic(magic)
            yield(entry)
        end
    end
end

#each_file_entryObject



187
188
189
190
191
192
193
194
# File 'lib/pliney/ipa.rb', line 187

def each_file_entry
    zipfile.entries.select do |ent|
        not ent.name_is_directory?
    end.each do |ent|
        yield(ent)
    end
    return nil
end

#entitlements_data_for_entry(file_entry) ⇒ Object



122
123
124
125
126
127
128
129
130
# File 'lib/pliney/ipa.rb', line 122

def entitlements_data_for_entry(file_entry)
    cs = self.codesignature_for_entry(file_entry)
    if cs
        ents_blob = cs.contents.find{|c| c.is_a? ::Pliney::AppleCodeSignature::Entitlement}
        if ents_blob
            return ents_blob.data
        end
    end
end

#entitlements_for_entry(file_entry) ⇒ Object



132
133
134
135
136
137
# File 'lib/pliney/ipa.rb', line 132

def entitlements_for_entry(file_entry)
    dat = entitlements_data_for_entry(file_entry)
    if dat
        return ::Pliney.parse_plist(dat)
    end
end

#executable_codesignatureObject



118
119
120
# File 'lib/pliney/ipa.rb', line 118

def executable_codesignature
    return codesignature_for_entry(self.executable_entry)
end

#executable_entitlementsObject



143
144
145
# File 'lib/pliney/ipa.rb', line 143

def executable_entitlements
    return entitlements_for_entry(self.executable_entry)
end

#executable_entitlements_dataObject



139
140
141
# File 'lib/pliney/ipa.rb', line 139

def executable_entitlements_data
    return entitlements_data_for_entry(self.executable_entry)
end

#executable_entriesObject



207
208
209
210
211
# File 'lib/pliney/ipa.rb', line 207

def executable_entries
    a = []
    each_executable_entry{|e| a << e}
    return a
end

#executable_entryObject



74
75
76
# File 'lib/pliney/ipa.rb', line 74

def executable_entry
    return get_entry(executable_path)
end

#executable_pathObject



70
71
72
# File 'lib/pliney/ipa.rb', line 70

def executable_path
    return appdir.join(info_plist["CFBundleExecutable"])
end

#extract(path) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/pliney/ipa.rb', line 154

def extract(path)
    if SYSTEM_HAS_UNZIP
        ret = system("unzip", "-qd", path.to_s, self.zipfile.name.to_s)
        unless ret
            raise(ZipExtractError, "'unzip' command returned non-zero status: #{$?.inspect}")
        end
    else
        path = Pathname(path)
        zipfile.each do |ent|
            extract_path = path.join(ent.name)
            FileUtils.mkdir_p(extract_path.dirname)
            ent.extract(extract_path.to_s)
            extract_path.chmod(ent.unix_perms & 0777)
        end
    end
    return path
end

#find_appdirObject



44
45
46
47
48
# File 'lib/pliney/ipa.rb', line 44

def find_appdir
    if e = @zipfile.find{|ent| ent.directory? and ent.name =~ /^Payload\/[^\/]*\.app\/$/ }
        return Pathname(e.name)
    end
end

#get_entry(path) ⇒ Object



86
87
88
# File 'lib/pliney/ipa.rb', line 86

def get_entry(path)
    return @zipfile.find_entry(path.to_s)
end

#info_plistObject



54
55
56
# File 'lib/pliney/ipa.rb', line 54

def info_plist
    return parse_plist_entry(appdir.join("Info.plist"))
end

#lsObject



78
79
80
# File 'lib/pliney/ipa.rb', line 78

def ls
    return @zipfile.entries.map{|e| e.name}
end

#parse_plist_entry(path) ⇒ Object



40
41
42
# File 'lib/pliney/ipa.rb', line 40

def parse_plist_entry(path)
    Pliney.parse_plist(read_path(path))
end

#provisioning_profileObject



90
91
92
93
94
95
96
97
98
# File 'lib/pliney/ipa.rb', line 90

def provisioning_profile
    begin
        profile_data = read_path(appdir.join("embedded.mobileprovision"))
    rescue Errno::ENOENT
        return nil
    end

    ProvisioningProfile.from_asn1(profile_data)
end

#read_path(path, *args) ⇒ Object



50
51
52
# File 'lib/pliney/ipa.rb', line 50

def read_path(path, *args)
    return @zipfile.get_input_stream(path.to_s){|sio| sio.read(*args)}
end

#team_identifierObject



147
148
149
150
151
152
# File 'lib/pliney/ipa.rb', line 147

def team_identifier
    entitlements = executable_entitlements()
    if entitlements
        return entitlements["com.apple.developer.team-identifier"]
    end
end

#with_executable_macho(&block) ⇒ Object



106
107
108
# File 'lib/pliney/ipa.rb', line 106

def with_executable_macho(&block)
    with_macho_for_entry(self.executable_entry, &block)
end

#with_extracted_tmpdir(&block) ⇒ Object



172
173
174
# File 'lib/pliney/ipa.rb', line 172

def with_extracted_tmpdir(&block)
    _with_tmpdir {|tmp_path| yield(extract(tmp_path)) }
end

#with_extracted_tmpfile(ent, &block) ⇒ Object



176
177
178
179
180
181
182
183
184
185
# File 'lib/pliney/ipa.rb', line 176

def with_extracted_tmpfile(ent, &block)
    tmpf = Tempfile.new("ent")
    begin
        Zip::IOExtras.copy_stream(tmpf, ent.get_input_stream)
        tmpf.rewind
        yield(tmpf)
    ensure
        tmpf.unlink()
    end
end

#with_macho_for_entry(file_entry) ⇒ Object



100
101
102
103
104
# File 'lib/pliney/ipa.rb', line 100

def with_macho_for_entry(file_entry)
    with_extracted_tmpfile(file_entry) do |tmpfile|
        yield ::Pliney::MachO.from_stream(tmpfile)
    end
end