|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 ############################################################################## |
|
5 # sisfield.py - Symbian OS v9.x SIS file utilities, SISField support classes |
|
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 struct |
|
26 import zlib |
|
27 |
|
28 import symbianutil |
|
29 |
|
30 |
|
31 # TODO: 1. Make all tostring() methods cache the result. |
|
32 # TODO: 2. Allow modifying objects after creation, keeping string cache in sync. |
|
33 # TODO: 3. Implement a list-of-strings type, which the tostring() (or |
|
34 # some other method with a better name) can use, to eliminate |
|
35 # superfluous string concatenations. |
|
36 |
|
37 |
|
38 ############################################################################## |
|
39 # Parameters |
|
40 ############################################################################## |
|
41 |
|
42 DEBUG = 0 # (0: None, 1: Basic, 2: Verbose) |
|
43 MAXNUMSIGNATURES = 8 # Maximum number of signatures in a SISController |
|
44 |
|
45 |
|
46 ############################################################################## |
|
47 # Public SISField constants (in original Symbian OS naming style) |
|
48 ############################################################################## |
|
49 |
|
50 ECompressAuto = -1 # Not a real compression type |
|
51 ECompressNone = 0 |
|
52 ECompressDeflate = 1 |
|
53 |
|
54 EInstInstallation = 0 |
|
55 EInstAugmentation = 1 |
|
56 EInstPartialUpgrade = 2 |
|
57 EInstPreInstalledApp = 3 |
|
58 EInstPreInstalledPatch = 4 |
|
59 |
|
60 EInstFlagShutdownApps = 1 |
|
61 |
|
62 EOpInstall = 1 |
|
63 EOpRun = 2 |
|
64 EOpText = 4 |
|
65 EOpNull = 8 |
|
66 |
|
67 EInstVerifyOnRestore = 1 << 15 |
|
68 |
|
69 EInstFileRunOptionInstall = 1 << 1 |
|
70 EInstFileRunOptionUninstall = 1 << 2 |
|
71 EInstFileRunOptionByMimeType = 1 << 3 |
|
72 EInstFileRunOptionWaitEnd = 1 << 4 |
|
73 EInstFileRunOptionSendEnd = 1 << 5 |
|
74 |
|
75 EInstFileTextOptionContinue = 1 << 9 # Not used by makesis v. 4, 0, 0, 2 |
|
76 EInstFileTextOptionSkipIfNo = 1 << 10 |
|
77 EInstFileTextOptionAbortIfNo = 1 << 11 |
|
78 EInstFileTextOptionExitIfNo = 1 << 12 |
|
79 |
|
80 ESISHashAlgSHA1 = 1 |
|
81 |
|
82 ESISSignatureAlgSHA1RSA = "1.2.840.113549.1.1.5" |
|
83 ESISSignatureAlgSHA1DSA = "1.2.840.10040.4.3" |
|
84 |
|
85 EBinOpEqual = 1 |
|
86 EBinOpNotEqual = 2 |
|
87 EBinOpGreaterThan = 3 |
|
88 EBinOpLessThan = 4 |
|
89 EBinOpGreaterOrEqual = 5 |
|
90 EBinOpLessOrEqual = 6 |
|
91 ELogOpAnd = 7 |
|
92 ELogOpOr = 8 |
|
93 EUnaryOpNot = 9 |
|
94 EFuncExists = 10 |
|
95 EFuncAppProperties = 11 |
|
96 EFuncDevProperties = 12 |
|
97 EPrimTypeString = 13 |
|
98 EPrimTypeOption = 14 |
|
99 EPrimTypeVariable = 15 |
|
100 EPrimTypeNumber = 16 |
|
101 |
|
102 EVarLanguage = 0x1000 |
|
103 EVarRemoteInstall = 0x1001 |
|
104 |
|
105 |
|
106 ############################################################################## |
|
107 # Public exception class for SIS parsing / generation |
|
108 ############################################################################## |
|
109 |
|
110 class SISException(StandardError): |
|
111 '''SIS parsing / generation error''' |
|
112 pass |
|
113 |
|
114 |
|
115 ############################################################################## |
|
116 # Public module-level functions |
|
117 ############################################################################## |
|
118 |
|
119 def SISField(fromstring, exactlength = True): |
|
120 '''Generator function for creating SISField subclass instances from a string |
|
121 |
|
122 If exactlength is False, return a tuple of (SISField, bytes consumed). |
|
123 Otherwise return SISField directly.''' |
|
124 |
|
125 # Determine SISField subclass type. |
|
126 ftype, hdrlen, flen, padlen = parsesisfieldheader(fromstring, None, |
|
127 exactlength) |
|
128 |
|
129 try: |
|
130 fclass = fieldnumtoclass[ftype] |
|
131 except KeyError: |
|
132 raise SISException("invalid SISField type '%d'" % ftype) |
|
133 |
|
134 # Limit string to actual data length (in case exactlength was False). |
|
135 fromstring = fromstring[:(hdrlen + flen + padlen)] |
|
136 |
|
137 # Create a subclass instance. |
|
138 field = fclass(fromstring = fromstring) |
|
139 |
|
140 if exactlength: |
|
141 return field |
|
142 else: |
|
143 # Normal SISField subclasses use SISField() to parse adjacent SISFields. |
|
144 # Number of consumed bytes need to be passed back in that case. This |
|
145 # may violate the idea of a generator function somewhat, but eliminates |
|
146 # a bit of duplicated code. |
|
147 return (field, len(fromstring)) |
|
148 |
|
149 def stripheaderandpadding(fromstring): |
|
150 '''Return the actual content of SISField string. |
|
151 |
|
152 stripheaderandpadding(...) -> contents |
|
153 |
|
154 fromstring a SISField string |
|
155 |
|
156 contents SISField contents without the header and padding''' |
|
157 |
|
158 # Parse field header. |
|
159 ftype, hdrlen, flen, padlen = parsesisfieldheader(fromstring) |
|
160 |
|
161 # Return field contents. |
|
162 return fromstring[hdrlen:(hdrlen + flen)] |
|
163 |
|
164 |
|
165 ############################################################################## |
|
166 # Module-level functions which are normally only used by this module |
|
167 ############################################################################## |
|
168 |
|
169 def parsesisfieldheader(string, requiredtype = None, exactlength = True): |
|
170 '''Parse the header of a SISField string and return the field type and |
|
171 lengths of the various parts (header, data, padding). Optionally, check |
|
172 that the type is correct and that the string length is not too long.''' |
|
173 |
|
174 hdrlen = 8 |
|
175 if hdrlen > len(string): |
|
176 raise SISException("not enough data for a complete SISField header") |
|
177 |
|
178 # Get SISField type and first part of the length. |
|
179 ftype, flen = struct.unpack("<LL", string[:8]) |
|
180 |
|
181 # Get rest of the SISField length, 31-bit or 63-bit. |
|
182 flen2 = None |
|
183 if flen & 0x8000000L: |
|
184 # 63-bit length, read rest of length. |
|
185 hdrlen = 12 |
|
186 if hdrlen > len(string): |
|
187 raise SISException("not enough data for a complete SISField header") |
|
188 flen2 = struct.unpack("<L", string[8:12])[0] |
|
189 flen = (flen & 0x7ffffffL) | (flen2 << 31) |
|
190 |
|
191 # Calculate padding to 32-bit boundary. |
|
192 padlen = ((flen + 3) & ~0x3L) - flen |
|
193 |
|
194 if requiredtype != None and ftype != requiredtype: |
|
195 raise SISException("invalid SISField type '%d'" % ftype) |
|
196 |
|
197 if (hdrlen + flen + padlen) > len(string): |
|
198 raise SISException("SISField contents too short") |
|
199 |
|
200 # Allow oversized strings when parsing recursive SISFields. |
|
201 if exactlength and (hdrlen + flen + padlen) < len(string): |
|
202 raise SISException("SISField contents too long") |
|
203 |
|
204 return ftype, hdrlen, flen, padlen |
|
205 |
|
206 def makesisfieldheader(fieldtype, fieldlen): |
|
207 '''Create a SISField header string from type and length.''' |
|
208 |
|
209 if fieldlen < 0x80000000L: |
|
210 # 31-bit length |
|
211 return struct.pack("<LL", fieldtype, fieldlen) |
|
212 else: |
|
213 # 63-bit length |
|
214 fieldlen2 = fieldlen >> 31 |
|
215 fieldlen = (fieldlen & 0x7fffffffL) | 0x80000000L |
|
216 return struct.pack("<LLL", fieldtype, fieldlen, fieldlen2) |
|
217 |
|
218 def makesisfieldpadding(fieldlen): |
|
219 '''Create a string of zero bytes for padding to 32-bit boundary. |
|
220 Parameter may be either the whole field length (header + data) |
|
221 or just the data length.''' |
|
222 |
|
223 # Calculate padding to 32-bit boundary. |
|
224 padlen = ((fieldlen + 3) & ~0x3L) - fieldlen |
|
225 |
|
226 return "\x00" * padlen |
|
227 |
|
228 |
|
229 ############################################################################## |
|
230 # SISField base classes |
|
231 ############################################################################## |
|
232 |
|
233 class SISFieldBase(object): |
|
234 '''SISField base class''' |
|
235 def __init__(self, **kwds): |
|
236 if DEBUG > 0: |
|
237 # DEBUG: Print class name during initialization. |
|
238 print "%s.__init__()" % self.__class__.__name__ |
|
239 |
|
240 # Get the names of all instance variables. |
|
241 validkwds = self.__dict__.keys() |
|
242 |
|
243 # Filter out private instance variables (all in lowercase). |
|
244 validkwds = filter(lambda s: s != s.lower(), validkwds) |
|
245 |
|
246 # Set type code. |
|
247 self.fieldtype = fieldnametonum[self.__class__.__name__] |
|
248 |
|
249 if "fromstring" in kwds: |
|
250 if DEBUG > 1: |
|
251 # DEBUG: Dump of string parameter. |
|
252 print repr(kwds["fromstring"]) |
|
253 |
|
254 # Load instance variables from string. |
|
255 if len(kwds) != 1: |
|
256 raise TypeError( |
|
257 "keyword 'fromstring' may not be given with other keywords") |
|
258 self.fromstring(kwds["fromstring"]) |
|
259 else: |
|
260 # Load instance variables from keywords. |
|
261 # Only accept existing variable names. |
|
262 for kwd in kwds.keys(): |
|
263 if kwd not in validkwds: |
|
264 raise AttributeError("'%s' object has no attribute '%s'" % |
|
265 (self.__class__.__name__, kwd)) |
|
266 self.__dict__[kwd] = kwds[kwd] |
|
267 |
|
268 def __str__(self): |
|
269 # Default __str__() for SISFields, only return the field name. |
|
270 return "<%s>" % self.__class__.__name__ |
|
271 |
|
272 class SISFieldNormal(SISFieldBase): |
|
273 '''SISField base class for normal fields (fields containing only other |
|
274 fields and integers)''' |
|
275 |
|
276 # Subfield types |
|
277 FTYPE_INTEGRAL = 0 # Integer |
|
278 FTYPE_MANDATORY = 1 # Mandatory SISField |
|
279 FTYPE_OPTIONAL = 2 # Optional SISField |
|
280 FTYPE_ARRAY = 3 # SISArray with zero or more items |
|
281 |
|
282 def __init__(self, **kwds): |
|
283 # Initialize instance variables to None. |
|
284 for fattr, fkind, ffmt in self.subfields: |
|
285 self.__dict__[fattr] = None |
|
286 |
|
287 # Set instance variables. |
|
288 SISFieldBase.__init__(self, **kwds) |
|
289 |
|
290 for fattr, fkind, ffmt in self.subfields: |
|
291 # Check that all required instance variables are set. |
|
292 if fkind != self.FTYPE_OPTIONAL and self.__dict__[fattr] == None: |
|
293 raise AttributeError("missing '%s' attribute for '%s'" % |
|
294 (fattr, self.__class__.__name__)) |
|
295 |
|
296 if fkind in (self.FTYPE_MANDATORY, self.FTYPE_OPTIONAL): |
|
297 # Verify SISField types. |
|
298 if (self.__dict__[fattr] != None and |
|
299 fieldnumtoname[self.__dict__[fattr].fieldtype] != ffmt): |
|
300 raise TypeError( |
|
301 "attribute '%s' for '%s' is of invalid SISField type" % |
|
302 (fattr, self.__class__.__name__)) |
|
303 elif fkind == self.FTYPE_ARRAY: |
|
304 # Verify SISArray contents. |
|
305 if (fieldnumtoname[self.__dict__[fattr].fieldtype] != "SISArray" |
|
306 or |
|
307 fieldnumtoname[self.__dict__[fattr].SISFieldType] != ffmt): |
|
308 raise TypeError( |
|
309 "SISArray attribute '%s' for '%s' is of invalid type" % |
|
310 (fattr, self.__class__.__name__)) |
|
311 |
|
312 def fromstring(self, string): |
|
313 # Parse field header. |
|
314 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
315 self.fieldtype) |
|
316 |
|
317 # Recursively parse subfields. |
|
318 pos = hdrlen |
|
319 reuse = None # SISField to re-use or None |
|
320 try: |
|
321 for fattr, fkind, ffmt in self.subfields: |
|
322 field = None # No value by default |
|
323 |
|
324 if fkind == self.FTYPE_INTEGRAL: |
|
325 # Integer, unpack it. |
|
326 if reuse: |
|
327 # It is an error if there is a field to |
|
328 # re-use present at this time. |
|
329 raise ValueError("integral field preceded optional") |
|
330 |
|
331 n = struct.calcsize(ffmt) |
|
332 field = struct.unpack(ffmt, string[pos:(pos + n)])[0] |
|
333 pos += n |
|
334 else: |
|
335 # SISField, read data from string or |
|
336 # re-use field from previous round. |
|
337 if not reuse: |
|
338 # No old field to handle, convert string to SISField. |
|
339 if pos < (hdrlen + flen): |
|
340 field, n = SISField(string[pos:(hdrlen + flen)], |
|
341 False) |
|
342 pos += n |
|
343 elif fkind != self.FTYPE_OPTIONAL: |
|
344 # No more data in string, raise an exception. |
|
345 raise ValueError("unexpected end-of-data") |
|
346 else: |
|
347 # Field from previous round present, re-use it. |
|
348 field = reuse |
|
349 reuse = None |
|
350 |
|
351 # Verify SISField type. |
|
352 if field != None: |
|
353 fname = fieldnumtoname[field.fieldtype] |
|
354 if fkind == self.FTYPE_ARRAY: |
|
355 if (fname != "SISArray" or |
|
356 fieldnumtoname[field.SISFieldType] != ffmt): |
|
357 # Wrong type of fields inside SISArray, |
|
358 # raise an exception. |
|
359 raise ValueError("invalid SISArray type") |
|
360 elif fkind == self.FTYPE_MANDATORY: |
|
361 if fname != ffmt: |
|
362 # Mandatory field missing, raise an exception. |
|
363 raise ValueError("mandatory field missing") |
|
364 elif fkind == self.FTYPE_OPTIONAL: |
|
365 if fname != ffmt: |
|
366 # Wrong type for optional field. Skip optional |
|
367 # field and re-use already parsed field on next |
|
368 # round. |
|
369 reuse = field |
|
370 field = None |
|
371 |
|
372 # Introduce field as an instance variable. |
|
373 self.__dict__[fattr] = field |
|
374 except (ValueError, KeyError, struct.error): |
|
375 if DEBUG > 0: |
|
376 # DEBUG: Raise a detailed exception. |
|
377 raise |
|
378 else: |
|
379 raise SISException("invalid '%s' structure" % |
|
380 self.__class__.__name__) |
|
381 |
|
382 def tostring(self): |
|
383 # Recursively create strings from subfields. |
|
384 fstrings = [None] |
|
385 totlen = 0 |
|
386 for fattr, fkind, ffmt in self.subfields: |
|
387 field = self.__dict__[fattr] |
|
388 if fkind == self.FTYPE_INTEGRAL: |
|
389 # Integer, pack it. |
|
390 try: |
|
391 string = struct.pack(ffmt, field) |
|
392 except: |
|
393 print "%s %s %s" % (self.__class__, ffmt, repr(field)) |
|
394 raise |
|
395 fstrings.append(string) |
|
396 totlen += len(string) |
|
397 else: |
|
398 if field == None: |
|
399 if fkind == self.FTYPE_OPTIONAL: |
|
400 # Optional field missing, skip it. |
|
401 pass |
|
402 else: |
|
403 # Mandatory field missing, raise an exception. |
|
404 raise SISException("field '%s' missing for '%s'" % |
|
405 (fattr, self.__dict__.__name__)) |
|
406 else: |
|
407 # Convert SISField to string. |
|
408 string = field.tostring() |
|
409 fstrings.append(string) |
|
410 totlen += len(string) |
|
411 |
|
412 try: |
|
413 del string # Try to free some memory early. |
|
414 except: |
|
415 pass |
|
416 |
|
417 fstrings[0] = makesisfieldheader(self.fieldtype, totlen) |
|
418 fstrings.append(makesisfieldpadding(totlen)) |
|
419 |
|
420 # TODO: Heavy on memory, optimize (new string type with concat.) |
|
421 return "".join(fstrings) |
|
422 |
|
423 class SISFieldSpecial(SISFieldBase): |
|
424 '''SISField base class for special fields (fields that do something |
|
425 special for the data they contain or the data is of variable length)''' |
|
426 def __init__(self, **kwds): |
|
427 # Set instance variables. |
|
428 SISFieldBase.__init__(self, **kwds) |
|
429 |
|
430 |
|
431 ############################################################################## |
|
432 # Special SISField subclasses |
|
433 ############################################################################## |
|
434 |
|
435 class SISString(SISFieldSpecial): |
|
436 '''UCS-2 (UTF-16LE) string''' |
|
437 def __init__(self, **kwds): |
|
438 # Set default values. |
|
439 self.String = None |
|
440 |
|
441 # Parse keyword parameters. |
|
442 SISFieldSpecial.__init__(self, **kwds) |
|
443 |
|
444 # Check that all required instance variables are set. |
|
445 if self.String == None: |
|
446 raise AttributeError("missing '%s' attribute for '%s'" % |
|
447 ("String", self.__class__.__name__)) |
|
448 |
|
449 def fromstring(self, string): |
|
450 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
451 self.fieldtype) |
|
452 self.String = string[hdrlen:(hdrlen + flen)].decode("UTF-16LE") |
|
453 |
|
454 def tostring(self): |
|
455 encstr = self.String.encode("UTF-16LE") |
|
456 return "%s%s%s" % (makesisfieldheader(self.fieldtype, len(encstr)), |
|
457 encstr, makesisfieldpadding(len(encstr))) |
|
458 |
|
459 def __str__(self): |
|
460 # Always return Unicode string. Let Python default encoding handle it. |
|
461 return u"<SISString '%s'>" % self.String |
|
462 |
|
463 class SISArray(SISFieldSpecial): |
|
464 '''An array of other SISFields, all of the same type''' |
|
465 def __init__(self, **kwds): |
|
466 # Set default values. |
|
467 self.SISFieldType = None # Invalid type, checked later. |
|
468 self.SISFields = [] |
|
469 |
|
470 # Parse keyword parameters. |
|
471 SISFieldSpecial.__init__(self, **kwds) |
|
472 |
|
473 # Make a copy of the supplied list |
|
474 # (caller may try to modify the original). |
|
475 self.SISFields = self.SISFields[:] |
|
476 |
|
477 # Allow type to be a string or number. |
|
478 self.SISFieldType = fieldnametonum.get(self.SISFieldType, |
|
479 self.SISFieldType) |
|
480 |
|
481 # Get type of first field if not given explicitly. |
|
482 if self.SISFieldType == None: |
|
483 if len(self.SISFields) > 0: |
|
484 self.SISFieldType = self.SISFields[0].fieldtype |
|
485 else: |
|
486 raise AttributeError("no SISFieldType given") |
|
487 |
|
488 # Check that all fields are of the same type. |
|
489 for f in self.SISFields: |
|
490 if f.fieldtype != self.SISFieldType: |
|
491 raise TypeError("SISFieldType mismatch for SISArray") |
|
492 |
|
493 def fromstring(self, string): |
|
494 # Parse field header. |
|
495 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
496 self.fieldtype) |
|
497 |
|
498 if flen < 4: |
|
499 raise SISException("not enough data for a complete SISArray header") |
|
500 |
|
501 # Get array type (type of SISFields in the array). |
|
502 atype = struct.unpack("<L", string[hdrlen:(hdrlen + 4)])[0] |
|
503 |
|
504 pos = hdrlen + 4 # Skip SISFieldType. |
|
505 totlen = hdrlen + flen |
|
506 fields = [] |
|
507 while pos < totlen: |
|
508 # Get first part of the SISField length. |
|
509 alen = struct.unpack("<L", string[pos:(pos+4)])[0] |
|
510 pos += 4 |
|
511 |
|
512 # Get rest of the SISField length, 31-bit or 63-bit. |
|
513 alen2 = None |
|
514 if alen & 0x8000000L: |
|
515 # 63-bit length, read rest of length. |
|
516 alen2 = struct.unpack("<L", string[pos:(pos+4)])[0] |
|
517 alen = (alen & 0x7ffffffL) | (alen2 << 31) |
|
518 pos += 4 |
|
519 |
|
520 # Calculate padding to 32-bit boundary. |
|
521 apadlen = ((alen + 3) & ~0x3L) - alen |
|
522 |
|
523 # Construct a valid SISField header and proper padding. |
|
524 fhdr = makesisfieldheader(atype, alen) |
|
525 fpad = makesisfieldpadding(alen) |
|
526 |
|
527 # Create a SISField. |
|
528 # TODO: Heavy on memory, optimize (new string type with concat.) |
|
529 field = SISField(fhdr + string[pos:(pos + alen)] + fpad) |
|
530 |
|
531 fields.append(field) |
|
532 |
|
533 pos += alen + apadlen |
|
534 |
|
535 self.SISFieldType = atype |
|
536 self.SISFields = fields |
|
537 |
|
538 def tostring(self): |
|
539 totlen = 4 # For the SISFieldType of the array |
|
540 fstrings = ["", struct.pack("<L", self.SISFieldType)] |
|
541 for f in self.SISFields: |
|
542 s = f.tostring()[4:] # Strip type code. |
|
543 fstrings.append(s) |
|
544 totlen += len(s) |
|
545 fstrings[0] = makesisfieldheader(self.fieldtype, totlen) |
|
546 fstrings.append(makesisfieldpadding(totlen)) # Not really necessary. |
|
547 return "".join(fstrings) |
|
548 |
|
549 def __str__(self): |
|
550 return "<SISArray of %d %s fields>" % ( |
|
551 len(self.SISFields), fieldnumtoname[self.SISFieldType]) |
|
552 |
|
553 # Standard list semantics ([n:m], len, append, insert, pop, del, iteration) |
|
554 def __getitem__(self, key): |
|
555 # Support older Python versions as well (v2.0 onwards). |
|
556 try: |
|
557 return self.SISFields[key] |
|
558 except TypeError: |
|
559 return self.SISFields[key.start:key.stop] |
|
560 |
|
561 def __setitem__(self, key, value): |
|
562 # Support older Python versions as well (v2.0 onwards). |
|
563 try: |
|
564 self.SISFields[key] = value |
|
565 except TypeError: |
|
566 self.SISFields[key.start:key.stop] = value |
|
567 |
|
568 def __delitem__(self, key): |
|
569 # Support older Python versions as well (v2.0 onwards). |
|
570 try: |
|
571 del self.SISFields[key] |
|
572 except TypeError: |
|
573 del self.SISFields[key.start:key.stop] |
|
574 |
|
575 # Not supported in Python v2.2, where __getitem__() is used instead. |
|
576 # def __iter__(self): |
|
577 # return self.SISFields.__iter__() |
|
578 |
|
579 def __len__(self): |
|
580 return self.SISFields.__len__() |
|
581 |
|
582 def append(self, obj): |
|
583 return self.SISFields.append(obj) |
|
584 |
|
585 def insert(self, idx, obj): |
|
586 return self.SISFields.insert(idx, obj) |
|
587 |
|
588 def extend(self, iterable): |
|
589 return self.SISFields.extend(iterable) |
|
590 |
|
591 def pop(self): |
|
592 return self.SISFields.pop() |
|
593 |
|
594 class SISCompressed(SISFieldSpecial): |
|
595 '''A compression wrapper for another SISField or raw data''' |
|
596 def __init__(self, **kwds): |
|
597 # Set default values. |
|
598 self.CompressionAlgorithm = None |
|
599 self.Data = None |
|
600 |
|
601 if "rawdatainside" in kwds: |
|
602 self.rawdatainside = kwds["rawdatainside"] |
|
603 del kwds["rawdatainside"] |
|
604 else: |
|
605 # Wrap a SISField by default. |
|
606 self.rawdatainside = False |
|
607 |
|
608 # Parse keyword parameters. |
|
609 SISFieldSpecial.__init__(self, **kwds) |
|
610 |
|
611 # Check that all required instance variables are set. |
|
612 if self.CompressionAlgorithm == None or self.Data == None: |
|
613 raise AttributeError("missing '%s' or '%s' attribute for '%s'" % |
|
614 ("CompressionAlgorithm", "Data", |
|
615 self.__class__.__name__)) |
|
616 |
|
617 # Check that the compression algorithm is a known one. |
|
618 if self.CompressionAlgorithm not in (ECompressAuto, ECompressNone, |
|
619 ECompressDeflate): |
|
620 raise TypeError("invalid CompressionAlgorithm '%d'" % |
|
621 self.CompressionAlgorithm) |
|
622 |
|
623 def fromstring(self, string): |
|
624 # Parse field header. |
|
625 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
626 self.fieldtype) |
|
627 |
|
628 if flen < 12: |
|
629 raise SISException("SISCompressed contents too short") |
|
630 |
|
631 compalgo, uncomplen = struct.unpack("<LQ", string[hdrlen:(hdrlen + 12)]) |
|
632 |
|
633 if compalgo == ECompressNone: |
|
634 # No compression, use as-is. |
|
635 dstring = string[(hdrlen + 12):(hdrlen + flen)] |
|
636 elif compalgo == ECompressDeflate: |
|
637 # RFC1950 (zlib header and checksum) compression, decompress. |
|
638 dstring = zlib.decompress(string[(hdrlen + 12):(hdrlen + flen)]) |
|
639 else: |
|
640 raise SISException("invalid SISCompressed algorithm '%d'" % |
|
641 compalgo) |
|
642 |
|
643 if uncomplen != len(dstring): |
|
644 raise SISException( |
|
645 "SISCompressed uncompressed data length mismatch") |
|
646 |
|
647 if self.rawdatainside: |
|
648 # Raw data inside |
|
649 self.Data = dstring |
|
650 else: |
|
651 # SISField inside |
|
652 if dstring != "": |
|
653 # Construct a SISField out of the decompressed data. |
|
654 self.Data = SISField(dstring) |
|
655 else: |
|
656 # Decompressed to nothing, duh! |
|
657 self.Data = None |
|
658 |
|
659 self.CompressionAlgorithm = compalgo |
|
660 |
|
661 def tostring(self): |
|
662 if self.rawdatainside: |
|
663 # Raw data inside |
|
664 string = self.Data |
|
665 else: |
|
666 # SISField inside |
|
667 if self.Data != None: |
|
668 # Compress the enclosed SISField. |
|
669 string = self.Data.tostring() |
|
670 else: |
|
671 # No data inside, compress an empty string. |
|
672 string = "" |
|
673 |
|
674 # Compress or not, depending on selected algorithm. |
|
675 if self.CompressionAlgorithm in (ECompressAuto, ECompressDeflate): |
|
676 cstring = zlib.compress(string, 9) # Maximum compression |
|
677 compalgo = ECompressDeflate |
|
678 |
|
679 if self.CompressionAlgorithm == ECompressAuto: |
|
680 if len(cstring) >= len(string): |
|
681 # Compression is not beneficial, use data as-is. |
|
682 cstring = string |
|
683 compalgo = ECompressNone |
|
684 elif self.CompressionAlgorithm == ECompressNone: |
|
685 # No compression, simply use data as-is. |
|
686 cstring = string |
|
687 compalgo = ECompressNone |
|
688 elif self.CompressionAlgorithm == ECompressDeflate: |
|
689 # Already handled above. |
|
690 pass |
|
691 else: |
|
692 raise SISException("invalid SISCompressed algorithm '%d'" % |
|
693 self.CompressionAlgorithm) |
|
694 |
|
695 # Construct the SISCompressed and SISField headers. |
|
696 chdr = struct.pack("<LQ", compalgo, len(string)) |
|
697 fhdr = makesisfieldheader(self.fieldtype, len(chdr) + len(cstring)) |
|
698 fpad = makesisfieldpadding(len(cstring)) |
|
699 |
|
700 del string # Try to free some memory early. |
|
701 |
|
702 # TODO: Heavy on memory, optimize (new string type with concat.) |
|
703 return "%s%s%s%s" % (fhdr, chdr, cstring, fpad) |
|
704 |
|
705 def __str__(self): |
|
706 dtype = (self.rawdatainside and "raw data") or "SISField" |
|
707 compalgo = ("not compressed", |
|
708 "compressed with \"deflate\"")[self.CompressionAlgorithm] |
|
709 return "<SISCompressed %s, %s>" % (dtype, compalgo) |
|
710 |
|
711 class SISBlob(SISFieldSpecial): |
|
712 '''Arbitrary binary data holder''' |
|
713 def __init__(self, **kwds): |
|
714 # Set default values. |
|
715 self.Data = None |
|
716 |
|
717 # Parse keyword parameters. |
|
718 SISFieldSpecial.__init__(self, **kwds) |
|
719 |
|
720 # Check that all required instance variables are set. |
|
721 if self.Data == None: |
|
722 raise AttributeError("missing '%s' attribute for '%s'" % |
|
723 ("Data", self.__class__.__name__)) |
|
724 |
|
725 def fromstring(self, string): |
|
726 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
727 self.fieldtype) |
|
728 |
|
729 # Does not get any simpler than this. |
|
730 self.Data = string[hdrlen:(hdrlen + flen)] |
|
731 |
|
732 def tostring(self): |
|
733 # TODO: Heavy on memory, optimize (new string type with concat.) |
|
734 return "%s%s%s" % (makesisfieldheader(self.fieldtype, len(self.Data)), |
|
735 self.Data, makesisfieldpadding(len(self.Data))) |
|
736 |
|
737 def __str__(self): |
|
738 return u"<SISBlob, %d bytes>" % len(self.Data) |
|
739 |
|
740 class SISFileData(SISFieldSpecial): |
|
741 '''File binary data holder (wraps a special SISCompressed SISField)''' |
|
742 def __init__(self, **kwds): |
|
743 # Create a special SISCompressed object. |
|
744 self.FileData = SISCompressed(CompressionAlgorithm = ECompressNone, |
|
745 Data = "", rawdatainside = True) |
|
746 |
|
747 # Parse keyword parameters. |
|
748 SISFieldSpecial.__init__(self, **kwds) |
|
749 |
|
750 def fromstring(self, string): |
|
751 # Parse field header. |
|
752 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
753 self.fieldtype) |
|
754 |
|
755 if flen < 20: |
|
756 raise SISException("SISFileData contents too short") |
|
757 |
|
758 self.FileData.fromstring(string[hdrlen:(hdrlen + flen)]) |
|
759 |
|
760 def tostring(self): |
|
761 string = self.FileData.tostring() |
|
762 |
|
763 # TODO: Heavy on memory, optimize (new string type with concat.) |
|
764 return "%s%s%s" % (makesisfieldheader(self.fieldtype, len(string)), |
|
765 string, makesisfieldpadding(len(string))) |
|
766 |
|
767 def getcompressedlength(self): |
|
768 # TODO: This is stupid! Compressing the data just |
|
769 # to find the resulting length is not very efficient... |
|
770 string = self.FileData.tostring() |
|
771 |
|
772 ftype, hdrlen, flen, padlen = parsesisfieldheader(string) |
|
773 |
|
774 return (flen - 12) # SISCompressed has an internal header of 12 bytes. |
|
775 |
|
776 def __str__(self): |
|
777 return "<SISFileData, %d bytes>" % len(self.FileData.Data) |
|
778 |
|
779 class SISCapabilities(SISFieldSpecial): |
|
780 '''Variable length capability bitmask''' |
|
781 def __init__(self, **kwds): |
|
782 # Set default values. |
|
783 self.Capabilities = None |
|
784 |
|
785 # Parse keyword parameters. |
|
786 SISFieldSpecial.__init__(self, **kwds) |
|
787 |
|
788 # Check that all required instance variables are set. |
|
789 if self.Capabilities == None: |
|
790 raise AttributeError("missing '%s' attribute for '%s'" % |
|
791 ("Capabilities", self.__class__.__name__)) |
|
792 |
|
793 # Check that the bitmask is a multiple of 32 bits. |
|
794 if len(self.Capabilities) & 3 != 0: |
|
795 raise SISException("capabilities length not a multiple of 32 bits") |
|
796 |
|
797 def fromstring(self, string): |
|
798 ftype, hdrlen, flen, padlen = parsesisfieldheader(string, |
|
799 self.fieldtype) |
|
800 |
|
801 caps = string[hdrlen:(hdrlen + flen)] |
|
802 if len(caps) & 3 != 0: |
|
803 raise SISException("capabilities length not a multiple of 32 bits") |
|
804 |
|
805 self.Capabilities = caps |
|
806 |
|
807 def tostring(self): |
|
808 if len(self.Capabilities) & 3 != 0: |
|
809 raise SISException("capabilities length not a multiple of 32 bits") |
|
810 |
|
811 return "%s%s" % (makesisfieldheader(self.fieldtype, |
|
812 len(self.Capabilities)), self.Capabilities) |
|
813 |
|
814 |
|
815 ############################################################################## |
|
816 # Normal SISField subclasses (fields containing only other fields and integers) |
|
817 ############################################################################## |
|
818 |
|
819 class SISVersion(SISFieldNormal): |
|
820 '''Major, minor and build numbers''' |
|
821 def __init__(self, **kwds): |
|
822 self.subfields = [ |
|
823 ("Major", self.FTYPE_INTEGRAL, "<l"), |
|
824 ("Minor", self.FTYPE_INTEGRAL, "<l"), |
|
825 ("Build", self.FTYPE_INTEGRAL, "<l")] |
|
826 |
|
827 # Parse keyword parameters. |
|
828 SISFieldNormal.__init__(self, **kwds) |
|
829 |
|
830 def __str__(self): |
|
831 return "<SISVersion %d, %d, %d>" % (self.Major, self.Minor, self.Build) |
|
832 |
|
833 class SISVersionRange(SISFieldNormal): |
|
834 '''A range of two SISVersions, or optionally only one''' |
|
835 def __init__(self, **kwds): |
|
836 self.subfields = [ |
|
837 ("FromVersion", self.FTYPE_MANDATORY, "SISVersion"), |
|
838 ("ToVersion", self.FTYPE_OPTIONAL, "SISVersion")] |
|
839 |
|
840 # Parse keyword parameters. |
|
841 SISFieldNormal.__init__(self, **kwds) |
|
842 |
|
843 def __str__(self): |
|
844 ver1 = "from %d, %d, %d" % (self.FromVersion.Major, |
|
845 self.FromVersion.Minor, |
|
846 self.FromVersion.Build) |
|
847 ver2 = "onwards" |
|
848 if self.ToVersion: |
|
849 ver2 = "to %d, %d, %d" % (self.ToVersion.Major, |
|
850 self.ToVersion.Minor, |
|
851 self.ToVersion.Build) |
|
852 return "<SISVersionRange %s %s>" % (ver1, ver2) |
|
853 |
|
854 class SISDate(SISFieldNormal): |
|
855 '''Year, month (0-11) and day (1-31)''' |
|
856 def __init__(self, **kwds): |
|
857 self.subfields = [ |
|
858 ("Year", self.FTYPE_INTEGRAL, "<H"), |
|
859 ("Month", self.FTYPE_INTEGRAL, "<B"), |
|
860 ("Day", self.FTYPE_INTEGRAL, "<B")] |
|
861 |
|
862 # Parse keyword parameters. |
|
863 SISFieldNormal.__init__(self, **kwds) |
|
864 |
|
865 def __str__(self): |
|
866 return "<SISDate %04d-%02d-%02d>" % (self.Year, self.Month + 1, |
|
867 self.Day) |
|
868 |
|
869 class SISTime(SISFieldNormal): |
|
870 '''Hours (0-23), minutes (0-59) and seconds (0-59)''' |
|
871 def __init__(self, **kwds): |
|
872 self.subfields = [ |
|
873 ("Hours", self.FTYPE_INTEGRAL, "<B"), |
|
874 ("Minutes", self.FTYPE_INTEGRAL, "<B"), |
|
875 ("Seconds", self.FTYPE_INTEGRAL, "<B")] |
|
876 |
|
877 # Parse keyword parameters. |
|
878 SISFieldNormal.__init__(self, **kwds) |
|
879 |
|
880 def __str__(self): |
|
881 return "<SISTime %02d:%02d:%02d>" % ( |
|
882 self.Hours, self.Minutes, self.Seconds) |
|
883 |
|
884 class SISDateTime(SISFieldNormal): |
|
885 '''A bundled SISDate and a SISTime''' |
|
886 def __init__(self, **kwds): |
|
887 self.subfields = [ |
|
888 ("Date", self.FTYPE_MANDATORY, "SISDate"), |
|
889 ("Time", self.FTYPE_MANDATORY, "SISTime")] |
|
890 |
|
891 # Parse keyword parameters. |
|
892 SISFieldNormal.__init__(self, **kwds) |
|
893 |
|
894 def __str__(self): |
|
895 return "<SISDateTime %04d-%02d-%02d %02d:%02d:%02d>" % ( |
|
896 self.Date.Year, self.Date.Month, self.Date.Day, |
|
897 self.Time.Hours, self.Time.Minutes, self.Time.Seconds) |
|
898 |
|
899 class SISUid(SISFieldNormal): |
|
900 '''A 32-bit Symbian OS UID''' |
|
901 def __init__(self, **kwds): |
|
902 self.subfields = [("UID1", self.FTYPE_INTEGRAL, "<L")] |
|
903 |
|
904 # Parse keyword parameters. |
|
905 SISFieldNormal.__init__(self, **kwds) |
|
906 |
|
907 def __str__(self): |
|
908 return "<SISUid 0x%08x>" % self.UID1 |
|
909 |
|
910 class SISLanguage(SISFieldNormal): |
|
911 '''A Symbian OS language number''' |
|
912 def __init__(self, **kwds): |
|
913 self.subfields = [("Language", self.FTYPE_INTEGRAL, "<L")] |
|
914 |
|
915 # Parse keyword parameters. |
|
916 SISFieldNormal.__init__(self, **kwds) |
|
917 |
|
918 def __str__(self): |
|
919 try: |
|
920 lname = symbianutil.langnumtoname[self.Language] |
|
921 except KeyError: |
|
922 lname = "Unknown" |
|
923 return "<SISLanguage %d (%s)>" % (self.Language, lname) |
|
924 |
|
925 class SISContents(SISFieldNormal): |
|
926 '''The root type of a SIS file''' |
|
927 def __init__(self, **kwds): |
|
928 self.subfields = [ |
|
929 ("ControllerChecksum", self.FTYPE_OPTIONAL, "SISControllerChecksum"), |
|
930 ("DataChecksum", self.FTYPE_OPTIONAL, "SISDataChecksum"), |
|
931 ("Controller", self.FTYPE_MANDATORY, "SISCompressed"), |
|
932 ("Data", self.FTYPE_MANDATORY, "SISData")] |
|
933 |
|
934 # Parse keyword parameters. |
|
935 SISFieldNormal.__init__(self, **kwds) |
|
936 |
|
937 def __str__(self): |
|
938 cksum1 = "N/A" |
|
939 if self.ControllerChecksum: |
|
940 cksum1 = "0x%04x" % self.ControllerChecksum.Checksum |
|
941 cksum2 = "N/A" |
|
942 if self.DataChecksum: |
|
943 cksum2 = "0x%04x" % self.DataChecksum.Checksum |
|
944 return "<SISContents, checksums: %s, %s>" % (cksum1, cksum2) |
|
945 |
|
946 class SISController(SISFieldNormal): |
|
947 '''SIS file metadata''' |
|
948 def __init__(self, **kwds): |
|
949 # Convert a list of signatures to separate parameters |
|
950 # so that base class constructor can parse them. |
|
951 # Support upto MAXNUMSIGNATURES signature certificates. |
|
952 if "Signatures" in kwds: |
|
953 signatures = kwds["Signatures"] |
|
954 if len(signatures) > MAXNUMSIGNATURES: |
|
955 raise ValueError("too many signatures for SISController") |
|
956 for n in xrange(len(signatures)): |
|
957 kwds["Signature%d" % n] = signatures[n] |
|
958 del kwds["Signatures"] |
|
959 |
|
960 # DataIndex is really not optional. However, calculating |
|
961 # signatures require that SISController strings without |
|
962 # the DataIndex field can be generated. |
|
963 self.subfields = [ |
|
964 ("Info", self.FTYPE_MANDATORY, "SISInfo"), |
|
965 ("Options", self.FTYPE_MANDATORY, "SISSupportedOptions"), |
|
966 ("Languages", self.FTYPE_MANDATORY, "SISSupportedLanguages"), |
|
967 ("Prerequisites", self.FTYPE_MANDATORY, "SISPrerequisites"), |
|
968 ("Properties", self.FTYPE_MANDATORY, "SISProperties"), |
|
969 ("Logo", self.FTYPE_OPTIONAL, "SISLogo"), |
|
970 ("InstallBlock", self.FTYPE_MANDATORY, "SISInstallBlock"), |
|
971 ("Signature0", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
972 ("Signature1", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
973 ("Signature2", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
974 ("Signature3", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
975 ("Signature4", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
976 ("Signature5", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
977 ("Signature6", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
978 ("Signature7", self.FTYPE_OPTIONAL, "SISSignatureCertificateChain"), |
|
979 ("DataIndex", self.FTYPE_OPTIONAL, "SISDataIndex")] |
|
980 |
|
981 # Parse keyword parameters. |
|
982 SISFieldNormal.__init__(self, **kwds) |
|
983 |
|
984 # Helper routines to deal with the special signature fields. |
|
985 def getsignatures(self): |
|
986 # Return signatures as a list. |
|
987 signatures = [] |
|
988 for n in xrange(MAXNUMSIGNATURES): |
|
989 sig = self.__dict__["Signature%d" % n] |
|
990 if sig != None: |
|
991 signatures.append(sig) |
|
992 return signatures |
|
993 |
|
994 def setsignatures(self, signatures): |
|
995 # Replace signatures with the ones from list. If there are |
|
996 # less than MAXNUMSIGNATURES signatures in the list, the |
|
997 # rest are erased. To erase all signatures, call |
|
998 # controller.setsignatures([]). |
|
999 numsig = len(signatures) |
|
1000 if numsig > MAXNUMSIGNATURES: |
|
1001 raise ValueError("too many signatures for SISController") |
|
1002 for n in xrange(MAXNUMSIGNATURES): |
|
1003 if n < numsig: |
|
1004 sig = signatures[n] |
|
1005 else: |
|
1006 sig = None |
|
1007 self.__dict__["Signature%d" % n] = sig |
|
1008 |
|
1009 class SISInfo(SISFieldNormal): |
|
1010 '''Information about the SIS file''' |
|
1011 def __init__(self, **kwds): |
|
1012 self.subfields = [ |
|
1013 ("UID", self.FTYPE_MANDATORY, "SISUid"), |
|
1014 ("VendorUniqueName", self.FTYPE_MANDATORY, "SISString"), |
|
1015 ("Names", self.FTYPE_ARRAY, "SISString"), |
|
1016 ("VendorNames", self.FTYPE_ARRAY, "SISString"), |
|
1017 ("Version", self.FTYPE_MANDATORY, "SISVersion"), |
|
1018 ("CreationTime", self.FTYPE_MANDATORY, "SISDateTime"), |
|
1019 ("InstallType", self.FTYPE_INTEGRAL, "<B"), |
|
1020 ("InstallFlags", self.FTYPE_INTEGRAL, "<B")] |
|
1021 |
|
1022 # Parse keyword parameters. |
|
1023 SISFieldNormal.__init__(self, **kwds) |
|
1024 |
|
1025 class SISSupportedLanguages(SISFieldNormal): |
|
1026 '''An array of SISLanguage fields''' |
|
1027 def __init__(self, **kwds): |
|
1028 self.subfields = [("Languages", self.FTYPE_ARRAY, "SISLanguage")] |
|
1029 |
|
1030 # Parse keyword parameters. |
|
1031 SISFieldNormal.__init__(self, **kwds) |
|
1032 |
|
1033 class SISSupportedOptions(SISFieldNormal): |
|
1034 '''An array of SISSupportedOption fields, user selectable options''' |
|
1035 def __init__(self, **kwds): |
|
1036 self.subfields = [("Options", self.FTYPE_ARRAY, "SISSupportedOption")] |
|
1037 |
|
1038 # Parse keyword parameters. |
|
1039 SISFieldNormal.__init__(self, **kwds) |
|
1040 |
|
1041 class SISPrerequisites(SISFieldNormal): |
|
1042 '''An array of SISDependency fields''' |
|
1043 def __init__(self, **kwds): |
|
1044 self.subfields = [ |
|
1045 ("TargetDevices", self.FTYPE_ARRAY, "SISDependency"), |
|
1046 ("Dependencies", self.FTYPE_ARRAY, "SISDependency")] |
|
1047 |
|
1048 # Parse keyword parameters. |
|
1049 SISFieldNormal.__init__(self, **kwds) |
|
1050 |
|
1051 class SISDependency(SISFieldNormal): |
|
1052 '''Versioned SIS package dependency''' |
|
1053 def __init__(self, **kwds): |
|
1054 self.subfields = [ |
|
1055 ("UID", self.FTYPE_MANDATORY, "SISUid"), |
|
1056 ("VersionRange", self.FTYPE_OPTIONAL, "SISVersionRange"), |
|
1057 ("DependencyNames", self.FTYPE_ARRAY, "SISString")] |
|
1058 |
|
1059 # Parse keyword parameters. |
|
1060 SISFieldNormal.__init__(self, **kwds) |
|
1061 |
|
1062 class SISProperties(SISFieldNormal): |
|
1063 '''An array of SISProperty fields''' |
|
1064 def __init__(self, **kwds): |
|
1065 self.subfields = [("Properties", self.FTYPE_ARRAY, "SISProperty")] |
|
1066 |
|
1067 # Parse keyword parameters. |
|
1068 SISFieldNormal.__init__(self, **kwds) |
|
1069 |
|
1070 class SISProperty(SISFieldNormal): |
|
1071 '''Key:value pair''' |
|
1072 def __init__(self, **kwds): |
|
1073 self.subfields = [ |
|
1074 ("Key", self.FTYPE_INTEGRAL, "<l"), |
|
1075 ("Value", self.FTYPE_INTEGRAL, "<l")] |
|
1076 |
|
1077 # Parse keyword parameters. |
|
1078 SISFieldNormal.__init__(self, **kwds) |
|
1079 |
|
1080 # SISSignatures: Legacy field type, not used |
|
1081 # |
|
1082 # class SISSignatures(SISFieldNormal): |
|
1083 # pass |
|
1084 |
|
1085 class SISCertificateChain(SISFieldNormal): |
|
1086 '''ASN.1 encoded X509 certificate chain''' |
|
1087 def __init__(self, **kwds): |
|
1088 self.subfields = [("CertificateData", self.FTYPE_MANDATORY, "SISBlob")] |
|
1089 |
|
1090 # Parse keyword parameters. |
|
1091 SISFieldNormal.__init__(self, **kwds) |
|
1092 |
|
1093 class SISLogo(SISFieldNormal): |
|
1094 '''A logo file to display during installation''' |
|
1095 def __init__(self, **kwds): |
|
1096 self.subfields = [ |
|
1097 ("LogoFile", self.FTYPE_MANDATORY, "SISFileDescription")] |
|
1098 |
|
1099 # Parse keyword parameters. |
|
1100 SISFieldNormal.__init__(self, **kwds) |
|
1101 |
|
1102 class SISFileDescription(SISFieldNormal): |
|
1103 '''Information about an enclosed file''' |
|
1104 def __init__(self, **kwds): |
|
1105 self.subfields = [ |
|
1106 ("Target", self.FTYPE_MANDATORY, "SISString"), |
|
1107 ("MIMEType", self.FTYPE_MANDATORY, "SISString"), |
|
1108 ("Capabilities", self.FTYPE_OPTIONAL, "SISCapabilities"), |
|
1109 ("Hash", self.FTYPE_MANDATORY, "SISHash"), |
|
1110 ("Operation", self.FTYPE_INTEGRAL, "<L"), |
|
1111 ("OperationOptions", self.FTYPE_INTEGRAL, "<L"), |
|
1112 ("Length", self.FTYPE_INTEGRAL, "<Q"), |
|
1113 ("UncompressedLength", self.FTYPE_INTEGRAL, "<Q"), |
|
1114 ("FileIndex", self.FTYPE_INTEGRAL, "<L")] |
|
1115 |
|
1116 # Parse keyword parameters. |
|
1117 SISFieldNormal.__init__(self, **kwds) |
|
1118 |
|
1119 class SISHash(SISFieldNormal): |
|
1120 '''File hash''' |
|
1121 def __init__(self, **kwds): |
|
1122 self.subfields = [ |
|
1123 ("HashAlgorithm", self.FTYPE_INTEGRAL, "<L"), |
|
1124 ("HashData", self.FTYPE_MANDATORY, "SISBlob")] |
|
1125 |
|
1126 # Parse keyword parameters. |
|
1127 SISFieldNormal.__init__(self, **kwds) |
|
1128 |
|
1129 # Check that the hash algorithm is a supported one (SHA-1 only for now). |
|
1130 if self.HashAlgorithm != ESISHashAlgSHA1: |
|
1131 raise SISException("invalid SISHash algorithm '%d'" % |
|
1132 self.HashAlgorithm) |
|
1133 |
|
1134 class SISIf(SISFieldNormal): |
|
1135 '''An "if"-branch of a conditional expression''' |
|
1136 def __init__(self, **kwds): |
|
1137 self.subfields = [ |
|
1138 ("Expression", self.FTYPE_MANDATORY, "SISExpression"), |
|
1139 ("InstallBlock", self.FTYPE_MANDATORY, "SISInstallBlock"), |
|
1140 ("ElseIfs", self.FTYPE_ARRAY, "SISElseIf")] |
|
1141 |
|
1142 # Parse keyword parameters. |
|
1143 SISFieldNormal.__init__(self, **kwds) |
|
1144 |
|
1145 class SISElseIf(SISFieldNormal): |
|
1146 '''An "else if"-branch of a conditional expression''' |
|
1147 def __init__(self, **kwds): |
|
1148 self.subfields = [ |
|
1149 ("Expression", self.FTYPE_MANDATORY, "SISExpression"), |
|
1150 ("InstallBlock", self.FTYPE_MANDATORY, "SISInstallBlock")] |
|
1151 |
|
1152 # Parse keyword parameters. |
|
1153 SISFieldNormal.__init__(self, **kwds) |
|
1154 |
|
1155 class SISInstallBlock(SISFieldNormal): |
|
1156 '''A conditional file installation hierarchy''' |
|
1157 def __init__(self, **kwds): |
|
1158 self.subfields = [ |
|
1159 ("Files", self.FTYPE_ARRAY, "SISFileDescription"), |
|
1160 ("EmbeddedSISFiles", self.FTYPE_ARRAY, "SISController"), |
|
1161 ("IfBlocks", self.FTYPE_ARRAY, "SISIf")] |
|
1162 |
|
1163 # Parse keyword parameters. |
|
1164 SISFieldNormal.__init__(self, **kwds) |
|
1165 |
|
1166 class SISExpression(SISFieldNormal): |
|
1167 '''A conditional expression''' |
|
1168 def __init__(self, **kwds): |
|
1169 self.subfields = [ |
|
1170 ("Operator", self.FTYPE_INTEGRAL, "<L"), |
|
1171 ("IntegerValue", self.FTYPE_INTEGRAL, "<l"), |
|
1172 ("StringValue", self.FTYPE_OPTIONAL, "SISString"), |
|
1173 ("LeftExpression", self.FTYPE_OPTIONAL, "SISExpression"), |
|
1174 ("RightExpression", self.FTYPE_OPTIONAL, "SISExpression")] |
|
1175 |
|
1176 # Parse keyword parameters. |
|
1177 SISFieldNormal.__init__(self, **kwds) |
|
1178 |
|
1179 class SISData(SISFieldNormal): |
|
1180 '''An array of SISDataUnit fields''' |
|
1181 def __init__(self, **kwds): |
|
1182 self.subfields = [("DataUnits", self.FTYPE_ARRAY, "SISDataUnit")] |
|
1183 |
|
1184 # Parse keyword parameters. |
|
1185 SISFieldNormal.__init__(self, **kwds) |
|
1186 |
|
1187 class SISDataUnit(SISFieldNormal): |
|
1188 '''An array of SISFileData fields''' |
|
1189 def __init__(self, **kwds): |
|
1190 self.subfields = [("FileData", self.FTYPE_ARRAY, "SISFileData")] |
|
1191 |
|
1192 # Parse keyword parameters. |
|
1193 SISFieldNormal.__init__(self, **kwds) |
|
1194 |
|
1195 class SISSupportedOption(SISFieldNormal): |
|
1196 '''An array of supported option names in different languages''' |
|
1197 def __init__(self, **kwds): |
|
1198 self.subfields = [("Names", self.FTYPE_ARRAY, "SISString")] |
|
1199 |
|
1200 # Parse keyword parameters. |
|
1201 SISFieldNormal.__init__(self, **kwds) |
|
1202 |
|
1203 class SISControllerChecksum(SISFieldNormal): |
|
1204 '''CCITT CRC-16 of the SISController SISField''' |
|
1205 def __init__(self, **kwds): |
|
1206 self.subfields = [("Checksum", self.FTYPE_INTEGRAL, "<H")] |
|
1207 |
|
1208 # Parse keyword parameters. |
|
1209 SISFieldNormal.__init__(self, **kwds) |
|
1210 |
|
1211 def __str__(self): |
|
1212 return "<SISControllerChecksum 0x%04x>" % self.Checksum |
|
1213 |
|
1214 class SISDataChecksum(SISFieldNormal): |
|
1215 '''CCITT CRC-16 of the SISData SISField''' |
|
1216 def __init__(self, **kwds): |
|
1217 self.subfields = [("Checksum", self.FTYPE_INTEGRAL, "<H")] |
|
1218 |
|
1219 # Parse keyword parameters. |
|
1220 SISFieldNormal.__init__(self, **kwds) |
|
1221 |
|
1222 def __str__(self): |
|
1223 return "<SISDataChecksum 0x%04x>" % self.Checksum |
|
1224 |
|
1225 class SISSignature(SISFieldNormal): |
|
1226 '''Cryptographic signature of preceding SIS metadata''' |
|
1227 def __init__(self, **kwds): |
|
1228 self.subfields = [ |
|
1229 ("SignatureAlgorithm", self.FTYPE_MANDATORY, "SISSignatureAlgorithm"), |
|
1230 ("SignatureData", self.FTYPE_MANDATORY, "SISBlob")] |
|
1231 |
|
1232 # Parse keyword parameters. |
|
1233 SISFieldNormal.__init__(self, **kwds) |
|
1234 |
|
1235 class SISSignatureAlgorithm(SISFieldNormal): |
|
1236 '''Object identifier string of a signature algorithm''' |
|
1237 def __init__(self, **kwds): |
|
1238 self.subfields = [ |
|
1239 ("AlgorithmIdentifier", self.FTYPE_MANDATORY, "SISString")] |
|
1240 |
|
1241 # Parse keyword parameters. |
|
1242 SISFieldNormal.__init__(self, **kwds) |
|
1243 |
|
1244 def __str__(self): |
|
1245 return "<SISSignatureAlgorithm '%s'>" % ( |
|
1246 self.AlgorithmIdentifier.String) |
|
1247 |
|
1248 class SISSignatureCertificateChain(SISFieldNormal): |
|
1249 '''An array of SISSignatures and a SIScertificateChain |
|
1250 for signature validation''' |
|
1251 def __init__(self, **kwds): |
|
1252 self.subfields = [ |
|
1253 ("Signatures", self.FTYPE_ARRAY, "SISSignature"), |
|
1254 ("CertificateChain", self.FTYPE_MANDATORY, "SISCertificateChain")] |
|
1255 |
|
1256 # Parse keyword parameters. |
|
1257 SISFieldNormal.__init__(self, **kwds) |
|
1258 |
|
1259 class SISDataIndex(SISFieldNormal): |
|
1260 '''Data index for files belonging to a SISController''' |
|
1261 def __init__(self, **kwds): |
|
1262 self.subfields = [("DataIndex", self.FTYPE_INTEGRAL, "<L")] |
|
1263 |
|
1264 # Parse keyword parameters. |
|
1265 SISFieldNormal.__init__(self, **kwds) |
|
1266 |
|
1267 def __str__(self): |
|
1268 return "<SISDataIndex %d>" % self.DataIndex |
|
1269 |
|
1270 |
|
1271 ############################################################################## |
|
1272 # Utility dictionaries |
|
1273 ############################################################################## |
|
1274 |
|
1275 fieldinfo = [ |
|
1276 (1, "SISString", SISString), |
|
1277 (2, "SISArray", SISArray), |
|
1278 (3, "SISCompressed", SISCompressed), |
|
1279 (4, "SISVersion", SISVersion), |
|
1280 (5, "SISVersionRange", SISVersionRange), |
|
1281 (6, "SISDate", SISDate), |
|
1282 (7, "SISTime", SISTime), |
|
1283 (8, "SISDateTime", SISDateTime), |
|
1284 (9, "SISUid", SISUid), |
|
1285 (11, "SISLanguage", SISLanguage), |
|
1286 (12, "SISContents", SISContents), |
|
1287 (13, "SISController", SISController), |
|
1288 (14, "SISInfo", SISInfo), |
|
1289 (15, "SISSupportedLanguages", SISSupportedLanguages), |
|
1290 (16, "SISSupportedOptions", SISSupportedOptions), |
|
1291 (17, "SISPrerequisites", SISPrerequisites), |
|
1292 (18, "SISDependency", SISDependency), |
|
1293 (19, "SISProperties", SISProperties), |
|
1294 (20, "SISProperty", SISProperty), |
|
1295 # SISSignatures: Legacy field type, not used |
|
1296 # (21, "SISSignatures", SISSignatures), |
|
1297 (22, "SISCertificateChain", SISCertificateChain), |
|
1298 (23, "SISLogo", SISLogo), |
|
1299 (24, "SISFileDescription", SISFileDescription), |
|
1300 (25, "SISHash", SISHash), |
|
1301 (26, "SISIf", SISIf), |
|
1302 (27, "SISElseIf", SISElseIf), |
|
1303 (28, "SISInstallBlock", SISInstallBlock), |
|
1304 (29, "SISExpression", SISExpression), |
|
1305 (30, "SISData", SISData), |
|
1306 (31, "SISDataUnit", SISDataUnit), |
|
1307 (32, "SISFileData", SISFileData), |
|
1308 (33, "SISSupportedOption", SISSupportedOption), |
|
1309 (34, "SISControllerChecksum", SISControllerChecksum), |
|
1310 (35, "SISDataChecksum", SISDataChecksum), |
|
1311 (36, "SISSignature", SISSignature), |
|
1312 (37, "SISBlob", SISBlob), |
|
1313 (38, "SISSignatureAlgorithm", SISSignatureAlgorithm), |
|
1314 (39, "SISSignatureCertificateChain", SISSignatureCertificateChain), |
|
1315 (40, "SISDataIndex", SISDataIndex), |
|
1316 (41, "SISCapabilities", SISCapabilities) |
|
1317 ] |
|
1318 |
|
1319 fieldnumtoclass = dict([(num, klass) for num, name, klass in fieldinfo]) |
|
1320 fieldnametonum = dict([(name, num) for num, name, klass in fieldinfo]) |
|
1321 fieldnumtoname = dict([(num, name) for num, name, klass in fieldinfo]) |