1 from xml
.dom
import minidom
2 from zeroinstall
import SafeException
3 from zeroinstall
.zerostore
import manifest
5 from zeroinstall
.zerostore
import unpack
7 # Older versions don't have it
9 from zeroinstall
.injector
import namespaces
10 import os
, shutil
, tempfile
16 """Like shutil.rmtree, except that we also delete with read-only items.
17 @param root: the root of the subtree to remove
20 for main
, dirs
, files
in os
.walk(root
):
24 def manifest_for_dir(dir, alg
):
26 # (for older versions of the injector)
29 def new_digest(self
): return sha
.new()
30 def generate_manifest(self
, dir): return manifest
.generate_manifest(dir)
31 def getID(self
, digest
): return 'sha1=' + digest
.hexdigest()
34 algorithm
= manifest
.get_algorithm(alg
)
36 digest
= algorithm
.new_digest()
37 for line
in algorithm
.generate_manifest(dir):
38 digest
.update(line
+ '\n')
39 return algorithm
.getID(digest
)
41 def autopackage_get_start_offset(package
):
42 for line
in file(package
):
43 if line
.startswith('export dataSize=') or line
.startswith('export data_size='):
44 return os
.path
.getsize(package
) - int(line
.split('"', 2)[1])
45 raise Exception("Can't find payload in autopackage (missing 'dataSize')")
47 def add_archive(data
, url
, local_file
, extract
, algs
):
48 if local_file
is None:
49 local_file
= os
.path
.abspath(os
.path
.basename(url
))
50 if not os
.path
.exists(local_file
):
51 raise SafeException("Use --archive-file option to specify a local copy of the archive "
52 "(default file '%s' does not exist)" % local_file
)
54 doc
= minidom
.parseString(data
)
58 if local_file
.endswith('.package'):
59 start_offset
= autopackage_get_start_offset(local_file
)
60 type = 'application/x-bzip-compressed-tar'
65 all_impls
= doc
.documentElement
.getElementsByTagNameNS(namespaces
.XMLNS_IFACE
, 'implementation')
66 tmpdir
= tempfile
.mkdtemp('-0publish')
68 if start_offset
or type:
69 unpack
.unpack_archive(url
, file(local_file
), tmpdir
, extract
, start_offset
= start_offset
, type = type)
71 unpack
.unpack_archive(url
, file(local_file
), tmpdir
, extract
)
73 extracted
= os
.path
.join(tmpdir
, extract
)
77 archive_id
= manifest_for_dir(extracted
, algs
[0])
78 extra_digests
= set([manifest_for_dir(extracted
, a
) for a
in algs
[1:]])
80 if '=' not in archive_id
:
81 # New-style digests can't go in the ID
82 extra_digests
.add(archive_id
)
87 for impl
in all_impls
:
88 this_id
= impl
.getAttribute('id')
89 if this_id
== archive_id
:
91 if this_id
.startswith('/') or this_id
.startswith('.'):
92 local_ifaces
.append(impl
)
94 if len(local_ifaces
) == 0:
95 raise Exception('Nothing with id "%s", and no local implementations' % archive_id
)
96 if len(local_ifaces
) > 1:
97 raise Exception('Nothing with id "%s", and multiple local implementations!' % archive_id
)
98 impl
= local_ifaces
[0]
99 impl
.setAttribute('id', archive_id
)
101 assert impl
.getAttribute('id') == archive_id
103 archive
= xmltools
.create_element(impl
, 'archive')
104 archive
.setAttribute('href', url
)
105 archive
.setAttribute('size', str(os
.stat(local_file
).st_size
- start_offset
))
106 if extract
is not None:
107 archive
.setAttribute('extract', extract
)
109 archive
.setAttribute('start-offset', str(start_offset
))
111 archive
.setAttribute('type', type)
113 # Remove digests we already
114 for x
in xmltools
.children(impl
, 'manifest-digest'):
118 digest_element
= doc
.createElementNS(namespaces
.XMLNS_IFACE
, 'manifest-digest')
119 xmltools
.insert_before(digest_element
, archive
)
120 for x
in extra_digests
- set([digest
.digests(impl
)]):
121 for old_alg
in ['sha1=', 'sha1new=', 'sha256=']:
122 if x
.startswith(old_alg
):
123 name
, value
= x
.split('=')
126 assert '_' in x
, "Invalid digest %s" % x
127 name
, value
= x
.split('_')
128 digest_element
.setAttribute(name
, value
)
130 return doc
.toxml('utf-8')