addon-sdk/source/python-lib/cuddlefish/preflight.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/python-lib/cuddlefish/preflight.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,77 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +import os, sys
     1.9 +import base64
    1.10 +import simplejson as json
    1.11 +
    1.12 +def create_jid():
    1.13 +    """Return 'jid1-XYZ', where 'XYZ' is a randomly-generated string. (in the
    1.14 +    previous jid0- series, the string securely identified a specific public
    1.15 +    key). To get a suitable add-on ID, append '@jetpack' to this string.
    1.16 +    """
    1.17 +    # per https://developer.mozilla.org/en/Install_Manifests#id all XPI id
    1.18 +    # values must either be in the form of a 128-bit GUID (crazy braces
    1.19 +    # and all) or in the form of an email address (crazy @ and all).
    1.20 +    # Firefox will refuse to install an add-on with an id that doesn't
    1.21 +    # match one of these forms. The actual regexp is at:
    1.22 +    # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm#130
    1.23 +    # So the JID needs an @-suffix, and the only legal punctuation is
    1.24 +    # "-._". So we start with a base64 encoding, and replace the
    1.25 +    # punctuation (+/) with letters (AB), losing a few bits of integrity.
    1.26 +
    1.27 +    # even better: windows has a maximum path length limitation of 256
    1.28 +    # characters:
    1.29 +    #  http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
    1.30 +    # (unless all paths are prefixed with "\\?\", I kid you not). The
    1.31 +    # typical install will put add-on code in a directory like:
    1.32 +    # C:\Documents and Settings\<username>\Application Data\Mozilla\Firefox\Profiles\232353483.default\extensions\$JID\...
    1.33 +    # (which is 108 chars long without the $JID).
    1.34 +    # Then the unpacked XPI contains packaged resources like:
    1.35 +    #  resources/$JID-api-utils-lib/main.js   (35 chars plus the $JID)
    1.36 +    #
    1.37 +    # We create a random 80 bit string, base64 encode that (with
    1.38 +    # AB instead of +/ to be path-safe), then bundle it into
    1.39 +    # "jid1-XYZ@jetpack". This gives us 27 characters. The resulting
    1.40 +    # main.js will have a path length of 211 characters, leaving us 45
    1.41 +    # characters of margin.
    1.42 +    #
    1.43 +    # 80 bits is enough to generate one billion JIDs and still maintain lower
    1.44 +    # than a one-in-a-million chance of accidental collision. (1e9 JIDs is 30
    1.45 +    # bits, square for the "birthday-paradox" to get 60 bits, add 20 bits for
    1.46 +    # the one-in-a-million margin to get 80 bits)
    1.47 +
    1.48 +    # if length were no issue, we'd prefer to use this:
    1.49 +    h = os.urandom(80/8)
    1.50 +    s = base64.b64encode(h, "AB").strip("=")
    1.51 +    jid = "jid1-" + s
    1.52 +    return jid
    1.53 +
    1.54 +def preflight_config(target_cfg, filename, stderr=sys.stderr):
    1.55 +    modified = False
    1.56 +    config = json.load(open(filename, 'r'))
    1.57 +
    1.58 +    if "id" not in config:
    1.59 +        print >>stderr, ("No 'id' in package.json: creating a new ID for you.")
    1.60 +        jid = create_jid()
    1.61 +        config["id"] = jid
    1.62 +        modified = True
    1.63 +
    1.64 +    if modified:
    1.65 +        i = 0
    1.66 +        backup = filename + ".backup"
    1.67 +        while os.path.exists(backup):
    1.68 +            if i > 1000:
    1.69 +                raise ValueError("I'm having problems finding a good name"
    1.70 +                                 " for the backup file. Please move %s out"
    1.71 +                                 " of the way and try again."
    1.72 +                                 % (filename + ".backup"))
    1.73 +            backup = filename + ".backup-%d" % i
    1.74 +            i += 1
    1.75 +        os.rename(filename, backup)
    1.76 +        new_json = json.dumps(config, indent=4)
    1.77 +        open(filename, 'w').write(new_json+"\n")
    1.78 +        return False, True
    1.79 +
    1.80 +    return True, False

mercurial