Build dialog box manually
[0export.git] / utils.py
blob55da21578d4bcdbb0d903bb9acffcc7f0106026a
1 import os, subprocess, shutil
2 from logging import info
4 from zeroinstall import SafeException
5 from zeroinstall.injector import model, namespaces, gpg, iface_cache
6 from zeroinstall.support import basedir
7 from zeroinstall.zerostore import manifest
9 def escape_slashes(path):
10 return path.replace('/', '#')
12 # From 0mirror
13 def get_feed_path(feed):
14 if '#' in feed:
15 raise SafeException("Invalid URL '%s'" % feed)
16 scheme, rest = feed.split('://', 1)
17 domain, rest = rest.split('/', 1)
18 assert scheme in ('http', 'https', 'ftp') # Just to check for mal-formed lines; add more as needed
19 for x in [scheme, domain, rest]:
20 if not x or x.startswith(','):
21 raise SafeException("Invalid URL '%s'" % feed)
22 return os.path.join('feeds', scheme, domain, escape_slashes(rest))
24 def export_key(fingerprint, key_dir):
25 key_path = os.path.join(key_dir, fingerprint[-16:] + '.gpg')
26 child = subprocess.Popen(['gpg', '-a', '--export', fingerprint], stdout = subprocess.PIPE)
27 keydata, unused = child.communicate()
28 stream = file(key_path, 'w')
29 stream.write(keydata)
30 stream.close()
31 info("Exported key %s", fingerprint)
33 class NoLocalVersions:
34 def meets_restriction(self, impl):
35 if isinstance(impl, model.ZeroInstallImplementation):
36 i = impl.id
37 return not (i.startswith('/') or i.startswith('.'))
38 # Should package impls be OK?
39 return False
41 no_local = NoLocalVersions()
43 class NoLocalRestrictions(dict):
44 # This restriction applies to all interfaces, so ignore key
45 def get(self, key, default):
46 return [no_local]
48 def export_feeds(export_dir, feeds, keys_used):
49 """Copy each feed in feeds from the cache to export_dir.
50 Add all signing key fingerprints to keys_used."""
51 for feed in feeds:
52 if feed.startswith('/'):
53 info("Skipping local feed %s", feed)
54 continue
55 # Store feed
56 cached = basedir.load_first_cache(namespaces.config_site,
57 'interfaces',
58 model.escape(feed))
59 if cached:
60 feed_dir = os.path.join(export_dir, get_feed_path(feed))
61 feed_dst = os.path.join(feed_dir, 'latest.xml')
62 if not os.path.isdir(feed_dir):
63 os.makedirs(feed_dir)
64 shutil.copyfile(cached, feed_dst)
65 info("Exported feed %s", feed)
67 # Get the keys
68 stream = file(cached)
69 unused, sigs = gpg.check_stream(stream)
70 stream.close()
71 for x in sigs:
72 if isinstance(x, gpg.ValidSig):
73 keys_used.add(x.fingerprint)
74 else:
75 warn("Signature problem: %s" % x)
76 else:
77 warn("Feed not cached: %s", feed)
79 def get_implementation_path(impl):
80 if impl.startswith('/'):
81 return impl
82 return iface_cache.iface_cache.stores.lookup(impl)
84 def export_impls(export_dir, impls):
85 implementations = os.path.join(export_dir, 'implementations')
86 for impl in impls:
87 # Store implementation
88 src = get_implementation_path(impl)
89 dst = os.path.join(implementations, impl)
90 shutil.copytree(src, dst, symlinks = True)
91 manifest.verify(dst, impl)
92 for root, dirs, files in os.walk(dst):
93 os.chmod(root, 0755)
94 os.unlink(os.path.join(dst, '.manifest'))
95 info("Exported implementation %s", impl)