src/tools/py2sis/ensymble/sisfile.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     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)