0
|
1 |
#!/usr/bin/env python
|
|
2 |
# -*- coding: utf-8 -*-
|
|
3 |
|
|
4 |
##############################################################################
|
|
5 |
# sisfile.py - Symbian OS v9.x SIS file utilities
|
|
6 |
# Copyright 2006, 2007 Jussi Ylänen
|
|
7 |
#
|
|
8 |
# This file is part of Ensymble developer utilities for Symbian OS(TM).
|
|
9 |
#
|
|
10 |
# Ensymble is free software; you can redistribute it and/or modify
|
|
11 |
# it under the terms of the GNU General Public License as published by
|
|
12 |
# the Free Software Foundation; either version 2 of the License, or
|
|
13 |
# (at your option) any later version.
|
|
14 |
#
|
|
15 |
# Ensymble is distributed in the hope that it will be useful,
|
|
16 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 |
# GNU General Public License for more details.
|
|
19 |
#
|
|
20 |
# You should have received a copy of the GNU General Public License
|
|
21 |
# along with Ensymble; if not, write to the Free Software
|
|
22 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
23 |
##############################################################################
|
|
24 |
|
|
25 |
import os
|
|
26 |
import time
|
|
27 |
import struct
|
|
28 |
import sha
|
|
29 |
|
|
30 |
import symbianutil
|
|
31 |
import cryptutil
|
|
32 |
import sisfield
|
|
33 |
|
|
34 |
|
|
35 |
##############################################################################
|
|
36 |
# Public module-level functions
|
|
37 |
##############################################################################
|
|
38 |
|
|
39 |
def parseexpression(expr):
|
|
40 |
'''Create a SISExpression SISField out of the given expression string.
|
|
41 |
|
|
42 |
parseexpression(...) -> SISExpression
|
|
43 |
|
|
44 |
expr the expression, a string
|
|
45 |
|
|
46 |
SISExpression the returned SISExpression SISField
|
|
47 |
|
|
48 |
NOTE: Only expressions of form "language == nn" are supported, for now.'''
|
|
49 |
|
|
50 |
elist = expr.lower().split()
|
|
51 |
|
|
52 |
# TODO: Only "language == nn" expressions supported for now.
|
|
53 |
# Going to need a real parser for general expression support, though.
|
|
54 |
try:
|
|
55 |
if len(elist) != 3 or elist[0] != 'language' or elist[1] != '==':
|
|
56 |
raise ValueError
|
|
57 |
langnum = int(elist[2])
|
|
58 |
except ValueError:
|
|
59 |
raise ValueError("invalid expression '%s'" % expr)
|
|
60 |
|
|
61 |
# Create a SISExpression SISField of type EPrimTypeVariable.
|
|
62 |
leftfield = sisfield.SISExpression(Operator = sisfield.EPrimTypeVariable,
|
|
63 |
IntegerValue = sisfield.EVarLanguage)
|
|
64 |
|
|
65 |
# Create a SISExpression SISField of type EPrimTypeNumber.
|
|
66 |
rightfield = sisfield.SISExpression(Operator = sisfield.EPrimTypeNumber,
|
|
67 |
IntegerValue = langnum)
|
|
68 |
|
|
69 |
# Create an equality test SISExpression SISField and return it.
|
|
70 |
return sisfield.SISExpression(Operator = sisfield.EBinOpEqual,
|
|
71 |
IntegerValue = 0,
|
|
72 |
LeftExpression = leftfield,
|
|
73 |
RightExpression = rightfield)
|
|
74 |
|
|
75 |
def signstring(privkey, passphrase, string):
|
|
76 |
'''Sign a binary string using a given private key and its pass phrase.
|
|
77 |
|
|
78 |
signstring(...) -> (signature, algorithm oid)
|
|
79 |
|
|
80 |
privkey private key (RSA or DSA), a binary string in PEM format
|
|
81 |
passphrase pass phrase (non-Unicode) for the private key or None
|
|
82 |
string binary string from which the signature is to be calculated
|
|
83 |
|
|
84 |
signature signature, a binary string
|
|
85 |
algorithm oid signature algorithm object identifier, a string'''
|
|
86 |
|
|
87 |
# Sign string.
|
|
88 |
signature, keytype = cryptutil.signstring(privkey, passphrase, string)
|
|
89 |
|
|
90 |
# Determine algorithm object identifier.
|
|
91 |
if keytype == "DSA":
|
|
92 |
algoid = "1.2.840.10040.4.3"
|
|
93 |
elif keytype == "RSA":
|
|
94 |
algoid = "1.2.840.113549.1.1.5"
|
|
95 |
else:
|
|
96 |
raise ValueError("unknown key type '%s'" % keytype)
|
|
97 |
|
|
98 |
return (signature, algoid)
|
|
99 |
|
|
100 |
|
|
101 |
##############################################################################
|
|
102 |
# Module-level functions which are normally only used by this module
|
|
103 |
##############################################################################
|
|
104 |
|
|
105 |
def makefiledata(contents):
|
|
106 |
'''Make a SISFileData SISField out of the given binary string.
|
|
107 |
|
|
108 |
makefiledata(...) -> SISFileData
|
|
109 |
|
|
110 |
contents file contents, a binary string
|
|
111 |
|
|
112 |
SISFileData the returned SISFileData instance
|
|
113 |
|
|
114 |
NOTE: Data is compressed only if it is beneficial.'''
|
|
115 |
|
|
116 |
# Wrap data inside SISCompressed SISField.
|
|
117 |
cfield = sisfield.SISCompressed(Data = contents,
|
|
118 |
CompressionAlgorithm = sisfield.ECompressAuto,
|
|
119 |
rawdatainside = True)
|
|
120 |
|
|
121 |
# Create a SISFileData SISField out of the wrapped data and return it.
|
|
122 |
return sisfield.SISFileData(FileData = cfield)
|
|
123 |
|
|
124 |
def makefiledesc(contents, compressedlen, index, target = None,
|
|
125 |
mimetype = None, capabilities = None,
|
|
126 |
operation = sisfield.EOpInstall, options = 0):
|
|
127 |
'''Make a SISFileDescription SISField for the given file.
|
|
128 |
|
|
129 |
makefiledesc(...) -> SISFileDescription
|
|
130 |
|
|
131 |
contents file contents for SHA-1 digest calc., a binary string
|
|
132 |
compressedlen length of file contents inside a SISCompressed SISField
|
|
133 |
index index of file inside a SISDataUnit SISField, an integer
|
|
134 |
target install path in target device, a string or None
|
|
135 |
mimetype MIME type, a string or None
|
|
136 |
capabilities Symbian OS capabilities for EXE-files, int. mask or None
|
|
137 |
operation what to do with the file, an integer bit mask
|
|
138 |
options operation dependent install options, an integer bit mask
|
|
139 |
|
|
140 |
SISFileDescription the returned SISFileDescription instance
|
|
141 |
|
|
142 |
Constants for operation and options can be found in the sisfield module.
|
|
143 |
Operation is one of EOpInstall, EOpRun, EOpText or EOpNull. Options
|
|
144 |
depend on the selected operation, for example EInstVerifyOnRestore.'''
|
|
145 |
|
|
146 |
# Create a SISString of target path.
|
|
147 |
if target == None:
|
|
148 |
# Target may be None. The file is not installed anywhere in that case.
|
|
149 |
target = ""
|
|
150 |
targetfield = sisfield.SISString(String = target)
|
|
151 |
|
|
152 |
# Create a SISString of MIME type.
|
|
153 |
if mimetype == None:
|
|
154 |
# MIME type may be None (and usually is).
|
|
155 |
mimetype = ""
|
|
156 |
mimetypefield = sisfield.SISString(String = mimetype)
|
|
157 |
|
|
158 |
# Create a SISCapabilities SISField for executable capabilities.
|
|
159 |
if capabilities != None and capabilities != 0L:
|
|
160 |
# SISCapabilities expects a binary string, need to convert the
|
|
161 |
# capability mask. If capability mask is 0, no capability field
|
|
162 |
# is generated. Otherwise signsis.exe cannot sign the resulting
|
|
163 |
# SIS file.
|
|
164 |
capstring = symbianutil.capmasktorawdata(capabilities)
|
|
165 |
capfield = sisfield.SISCapabilities(Capabilities = capstring)
|
|
166 |
else:
|
|
167 |
# Only EXE- and DLL-files have a concept of capability.
|
|
168 |
capfield = None
|
|
169 |
|
|
170 |
# Calculate file hash using SHA-1. Create a SISHash SISField out of it.
|
|
171 |
# Contents may be None, to properly support the EOpNull install operation.
|
|
172 |
if contents != None:
|
|
173 |
sha1hash = sha.new(contents).digest()
|
|
174 |
else:
|
|
175 |
# No data, the containing SISBlob is mandatory but empty.
|
|
176 |
sha1hash = ""
|
|
177 |
hashblob = sisfield.SISBlob(Data = sha1hash)
|
|
178 |
hashfield = sisfield.SISHash(HashAlgorithm = sisfield.ESISHashAlgSHA1,
|
|
179 |
HashData = hashblob)
|
|
180 |
|
|
181 |
# Create a SISFileDescription SISField and return it.
|
|
182 |
return sisfield.SISFileDescription(Target = targetfield,
|
|
183 |
MIMEType = mimetypefield,
|
|
184 |
Capabilities = capfield,
|
|
185 |
Hash = hashfield,
|
|
186 |
Operation = operation,
|
|
187 |
OperationOptions = options,
|
|
188 |
Length = compressedlen,
|
|
189 |
UncompressedLength = len(contents),
|
|
190 |
FileIndex = index)
|
|
191 |
|
|
192 |
def makedependency(uid, fromversion, toversion, names):
|
|
193 |
'''Make a SISDependency SISField for the given UID, version dependency.
|
|
194 |
|
|
195 |
makedependency(...) -> SISDependency
|
|
196 |
|
|
197 |
uid UID, an unsigned integer
|
|
198 |
fromversion from-version, a triple-item list/tuple (major, minor, build)
|
|
199 |
toversion to-version, a triple-item list/tuple or None
|
|
200 |
names names for the dependency, a list of string per language
|
|
201 |
|
|
202 |
SISDependency the returned SISDependency SISField
|
|
203 |
|
|
204 |
NOTE: toversion may be None, indicating any version after fromversion.'''
|
|
205 |
|
|
206 |
# Convert parameters to SISFields.
|
|
207 |
uidfield = sisfield.SISUid(UID1 = uid)
|
|
208 |
|
|
209 |
fromverfield = sisfield.SISVersion(Major = fromversion[0],
|
|
210 |
Minor = fromversion[1],
|
|
211 |
Build = fromversion[2])
|
|
212 |
if toversion != None:
|
|
213 |
toverfield = sisfield.SISVersion(Major = toversion[0],
|
|
214 |
Minor = toversion[1],
|
|
215 |
Build = toversion[2])
|
|
216 |
else:
|
|
217 |
toverfield = None
|
|
218 |
|
|
219 |
verrangefield = sisfield.SISVersionRange(FromVersion = fromverfield,
|
|
220 |
ToVersion = toverfield)
|
|
221 |
|
|
222 |
l = []
|
|
223 |
for name in names:
|
|
224 |
l.append(sisfield.SISString(String = name))
|
|
225 |
namesfield = sisfield.SISArray(SISFields = l, SISFieldType = "SISString")
|
|
226 |
|
|
227 |
# Create a SISDependency SISField and return it.
|
|
228 |
return sisfield.SISDependency(UID = uidfield,
|
|
229 |
VersionRange = verrangefield,
|
|
230 |
DependencyNames = namesfield)
|
|
231 |
|
|
232 |
|
|
233 |
def makeinstallblock(files = [], embeddedsisfiles = [], ifblocks = []):
|
|
234 |
'''Make a SISInstallBlock SISField out of the given lists of SISFields.
|
|
235 |
|
|
236 |
makeinstallblock(...) -> SISInstallBlock
|
|
237 |
|
|
238 |
files a list of SISFileDescription SISFields (normal files)
|
|
239 |
embeddedsisfiles a list of SISController SISFields (embedded SIS files)
|
|
240 |
ifblocks a list of SISIf SISFields (conditionally installed files)
|
|
241 |
|
|
242 |
SISInstallBlock the returned SISInstallBlock instance
|
|
243 |
|
|
244 |
NOTE: Any of the lists may be empty (and are, by default).'''
|
|
245 |
|
|
246 |
|
|
247 |
# Convert lists to SISArrays.
|
|
248 |
sa1 = sisfield.SISArray(SISFields = files,
|
|
249 |
SISFieldType = "SISFileDescription")
|
|
250 |
sa2 = sisfield.SISArray(SISFields = embeddedsisfiles,
|
|
251 |
SISFieldType = "SISController")
|
|
252 |
sa3 = sisfield.SISArray(SISFields = ifblocks,
|
|
253 |
SISFieldType = "SISIf")
|
|
254 |
|
|
255 |
# Create a SISInstallBlock SISField and return it.
|
|
256 |
return sisfield.SISInstallBlock(Files = sa1, EmbeddedSISFiles = sa2,
|
|
257 |
IfBlocks = sa3)
|
|
258 |
|
|
259 |
def makelangconditional(languages, langdepfiles):
|
|
260 |
'''Make a SISIf and SISElseIfs for language dependent installation of files.
|
|
261 |
|
|
262 |
makelangconditional(...) -> SISIf or None
|
|
263 |
|
|
264 |
languages a list of language numbers (not names, IDs or SISLanguages)
|
|
265 |
landepfiles a list of file lists, where each file list is a list of
|
|
266 |
alternative SISFileDescription SISFields for each language
|
|
267 |
|
|
268 |
SISIf the returned SISIf instance or None if no files'''
|
|
269 |
|
|
270 |
if len(langdepfiles) == 0:
|
|
271 |
# No language dependent files, leave.
|
|
272 |
return None
|
|
273 |
|
|
274 |
# Create a file list per language.
|
|
275 |
filesperlang = []
|
|
276 |
for n in xrange(len(languages)):
|
|
277 |
filesperlang.append([])
|
|
278 |
|
|
279 |
# Gather all files from the same language to a single list.
|
|
280 |
for files in langdepfiles:
|
|
281 |
if len(files) != len(languages):
|
|
282 |
raise ValueError("%d files given but number of languages is %d" %
|
|
283 |
(len(files), len(languages)))
|
|
284 |
|
|
285 |
for n in xrange(len(languages)):
|
|
286 |
filesperlang[n].append(files[n])
|
|
287 |
|
|
288 |
if len(languages) == 0:
|
|
289 |
# No languages, leave. (This is down here so that errors
|
|
290 |
# can still be caught above.)
|
|
291 |
return None
|
|
292 |
|
|
293 |
# Create a SISArray of SISElseIf SISFields.
|
|
294 |
elseiffields = []
|
|
295 |
for n in xrange(1, len(languages)):
|
|
296 |
elseifexpfield = parseexpression("language == %d" % languages[n])
|
|
297 |
elseiffield = sisfield.SISElseIf(Expression = elseifexpfield,
|
|
298 |
InstallBlock = makeinstallblock(filesperlang[n]))
|
|
299 |
elseiffields.append(elseiffield)
|
|
300 |
elseiffieldarray = sisfield.SISArray(SISFields = elseiffields,
|
|
301 |
SISFieldType = "SISElseIf")
|
|
302 |
|
|
303 |
# Create and return the final SISIf SISField.
|
|
304 |
ifexpfield = parseexpression("language == %d" % languages[0])
|
|
305 |
return sisfield.SISIf(Expression = ifexpfield,
|
|
306 |
InstallBlock = makeinstallblock(filesperlang[0]),
|
|
307 |
ElseIfs = elseiffieldarray)
|
|
308 |
|
|
309 |
|
|
310 |
##############################################################################
|
|
311 |
# SimpleSISWriter class for no-frills SIS file generation
|
|
312 |
##############################################################################
|
|
313 |
|
|
314 |
class SimpleSISWriter(object):
|
|
315 |
'''A no-frills SIS file generator
|
|
316 |
|
|
317 |
Limitations:
|
|
318 |
|
|
319 |
- Option lists are not supported.
|
|
320 |
- Condition blocks are not supported. Languages are, however.
|
|
321 |
- Nested SIS files are not supported.
|
|
322 |
- SIS type is always a full installation package (type EInstInstallation).
|
|
323 |
- Package options (EInstFlagShutdownApps) are not supported.'''
|
|
324 |
|
|
325 |
def __init__(self, languages, names, uid, version,
|
|
326 |
vendorname, vendornames, creationtime = None):
|
|
327 |
# Set empty list of languages, names, files, certificates and so on.
|
|
328 |
self.languages = []
|
|
329 |
self.filedata = []
|
|
330 |
self.files = []
|
|
331 |
self.langdepfiles = []
|
|
332 |
self.logo = None
|
|
333 |
self.certificates = []
|
|
334 |
self.targetdevices = []
|
|
335 |
self.dependencies = []
|
|
336 |
self.properties = []
|
|
337 |
|
|
338 |
# Convert language IDs/names to language numbers.
|
|
339 |
for lang in languages:
|
|
340 |
try:
|
|
341 |
langnum = symbianutil.langidtonum[lang]
|
|
342 |
except KeyError:
|
|
343 |
# Not a language ID, try names next.
|
|
344 |
try:
|
|
345 |
langnum = symbianutil.langnametonum[lang]
|
|
346 |
except KeyError:
|
|
347 |
raise ValueError("invalid language '%s'" % lang)
|
|
348 |
self.languages.append(langnum)
|
|
349 |
|
|
350 |
# Verify number of names and vendor names wrt. number of languages.
|
|
351 |
if len(names) != len(self.languages):
|
|
352 |
raise ValueError(
|
|
353 |
"%d package names given but number of languages is %d" %
|
|
354 |
(len(names), len(self.languages)))
|
|
355 |
|
|
356 |
if len(vendornames) != len(self.languages):
|
|
357 |
raise ValueError(
|
|
358 |
"%d vendor names given but number of languages is %d" %
|
|
359 |
(len(vendornames), len(self.languages)))
|
|
360 |
|
|
361 |
# Convert language dependent names to a SISArray of SISStrings.
|
|
362 |
l = []
|
|
363 |
for name in names:
|
|
364 |
l.append(sisfield.SISString(String = name))
|
|
365 |
self.names = sisfield.SISArray(SISFields = l,
|
|
366 |
SISFieldType = "SISString")
|
|
367 |
|
|
368 |
# Convert integer UID to SISUid SISField.
|
|
369 |
self.uid = sisfield.SISUid(UID1 = uid)
|
|
370 |
|
|
371 |
# Convert version number triplet to SISVersion SISField.
|
|
372 |
self.version = sisfield.SISVersion(Major = version[0],
|
|
373 |
Minor = version[1],
|
|
374 |
Build = version[2])
|
|
375 |
|
|
376 |
# Convert unique vendor name to SISString SISField.
|
|
377 |
self.vendorname = sisfield.SISString(String = vendorname)
|
|
378 |
|
|
379 |
# Convert language dependent vendor names to a SISArray of SISStrings.
|
|
380 |
l = []
|
|
381 |
for name in vendornames:
|
|
382 |
l.append(sisfield.SISString(String = name))
|
|
383 |
self.vendornames = sisfield.SISArray(SISFields = l,
|
|
384 |
SISFieldType = "SISString")
|
|
385 |
|
|
386 |
if creationtime == None:
|
|
387 |
# If no creation time given, use the time
|
|
388 |
# of SimpleSISWriter instantiation.
|
|
389 |
creationtime = time.gmtime()
|
|
390 |
|
|
391 |
# Convert standard Python time representation to SISFields.
|
|
392 |
datefield = sisfield.SISDate(Year = creationtime.tm_year,
|
|
393 |
Month = creationtime.tm_mon - 1,
|
|
394 |
Day = creationtime.tm_mday)
|
|
395 |
timefield = sisfield.SISTime(Hours = creationtime.tm_hour,
|
|
396 |
Minutes = creationtime.tm_min,
|
|
397 |
Seconds = creationtime.tm_sec)
|
|
398 |
self.creationtime = sisfield.SISDateTime(Date = datefield,
|
|
399 |
Time = timefield)
|
|
400 |
|
|
401 |
def setlogo(self, contents, mimetype):
|
|
402 |
'''Add a logo graphics to generated SIS file.
|
|
403 |
|
|
404 |
NOTE: Not all devices display a logo during installation.'''
|
|
405 |
|
|
406 |
if self.logo != None:
|
|
407 |
raise ValueError("logo already set")
|
|
408 |
|
|
409 |
# Create SISFileData and SISFileDescription SISFields.
|
|
410 |
filedata = makefiledata(contents)
|
|
411 |
complen = filedata.getcompressedlength()
|
|
412 |
runopts = (sisfield.EInstFileRunOptionInstall |
|
|
413 |
sisfield.EInstFileRunOptionByMimeType)
|
|
414 |
filedesc = makefiledesc(contents, complen, len(self.filedata),
|
|
415 |
None, mimetype, None, sisfield.EOpRun, runopts)
|
|
416 |
self.logo = sisfield.SISLogo(LogoFile = filedesc)
|
|
417 |
self.filedata.append(filedata)
|
|
418 |
|
|
419 |
def addfile(self, contents, target = None, mimetype = None,
|
|
420 |
capabilities = None, operation = sisfield.EOpInstall,
|
|
421 |
options = 0):
|
|
422 |
'''Add a file that is same for all languages to generated SIS file.'''
|
|
423 |
|
|
424 |
# Create SISFileData and SISFileDescription SISFields.
|
|
425 |
filedata = makefiledata(contents)
|
|
426 |
complen = filedata.getcompressedlength()
|
|
427 |
metadata = makefiledesc(contents, complen, len(self.filedata),
|
|
428 |
target, mimetype, capabilities,
|
|
429 |
operation, options)
|
|
430 |
self.files.append(metadata)
|
|
431 |
self.filedata.append(filedata)
|
|
432 |
|
|
433 |
def addlangdepfile(self, clist, target = None, mimetype = None,
|
|
434 |
capabilities = None, operation = sisfield.EOpInstall,
|
|
435 |
options = 0):
|
|
436 |
'''Add language dependent files to generated SIS file.
|
|
437 |
|
|
438 |
A conditional expression is automatically generated for the file.'''
|
|
439 |
|
|
440 |
if len(clist) != len(self.languages):
|
|
441 |
raise ValueError("%d files given but number of languages is %d" %
|
|
442 |
(len(clist), len(self.languages)))
|
|
443 |
|
|
444 |
data = []
|
|
445 |
files = []
|
|
446 |
index = len(self.filedata)
|
|
447 |
for contents in clist:
|
|
448 |
# Create SISFileData and SISFileDescription SISFields.
|
|
449 |
filedata = makefiledata(contents)
|
|
450 |
complen = filedata.getcompressedlength()
|
|
451 |
metadata = makefiledesc(contents, complen, index,
|
|
452 |
target, mimetype, capabilities,
|
|
453 |
operation, options)
|
|
454 |
files.append(metadata)
|
|
455 |
data.append(filedata)
|
|
456 |
index += 1
|
|
457 |
|
|
458 |
self.langdepfiles.append(files)
|
|
459 |
self.filedata.extend(data)
|
|
460 |
|
|
461 |
def addcertificate(self, privkey, cert, passphrase):
|
|
462 |
'''Add a certificate to SIS file.
|
|
463 |
|
|
464 |
Private key and certificate are in PEM (base-64) format.'''
|
|
465 |
|
|
466 |
self.certificates.append((privkey, cert, passphrase))
|
|
467 |
|
|
468 |
def addtargetdevice(self, uid, fromversion, toversion, names):
|
|
469 |
'''Add a mandatory target device UID to generated SIS file.
|
|
470 |
|
|
471 |
NOTE: Names are not usually displayed. Instead, the device vendor
|
|
472 |
has specified what the names must be.'''
|
|
473 |
|
|
474 |
if len(names) != len(self.languages):
|
|
475 |
raise ValueError(
|
|
476 |
"%d device names given but number of languages is %d" %
|
|
477 |
(len(names), len(self.languages)))
|
|
478 |
|
|
479 |
depfield = makedependency(uid, fromversion, toversion, names)
|
|
480 |
self.targetdevices.append(depfield)
|
|
481 |
|
|
482 |
def adddependency(self, uid, fromversion, toversion, names):
|
|
483 |
'''Add an installed package dependency to generated SIS file.
|
|
484 |
|
|
485 |
NOTE: Some devices display the first name of the dependency
|
|
486 |
regardless of the device language.'''
|
|
487 |
|
|
488 |
if len(names) != len(self.languages):
|
|
489 |
raise ValueError(
|
|
490 |
"%d dependency names given but number of languages is %d" %
|
|
491 |
(len(names), len(self.languages)))
|
|
492 |
|
|
493 |
depfield = makedependency(uid, fromversion, toversion, names)
|
|
494 |
self.dependencies.append(depfield)
|
|
495 |
|
|
496 |
def addproperty(self, key, value):
|
|
497 |
'''Add a property key, value pair to generated SIS file.
|
|
498 |
|
|
499 |
When installing other SIS files, they may query these properties.'''
|
|
500 |
|
|
501 |
# Convert parameters to a SISProperty SISField.
|
|
502 |
self.properties.append(sisfield.SISProperty(Key = key,
|
|
503 |
Value = value))
|
|
504 |
|
|
505 |
def tostring(self):
|
|
506 |
'''Convert this SIS instance to a (possibly very large) string.'''
|
|
507 |
|
|
508 |
# Generate a SISInfo SISField.
|
|
509 |
infofield = sisfield.SISInfo(UID = self.uid,
|
|
510 |
VendorUniqueName = self.vendorname,
|
|
511 |
Names = self.names,
|
|
512 |
VendorNames = self.vendornames,
|
|
513 |
Version = self.version,
|
|
514 |
CreationTime = self.creationtime,
|
|
515 |
InstallType = sisfield.EInstInstallation,
|
|
516 |
InstallFlags = 0)
|
|
517 |
|
|
518 |
# Generate an empty SISSupportedOptions SISField.
|
|
519 |
# Option lists are not supported by SimpleSISWriter.
|
|
520 |
sa = sisfield.SISArray(SISFields = [],
|
|
521 |
SISFieldType = "SISSupportedOption")
|
|
522 |
optfield = sisfield.SISSupportedOptions(Options = sa)
|
|
523 |
|
|
524 |
# Convert language numbers to SISArray of SISLanguages
|
|
525 |
# and generate a SISSupportedLanguages SISField.
|
|
526 |
langfieldlist = []
|
|
527 |
for lang in self.languages:
|
|
528 |
langfieldlist.append(sisfield.SISLanguage(Language = lang))
|
|
529 |
sa = sisfield.SISArray(SISFields = langfieldlist,
|
|
530 |
SISFieldType = "SISLanguage")
|
|
531 |
langfield = sisfield.SISSupportedLanguages(Languages = sa)
|
|
532 |
|
|
533 |
# Generate SISPrerequisites SISField.
|
|
534 |
sa1 = sisfield.SISArray(SISFields = self.targetdevices,
|
|
535 |
SISFieldType = "SISDependency")
|
|
536 |
sa2 = sisfield.SISArray(SISFields = self.dependencies,
|
|
537 |
SISFieldType = "SISDependency")
|
|
538 |
prereqfield = sisfield.SISPrerequisites(TargetDevices = sa1,
|
|
539 |
Dependencies = sa2)
|
|
540 |
|
|
541 |
# Generate SISProperties SISField.
|
|
542 |
sa = sisfield.SISArray(SISFields = self.properties,
|
|
543 |
SISFieldType = "SISProperty")
|
|
544 |
propfield = sisfield.SISProperties(Properties = sa)
|
|
545 |
|
|
546 |
# Generate SISInstallBlock SISField.
|
|
547 |
iffield = makelangconditional(self.languages, self.langdepfiles)
|
|
548 |
if iffield:
|
|
549 |
# Some language dependent files
|
|
550 |
iffieldlist = [iffield]
|
|
551 |
else:
|
|
552 |
# No language dependent files
|
|
553 |
iffieldlist = []
|
|
554 |
ibfield = makeinstallblock(self.files, [], iffieldlist)
|
|
555 |
|
|
556 |
# Generate a data index field. No embedded SIS files, index is 0.
|
|
557 |
didxfield = sisfield.SISDataIndex(DataIndex = 0)
|
|
558 |
|
|
559 |
# Generate a SISController SISField without any signatures.
|
|
560 |
ctrlfield = sisfield.SISController(Info = infofield,
|
|
561 |
Options = optfield,
|
|
562 |
Languages = langfield,
|
|
563 |
Prerequisites = prereqfield,
|
|
564 |
Properties = propfield,
|
|
565 |
Logo = self.logo,
|
|
566 |
InstallBlock = ibfield)
|
|
567 |
|
|
568 |
# Calculate metadata signature for each certificate.
|
|
569 |
certfieldlist = []
|
|
570 |
for cert in self.certificates:
|
|
571 |
# Calculate a signature of the SISController so far.
|
|
572 |
string = ctrlfield.tostring()
|
|
573 |
string = sisfield.stripheaderandpadding(string)
|
|
574 |
signature, algoid = signstring(cert[0], cert[2], string)
|
|
575 |
|
|
576 |
# Create a SISCertificateChain SISField from certificate data.
|
|
577 |
sf1 = sisfield.SISBlob(Data = cryptutil.certtobinary(cert[1]))
|
|
578 |
sf2 = sisfield.SISCertificateChain(CertificateData = sf1)
|
|
579 |
|
|
580 |
# Create a SISSignature SISField from calculated signature.
|
|
581 |
sf3 = sisfield.SISString(String = algoid)
|
|
582 |
sf4 = sisfield.SISSignatureAlgorithm(AlgorithmIdentifier = sf3)
|
|
583 |
sf5 = sisfield.SISBlob(Data = signature)
|
|
584 |
sf6 = sisfield.SISSignature(SignatureAlgorithm = sf4,
|
|
585 |
SignatureData = sf5)
|
|
586 |
|
|
587 |
# Create a new SISSignatureCertificateChain SISField.
|
|
588 |
sa = sisfield.SISArray(SISFields = [sf6])
|
|
589 |
certfieldlist.append(sisfield.SISSignatureCertificateChain(
|
|
590 |
Signatures = sa, CertificateChain = sf2))
|
|
591 |
|
|
592 |
# Add certificate to SISController SISField.
|
|
593 |
ctrlfield.setsignatures(certfieldlist)
|
|
594 |
|
|
595 |
# Finally add a data index field to SISController SISField.
|
|
596 |
# and wrap it in SISCompressed SISField.
|
|
597 |
ctrlfield.DataIndex = didxfield
|
|
598 |
ctrlcompfield = sisfield.SISCompressed(Data = ctrlfield,
|
|
599 |
CompressionAlgorithm = sisfield.ECompressDeflate)
|
|
600 |
|
|
601 |
# Generate SISData SISField.
|
|
602 |
sa = sisfield.SISArray(SISFields = self.filedata,
|
|
603 |
SISFieldType = "SISFileData")
|
|
604 |
dufield = sisfield.SISDataUnit(FileData = sa)
|
|
605 |
sa = sisfield.SISArray(SISFields = [dufield])
|
|
606 |
datafield = sisfield.SISData(DataUnits = sa)
|
|
607 |
|
|
608 |
# Calculate SISController checksum.
|
|
609 |
# TODO: Requires an extra tostring() conversion.
|
|
610 |
ctrlcs = symbianutil.crc16ccitt(ctrlcompfield.tostring())
|
|
611 |
ctrlcsfield = sisfield.SISControllerChecksum(Checksum = ctrlcs)
|
|
612 |
|
|
613 |
# Calculate SISData checksum.
|
|
614 |
# TODO: Requires an extra tostring() conversion.
|
|
615 |
datacs = symbianutil.crc16ccitt(datafield.tostring())
|
|
616 |
datacsfield = sisfield.SISDataChecksum(Checksum = datacs)
|
|
617 |
|
|
618 |
# Generate SISContents SISField.
|
|
619 |
contentsfield = sisfield.SISContents(ControllerChecksum = ctrlcsfield,
|
|
620 |
DataChecksum = datacsfield,
|
|
621 |
Controller = ctrlcompfield,
|
|
622 |
Data = datafield)
|
|
623 |
|
|
624 |
# Generate a SIS UID string.
|
|
625 |
uidstring = symbianutil.uidstostring(0x10201a7aL, 0x00000000L,
|
|
626 |
self.uid.UID1)
|
|
627 |
|
|
628 |
# Return the completed SIS file as a string.
|
|
629 |
return uidstring + contentsfield.tostring()
|
|
630 |
|
|
631 |
def tofile(self, outfile):
|
|
632 |
'''Write this SIS instance to a file object or a named file.'''
|
|
633 |
|
|
634 |
s = self.tostring()
|
|
635 |
|
|
636 |
try:
|
|
637 |
f = file(outfile, "wb")
|
|
638 |
try:
|
|
639 |
f.write(s)
|
|
640 |
finally:
|
|
641 |
f.close()
|
|
642 |
except TypeError:
|
|
643 |
f.write(s)
|