def run(pgmname, argv): global debug # Determine system character encodings. try: # getdefaultlocale() may sometimes return None. # Fall back to ASCII encoding in that case. terminalenc = locale.getdefaultlocale()[1] + "" except TypeError: # Invalid locale, fall back to ASCII terminal encoding. terminalenc = "ascii" try: # sys.getfilesystemencoding() was introduced in Python v2.3 and # it can sometimes return None. Fall back to ASCII if something # goes wrong. filesystemenc = sys.getfilesystemencoding() + "" except (AttributeError, TypeError): filesystemenc = "ascii" try: gopt = getopt.gnu_getopt except: # Python <v2.3, GNU-style parameter ordering not supported. gopt = getopt.getopt # Parse command line arguments. short_opts = "ua:k:p:b:d:e:vh" long_opts = [ "unsign", "cert=", "privkey=", "passphrase=", "execaps=", "dllcaps=", "encoding=", "verbose", "debug", "help" ] args = gopt(argv, short_opts, long_opts) opts = dict(args[0]) pargs = args[1] if len(pargs) == 0: raise ValueError("no SIS file name given") # Override character encoding of command line and filesystem. encs = opts.get("--encoding", opts.get("-e", "%s,%s" % (terminalenc, filesystemenc))) try: terminalenc, filesystemenc = encs.split(",") except (ValueError, TypeError): raise ValueError("invalid encoding string '%s'" % encs) # Get input SIS file name. infile = pargs[0].decode(terminalenc).encode(filesystemenc) # Determine output SIS file name. if len(pargs) == 1: # No output file, overwrite original SIS file. outfile = infile elif len(pargs) == 2: outfile = pargs[1].decode(terminalenc).encode(filesystemenc) if os.path.isdir(outfile): # Output to directory, use input file name. outfile = os.path.join(outfile, os.path.basename(infile)) else: raise ValueError("wrong number of arguments") # Get unsign option. unsign = False if "--unsign" in opts.keys() or "-u" in opts.keys(): unsign = True # Get certificate and its private key file names. cert = opts.get("--cert", opts.get("-a", None)) privkey = opts.get("--privkey", opts.get("-k", None)) if unsign: if cert != None or privkey != None: raise ValueError("certificate or private key given when unsigning") elif cert != None and privkey != None: # Convert file names from terminal encoding to filesystem encoding. cert = cert.decode(terminalenc).encode(filesystemenc) privkey = privkey.decode(terminalenc).encode(filesystemenc) # Read certificate file. f = file(cert, "rb") certdata = f.read(MAXCERTIFICATELENGTH + 1) f.close() if len(certdata) > MAXCERTIFICATELENGTH: raise ValueError("certificate file too large") # Read private key file. f = file(privkey, "rb") privkeydata = f.read(MAXPRIVATEKEYLENGTH + 1) f.close() if len(privkeydata) > MAXPRIVATEKEYLENGTH: raise ValueError("private key file too large") elif cert == None and privkey == None: # No certificate given, use the Ensymble default certificate. # defaultcert.py is not imported when not needed. This speeds # up program start-up a little. import defaultcert certdata = defaultcert.cert privkeydata = defaultcert.privkey print( "%s: warning: no certificate given, using " "insecure built-in one" % pgmname) else: raise ValueError("missing certificate or private key") # Get pass phrase. Pass phrase remains in terminal encoding. passphrase = opts.get("--passphrase", opts.get("-p", None)) if passphrase == None and privkey != None: # Private key given without "--passphrase" option, ask it. if sys.stdin.isatty(): # Standard input is a TTY, ask password interactively. passphrase = getpass.getpass("Enter private key pass phrase:") else: # Not connected to a TTY, read stdin non-interactively instead. passphrase = sys.stdin.read(MAXPASSPHRASELENGTH + 1) if len(passphrase) > MAXPASSPHRASELENGTH: raise ValueError("pass phrase too long") passphrase = passphrase.strip() # Get EXE capabilities and normalize the names. execaps = opts.get("--execaps", opts.get("-b", None)) if execaps != None: execapmask = symbianutil.capstringtomask(execaps) execaps = symbianutil.capmasktostring(execapmask, True) else: execapmask = None # Get DLL capabilities and normalize the names. dllcaps = opts.get("--dllcaps", opts.get("-d", None)) if dllcaps != None: dllcapmask = symbianutil.capstringtomask(dllcaps) dllcaps = symbianutil.capmasktostring(dllcapmask, True) else: dllcapmask = None # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # infile Input SIS file name, filesystemenc encoded # outfile Output SIS file name, filesystemenc encoded # cert Certificate in PEM format # privkey Certificate private key in PEM format # passphrase Pass phrase of priv. key, terminalenc encoded string # execaps, execapmask Capability names and bitmask for EXE files or None # dllcaps, dllcapmask Capability names and bitmask for DLL files or None # verbose Boolean indicating verbose terminal output if verbose: print print "Input SIS file %s" % ( infile.decode(filesystemenc).encode(terminalenc)) print "Output SIS file %s" % ( outfile.decode(filesystemenc).encode(terminalenc)) if unsign: print "Remove signatures Yes" else: print "Certificate %s" % ( (cert and cert.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Private key %s" % ( (privkey and privkey.decode(filesystemenc).encode(terminalenc)) or "<default>") if execaps != None: print "EXE capabilities 0x%x (%s)" % (execapmask, execaps) else: print "EXE capabilities <not set>" if dllcaps != None: print "DLL capabilities 0x%x (%s)" % (dllcapmask, dllcaps) else: print "DLL capabilities <not set>" print # Read input SIS file. f = file(infile, "rb") instring = f.read(MAXSISFILESIZE + 1) f.close() if len(instring) > MAXSISFILESIZE: raise ValueError("input SIS file too large") # Convert input SIS file to SISFields. uids = instring[:16] # UID1, UID2, UID3 and UIDCRC insis, rlen = sisfield.SISField(instring[16:], False) # Ignore extra bytes after SIS file. if len(instring) > (rlen + 16): print("%s: warning: %d extra bytes after input SIS file (ignored)" % (pgmname, (len(instring) - (rlen + 16)))) # Try to release some memory early. del instring # Check if there are embedded SIS files. Warn if there are. if len(insis.Data.DataUnits) > 1: print( "%s: warning: input SIS file contains " "embedded SIS files (ignored)" % pgmname) # Modify EXE- and DLL-files according to new capabilities. if execaps != None or dllcaps != None: # Generate FileIndex to SISFileDescription mapping. sisfiledescmap = mapfiledesc(insis.Controller.Data.InstallBlock) exemods, dllmods = modifycaps(insis, sisfiledescmap, execapmask, dllcapmask) print( "%s: %d EXE-files will be modified, " "%d DLL-files will be modified" % (pgmname, exemods, dllmods)) # Temporarily remove the SISDataIndex SISField from SISController. ctrlfield = insis.Controller.Data didxfield = ctrlfield.DataIndex ctrlfield.DataIndex = None if not unsign: # Remove old signatures. if len(ctrlfield.getsignatures()) > 0: print( "%s: warning: removing old signatures " "from input SIS file" % pgmname) ctrlfield.setsignatures([]) # Calculate a signature of the modified SISController. string = ctrlfield.tostring() string = sisfield.stripheaderandpadding(string) signature, algoid = sisfile.signstring(privkeydata, passphrase, string) # Create a SISCertificateChain SISField from certificate data. sf1 = sisfield.SISBlob(Data=cryptutil.certtobinary(certdata)) sf2 = sisfield.SISCertificateChain(CertificateData=sf1) # Create a SISSignature SISField from calculated signature. sf3 = sisfield.SISString(String=algoid) sf4 = sisfield.SISSignatureAlgorithm(AlgorithmIdentifier=sf3) sf5 = sisfield.SISBlob(Data=signature) sf6 = sisfield.SISSignature(SignatureAlgorithm=sf4, SignatureData=sf5) # Create a new SISSignatureCertificateChain SISField. sa = sisfield.SISArray(SISFields=[sf6]) sf7 = sisfield.SISSignatureCertificateChain(Signatures=sa, CertificateChain=sf2) # Set new certificate. ctrlfield.Signature0 = sf7 else: # Unsign, remove old signatures. ctrlfield.setsignatures([]) # Restore data index. ctrlfield.DataIndex = didxfield # Convert SISFields to string. outstring = insis.tostring() # Write output SIS file. f = file(outfile, "wb") f.write(uids) f.write(outstring) f.close()
def run(pgmname, argv): global debug # Determine system character encodings. try: # getdefaultlocale() may sometimes return None. # Fall back to ASCII encoding in that case. terminalenc = locale.getdefaultlocale()[1] + "" except TypeError: # Invalid locale, fall back to ASCII terminal encoding. terminalenc = "ascii" try: # sys.getfilesystemencoding() was introduced in Python v2.3 and # it can sometimes return None. Fall back to ASCII if something # goes wrong. filesystemenc = sys.getfilesystemencoding() + "" except (AttributeError, TypeError): filesystemenc = "ascii" try: gopt = getopt.gnu_getopt except: # Python <v2.3, GNU-style parameter ordering not supported. gopt = getopt.getopt # Parse command line arguments. short_opts = "u:n:r:l:i:s:c:f:x:t:a:k:p:b:d:gRH:e:vh" long_opts = [ "uid=", "appname=", "version=", "lang=", "icon=", "shortcaption=", "caption=", "drive=", "extrasdir=", "textfile=", "cert=", "privkey=", "passphrase=", "unsigned", "caps=", "vendor=", "autostart", "runinstall", "heapsize=", "encoding=", "verbose", "debug", "help" ] args = gopt(argv, short_opts, long_opts) opts = dict(args[0]) pargs = args[1] if len(pargs) == 0: raise ValueError("no source file name given") # Override character encoding of command line and filesystem. encs = opts.get("--encoding", opts.get("-e", "%s,%s" % (terminalenc, filesystemenc))) try: terminalenc, filesystemenc = encs.split(",") except (ValueError, TypeError): raise ValueError("invalid encoding string '%s'" % encs) # Get source name, either a Python program or a directory. src = pargs[0].decode(terminalenc).encode(filesystemenc) if os.path.isdir(src): # Remove trailing slashes (or whatever the separator is). src = os.path.split(src + os.sep)[0] # Use last directory component as the name. basename = os.path.basename(src) # Source is a directory, recursively collect files it contains. srcdir = src srcfiles = [] prefixlen = len(srcdir) + len(os.sep) def getfiles(arg, dirname, names): for name in names: path = os.path.join(dirname, name) if not os.path.isdir(path): arg.append(path[prefixlen:]) os.path.walk(srcdir, getfiles, srcfiles) # Read application version and UID3 from default.py. version, uid3 = scandefaults(os.path.join(srcdir, "default.py")) else: if src.lower().endswith(".py"): # Use program name without the .py extension. basename = os.path.basename(src)[:-3] else: # Unknown extension, use program name as-is. basename = os.path.basename(src) # Source is a file, use it. srcdir, srcfiles = os.path.split(src) srcfiles = [srcfiles] # Read application version and UID3 from file. version, uid3 = scandefaults(os.path.join(srcdir, srcfiles[0])) # Parse version string, use 1.0.0 by default. version = opts.get("--version", opts.get("-r", version)) if version == None: version = "1.0.0" print ("%s: warning: no application version given, " "using %s" % (pgmname, version)) try: version = parseversion(version) except (ValueError, IndexError, TypeError): raise ValueError("invalid version string '%s'" % version) # Determine output SIS file name. if len(pargs) == 1: # Derive output file name from input file name. outfile = "%s_v%d_%d_%d.sis" % (basename, version[0], version[1], version[2]) elif len(pargs) == 2: outfile = pargs[1].decode(terminalenc).encode(filesystemenc) if os.path.isdir(outfile): # Output to directory, derive output name from input file name. outfile = os.path.join(outfile, "%s_v%d_%d_%d.sis" % ( basename, version[0], version[1], version[2])) if not outfile.lower().endswith(".sis"): outfile += ".sis" else: raise ValueError("wrong number of arguments") # Determine application name (install dir.), use basename by default. appname = opts.get("--appname", opts.get("-n", basename)) appname = appname.decode(terminalenc) # Auto-generate a test-range UID from application name. autouid = symbianutil.uidfromname(appname) # Get UID3. uid3 = opts.get("--uid", opts.get("-u", uid3)) if uid3 == None: # No UID given, use auto-generated UID. uid3 = autouid print ("%s: warning: no UID given, using auto-generated " "test-range UID 0x%08x" % (pgmname, uid3)) elif uid3.lower().startswith("0x"): # Prefer hex UIDs with leading "0x". uid3 = long(uid3, 16) else: try: if len(uid3) == 8: # Assuming hex UID even without leading "0x". print ('%s: warning: assuming hex UID even ' 'without leading "0x"' % pgmname) uid3 = long(uid3, 16) else: # Decimal UID. uid3 = long(uid3) print ('%s: warning: decimal UID converted to 0x%08x' % (pgmname, uid3)) except ValueError: raise ValueError("invalid UID string '%s'" % uid3) # Warn against specifying a test-range UID manually. #if uid3 & 0xf0000000L == 0xe0000000L and uid3 != autouid: # print ("%s: warning: manually specifying a test-range UID is " # "not recommended" % pgmname) # Determine application language(s), use "EN" by default. lang = opts.get("--lang", opts.get("-l", "EN")).split(",") numlang = len(lang) # Verify that the language codes are correct. for l in lang: try: symbianutil.langidtonum[l] except KeyError: raise ValueError("%s: no such language code" % l) # Get icon file name. icon = opts.get("--icon", opts.get("-i", None)) if icon != None: icon = icon.decode(terminalenc).encode(filesystemenc) # Read icon file. f = file(icon, "rb") icondata = f.read(MAXICONFILESIZE + 1) f.close() if len(icondata) > MAXICONFILESIZE: raise ValueError("icon file too large") else: # No icon given, use a default icon. icondata = zlib.decompress(defaulticondata.decode("base-64")) # Determine application short caption(s). shortcaption = opts.get("--shortcaption", opts.get("-s", "")) shortcaption = shortcaption.decode(terminalenc) if len(shortcaption) == 0: # Short caption not given, use application name. shortcaption = [appname] * numlang else: shortcaption = shortcaption.split(",") # Determine application long caption(s), use short caption by default. caption = opts.get("--caption", opts.get("-c", "")) caption = caption.decode(terminalenc) if len(caption) == 0: # Caption not given, use short caption. caption = shortcaption else: caption = caption.split(",") # Compare the number of languages and captions. if len(shortcaption) != numlang or len(caption) != numlang: raise ValueError("invalid number of captions") # Determine installation drive, any by default. drive = opts.get("--drive", opts.get("-f", "any")).upper() if drive == "ANY" or drive == "!": drive = "!" elif drive != "C" and drive != "E": raise ValueError("%s: invalid drive letter" % drive) # Determine vendor name(s), use "Ensymble" by default. vendor = opts.get("--vendor", opts.get("-d", "Ensymble")) vendor = vendor.decode(terminalenc) vendor = vendor.split(",") if len(vendor) == 1: # Only one vendor name given, use it for all languages. vendor = vendor * numlang elif len(vendor) != numlang: raise ValueError("invalid number of vendor names") extrasdir = opts.get("--extrasdir", opts.get("-x", None)) if extrasdir != None: extrasdir = extrasdir.decode(terminalenc).encode(filesystemenc) if extrasdir[-1] == os.sep: # Strip trailing slash (or backslash). extrasdir = extrasdir[:-1] if os.sep in extrasdir: raise ValueError("%s: too many path components" % extrasdir) # Load text files. texts = [] textfile = opts.get("--textfile", opts.get("-t", None)) if textfile != None: texts = readtextfiles(textfile, lang) # Get certificate and its private key file names. cert = opts.get("--cert", opts.get("-a", None)) privkey = opts.get("--privkey", opts.get("-k", None)) unsign = ("--unsigned" in opts) if unsign: certdata = None privkeydata = None elif cert != None and privkey != None: # Convert file names from terminal encoding to filesystem encoding. cert = cert.decode(terminalenc).encode(filesystemenc) privkey = privkey.decode(terminalenc).encode(filesystemenc) # Read certificate file. f = file(cert, "rb") certdata = f.read(MAXCERTIFICATELENGTH + 1) f.close() if len(certdata) > MAXCERTIFICATELENGTH: raise ValueError("certificate file too large") # Read private key file. f = file(privkey, "rb") privkeydata = f.read(MAXPRIVATEKEYLENGTH + 1) f.close() if len(privkeydata) > MAXPRIVATEKEYLENGTH: raise ValueError("private key file too large") elif cert == None and privkey == None: # No certificate given, use the Ensymble default certificate. # defaultcert.py is not imported when not needed. This speeds # up program start-up a little. from utils import defaultcert certdata = defaultcert.cert privkeydata = defaultcert.privkey print ("%s: warning: no certificate given, using " "insecure built-in one" % pgmname) # Warn if the UID is in the protected range. # Resulting SIS file will probably not install. if uid3 < 0x80000000L: print ("%s: warning: UID is in the protected range " "(0x00000000 - 0x7ffffff)" % pgmname) else: raise ValueError("missing certificate or private key") # Get pass phrase. Pass phrase remains in terminal encoding. passphrase = opts.get("--passphrase", opts.get("-p", None)) if passphrase == None and privkey != None: # Private key given without "--passphrase" option, ask it. if sys.stdin.isatty(): # Standard input is a TTY, ask password interactively. passphrase = getpass.getpass("Enter private key pass phrase:") else: # Not connected to a TTY, read stdin non-interactively instead. passphrase = sys.stdin.read(MAXPASSPHRASELENGTH + 1) if len(passphrase) > MAXPASSPHRASELENGTH: raise ValueError("pass phrase too long") passphrase = passphrase.strip() # Get capabilities and normalize the names. caps = opts.get("--caps", opts.get("-b", "")) capmask = symbianutil.capstringtomask(caps) caps = symbianutil.capmasktostring(capmask, True) # Determine if the application is requested to start on each device boot. autostart = False if "--autostart" in opts.keys() or "-g" in opts.keys(): autostart = True runinstall = False if "--runinstall" in opts.keys() or "-R" in opts.keys(): runinstall = True # Get heap sizes. heapsize = opts.get("--heapsize", opts.get("-H", "4k,1M")).split(",", 1) try: heapsizemin = symbianutil.parseintmagnitude(heapsize[0]) if len(heapsize) == 1: # Only one size given, use it as both. heapsizemax = heapsizemin else: heapsizemax = symbianutil.parseintmagnitude(heapsize[1]) except (ValueError, TypeError, IndexError): raise ValueError("%s: invalid heap size, one or two values expected" % ",".join(heapsize)) # Warn if the minimum heap size is larger than the maximum heap size. # Resulting SIS file will probably not install. if heapsizemin > heapsizemax: print ("%s: warning: minimum heap size larger than " "maximum heap size" % pgmname) # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. import cryptutil cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # basename Base for generated file names on host, filesystemenc encoded # srcdir Directory of source files, filesystemenc encoded # srcfiles List of filesystemenc encoded source file names in srcdir # outfile Output SIS file name, filesystemenc encoded # uid3 Application UID3, long integer # appname Application name and install directory in device, in Unicode # version A triple-item tuple (major, minor, build) # lang List of two-character language codes, ASCII strings # icon Icon data, a binary string typically containing a SVG-T file # shortcaption List of Unicode short captions, one per language # caption List of Unicode long captions, one per language # drive Installation drive letter or "!" # extrasdir Path prefix for extra files, filesystemenc encoded or None # textfile File name pattern of text file(s) to display during install # texts Actual texts to display during install, one per language # cert Certificate in PEM format # privkey Certificate private key in PEM format # passphrase Pass phrase of private key, terminalenc encoded string # caps, capmask Capability names and bitmask # vendor List of Unicode vendor names, one per language # autostart Boolean requesting application autostart on device boot # runinstall Boolean requesting application autorun after installation # heapsizemin Heap that must be available for the application to start # heapsizemax Maximum amount of heap the application can allocate # verbose Boolean indicating verbose terminal output if verbose: print print "Input file(s) %s" % " ".join( [s.decode(filesystemenc).encode(terminalenc) for s in srcfiles]) print "Output SIS file %s" % ( outfile.decode(filesystemenc).encode(terminalenc)) print "UID 0x%08x" % uid3 print "Application name %s" % appname.encode(terminalenc) print "Version %d.%d.%d" % ( version[0], version[1], version[2]) print "Language(s) %s" % ", ".join(lang) print "Icon %s" % ((icon and icon.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Short caption(s) %s" % ", ".join( [s.encode(terminalenc) for s in shortcaption]) print "Long caption(s) %s" % ", ".join( [s.encode(terminalenc) for s in caption]) print "Install drive %s" % ((drive == "!") and "<any>" or drive) print "Extras directory %s" % ((extrasdir and extrasdir.decode(filesystemenc).encode(terminalenc)) or "<none>") print "Text file(s) %s" % ((textfile and textfile.decode(filesystemenc).encode(terminalenc)) or "<none>") print "Sign? %s" % (unsign and "no" or "yes") print "Certificate %s" % ((cert and cert.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Private key %s" % ((privkey and privkey.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Capabilities 0x%x (%s)" % (capmask, caps) print "Vendor name(s) %s" % ", ".join( [s.encode(terminalenc) for s in vendor]) print "Autostart on boot %s" % ((autostart and "Yes") or "No") print "Run after install %s" % ((runinstall and "Yes") or "No") print "Heap size in bytes %d, %d" % (heapsizemin, heapsizemax) print # Generate SimpleSISWriter object. sw = sisfile.SimpleSISWriter(lang, caption, uid3, version, vendor[0], vendor) # Add text file or files to the SIS object. Text dialog is # supposed to be displayed before anything else is installed. if len(texts) == 1: sw.addfile(texts[0], operation = sisfield.EOpText) elif len(texts) > 1: sw.addlangdepfile(texts, operation = sisfield.EOpText) # Generate "Python for S60" resource file. rsctarget = u"%s:\\resource\\apps\\%s_0x%08x.rsc" % (drive, appname, uid3) string = zlib.decompress(pythons60rscdata.decode("base-64")) sw.addfile(string, rsctarget) del string # Generate application registration resource file. regtarget = u"%s:\\private\\10003a3f\\import\\apps\\%s_0x%08x_reg.rsc" % ( drive, appname, uid3) exename = u"%s_0x%08x" % (appname, uid3) locpath = u"\\resource\\apps\\%s_0x%08x_loc" % (appname, uid3) rw = rscfile.RSCWriter(uid2 = 0x101f8021, uid3 = uid3) # STRUCT APP_REGISTRATION_INFO from appinfo.rh res = rscfile.Resource(["LONG", "LLINK", "LTEXT", "LONG", "LTEXT", "LONG", "BYTE", "BYTE", "BYTE", "BYTE", "LTEXT", "BYTE", "WORD", "WORD", "WORD", "LLINK"], 0, 0, exename, 0, locpath, 1, 0, 0, 0, 0, "", 0, 0, 0, 0, 0) rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, regtarget) del string # EXE target name exetarget = u"%s:\\sys\\bin\\%s_0x%08x.exe" % (drive, appname, uid3) # Generate autostart registration resource file, if requested. if autostart: autotarget = u"%s:\\private\\101f875a\\import\\[%08x].rsc" % ( drive, uid3) rw = rscfile.RSCWriter(uid2 = 0, offset = " ") # STRUCT STARTUP_ITEM_INFO from startupitem.rh res = rscfile.Resource(["BYTE", "LTEXT", "WORD", "LONG", "BYTE", "BYTE"], 0, exetarget, 0, 0, 0, 0) rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, autotarget) del string # Generate localisable icon/caption definition resource files. iconpath = "\\resource\\apps\\%s_0x%08x_aif.mif" % (appname, uid3) for n in xrange(numlang): loctarget = u"%s:\\resource\\apps\\%s_0x%08x_loc.r%02d" % ( drive, appname, uid3, symbianutil.langidtonum[lang[n]]) rw = rscfile.RSCWriter(uid2 = 0, offset = " ") # STRUCT LOCALISABLE_APP_INFO from appinfo.rh res = rscfile.Resource(["LONG", "LLINK", "LTEXT", "LONG", "LLINK", "LTEXT", "WORD", "LTEXT", "WORD", "LTEXT"], 0, 0, shortcaption[n], 0, 0, caption[n], 1, iconpath, 0, "") rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, loctarget) del string # Generate MIF file for icon. icontarget = "%s:\\resource\\apps\\%s_0x%08x_aif.mif" % ( drive, appname, uid3) mw = miffile.MIFWriter() mw.addfile(icondata) del icondata string = mw.tostring() del mw sw.addfile(string, icontarget) del string # Add files to SIS object. if len(srcfiles) == 1: # Read file. f = file(os.path.join(srcdir, srcfiles[0]), "rb") string = f.read(MAXOTHERFILESIZE + 1) f.close() if len(string) > MAXOTHERFILESIZE: raise ValueError("%s: input file too large" % srcfiles[0]) # Add file to the SIS object. One file only, rename it to default.py. target = "default.py" sw.addfile(string, "%s:\\private\\%08x\\%s" % (drive, uid3, target)) del string else: if extrasdir != None: sysbinprefix = os.path.join(extrasdir, "sys", "bin", "") else: sysbinprefix = os.path.join(os.sep, "sys", "bin", "") # More than one file, use original path names. for srcfile in srcfiles: # Read file. f = file(os.path.join(srcdir, srcfile), "rb") string = f.read(MAXOTHERFILESIZE + 1) f.close() if len(string) > MAXOTHERFILESIZE: raise ValueError("%s: input file too large" % srcfile) # Split path into components. srcpathcomp = srcfile.split(os.sep) targetpathcomp = [s.decode(filesystemenc) for s in srcpathcomp] # Check if the file is an E32Image (EXE or DLL). filecapmask = symbianutil.e32imagecaps(string) # Warn against common mistakes when dealing with E32Image files. if filecapmask != None: if not srcfile.startswith(sysbinprefix): # Warn against E32Image files outside /sys/bin. print ("%s: warning: %s is an E32Image (EXE or DLL) " "outside %s" % (pgmname, srcfile, sysbinprefix)) elif (symbianutil.ise32image(string) == "DLL" and (filecapmask & ~capmask) != 0x00000000L): # Warn about insufficient capabilities to load # a DLL from the PyS60 application. print ("%s: warning: insufficient capabilities to " "load %s" % (pgmname, srcfile)) # Handle the extras directory. if extrasdir != None and extrasdir == srcpathcomp[0]: # Path is rooted at the drive root. targetfile = u"%s:\\%s" % (drive, "\\".join(targetpathcomp[1:])) else: # Path is rooted at the application private directory. targetfile = u"%s:\\private\\%08x\\%s" % ( drive, uid3, "\\".join(targetpathcomp)) # Add file to the SIS object. sw.addfile(string, targetfile, capabilities = filecapmask) del string # Add target device dependency. sw.addtargetdevice(0x101f7961L, (0, 0, 0), None, ["Series60ProductID"] * numlang) # Add "Python for S60" dependency, version 1.4.0 onwards. # NOTE: Previous beta versions of Python for S60 had a # different UID3 (0xf0201510). sw.adddependency(0x2000b1a0L, (1, 4, 0), None, ["Python for S60"] * numlang) # Add certificate. if certdata is not None: #print (privkeydata, certdata, passphrase) sw.addcertificate(privkeydata, certdata, passphrase) # Generate an EXE stub and add it to the SIS object. string = execstubdata.decode("base-64") string = symbianutil.e32imagecrc(string, uid3, uid3, None, heapsizemin, heapsizemax, capmask) if runinstall: # To avoid running without dependencies, this has to be in the end. sw.addfile(string, exetarget, None, capabilities = capmask, operation = sisfield.EOpRun, options = sisfield.EInstFileRunOptionInstall) else: sw.addfile(string, exetarget, None, capabilities = capmask) del string # Generate SIS file out of the SimpleSISWriter object. sw.tofile(outfile)
if heapsizemin > heapsizemax: print ("%s: warning: minimum heap size larger than " "maximum heap size" % pgmname) # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. import cryptutil cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # basename Base for generated file names on host, filesystemenc encoded # srcdir Directory of source files, filesystemenc encoded # srcfiles List of filesystemenc encoded source file names in srcdir # outfile Output SIS file name, filesystemenc encoded # uid3 Application UID3, long integer # appname Application name and install directory in device, in Unicode # version A triple-item tuple (major, minor, build) # lang List of two-character language codes, ASCII strings # icon Icon data, a binary string typically containing a SVG-T file # shortcaption List of Unicode short captions, one per language
def run(pgmname, argv): global debug # Determine system character encodings. try: # getdefaultlocale() may sometimes return None. # Fall back to ASCII encoding in that case. terminalenc = locale.getdefaultlocale()[1] + "" except TypeError: # Invalid locale, fall back to ASCII terminal encoding. terminalenc = "ascii" try: # sys.getfilesystemencoding() was introduced in Python v2.3 and # it can sometimes return None. Fall back to ASCII if something # goes wrong. filesystemenc = sys.getfilesystemencoding() + "" except (AttributeError, TypeError): filesystemenc = "ascii" try: gopt = getopt.gnu_getopt except: # Python <v2.3, GNU-style parameter ordering not supported. gopt = getopt.getopt # Parse command line arguments. short_opts = "a:k:p:e:vh" long_opts = [ "cert=", "privkey=", "passphrase=", "encoding=", "verbose", "debug", "help" ] args = gopt(argv, short_opts, long_opts) opts = dict(args[0]) pargs = args[1] if len(pargs) < 2: raise ValueError("wrong number of arguments") # Override character encoding of command line and filesystem. encs = opts.get("--encoding", opts.get("-e", "%s,%s" % (terminalenc, filesystemenc))) try: terminalenc, filesystemenc = encs.split(",") except (ValueError, TypeError): raise ValueError("invalid encoding string '%s'" % encs) # Get input SIS file names. infiles = [f.decode(terminalenc).encode(filesystemenc) for f in pargs[:-1]] # Determine output SIS file name. outfile = pargs[-1].decode(terminalenc).encode(filesystemenc) if os.path.isdir(outfile): # Output to directory, use input file name. outfile = os.path.join(outfile, os.path.basename(infiles[0])) # Get certificate and its private key file names. cert = opts.get("--cert", opts.get("-a", None)) privkey = opts.get("--privkey", opts.get("-k", None)) if cert != None and privkey != None: # Convert file names from terminal encoding to filesystem encoding. cert = cert.decode(terminalenc).encode(filesystemenc) privkey = privkey.decode(terminalenc).encode(filesystemenc) # Read certificate file. f = file(cert, "rb") certdata = f.read(MAXCERTIFICATELENGTH + 1) f.close() if len(certdata) > MAXCERTIFICATELENGTH: raise ValueError("certificate file too large") # Read private key file. f = file(privkey, "rb") privkeydata = f.read(MAXPRIVATEKEYLENGTH + 1) f.close() if len(privkeydata) > MAXPRIVATEKEYLENGTH: raise ValueError("private key file too large") elif cert == None and privkey == None: # No certificate given, use the Ensymble default certificate. # defaultcert.py is not imported when not needed. This speeds # up program start-up a little. import defaultcert certdata = defaultcert.cert privkeydata = defaultcert.privkey print( "%s: warning: no certificate given, using " "insecure built-in one" % pgmname) else: raise ValueError("missing certificate or private key") # Get pass phrase. Pass phrase remains in terminal encoding. passphrase = opts.get("--passphrase", opts.get("-p", None)) if passphrase == None and privkey != None: # Private key given without "--passphrase" option, ask it. if sys.stdin.isatty(): # Standard input is a TTY, ask password interactively. passphrase = getpass.getpass("Enter private key pass phrase:") else: # Not connected to a TTY, read stdin non-interactively instead. passphrase = sys.stdin.read(MAXPASSPHRASELENGTH + 1) if len(passphrase) > MAXPASSPHRASELENGTH: raise ValueError("pass phrase too long") passphrase = passphrase.strip() # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # infiles A list of input SIS file names, filesystemenc encoded # outfile Output SIS file name, filesystemenc encoded # cert Certificate in PEM format # privkey Certificate private key in PEM format # passphrase Pass phrase of priv. key, terminalenc encoded string # verbose Boolean indicating verbose terminal output if verbose: print print "Input SIS files %s" % " ".join( [f.decode(filesystemenc).encode(terminalenc) for f in infiles]) print "Output SIS file %s" % ( outfile.decode(filesystemenc).encode(terminalenc)) print "Certificate %s" % ( (cert and cert.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Private key %s" % ( (privkey and privkey.decode(filesystemenc).encode(terminalenc)) or "<default>") print insis = [] for n in xrange(len(infiles)): # Read input SIS files. f = file(infiles[n], "rb") instring = f.read(MAXSISFILESIZE + 1) f.close() if len(instring) > MAXSISFILESIZE: raise ValueError("%s: input SIS file too large" % infiles[n]) if n == 0: # Store UIDs for later use. uids = instring[:16] # UID1, UID2, UID3 and UIDCRC # Convert input SIS file to SISFields. sf, rlen = sisfield.SISField(instring[16:], False) # Ignore extra bytes after SIS file. if len(instring) > (rlen + 16): print("%s: %s: warning: %d extra bytes after SIS file (ignored)" % (pgmname, infiles[n], (len(instring) - (rlen + 16)))) # Try to release some memory early. del instring # Check that there are no embedded SIS files. if len(sf.Data.DataUnits) > 1: raise ValueError("%s: input SIS file contains " "embedded SIS files" % infiles[n]) insis.append(sf) # Temporarily remove the SISDataIndex SISField from the first SISController. ctrlfield = insis[0].Controller.Data didxfield = ctrlfield.DataIndex ctrlfield.DataIndex = None # Remove old signatures from the first SIS file. if len(ctrlfield.getsignatures()) > 0: print( "%s: warning: removing old signatures " "from the first input SIS file" % pgmname) ctrlfield.setsignatures([]) for n in xrange(1, len(insis)): # Append SISDataUnit SISFields into SISData array of the first SIS file. insis[0].Data.DataUnits.append(insis[n].Data.DataUnits[0]) # Set data index in SISController SISField. insis[n].Controller.Data.DataIndex.DataIndex = n # Embed SISController into SISInstallBlock of the first SIS file. ctrlfield.InstallBlock.EmbeddedSISFiles.append( insis[n].Controller.Data) # Calculate a signature of the modified SISController. string = ctrlfield.tostring() string = sisfield.stripheaderandpadding(string) signature, algoid = sisfile.signstring(privkeydata, passphrase, string) # Create a SISCertificateChain SISField from certificate data. sf1 = sisfield.SISBlob(Data=cryptutil.certtobinary(certdata)) sf2 = sisfield.SISCertificateChain(CertificateData=sf1) # Create a SISSignature SISField from calculated signature. sf3 = sisfield.SISString(String=algoid) sf4 = sisfield.SISSignatureAlgorithm(AlgorithmIdentifier=sf3) sf5 = sisfield.SISBlob(Data=signature) sf6 = sisfield.SISSignature(SignatureAlgorithm=sf4, SignatureData=sf5) # Create a new SISSignatureCertificateChain SISField. sa = sisfield.SISArray(SISFields=[sf6]) sf7 = sisfield.SISSignatureCertificateChain(Signatures=sa, CertificateChain=sf2) # Set certificate, restore data index. ctrlfield.Signature0 = sf7 ctrlfield.DataIndex = didxfield # Convert SISFields to string. outstring = insis[0].tostring() # Write output SIS file. f = file(outfile, "wb") f.write(uids) f.write(outstring) f.close()
def run(pgmname, argv): global debug # Determine system character encodings. try: # getdefaultlocale() may sometimes return None. # Fall back to ASCII encoding in that case. terminalenc = locale.getdefaultlocale()[1] + "" except TypeError: # Invalid locale, fall back to ASCII terminal encoding. terminalenc = "ascii" try: # sys.getfilesystemencoding() was introduced in Python v2.3 and # it can sometimes return None. Fall back to ASCII if something # goes wrong. filesystemenc = sys.getfilesystemencoding() + "" except (AttributeError, TypeError): filesystemenc = "ascii" try: gopt = getopt.gnu_getopt except: # Python <v2.3, GNU-style parameter ordering not supported. gopt = getopt.getopt # Parse command line arguments. short_opts = "u:n:r:l:i:s:c:f:x:t:a:k:p:b:d:gRH:e:vh" long_opts = [ "uid=", "appname=", "version=", "lang=", "icon=", "shortcaption=", "caption=", "drive=", "extrasdir=", "textfile=", "cert=", "privkey=", "passphrase=", "unsigned", "caps=", "vendor=", "autostart", "runinstall", "heapsize=", "encoding=", "verbose", "debug", "help" ] args = gopt(argv, short_opts, long_opts) opts = dict(args[0]) pargs = args[1] if len(pargs) == 0: raise ValueError("no source file name given") # Override character encoding of command line and filesystem. encs = opts.get("--encoding", opts.get("-e", "%s,%s" % (terminalenc, filesystemenc))) try: terminalenc, filesystemenc = encs.split(",") except (ValueError, TypeError): raise ValueError("invalid encoding string '%s'" % encs) # Get source name, either a Python program or a directory. src = pargs[0].decode(terminalenc).encode(filesystemenc) if os.path.isdir(src): # Remove trailing slashes (or whatever the separator is). src = os.path.split(src + os.sep)[0] # Use last directory component as the name. basename = os.path.basename(src) # Source is a directory, recursively collect files it contains. srcdir = src srcfiles = [] prefixlen = len(srcdir) + len(os.sep) def getfiles(arg, dirname, names): for name in names: path = os.path.join(dirname, name) if not os.path.isdir(path): arg.append(path[prefixlen:]) os.path.walk(srcdir, getfiles, srcfiles) # Read application version and UID3 from default.py. version, uid3 = scandefaults(os.path.join(srcdir, "default.py")) else: if src.lower().endswith(".py"): # Use program name without the .py extension. basename = os.path.basename(src)[:-3] else: # Unknown extension, use program name as-is. basename = os.path.basename(src) # Source is a file, use it. srcdir, srcfiles = os.path.split(src) srcfiles = [srcfiles] # Read application version and UID3 from file. version, uid3 = scandefaults(os.path.join(srcdir, srcfiles[0])) # Parse version string, use 1.0.0 by default. version = opts.get("--version", opts.get("-r", version)) if version == None: version = "1.0.0" print("%s: warning: no application version given, " "using %s" % (pgmname, version)) try: version = parseversion(version) except (ValueError, IndexError, TypeError): raise ValueError("invalid version string '%s'" % version) # Determine output SIS file name. if len(pargs) == 1: # Derive output file name from input file name. outfile = "%s_v%d_%d_%d.sis" % (basename, version[0], version[1], version[2]) elif len(pargs) == 2: outfile = pargs[1].decode(terminalenc).encode(filesystemenc) if os.path.isdir(outfile): # Output to directory, derive output name from input file name. outfile = os.path.join( outfile, "%s_v%d_%d_%d.sis" % (basename, version[0], version[1], version[2])) if not outfile.lower().endswith(".sis"): outfile += ".sis" else: raise ValueError("wrong number of arguments") # Determine application name (install dir.), use basename by default. appname = opts.get("--appname", opts.get("-n", basename)) appname = appname.decode(terminalenc) # Auto-generate a test-range UID from application name. autouid = symbianutil.uidfromname(appname) # Get UID3. uid3 = opts.get("--uid", opts.get("-u", uid3)) if uid3 == None: # No UID given, use auto-generated UID. uid3 = autouid print( "%s: warning: no UID given, using auto-generated " "test-range UID 0x%08x" % (pgmname, uid3)) elif uid3.lower().startswith("0x"): # Prefer hex UIDs with leading "0x". uid3 = long(uid3, 16) else: try: if len(uid3) == 8: # Assuming hex UID even without leading "0x". print( '%s: warning: assuming hex UID even ' 'without leading "0x"' % pgmname) uid3 = long(uid3, 16) else: # Decimal UID. uid3 = long(uid3) print('%s: warning: decimal UID converted to 0x%08x' % (pgmname, uid3)) except ValueError: raise ValueError("invalid UID string '%s'" % uid3) # Warn against specifying a test-range UID manually. #if uid3 & 0xf0000000L == 0xe0000000L and uid3 != autouid: # print ("%s: warning: manually specifying a test-range UID is " # "not recommended" % pgmname) # Determine application language(s), use "EN" by default. lang = opts.get("--lang", opts.get("-l", "EN")).split(",") numlang = len(lang) # Verify that the language codes are correct. for l in lang: try: symbianutil.langidtonum[l] except KeyError: raise ValueError("%s: no such language code" % l) # Get icon file name. icon = opts.get("--icon", opts.get("-i", None)) if icon != None: icon = icon.decode(terminalenc).encode(filesystemenc) # Read icon file. f = file(icon, "rb") icondata = f.read(MAXICONFILESIZE + 1) f.close() if len(icondata) > MAXICONFILESIZE: raise ValueError("icon file too large") else: # No icon given, use a default icon. icondata = zlib.decompress(defaulticondata.decode("base-64")) # Determine application short caption(s). shortcaption = opts.get("--shortcaption", opts.get("-s", "")) shortcaption = shortcaption.decode(terminalenc) if len(shortcaption) == 0: # Short caption not given, use application name. shortcaption = [appname] * numlang else: shortcaption = shortcaption.split(",") # Determine application long caption(s), use short caption by default. caption = opts.get("--caption", opts.get("-c", "")) caption = caption.decode(terminalenc) if len(caption) == 0: # Caption not given, use short caption. caption = shortcaption else: caption = caption.split(",") # Compare the number of languages and captions. if len(shortcaption) != numlang or len(caption) != numlang: raise ValueError("invalid number of captions") # Determine installation drive, any by default. drive = opts.get("--drive", opts.get("-f", "any")).upper() if drive == "ANY" or drive == "!": drive = "!" elif drive != "C" and drive != "E": raise ValueError("%s: invalid drive letter" % drive) # Determine vendor name(s), use "Ensymble" by default. vendor = opts.get("--vendor", opts.get("-d", "Ensymble")) vendor = vendor.decode(terminalenc) vendor = vendor.split(",") if len(vendor) == 1: # Only one vendor name given, use it for all languages. vendor = vendor * numlang elif len(vendor) != numlang: raise ValueError("invalid number of vendor names") extrasdir = opts.get("--extrasdir", opts.get("-x", None)) if extrasdir != None: extrasdir = extrasdir.decode(terminalenc).encode(filesystemenc) if extrasdir[-1] == os.sep: # Strip trailing slash (or backslash). extrasdir = extrasdir[:-1] if os.sep in extrasdir: raise ValueError("%s: too many path components" % extrasdir) # Load text files. texts = [] textfile = opts.get("--textfile", opts.get("-t", None)) if textfile != None: texts = readtextfiles(textfile, lang) # Get certificate and its private key file names. cert = opts.get("--cert", opts.get("-a", None)) privkey = opts.get("--privkey", opts.get("-k", None)) unsign = ("--unsigned" in opts) if unsign: certdata = None privkeydata = None elif cert != None and privkey != None: # Convert file names from terminal encoding to filesystem encoding. cert = cert.decode(terminalenc).encode(filesystemenc) privkey = privkey.decode(terminalenc).encode(filesystemenc) # Read certificate file. f = file(cert, "rb") certdata = f.read(MAXCERTIFICATELENGTH + 1) f.close() if len(certdata) > MAXCERTIFICATELENGTH: raise ValueError("certificate file too large") # Read private key file. f = file(privkey, "rb") privkeydata = f.read(MAXPRIVATEKEYLENGTH + 1) f.close() if len(privkeydata) > MAXPRIVATEKEYLENGTH: raise ValueError("private key file too large") elif cert == None and privkey == None: # No certificate given, use the Ensymble default certificate. # defaultcert.py is not imported when not needed. This speeds # up program start-up a little. from utils import defaultcert certdata = defaultcert.cert privkeydata = defaultcert.privkey print( "%s: warning: no certificate given, using " "insecure built-in one" % pgmname) # Warn if the UID is in the protected range. # Resulting SIS file will probably not install. if uid3 < 0x80000000L: print( "%s: warning: UID is in the protected range " "(0x00000000 - 0x7ffffff)" % pgmname) else: raise ValueError("missing certificate or private key") # Get pass phrase. Pass phrase remains in terminal encoding. passphrase = opts.get("--passphrase", opts.get("-p", None)) if passphrase == None and privkey != None: # Private key given without "--passphrase" option, ask it. if sys.stdin.isatty(): # Standard input is a TTY, ask password interactively. passphrase = getpass.getpass("Enter private key pass phrase:") else: # Not connected to a TTY, read stdin non-interactively instead. passphrase = sys.stdin.read(MAXPASSPHRASELENGTH + 1) if len(passphrase) > MAXPASSPHRASELENGTH: raise ValueError("pass phrase too long") passphrase = passphrase.strip() # Get capabilities and normalize the names. caps = opts.get("--caps", opts.get("-b", "")) capmask = symbianutil.capstringtomask(caps) caps = symbianutil.capmasktostring(capmask, True) # Determine if the application is requested to start on each device boot. autostart = False if "--autostart" in opts.keys() or "-g" in opts.keys(): autostart = True runinstall = False if "--runinstall" in opts.keys() or "-R" in opts.keys(): runinstall = True # Get heap sizes. heapsize = opts.get("--heapsize", opts.get("-H", "4k,1M")).split(",", 1) try: heapsizemin = symbianutil.parseintmagnitude(heapsize[0]) if len(heapsize) == 1: # Only one size given, use it as both. heapsizemax = heapsizemin else: heapsizemax = symbianutil.parseintmagnitude(heapsize[1]) except (ValueError, TypeError, IndexError): raise ValueError("%s: invalid heap size, one or two values expected" % ",".join(heapsize)) # Warn if the minimum heap size is larger than the maximum heap size. # Resulting SIS file will probably not install. if heapsizemin > heapsizemax: print( "%s: warning: minimum heap size larger than " "maximum heap size" % pgmname) # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. import cryptutil cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # basename Base for generated file names on host, filesystemenc encoded # srcdir Directory of source files, filesystemenc encoded # srcfiles List of filesystemenc encoded source file names in srcdir # outfile Output SIS file name, filesystemenc encoded # uid3 Application UID3, long integer # appname Application name and install directory in device, in Unicode # version A triple-item tuple (major, minor, build) # lang List of two-character language codes, ASCII strings # icon Icon data, a binary string typically containing a SVG-T file # shortcaption List of Unicode short captions, one per language # caption List of Unicode long captions, one per language # drive Installation drive letter or "!" # extrasdir Path prefix for extra files, filesystemenc encoded or None # textfile File name pattern of text file(s) to display during install # texts Actual texts to display during install, one per language # cert Certificate in PEM format # privkey Certificate private key in PEM format # passphrase Pass phrase of private key, terminalenc encoded string # caps, capmask Capability names and bitmask # vendor List of Unicode vendor names, one per language # autostart Boolean requesting application autostart on device boot # runinstall Boolean requesting application autorun after installation # heapsizemin Heap that must be available for the application to start # heapsizemax Maximum amount of heap the application can allocate # verbose Boolean indicating verbose terminal output if verbose: print print "Input file(s) %s" % " ".join( [s.decode(filesystemenc).encode(terminalenc) for s in srcfiles]) print "Output SIS file %s" % ( outfile.decode(filesystemenc).encode(terminalenc)) print "UID 0x%08x" % uid3 print "Application name %s" % appname.encode(terminalenc) print "Version %d.%d.%d" % (version[0], version[1], version[2]) print "Language(s) %s" % ", ".join(lang) print "Icon %s" % ( (icon and icon.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Short caption(s) %s" % ", ".join( [s.encode(terminalenc) for s in shortcaption]) print "Long caption(s) %s" % ", ".join( [s.encode(terminalenc) for s in caption]) print "Install drive %s" % ((drive == "!") and "<any>" or drive) print "Extras directory %s" % ( (extrasdir and extrasdir.decode(filesystemenc).encode(terminalenc)) or "<none>") print "Text file(s) %s" % ( (textfile and textfile.decode(filesystemenc).encode(terminalenc)) or "<none>") print "Sign? %s" % (unsign and "no" or "yes") print "Certificate %s" % ( (cert and cert.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Private key %s" % ( (privkey and privkey.decode(filesystemenc).encode(terminalenc)) or "<default>") print "Capabilities 0x%x (%s)" % (capmask, caps) print "Vendor name(s) %s" % ", ".join( [s.encode(terminalenc) for s in vendor]) print "Autostart on boot %s" % ((autostart and "Yes") or "No") print "Run after install %s" % ((runinstall and "Yes") or "No") print "Heap size in bytes %d, %d" % (heapsizemin, heapsizemax) print # Generate SimpleSISWriter object. sw = sisfile.SimpleSISWriter(lang, caption, uid3, version, vendor[0], vendor) # Add text file or files to the SIS object. Text dialog is # supposed to be displayed before anything else is installed. if len(texts) == 1: sw.addfile(texts[0], operation=sisfield.EOpText) elif len(texts) > 1: sw.addlangdepfile(texts, operation=sisfield.EOpText) # Generate "Python for S60" resource file. rsctarget = u"%s:\\resource\\apps\\%s_0x%08x.rsc" % (drive, appname, uid3) string = zlib.decompress(pythons60rscdata.decode("base-64")) sw.addfile(string, rsctarget) del string # Generate application registration resource file. regtarget = u"%s:\\private\\10003a3f\\import\\apps\\%s_0x%08x_reg.rsc" % ( drive, appname, uid3) exename = u"%s_0x%08x" % (appname, uid3) locpath = u"\\resource\\apps\\%s_0x%08x_loc" % (appname, uid3) rw = rscfile.RSCWriter(uid2=0x101f8021, uid3=uid3) # STRUCT APP_REGISTRATION_INFO from appinfo.rh res = rscfile.Resource([ "LONG", "LLINK", "LTEXT", "LONG", "LTEXT", "LONG", "BYTE", "BYTE", "BYTE", "BYTE", "LTEXT", "BYTE", "WORD", "WORD", "WORD", "LLINK" ], 0, 0, exename, 0, locpath, 1, 0, 0, 0, 0, "", 0, 0, 0, 0, 0) rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, regtarget) del string # EXE target name exetarget = u"%s:\\sys\\bin\\%s_0x%08x.exe" % (drive, appname, uid3) # Generate autostart registration resource file, if requested. if autostart: autotarget = u"%s:\\private\\101f875a\\import\\[%08x].rsc" % (drive, uid3) rw = rscfile.RSCWriter(uid2=0, offset=" ") # STRUCT STARTUP_ITEM_INFO from startupitem.rh res = rscfile.Resource( ["BYTE", "LTEXT", "WORD", "LONG", "BYTE", "BYTE"], 0, exetarget, 0, 0, 0, 0) rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, autotarget) del string # Generate localisable icon/caption definition resource files. iconpath = "\\resource\\apps\\%s_0x%08x_aif.mif" % (appname, uid3) for n in xrange(numlang): loctarget = u"%s:\\resource\\apps\\%s_0x%08x_loc.r%02d" % ( drive, appname, uid3, symbianutil.langidtonum[lang[n]]) rw = rscfile.RSCWriter(uid2=0, offset=" ") # STRUCT LOCALISABLE_APP_INFO from appinfo.rh res = rscfile.Resource([ "LONG", "LLINK", "LTEXT", "LONG", "LLINK", "LTEXT", "WORD", "LTEXT", "WORD", "LTEXT" ], 0, 0, shortcaption[n], 0, 0, caption[n], 1, iconpath, 0, "") rw.addresource(res) string = rw.tostring() del rw sw.addfile(string, loctarget) del string # Generate MIF file for icon. icontarget = "%s:\\resource\\apps\\%s_0x%08x_aif.mif" % (drive, appname, uid3) mw = miffile.MIFWriter() mw.addfile(icondata) del icondata string = mw.tostring() del mw sw.addfile(string, icontarget) del string # Add files to SIS object. if len(srcfiles) == 1: # Read file. f = file(os.path.join(srcdir, srcfiles[0]), "rb") string = f.read(MAXOTHERFILESIZE + 1) f.close() if len(string) > MAXOTHERFILESIZE: raise ValueError("%s: input file too large" % srcfiles[0]) # Add file to the SIS object. One file only, rename it to default.py. target = "default.py" sw.addfile(string, "%s:\\private\\%08x\\%s" % (drive, uid3, target)) del string else: if extrasdir != None: sysbinprefix = os.path.join(extrasdir, "sys", "bin", "") else: sysbinprefix = os.path.join(os.sep, "sys", "bin", "") # More than one file, use original path names. for srcfile in srcfiles: # Read file. f = file(os.path.join(srcdir, srcfile), "rb") string = f.read(MAXOTHERFILESIZE + 1) f.close() if len(string) > MAXOTHERFILESIZE: raise ValueError("%s: input file too large" % srcfile) # Split path into components. srcpathcomp = srcfile.split(os.sep) targetpathcomp = [s.decode(filesystemenc) for s in srcpathcomp] # Check if the file is an E32Image (EXE or DLL). filecapmask = symbianutil.e32imagecaps(string) # Warn against common mistakes when dealing with E32Image files. if filecapmask != None: if not srcfile.startswith(sysbinprefix): # Warn against E32Image files outside /sys/bin. print( "%s: warning: %s is an E32Image (EXE or DLL) " "outside %s" % (pgmname, srcfile, sysbinprefix)) elif (symbianutil.ise32image(string) == "DLL" and (filecapmask & ~capmask) != 0x00000000L): # Warn about insufficient capabilities to load # a DLL from the PyS60 application. print( "%s: warning: insufficient capabilities to " "load %s" % (pgmname, srcfile)) # Handle the extras directory. if extrasdir != None and extrasdir == srcpathcomp[0]: # Path is rooted at the drive root. targetfile = u"%s:\\%s" % (drive, "\\".join( targetpathcomp[1:])) else: # Path is rooted at the application private directory. targetfile = u"%s:\\private\\%08x\\%s" % ( drive, uid3, "\\".join(targetpathcomp)) # Add file to the SIS object. sw.addfile(string, targetfile, capabilities=filecapmask) del string # Add target device dependency. sw.addtargetdevice(0x101f7961L, (0, 0, 0), None, ["Series60ProductID"] * numlang) # Add "Python for S60" dependency, version 1.4.0 onwards. # NOTE: Previous beta versions of Python for S60 had a # different UID3 (0xf0201510). sw.adddependency(0x2000b1a0L, (1, 4, 0), None, ["Python for S60"] * numlang) # Add certificate. if certdata is not None: #print (privkeydata, certdata, passphrase) sw.addcertificate(privkeydata, certdata, passphrase) # Generate an EXE stub and add it to the SIS object. string = execstubdata.decode("base-64") string = symbianutil.e32imagecrc(string, uid3, uid3, None, heapsizemin, heapsizemax, capmask) if runinstall: # To avoid running without dependencies, this has to be in the end. sw.addfile(string, exetarget, None, capabilities=capmask, operation=sisfield.EOpRun, options=sisfield.EInstFileRunOptionInstall) else: sw.addfile(string, exetarget, None, capabilities=capmask) del string # Generate SIS file out of the SimpleSISWriter object. sw.tofile(outfile)
raise ValueError("pass phrase too long") passphrase = passphrase.strip() # Determine verbosity. verbose = False if "--verbose" in opts.keys() or "-v" in opts.keys(): verbose = True # Determine if debug output is requested. if "--debug" in opts.keys(): debug = True # Enable debug output for OpenSSL-related functions. import cryptutil cryptutil.setdebug(True) # Ingredients for successful SIS generation: # # terminalenc Terminal character encoding (autodetected) # filesystemenc File system name encoding (autodetected) # basename Base for generated file names on host, filesystemenc encoded # srcdir Directory of source files, filesystemenc encoded # srcfiles List of filesystemenc encoded source file names in srcdir # outfile Output SIS file name, filesystemenc encoded # puid Package UID, long integer # version A triple-item tuple (major, minor, build) # lang List of two-character language codes, ASCII strings # caption List of Unicode package captions, one per language # drive Installation drive letter or "!" # textfile File name pattern of text file(s) to display during install