This file is indexed.

/usr/share/pyshared/ensymble/actions/py2sis.py is in ensymble 0.29-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
#!/usr/bin/env python
# -*- coding: utf-8 -*-

##############################################################################
# cmd_py2sis.py - Ensymble command line tool, py2sis command
# Copyright 2006, 2007, 2008, 2009 Jussi Ylänen
#
# This file is part of Ensymble developer utilities for Symbian OS(TM).
#
# Ensymble is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Ensymble is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ensymble; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
##############################################################################

import sys
import os
import re
import getopt
import getpass
import locale
import zlib

from utils import sisfile
from utils import sisfield
from utils import symbianutil
from utils import rscfile
from utils import miffile


##############################################################################
# Help texts
##############################################################################

shorthelp = 'Create a SIS package for a "Python for S60" application'
longhelp  = '''py2sis
    [--uid=0x01234567] [--appname=AppName] [--version=1.0.0]
    [--lang=EN,...] [--icon=icon.svg] [--shortcaption="App. Name",...]
    [--caption="Application Name",...] [--drive=C] [extrasdir=root]
    [--textfile=mytext_%C.txt] [--cert=mycert.cer] [--privkey=mykey.key]
    [--passphrase=12345] [--heapsize=min,max] [--caps=Cap1+Cap2+...]
    [--vendor="Vendor Name",...] [--autostart] [--runinstall]
    [--encoding=terminal,filesystem] [--verbose]
    <src> [sisfile]

Create a SIS package for a "Python for S60" application.

Options:
    src          - Source script or directory
    sisfile      - Path of the created SIS file
    uid          - Symbian OS UID for the application
    appname      - Name of the application
    version      - Application version: X.Y.Z or X,Y,Z (major, minor, build)
    lang         - Comma separated list of two-character language codes
    icon         - Icon file in SVG-Tiny format
    shortcaption - Comma separated list of short captions in all languages
    caption      - Comma separated list of long captions in all languages
    drive        - Drive where the package will be installed (any by default)
    extrasdir    - Name of dir. tree placed under drive root (none by default)
    textfile     - Text file (or pattern, see below) to display during install
    cert         - Certificate to use for signing (PEM format)
    privkey      - Private key of the certificate (PEM format)
    passphrase   - Pass phrase of the private key (insecure, use stdin instead)
    caps         - Capability names, separated by "+" (none by default)
    vendor       - Vendor name or a comma separated list of names in all lang.
    autostart    - Application is registered to start on each device boot
    runinstall   - Application is automatically started after installation
    heapsize     - Application heap size, min. and/or max. ("4k,1M" by default)
    encoding     - Local character encodings for terminal and filesystem
    verbose      - Print extra statistics

If no certificate and its private key are given, a default self-signed
certificate is used to sign the SIS file. Software authors are encouraged
to create their own unique certificates for SIS packages that are to be
distributed.

If no icon is given, the Python logo is used as the icon. The Python
logo is a trademark of the Python Software Foundation.

Text to display uses UTF-8 encoding. The file name may contain formatting
characters that are substituted for each selected language. If no formatting
characters are present, the same text will be used for all languages.

    %%           - literal %
    %n           - language number (01 - 99)
    %c           - two-character language code in lowercase letters
    %C           - two-character language code in capital letters
    %l           - language name in English, using only lowercase letters
    %l           - language name in English, using mixed case letters
'''


##############################################################################
# Parameters
##############################################################################

MAXPASSPHRASELENGTH     = 256
MAXCERTIFICATELENGTH    = 65536
MAXPRIVATEKEYLENGTH     = 65536
MAXICONFILESIZE         = 65536
MAXOTHERFILESIZE        = 1024 * 1024 * 8   # Eight megabytes
MAXTEXTFILELENGTH       = 1024


##############################################################################
# Global variables
##############################################################################

debug = False


##############################################################################
# Public module-level functions
##############################################################################

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=", "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))
    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.
        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 "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.
    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)


##############################################################################
# Module-level functions which are normally only used by this module
##############################################################################

def scandefaults(filename):
    '''Scan a Python source file for application version string and UID3.'''

    version = None
    uid3    = None

    # Regular expression for the version string. Version may optionally
    # be enclosed in double or single quotes.
    version_ro = re.compile(r'SIS_VERSION\s*=\s*(?:(?:"([^"]*)")|'
                            r"(?:'([^']*)')|(\S+))")

    # Original py2is uses a regular expression
    # r"SYMBIAN_UID\s*=\s*(0x[0-9a-fA-F]{8})".
    # This version is a bit more lenient.
    uid3_ro = re.compile(r"SYMBIAN_UID\s*=\s*(\S+)")

    # First match of each regular expression is used.
    f = file(filename, "rb")
    try:
        while version == None or uid3 == None:
            line = f.readline()
            if line == "":
                break
            if version == None:
                mo = version_ro.search(line)
                if mo:
                    # Get first group that matched in the regular expression.
                    version = filter(None, mo.groups())[0]
            if uid3 == None:
                mo = uid3_ro.search(line)
                if mo:
                    uid3 = mo.group(1)
    finally:
        f.close()
    return version, uid3

def parseversion(version):
    '''Parse a version string: "v1.2.3" or similar.

    Initial "v" can optionally be a capital "V" or omitted altogether. Minor
    and build numbers can also be omitted. Separator can be a comma or a
    period.'''

    version = version.strip().lower()

    # Strip initial "v" or "V".
    if version[0] == "v":
        version = version[1:]

    if "." in version:
        parts = [int(n) for n in version.split(".")]
    else:
        parts = [int(n) for n in version.split(",")]

    # Allow missing minor and build numbers.
    parts.extend([0, 0])

    return parts[0:3]

def readtextfiles(pattern, languages):
    '''Read language dependent text files.

    Files are assumed to be in UTF-8 encoding and re-encoded
    in UCS-2 (UTF-16LE) for Symbian OS to display during installation.'''

    if "%" not in pattern:
        # Only one file, read it.
        filenames = [pattern]
    else:
        filenames = []
        for langid in languages:
            langnum  = symbianutil.langidtonum[langid]
            langname = symbianutil.langnumtoname[langnum]

            # Replace formatting characters in file name pattern.
            filename = pattern
            filename = filename.replace("%n", "%02d" % langnum)
            filename = filename.replace("%c", langid.lower())
            filename = filename.replace("%C", langid.upper())
            filename = filename.replace("%l", langname.lower())
            filename = filename.replace("%L", langname)
            filename = filename.replace("%%", "%")

            filenames.append(filename)

    texts = []

    for filename in filenames:
        f = file(filename, "r") # Read as text.
        text = f.read(MAXTEXTFILELENGTH + 1)
        f.close()

        if len(text) > MAXTEXTFILELENGTH:
            raise ValueError("%s: text file too large" % filename)

        texts.append(text.decode("UTF-8").encode("UTF-16LE"))

    return texts

##############################################################################
# Embedded data: EXE stub, private key and icon, application resource
##############################################################################

# This is the Symbian application stub, which starts the Python interpreter
# and loads default.py. It is represented here as a base-64-encoded string.
# The stub needs to be patched with correct UID3, Secure ID and capabilities.
# After that, a couple of checksums need to be updated. Function
# e32imagecrc(...) in module symbianutils takes care of all that.
#
# This stub will examine process SID and report that as the UID3 for the
# GUI framework. Therefore, no code parts need to be patched at all.
execstubdata = '''
    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
    ey9synTLgyLZTMIy/ElM4mOC8BOZP3wLw+SmeV7oa+yUmY/uwI9n7xn8KZI+CDTufgg07l/KUzWI
    Jn4BwP5Q6fBg2exP0/pQWi9ksJr1hcvWDrrC22B48cTVItH+sDe5SgwD7AYMO4CZ2EMdukMZVhZX
    AwVhcHWFUdeL+deGXXj7xxVulBg3sq8V314S44Q44bccez8DcAfojix/HHqnjja44a6wR1hbfWCu
    LQvopQW3+wGXslht6wU1haLWHs3A26wvfrCJYGKsF+BvFg+4FBYew9YMtYV5gcJGLCLXiwvXjFeB
    4McK8cP/HGIMa/vI4XLBs9YQfjhrxxBuOKI1glrCH6w7b1hwsC2WF88CtTghfGWGO+VK6LSWv9jD
    DGoYHz+37POD/2g1Gg1XV562t7coPbH/PqJcvY9bxTzPaq7uy1HX/a12tyShzvPannurf1dVr9hq
    NXqOsyaZq+t+1qddr9Xkk6ys9Dn+rg19j12r1+Tc3Q6vnYNXrtTLl63+rVXeSYmwP8L0X97Y5p1e
    yVoK4yun5S65XVZnccrZ10WOrHx4/uR7ZaitrK/u5DMjvZG3keDInSNxI8WR48ifI8qRupHnSIL5
    jBwbjyI9hHXj6yPdx9UtrFrtbsVvBW3C3ireOtjLKV8ivs6/T19zyX//'''

# Python logo as a base-64-encoded, zlib-compressed SVG XML data
defaulticondata = '''
    eJyFVF1v20YQfC/Q/3Bl0be74+3tfQZRA1h24gJJa6COij66EiMSdSVDUiW3vz5zRyp2CgMVLHrJ
    /ZjZmRNfv3n8614cu91+2G5mDWnTiG6z3K6GzXrWfLx9q1Lz5sdvv3n93eUv89vfb67E/rgWNx8v
    3v80F41q29943raXt5fi18U7QZrE7bD5p22vfm5E0x8OD6/a9nQ66RPr7W7dvtvdPfTDct+iukV1
    6WwxkUgd0KdXh1VT0ArIH3f77ma3/TTcd7OmZJvnPKkRYL7Zv9qD6wO+szPc+YHeb//eLbtPwO30
    pjuMWFNSmYo1zpi9wNQaYwqzM8zj/bD586VCyjm3NduI07A69GBnzA+N6Lth3R/Od8ehO11sH2eN
    EUYEh7+66LpcHu4OvcCe97Pmew4hZ9eI1az5QEln5yVZ7YxfGsXaJyeNzoGU156dDNqGpIJ2gZes
    g2HsFZhk0tZaxJFKt8cTI4RAiTOMAT7Y2pola5PjFNcxC+u05aVBxmG01TERMkzqXMR0bb22ZJcK
    pd5Ko6JOHNERbJjiqKP1R69DdD3K2OSCjw2CwwZgH0PA8GAL++DLlbGjIm2NRUN2CMlTGVd2Vlgj
    EETQOSfkyUaJa5oaZUHlMe6djoavmbRLR0zxsWBfj2IuRjHffyXtv037Xxuu4oVnM9rgnDZkpTfa
    ulSVgQ1YhYik15zTkzRlBcC7LElz8CpBaliATYJ6bgS6mbwqVgY1mmh15qzOhmLSgpN21lbfnbHS
    FmFLir7YztSPU5fQIkKmqkMsMpswxUVAeyznhQKkwekaT0JwCfXgHyJGR5qmTlvAB89QOOdCH/bh
    SEVbECYjilOoZh1jqka6sVM9m9KPN5MVxYkE7InyYtTzBe3f1s+ovbXaRKhJQIASVLbHS8plYDJw
    cPVjWChnD4K4q/obn6a45svWpu7iVUm6+pjVUwnPLUy1SRLrnFieseGM9fI5k/8hzQFuZOkBwzyy
    xhGtoJWre6LtKu080q41YYxq8olzrpypPo7qS0Wcc8TPFYcTZ0SecctKVn7FYmLc1vdNea/h/2dD
    N2YO'''

# "Python for S60" compiled resource as a base-64-encoded, zlib-compressed data
pythons60rscdata = '''
    eJzL9pIXYACCVnFWhouea4oYtRk4QHwWIGYMqAgEs6E0CEgxnOGAsRnYGRlYgXKcnK4VJal5xZn5
    eYJg8f9AwDDkgQSDAhDaMCQypDPkMhQzVDLUM7QydDNMZJjOMJdhMcNKhvUMWxl2MxxkOM5wluEy
    w02G+wxPGV4zfGT4zvCXgZmRk5GfUZRRmhEAjnEjdg=='''