def testDomain(self): self.assertEqual("example.com", trust.domain_from_url('http://example.com/foo')) self.assertRaises(SafeException, lambda: trust.domain_from_url('/tmp/feed.xml')) self.assertRaises(SafeException, lambda: trust.domain_from_url('http:///foo')) self.assertRaises(SafeException, lambda: trust.domain_from_url('http://*/foo')) self.assertRaises(SafeException, lambda: trust.domain_from_url(''))
def _queue_confirm_import_feed(self, pending, valid_sigs): # If we're already confirming something else, wait for that to finish... while self._current_confirm is not None: yield self._current_confirm # Check whether we still need to confirm. The user may have # already approved one of the keys while dealing with another # feed. from zeroinstall.injector import trust domain = trust.domain_from_url(pending.url) for sig in valid_sigs: is_trusted = trust.trust_db.is_trusted(sig.fingerprint, domain) if is_trusted: return # Take the lock and confirm this feed self._current_confirm = lock = tasks.Blocker('confirm key lock') try: done = self.confirm_import_feed(pending, valid_sigs) if done is not None: yield done tasks.check(done) finally: self._current_confirm = None lock.trigger()
def confirm_keys(self, pending): assert pending.sigs from zeroinstall.injector import gpg valid_sigs = [s for s in pending.sigs if isinstance(s, gpg.ValidSig)] if not valid_sigs: def format_sig(sig): msg = str(sig) if sig.messages: msg += "\nMessages from GPG:\n" + sig.messages return msg raise SafeException(_('No valid signatures found on "%(url)s". Signatures:%(signatures)s') % {'url': pending.url, 'signatures': ''.join(['\n- ' + format_sig(s) for s in pending.sigs])}) domain = trust.domain_from_url(pending.url) for sig in valid_sigs: if self.config.auto_approve_keys: existing_feed = self.config.iface_cache.get_feed(pending.url) if not existing_feed: trust_db.trust_key(sig.fingerprint, domain) trust_db.notify() # Take the lock and confirm this feed self._current_confirm = lock = tasks.Blocker('confirm key lock') self._current_confirm = None lock.trigger()
def add_feed(self, feed, active): self.active[feed] = active metadata = ET.Element('metadata') metadata.attrib["active"] = str(active) sig_data = sig_cache.get(feed.url) for sig in sig_data: fingerprint = sig.get("fingerprint") if fingerprint is None: signer = ET.SubElement(metadata, "signer") signer.attrib["error"] = sig["error"] else: fingerprint = aliases.get(fingerprint, fingerprint) assert fingerprint not in aliases, fingerprint if active: assert fingerprint not in test_keys, (fingerprint, feed) if fingerprint not in self.users: self.users[fingerprint] = User() self.users[fingerprint].add_feed(feed, sig, active) signer = ET.SubElement(metadata, "signer") signer.attrib["user"] = fingerprint signer.attrib["date"] = format_date(sig["date"]) domain = trust.domain_from_url(feed.url) if domain not in self.sites: self.sites[domain] = [] self.sites[domain].append(feed) self.feeds.append((feed, metadata))
def confirm_import_feed(self, pending, valid_sigs): """Sub-classes should override this method to interact with the user about new feeds. If multiple feeds need confirmation, L{confirm_keys} will only invoke one instance of this method at a time. @param pending: the new feed to be imported @type pending: L{PendingFeed} @param valid_sigs: maps signatures to a list of fetchers collecting information about the key @type valid_sigs: {L{gpg.ValidSig} : L{fetch.KeyInfoFetcher}} @since: 0.42 @see: L{confirm_keys}""" from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print >>sys.stderr, _("Feed: %s") % pending.url print >>sys.stderr, _("The feed is correctly signed with the following keys:") for x in valid_sigs: print >>sys.stderr, "-", x def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print "%s: " % kf.fingerprint for key_info in infos: print >>sys.stderr, "-", text(key_info) shown.add(key_info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print >>sys.stderr, kf.status stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception, ex: warn(_("Failed to get key info: %s"), ex) if stdin.happened: print >>sys.stderr, _("Skipping remaining key lookups due to input from user") break
def confirm_import_feed(self, pending, valid_sigs): """ verify the feed """ from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print "Feed: %s" % pending.url print "The feed is correctly signed with the following keys:" for x in valid_sigs: print "-", x def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print("%s: " % kf.fingerprint) for key_info in infos: print("-", text(key_info) ) shown.add(key_info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print(kf.status) stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception as ex: warn(_("Failed to get key info: %s"), ex) if stdin.happened: print("Skipping remaining key lookups due to input from user") break for key in valid_sigs: print("Trusting %(key_fingerprint)s for %(domain)s") % {'key_fingerprint': key.fingerprint, 'domain': domain} trust.trust_db.trust_key(key.fingerprint, domain)
def confirm_import_feed(self, pending, gpg_sigs): from zeroinstall.injector import trust domain = trust.domain_from_url(pending.url) self._confirm_keys = [] for sig in gpg_sigs.keys(): self._confirm_keys.append(_Key(sig, domain)) self._confirmed = tasks.Blocker('confirm_import_feed') self._confirm_next() yield [self._confirmed] tasks.check([self._confirmed])
def confirm_trust_keys(self, interface, sigs, iface_xml): """We don't trust any of the signatures yet. Ask the user. When done update the L{trust} database, and then call L{trust.TrustDB.notify}. @deprecated: see L{confirm_keys} @arg interface: the interface being updated @arg sigs: a list of signatures (from L{gpg.check_stream}) @arg iface_xml: the downloaded data (not yet trusted) @return: a blocker, if confirmation will happen asynchronously, or None @rtype: L{tasks.Blocker}""" import warnings warnings.warn(_("Use confirm_keys, not confirm_trust_keys"), DeprecationWarning, stacklevel=2) from zeroinstall.injector import trust, gpg assert sigs valid_sigs = [s for s in sigs if isinstance(s, gpg.ValidSig)] if not valid_sigs: raise SafeException( 'No valid signatures found on "%s". Signatures:%s' % (interface.uri, ''.join(['\n- ' + str(s) for s in sigs]))) domain = trust.domain_from_url(interface.uri) # Ask on stderr, because we may be writing XML to stdout print >> sys.stderr, "\nInterface:", interface.uri print >> sys.stderr, "The interface is correctly signed with the following keys:" for x in valid_sigs: print >> sys.stderr, "-", x if len(valid_sigs) == 1: print >> sys.stderr, "Do you want to trust this key to sign feeds from '%s'?" % domain else: print >> sys.stderr, "Do you want to trust all of these keys to sign feeds from '%s'?" % domain while True: print >> sys.stderr, "Trust [Y/N] ", i = raw_input() if not i: continue if i in 'Nn': raise NoTrustedKeys(_('Not signed with a trusted key')) if i in 'Yy': break for key in valid_sigs: print >> sys.stderr, "Trusting", key.fingerprint, "for", domain trust.trust_db.trust_key(key.fingerprint, domain) trust.trust_db.notify()
def confirm_trust_keys(self, interface, sigs, iface_xml): """We don't trust any of the signatures yet. Ask the user. When done update the L{trust} database, and then call L{trust.TrustDB.notify}. @deprecated: see L{confirm_keys} @arg interface: the interface being updated @arg sigs: a list of signatures (from L{gpg.check_stream}) @arg iface_xml: the downloaded data (not yet trusted) @return: a blocker, if confirmation will happen asynchronously, or None @rtype: L{tasks.Blocker}""" import warnings warnings.warn("Use confirm_keys, not confirm_trust_keys", DeprecationWarning, stacklevel = 2) from zeroinstall.injector import trust, gpg assert sigs valid_sigs = [s for s in sigs if isinstance(s, gpg.ValidSig)] if not valid_sigs: raise SafeException('No valid signatures found on "%s". Signatures:%s' % (interface.uri, ''.join(['\n- ' + str(s) for s in sigs]))) domain = trust.domain_from_url(interface.uri) # Ask on stderr, because we may be writing XML to stdout print >>sys.stderr, "\nInterface:", interface.uri print >>sys.stderr, _("The feed is correctly signed with the following keys:") for x in valid_sigs: print >>sys.stderr, "-", x if len(valid_sigs) == 1: print >>sys.stderr, _("Do you want to trust this key to sign feeds from '%s'?") % domain else: print >>sys.stderr, _("Do you want to trust all of these keys to sign feeds from '%s'?") % domain while True: print >>sys.stderr, _("Trust [Y/N] "), i = raw_input() if not i: continue if i in 'Nn': raise NoTrustedKeys(_('Not signed with a trusted key')) if i in 'Yy': break for key in valid_sigs: print >>sys.stderr, _("Trusting %s for %s") % (key.fingerprint, domain) trust.trust_db.trust_key(key.fingerprint, domain) trust.trust_db.notify()
def confirm_keys(self, pending): assert pending.sigs from zeroinstall.injector import gpg valid_sigs = [s for s in pending.sigs if isinstance(s, gpg.ValidSig)] if not valid_sigs: def format_sig(sig): msg = str(sig) if sig.messages: msg += "\nMessages from GPG:\n" + sig.messages return msg raise SafeException( _('No valid signatures found on "%(url)s". Signatures:%(signatures)s' ) % { 'url': pending.url, 'signatures': ''.join(['\n- ' + format_sig(s) for s in pending.sigs]) }) domain = trust.domain_from_url(pending.url) for sig in valid_sigs: if self.config.auto_approve_keys: existing_feed = self.config.iface_cache.get_feed(pending.url) if not existing_feed: trust_db.trust_key(sig.fingerprint, domain) trust_db.notify() # Take the lock and confirm this feed self._current_confirm = lock = tasks.Blocker('confirm key lock') self._current_confirm = None lock.trigger()
def __init__(self, pending, valid_sigs, parent): """@since: 0.42""" assert valid_sigs gtk.Dialog.__init__(self) self.set_has_separator(False) self.set_position(gtk.WIN_POS_CENTER) self.set_transient_for(parent) self.closed = tasks.Blocker(_("confirming keys with user")) domain = trust.domain_from_url(pending.url) assert domain def destroy(box): self.closed.trigger() self.connect('destroy', destroy) self.set_title(_('Confirm trust')) vbox = gtk.VBox(False, 4) vbox.set_border_width(4) self.vbox.pack_start(vbox, True, True, 0) notebook = gtk.Notebook() if len(valid_sigs) == 1: notebook.set_show_tabs(False) label = left(_('Checking: %s') % pending.url) label.set_padding(4, 4) vbox.pack_start(label, False, True, 0) currently_trusted_keys = trust.trust_db.get_keys_for_domain(domain) if currently_trusted_keys: keys = [ gpg.load_key(fingerprint) for fingerprint in currently_trusted_keys ] descriptions = [ _("%(key_name)s\n(fingerprint: %(key_fingerprint)s)") % { 'key_name': key.name, 'key_fingerprint': pretty_fp(key.fingerprint) } for key in keys ] else: descriptions = [_('None')] frame(vbox, _('Keys already approved for "%s"') % domain, '\n'.join(descriptions)) label = left( translation.ngettext('This key signed the feed:', 'These keys signed the feed:', len(valid_sigs))) label.set_padding(4, 4) vbox.pack_start(label, False, True, 0) vbox.pack_start(notebook, True, True, 0) self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP) self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK) self.set_default_response(gtk.RESPONSE_OK) trust_checkbox = {} # Sig -> CheckButton def ok_sensitive(): trust_any = False for toggle in trust_checkbox.values(): if toggle.get_active(): trust_any = True break self.set_response_sensitive(gtk.RESPONSE_OK, trust_any) first = True for sig in valid_sigs: if hasattr(sig, 'get_details'): name = '<unknown>' details = sig.get_details() for item in details: if item[0] == 'uid' and len(item) > 9: name = item[9] break else: name = None page = gtk.VBox(False, 4) page.set_border_width(8) frame(page, _('Fingerprint'), pretty_fp(sig.fingerprint)) if name is not None: frame(page, _('Claimed identity'), name) frame(page, _('Unreliable hints database says'), make_hints_area(self.closed, valid_sigs[sig])) already_trusted = trust.trust_db.get_trust_domains(sig.fingerprint) if already_trusted: frame(page, _('You already trust this key for these domains'), '\n'.join(already_trusted)) trust_checkbox[sig] = gtk.CheckButton(_('_Trust this key')) page.pack_start(trust_checkbox[sig], False, True, 0) trust_checkbox[sig].connect('toggled', lambda t: ok_sensitive()) notebook.append_page(page, gtk.Label(name or 'Signature')) if first: trust_checkbox[sig].set_active(True) first = False ok_sensitive() self.vbox.show_all() if len(valid_sigs) == 1: for box in trust_checkbox.values(): box.hide() def response(box, resp): if resp == gtk.RESPONSE_HELP: trust_help.display() return if resp == gtk.RESPONSE_OK: to_trust = [ sig for sig in trust_checkbox if trust_checkbox[sig].get_active() ] if not self._confirm_unknown_keys(to_trust, valid_sigs): return self.trust_keys(to_trust, domain) self.destroy() self.connect('response', response)
def confirm_import_feed(self, pending, valid_sigs): """ verify the feed """ from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print "Feed: %s" % pending.url print "The feed is correctly signed with the following keys:" for x in valid_sigs: print "-", x def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print("%s: " % kf.fingerprint) for key_info in infos: print("-", text(key_info)) shown.add(key_info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print(kf.status) stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception as ex: warn(_("Failed to get key info: %s"), ex) if stdin.happened: print( "Skipping remaining key lookups due to input from user" ) break for key in valid_sigs: print("Trusting %(key_fingerprint)s for %(domain)s") % { 'key_fingerprint': key.fingerprint, 'domain': domain } trust.trust_db.trust_key(key.fingerprint, domain)
def confirm_import_feed(self, pending, valid_sigs): """Sub-classes should override this method to interact with the user about new feeds. If multiple feeds need confirmation, L{trust.TrustMgr.confirm_keys} will only invoke one instance of this method at a time. @param pending: the new feed to be imported @type pending: L{PendingFeed} @param valid_sigs: maps signatures to a list of fetchers collecting information about the key @type valid_sigs: {L{gpg.ValidSig} : L{fetch.KeyInfoFetcher}} @since: 0.42""" from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print(_("Feed: %s") % pending.url, file=sys.stderr) print(_("The feed is correctly signed with the following keys:"), file=sys.stderr) for x in valid_sigs: print("-", x, file=sys.stderr) def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print("%s: " % kf.fingerprint) for key_info in infos: print("-", text(key_info), file=sys.stderr) shown.add(key_info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print(kf.status, file=sys.stderr) stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception as ex: logger.warning(_("Failed to get key info: %s"), ex) if stdin.happened: print(_("Skipping remaining key lookups due to input from user"), file=sys.stderr) break if not shown: print(_("Warning: Nothing known about this key!"), file=sys.stderr) if len(valid_sigs) == 1: print(_("Do you want to trust this key to sign feeds from '%s'?") % domain, file=sys.stderr) else: print(_("Do you want to trust all of these keys to sign feeds from '%s'?") % domain, file=sys.stderr) while True: print(_("Trust [Y/N] "), end=' ', file=sys.stderr) i = support.raw_input() if not i: continue if i in 'Nn': raise NoTrustedKeys(_('Not signed with a trusted key')) if i in 'Yy': break trust.trust_db._dry_run = self.dry_run for key in valid_sigs: print(_("Trusting %(key_fingerprint)s for %(domain)s") % {'key_fingerprint': key.fingerprint, 'domain': domain}, file=sys.stderr) trust.trust_db.trust_key(key.fingerprint, domain)
def confirm_import_feed(self, pending, valid_sigs): """Sub-classes should override this method to interact with the user about new feeds. If multiple feeds need confirmation, L{trust.TrustMgr.confirm_keys} will only invoke one instance of this method at a time. @param pending: the new feed to be imported @type pending: L{PendingFeed} @param valid_sigs: maps signatures to a list of fetchers collecting information about the key @type valid_sigs: {L{gpg.ValidSig} : L{fetch.KeyInfoFetcher}} @since: 0.42""" from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print >>sys.stderr, _("Feed: %s") % pending.url print >>sys.stderr, _("The feed is correctly signed with the following keys:") for x in valid_sigs: print >>sys.stderr, "-", x def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print "%s: " % kf.fingerprint for key_info in infos: print >>sys.stderr, "-", text(key_info) shown.add(key_info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print >>sys.stderr, kf.status stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception as ex: warn(_("Failed to get key info: %s"), ex) if stdin.happened: print >>sys.stderr, _("Skipping remaining key lookups due to input from user") break if not shown: print >>sys.stderr, _("Warning: Nothing known about this key!") if len(valid_sigs) == 1: print >>sys.stderr, _("Do you want to trust this key to sign feeds from '%s'?") % domain else: print >>sys.stderr, _("Do you want to trust all of these keys to sign feeds from '%s'?") % domain while True: print >>sys.stderr, _("Trust [Y/N] "), i = raw_input() if not i: continue if i in 'Nn': raise NoTrustedKeys(_('Not signed with a trusted key')) if i in 'Yy': break for key in valid_sigs: print >>sys.stderr, _("Trusting %(key_fingerprint)s for %(domain)s") % {'key_fingerprint': key.fingerprint, 'domain': domain} trust.trust_db.trust_key(key.fingerprint, domain)
def do_install(self, archive_stream, progress_bar, archive_offset): # Step 1. Import GPG keys # Maybe GPG has never been run before. Let it initialse, or we'll get an error code # from the first import... (ignore return value here) subprocess.call([get_gpg(), '--check-trustdb', '-q']) key_dir = os.path.join(mydir, 'keys') for key in os.listdir(key_dir): check_call([get_gpg(), '--import', '-q', os.path.join(key_dir, key)]) # Step 2. Import feeds and trust their signing keys for root, dirs, files in os.walk(os.path.join(mydir, 'feeds')): if 'latest.xml' in files: feed_path = os.path.join(root, 'latest.xml') icon_path = os.path.join(root, 'icon.png') # Get URI feed_stream = file(feed_path) doc = qdom.parse(feed_stream) uri = doc.getAttribute('uri') assert uri, "Missing 'uri' attribute on root element in '%s'" % feed_path domain = trust.domain_from_url(uri) feed_stream.seek(0) stream, sigs = gpg.check_stream(feed_stream) for s in sigs: if not trust.trust_db.is_trusted(s.fingerprint, domain): print "Adding key %s to trusted list for %s" % (s.fingerprint, domain) trust.trust_db.trust_key(s.fingerprint, domain) oldest_sig = min([s.get_timestamp() for s in sigs]) try: config.iface_cache.update_feed_from_network(uri, stream.read(), oldest_sig) except iface_cache.ReplayAttack: # OK, the user has a newer copy already pass if feed_stream != stream: feed_stream.close() stream.close() if os.path.exists(icon_path): icons_cache = basedir.save_cache_path(namespaces.config_site, 'interface_icons') icon_file = os.path.join(icons_cache, model.escape(uri)) if not os.path.exists(icon_file): shutil.copyfile(icon_path, icon_file) # Step 3. Solve to find out which implementations we actually need archive_stream.seek(archive_offset) extract_impls = {} # Impls we need but which are compressed (ID -> Impl) tmp = tempfile.mkdtemp(prefix = '0export-') try: # Create a "fake store" with the implementation in the archive archive = tarfile.open(name=archive_stream.name, mode='r|', fileobj=archive_stream) fake_store = FakeStore() for tarmember in archive: if tarmember.name.startswith('implementations'): impl = os.path.basename(tarmember.name).split('.')[0] fake_store.impls.add(impl) bootstrap_store = zerostore.Store(os.path.join(mydir, 'implementations')) stores = config.stores toplevel_uris = [uri.strip() for uri in file(os.path.join(mydir, 'toplevel_uris'))] ZEROINSTALL_URI = "@ZEROINSTALL_URI@" for uri in [ZEROINSTALL_URI] + toplevel_uris: # This is so the solver treats versions in the setup archive as 'cached', # meaning that it will prefer using them to doing a download stores.stores.append(bootstrap_store) stores.stores.append(fake_store) # Shouldn't need to download anything, but we might not have all feeds r = requirements.Requirements(uri) d = driver.Driver(config = config, requirements = r) config.network_use = model.network_minimal download_feeds = d.solve_with_downloads() h.wait_for_blocker(download_feeds) assert d.solver.ready, d.solver.get_failure_reason() # Add anything chosen from the setup store to the main store stores.stores.remove(fake_store) stores.stores.remove(bootstrap_store) for iface, impl in d.get_uncached_implementations(): print >>sys.stderr, "Need to import", impl if impl.id in fake_store.impls: # Delay extraction extract_impls[impl.id] = impl else: impl_src = os.path.join(mydir, 'implementations', impl.id) if os.path.isdir(impl_src): stores.add_dir_to_cache(impl.id, impl_src) else: print >>sys.stderr, "Required impl %s (for %s) not present" % (impl, iface) # Remember where we copied 0launch to, because we'll need it after # the temporary directory is deleted. if uri == ZEROINSTALL_URI: global copied_0launch_in_cache impl = d.solver.selections.selections[uri] if not impl.id.startswith('package:'): copied_0launch_in_cache = impl.get_path(stores = config.stores) # (else we selected the distribution version of Zero Install) finally: shutil.rmtree(tmp) # Count total number of bytes to extract extract_total = 0 for impl in extract_impls.values(): impl_info = archive.getmember('implementations/' + impl.id + '.tar.bz2') extract_total += impl_info.size self.sent = 0 # Actually extract+import implementations in archive archive_stream.seek(archive_offset) archive = tarfile.open(name=archive_stream.name, mode='r|', fileobj=archive_stream) for tarmember in archive: if not tarmember.name.startswith('implementations'): continue impl_id = tarmember.name.split('/')[1].split('.')[0] if impl_id not in extract_impls: print "Skip", impl_id continue print "Extracting", impl_id tmp = tempfile.mkdtemp(prefix = '0export-') try: impl_stream = archive.extractfile(tarmember) self.child = subprocess.Popen('bunzip2|tar xf -', shell = True, stdin = subprocess.PIPE, cwd = tmp) mainloop = gobject.MainLoop(gobject.main_context_default()) def pipe_ready(src, cond): data = impl_stream.read(4096) if not data: mainloop.quit() self.child.stdin.close() return False self.sent += len(data) if progress_bar: progress_bar.set_fraction(float(self.sent) / extract_total) self.child.stdin.write(data) return True gobject.io_add_watch(self.child.stdin, gobject.IO_OUT | gobject.IO_HUP, pipe_ready, priority = gobject.PRIORITY_LOW) mainloop.run() self.child.wait() if self.child.returncode: raise Exception("Failed to unpack archive (code %d)" % self.child.returncode) stores.add_dir_to_cache(impl_id, tmp) finally: shutil.rmtree(tmp) return toplevel_uris
def __init__(self, pending, valid_sigs, parent): """@since: 0.42""" assert valid_sigs gtk.Dialog.__init__(self) self.set_has_separator(False) self.set_position(gtk.WIN_POS_CENTER) self.set_transient_for(parent) self.closed = tasks.Blocker(_("confirming keys with user")) domain = trust.domain_from_url(pending.url) assert domain def destroy(box): self.closed.trigger() self.connect('destroy', destroy) self.set_title(_('Confirm trust')) vbox = gtk.VBox(False, 4) vbox.set_border_width(4) self.vbox.pack_start(vbox, True, True, 0) notebook = gtk.Notebook() if len(valid_sigs) == 1: notebook.set_show_tabs(False) label = left(_('Checking: %s') % pending.url) label.set_padding(4, 4) vbox.pack_start(label, False, True, 0) currently_trusted_keys = trust.trust_db.get_keys_for_domain(domain) if currently_trusted_keys: keys = [gpg.load_key(fingerprint) for fingerprint in currently_trusted_keys] descriptions = [_("%(key_name)s\n(fingerprint: %(key_fingerprint)s)") % {'key_name': key.name, 'key_fingerprint': pretty_fp(key.fingerprint)} for key in keys] else: descriptions = [_('None')] frame(vbox, _('Keys already approved for "%s"') % domain, '\n'.join(descriptions)) label = left(translation.ngettext('This key signed the feed:', 'These keys signed the feed:', len(valid_sigs))) label.set_padding(4, 4) vbox.pack_start(label, False, True, 0) vbox.pack_start(notebook, True, True, 0) self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP) self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK) self.set_default_response(gtk.RESPONSE_OK) trust_checkbox = {} # Sig -> CheckButton def ok_sensitive(): trust_any = False for toggle in trust_checkbox.values(): if toggle.get_active(): trust_any = True break self.set_response_sensitive(gtk.RESPONSE_OK, trust_any) first = True for sig in valid_sigs: if hasattr(sig, 'get_details'): name = '<unknown>' details = sig.get_details() for item in details: if item[0] == 'uid' and len(item) > 9: name = item[9] break else: name = None page = gtk.VBox(False, 4) page.set_border_width(8) frame(page, _('Fingerprint'), pretty_fp(sig.fingerprint)) if name is not None: frame(page, _('Claimed identity'), name) frame(page, _('Unreliable hints database says'), make_hints_area(self.closed, valid_sigs[sig])) already_trusted = trust.trust_db.get_trust_domains(sig.fingerprint) if already_trusted: frame(page, _('You already trust this key for these domains'), '\n'.join(already_trusted)) trust_checkbox[sig] = gtk.CheckButton(_('_Trust this key')) page.pack_start(trust_checkbox[sig], False, True, 0) trust_checkbox[sig].connect('toggled', lambda t: ok_sensitive()) notebook.append_page(page, gtk.Label(name or 'Signature')) if first: trust_checkbox[sig].set_active(True) first = False ok_sensitive() self.vbox.show_all() if len(valid_sigs) == 1: for box in trust_checkbox.values(): box.hide() def response(box, resp): if resp == gtk.RESPONSE_HELP: trust_help.display() return if resp == gtk.RESPONSE_OK: to_trust = [sig for sig in trust_checkbox if trust_checkbox[sig].get_active()] if not self._confirm_unknown_keys(to_trust, valid_sigs): return self.trust_keys(to_trust, domain) self.destroy() self.connect('response', response)
def confirm_import_feed(self, pending, valid_sigs): """Sub-classes should override this method to interact with the user about new feeds. If multiple feeds need confirmation, L{confirm_keys} will only invoke one instance of this method at a time. @param pending: the new feed to be imported @type pending: L{PendingFeed} @param valid_sigs: maps signatures to a list of fetchers collecting information about the key @type valid_sigs: {L{gpg.ValidSig} : L{fetch.KeyInfoFetcher}} @since: 0.42 @see: L{confirm_keys}""" from zeroinstall.injector import trust assert valid_sigs domain = trust.domain_from_url(pending.url) # Ask on stderr, because we may be writing XML to stdout print >> sys.stderr, _("Feed: %s") % pending.url print >> sys.stderr, _( "The feed is correctly signed with the following keys:") for x in valid_sigs: print >> sys.stderr, "-", x def text(parent): text = "" for node in parent.childNodes: if node.nodeType == node.TEXT_NODE: text = text + node.data return text shown = set() key_info_fetchers = valid_sigs.values() while key_info_fetchers: old_kfs = key_info_fetchers key_info_fetchers = [] for kf in old_kfs: infos = set(kf.info) - shown if infos: if len(valid_sigs) > 1: print "%s: " % kf.fingerprint for info in infos: print >> sys.stderr, "-", text(info) shown.add(info) if kf.blocker: key_info_fetchers.append(kf) if key_info_fetchers: for kf in key_info_fetchers: print >> sys.stderr, kf.status stdin = tasks.InputBlocker(0, 'console') blockers = [kf.blocker for kf in key_info_fetchers] + [stdin] yield blockers for b in blockers: try: tasks.check(b) except Exception, ex: warn(_("Failed to get key info: %s"), ex) if stdin.happened: print >> sys.stderr, _( "Skipping remaining key lookups due to input from user" ) break