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('/', '#')
13 def get_feed_path(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')
31 info("Exported key %s", fingerprint
)
33 class NoLocalVersions
:
34 def meets_restriction(self
, impl
):
35 if isinstance(impl
, model
.ZeroInstallImplementation
):
37 return not (i
.startswith('/') or i
.startswith('.'))
38 # Should package impls be OK?
41 no_local
= NoLocalVersions()
43 class NoLocalRestrictions(dict):
44 # This restriction applies to all interfaces, so ignore key
45 def get(self
, key
, default
):
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."""
52 if feed
.startswith('/'):
53 info("Skipping local feed %s", feed
)
56 cached
= basedir
.load_first_cache(namespaces
.config_site
,
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
):
64 shutil
.copyfile(cached
, feed_dst
)
65 info("Exported feed %s", feed
)
69 unused
, sigs
= gpg
.check_stream(stream
)
72 if isinstance(x
, gpg
.ValidSig
):
73 keys_used
.add(x
.fingerprint
)
75 warn("Signature problem: %s" % x
)
77 warn("Feed not cached: %s", feed
)
79 def get_implementation_path(impl
):
80 if impl
.startswith('/'):
82 return iface_cache
.iface_cache
.stores
.lookup(impl
)
84 def export_impls(export_dir
, impls
):
85 implementations
= os
.path
.join(export_dir
, 'implementations')
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
):
94 os
.unlink(os
.path
.join(dst
, '.manifest'))
95 info("Exported implementation %s", impl
)