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