|
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) |