preflight.py 3.4 KB

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