michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import os, sys michael@0: import base64 michael@0: import simplejson as json michael@0: michael@0: def create_jid(): michael@0: """Return 'jid1-XYZ', where 'XYZ' is a randomly-generated string. (in the michael@0: previous jid0- series, the string securely identified a specific public michael@0: key). To get a suitable add-on ID, append '@jetpack' to this string. michael@0: """ michael@0: # per https://developer.mozilla.org/en/Install_Manifests#id all XPI id michael@0: # values must either be in the form of a 128-bit GUID (crazy braces michael@0: # and all) or in the form of an email address (crazy @ and all). michael@0: # Firefox will refuse to install an add-on with an id that doesn't michael@0: # match one of these forms. The actual regexp is at: michael@0: # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm#130 michael@0: # So the JID needs an @-suffix, and the only legal punctuation is michael@0: # "-._". So we start with a base64 encoding, and replace the michael@0: # punctuation (+/) with letters (AB), losing a few bits of integrity. michael@0: michael@0: # even better: windows has a maximum path length limitation of 256 michael@0: # characters: michael@0: # http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx michael@0: # (unless all paths are prefixed with "\\?\", I kid you not). The michael@0: # typical install will put add-on code in a directory like: michael@0: # C:\Documents and Settings\\Application Data\Mozilla\Firefox\Profiles\232353483.default\extensions\$JID\... michael@0: # (which is 108 chars long without the $JID). michael@0: # Then the unpacked XPI contains packaged resources like: michael@0: # resources/$JID-api-utils-lib/main.js (35 chars plus the $JID) michael@0: # michael@0: # We create a random 80 bit string, base64 encode that (with michael@0: # AB instead of +/ to be path-safe), then bundle it into michael@0: # "jid1-XYZ@jetpack". This gives us 27 characters. The resulting michael@0: # main.js will have a path length of 211 characters, leaving us 45 michael@0: # characters of margin. michael@0: # michael@0: # 80 bits is enough to generate one billion JIDs and still maintain lower michael@0: # than a one-in-a-million chance of accidental collision. (1e9 JIDs is 30 michael@0: # bits, square for the "birthday-paradox" to get 60 bits, add 20 bits for michael@0: # the one-in-a-million margin to get 80 bits) michael@0: michael@0: # if length were no issue, we'd prefer to use this: michael@0: h = os.urandom(80/8) michael@0: s = base64.b64encode(h, "AB").strip("=") michael@0: jid = "jid1-" + s michael@0: return jid michael@0: michael@0: def preflight_config(target_cfg, filename, stderr=sys.stderr): michael@0: modified = False michael@0: config = json.load(open(filename, 'r')) michael@0: michael@0: if "id" not in config: michael@0: print >>stderr, ("No 'id' in package.json: creating a new ID for you.") michael@0: jid = create_jid() michael@0: config["id"] = jid michael@0: modified = True michael@0: michael@0: if modified: michael@0: i = 0 michael@0: backup = filename + ".backup" michael@0: while os.path.exists(backup): michael@0: if i > 1000: michael@0: raise ValueError("I'm having problems finding a good name" michael@0: " for the backup file. Please move %s out" michael@0: " of the way and try again." michael@0: % (filename + ".backup")) michael@0: backup = filename + ".backup-%d" % i michael@0: i += 1 michael@0: os.rename(filename, backup) michael@0: new_json = json.dumps(config, indent=4) michael@0: open(filename, 'w').write(new_json+"\n") michael@0: return False, True michael@0: michael@0: return True, False