From 6738cf228beb241b17a2ac4a0a49227a7114a799 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 25 Jan 2009 14:07:49 +0000 Subject: [PATCH] New --add-digest option allows adding additional manifest digests These are added to the element, which is created if necessary. This allows a smooth upgrade to newer digest algorithms without breaking old clients. --- 0publish | 4 +++ digest.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ validator.py | 1 + 3 files changed, 84 insertions(+) create mode 100644 digest.py diff --git a/0publish b/0publish index 8d35b0e..dc142ca 100755 --- a/0publish +++ b/0publish @@ -15,6 +15,7 @@ parser.add_option("--archive-url", help="add archive at this URL", action='store parser.add_option("--archive-file", help="local copy of archive-url", action='store', metavar='FILE') parser.add_option("--archive-extract", help="subdirectory of archive to extract", action='store', metavar='DIR') parser.add_option("-c", "--create", help="create file if nonexistant", action='store_true') +parser.add_option("-d", "--add-digest", help="add extra digests", action='store', metavar='ALG') parser.add_option("-e", "--edit", help="edit with $EDITOR", action='store_true') parser.add_option("-g", "--gpgsign", help="add a GPG signature block", action='store_true') parser.add_option("-k", "--key", help="key to use for signing") @@ -150,6 +151,9 @@ try: if options.local: import merge data = merge.merge(data, options.local) + if options.add_digest: + import digest + data = digest.add_digests(data, alg = options.add_digest) if options.edit: data = edit.edit(data) diff --git a/digest.py b/digest.py new file mode 100644 index 0000000..d2777ec --- /dev/null +++ b/digest.py @@ -0,0 +1,79 @@ +from xml.dom import minidom +from zeroinstall.injector import namespaces +from zeroinstall.zerostore import manifest, Stores, NotStored +import xmltools +from logging import info + +stores = Stores() + +def digests(impl): + yield impl.getAttribute('id').split('=', 1) + for x in xmltools.children(impl, 'manifest-digest'): + for name, value in x.attributes.itemsNS(): + if name[0] is None: + yield name[1], value + +def get_version(impl): + while impl: + v = impl.getAttribute('version') + if v: return v + impl = impl.parentNode + +def add_digest(impl, alg_name): + alg = manifest.get_algorithm(alg_name) + + # Scan through the existing digests + # - If we've already got the one we need, return + # - Otherwise, find a cached implementation we can use + existing_path = None + for a, value in digests(impl): + digest = '%s=%s' % (a, value) + if a == alg_name: + return False # Already signed with this algorithm + if not existing_path: + try: + existing_path = stores.lookup(digest) + if existing_path: + existing_digest = digest + except NotStored: + pass # OK + + if existing_path is None: + print "No implementations of %s cached; can't calculate new digest" % get_version(impl) + return False + + info("Verifying %s", existing_path) + manifest.verify(existing_path, existing_digest) + + print "Adding new digest to version %s" % get_version(impl) + + new_digest = alg.new_digest() + for line in alg.generate_manifest(existing_path): + new_digest.update(line + '\n') + + for md in xmltools.children(impl, 'manifest-digest'): + break + else: + md = xmltools.create_element(impl, 'manifest-digest') + md.setAttribute(alg_name, new_digest.hexdigest()) + + return True + +def add_digests(data, alg = None): + doc = minidom.parseString(data) + + if alg is None: + alg = 'sha1new' + + changed = False + for impl in doc.documentElement.getElementsByTagNameNS(namespaces.XMLNS_IFACE, 'implementation'): + if impl.getAttribute('id') in "./": + continue # Local implementation + + if add_digest(impl, alg): + changed = True + + if changed: + return doc.toxml() + else: + return data diff --git a/validator.py b/validator.py index 5df10b9..217ffdd 100644 --- a/validator.py +++ b/validator.py @@ -22,6 +22,7 @@ known_elements = { 'group' : group_impl_attribs, 'implementation' : ['id'] + group_impl_attribs, 'package-implementation' : ['package', 'main'], + 'manifest-digest' : ['sha1new', 'sha256'], 'archive' : ['href', 'size', 'extract', 'type', 'start-offset'], 'recipe' : [], -- 2.11.4.GIT