Added self-test attribute to validator.
[0publish.git] / archive.py
blob7f9fefe802f71ffebc8671b6f0180491d541062b
1 from xml.dom import minidom
2 from zeroinstall import SafeException
3 from zeroinstall.zerostore import Store, manifest
4 try:
5 from zeroinstall.zerostore import unpack
6 except ImportError:
7 # Older versions don't have it
8 import unpack
9 from zeroinstall.injector import namespaces
10 import os, time, re, shutil, tempfile
12 def ro_rmtree(root):
13 """Like shutil.rmtree, except that we also delete with read-only items.
14 @param root: the root of the subtree to remove
15 @type root: str
16 @since: 0.28"""
17 for main, dirs, files in os.walk(root):
18 os.chmod(main, 0700)
19 shutil.rmtree(root)
21 def manifest_for_dir(dir, alg):
22 if alg == 'sha1':
23 # (for older versions of the injector)
24 import sha
25 class SHA1:
26 def new_digest(self): return sha.new()
27 def generate_manifest(self, dir): return manifest.generate_manifest(dir)
28 def getID(self, digest): return 'sha1=' + digest.hexdigest()
29 algorithm = SHA1()
30 else:
31 algorithm = manifest.get_algorithm(alg)
33 digest = algorithm.new_digest()
34 for line in algorithm.generate_manifest(dir):
35 digest.update(line + '\n')
36 return algorithm.getID(digest)
38 def autopackage_get_start_offset(package):
39 for line in file(package):
40 if line.startswith('export dataSize=') or line.startswith('export data_size='):
41 return os.path.getsize(package) - int(line.split('"', 2)[1])
42 raise Exception("Can't find payload in autopackage (missing 'dataSize')")
44 def add_archive(data, url, local_file, extract, alg):
45 if local_file is None:
46 local_file = os.path.abspath(os.path.basename(url))
47 if not os.path.exists(local_file):
48 raise SafeException("Use --archive-file option to specify a local copy of the archive "
49 "(default file '%s' does not exist)" % local_file)
51 doc = minidom.parseString(data)
53 if alg is None:
54 alg = 'sha1new'
56 if local_file.endswith('.package'):
57 start_offset = autopackage_get_start_offset(local_file)
58 type = 'application/x-bzip-compressed-tar'
59 else:
60 start_offset = 0
61 type = None
63 all_impls = doc.documentElement.getElementsByTagNameNS(namespaces.XMLNS_IFACE, 'implementation')
64 tmpdir = tempfile.mkdtemp('-0publish')
65 try:
66 if start_offset or type:
67 unpack.unpack_archive(url, file(local_file), tmpdir, extract, start_offset = start_offset, type = type)
68 else:
69 unpack.unpack_archive(url, file(local_file), tmpdir, extract)
70 if extract:
71 extracted = os.path.join(tmpdir, extract)
72 else:
73 extracted = tmpdir
75 archive_id = manifest_for_dir(extracted, alg)
76 finally:
77 ro_rmtree(tmpdir)
79 local_ifaces = []
80 for impl in all_impls:
81 this_id = impl.getAttribute('id')
82 if this_id == archive_id:
83 break
84 if this_id.startswith('/') or this_id.startswith('.'):
85 local_ifaces.append(impl)
86 else:
87 if len(local_ifaces) == 0:
88 raise Exception('Nothing with id "%s", and no local implementations' % archive_id)
89 if len(local_ifaces) > 1:
90 raise Exception('Nothing with id "%s", and multiple local implementations!' % archive_id)
91 impl = local_ifaces[0]
92 impl.setAttribute('id', archive_id)
94 assert impl.getAttribute('id') == archive_id
96 nl = doc.createTextNode('\n ')
97 impl.appendChild(nl)
99 archive = doc.createElementNS(namespaces.XMLNS_IFACE, 'archive')
100 impl.appendChild(archive)
101 archive.setAttribute('href', url)
102 archive.setAttribute('size', str(os.stat(local_file).st_size - start_offset))
103 if extract is not None:
104 archive.setAttribute('extract', extract)
105 if start_offset:
106 archive.setAttribute('start-offset', str(start_offset))
107 if type:
108 archive.setAttribute('type', type)
110 nl = doc.createTextNode('\n ')
111 impl.appendChild(nl)
113 return doc.toxml()