GUI monitoring is now based on console monitoring.
[zeroinstall/zeroinstall-rsl.git] / zeroinstall / injector / handler.py
blob8ebb1f029600da0d03d9c629c60182a841e04780
1 """
2 Integrates download callbacks with an external mainloop.
3 While things are being downloaded, Zero Install returns control to your program.
4 Your mainloop is responsible for monitoring the state of the downloads and notifying
5 Zero Install when they are complete.
7 To do this, you supply a L{Handler} to the L{policy}.
8 """
10 # Copyright (C) 2006, Thomas Leonard
11 # See the README file for details, or visit http://0install.net.
13 import os, sys
14 from logging import debug, info, warn
16 from zeroinstall.support import tasks
17 from zeroinstall.injector import model, download
18 from zeroinstall.injector.iface_cache import iface_cache
20 class Handler(object):
21 """
22 This implementation of the handler interface uses the GLib mainloop.
24 @ivar monitored_downloads: dict of downloads in progress
25 @type monitored_downloads: {URL: (error_stream, L{download.Download})}
26 """
28 __slots__ = ['monitored_downloads', '_loop', '_loop_errors']
30 def __init__(self, mainloop = None):
31 self.monitored_downloads = {}
32 self._loop = None
33 self._loop_errors = None
35 def monitor_download(self, dl):
36 """Called when a new L{download} is started.
37 This is mainly used by the GUI to display the progress bar."""
38 dl.start()
39 self.monitored_downloads[dl.url] = dl
40 self.downloads_changed()
42 def download_done():
43 yield dl.downloaded
44 del self.monitored_downloads[dl.url]
45 self.downloads_changed()
46 monitor = tasks.Task(download_done(), "download monitor")
48 def downloads_changed(self):
49 # This is just for the GUI to override
50 pass
52 def wait_for_blocker(self, blocker):
53 if not blocker.happened:
54 import gobject
56 def quitter():
57 yield blocker
58 self._loop.quit()
59 quit = tasks.Task(quitter(), "quitter")
61 assert self._loop is None # Avoid recursion
62 self._loop = gobject.MainLoop(gobject.main_context_default())
63 try:
64 debug("Entering mainloop, waiting for %s", blocker)
65 self._loop.run()
66 finally:
67 self._loop = None
69 assert blocker.happened, "Someone quit the main loop!"
71 tasks.check(blocker)
73 def get_download(self, url, force = False):
74 """Return the Download object currently downloading 'url'.
75 If no download for this URL has been started, start one now (and
76 start monitoring it).
77 If the download failed and force is False, return it anyway.
78 If force is True, abort any current or failed download and start
79 a new one.
80 @rtype: L{download.Download}
81 """
82 try:
83 e, dl = self.monitored_downloads[url]
84 if dl and force:
85 dl.abort()
86 raise KeyError
87 except KeyError:
88 dl = download.Download(url)
89 self.monitor_download(dl)
90 return dl
92 def confirm_trust_keys(self, interface, sigs, iface_xml):
93 """We don't trust any of the signatures yet. Ask the user.
94 When done update the L{trust} database, and then call L{trust.TrustDB.notify}.
95 @arg interface: the interface being updated
96 @arg sigs: a list of signatures (from L{gpg.check_stream})
97 @arg iface_xml: the downloaded data (not yet trusted)
98 @return: a blocker, if confirmation will happen asynchronously, or None
99 """
100 from zeroinstall.injector import trust, gpg
101 assert sigs
102 valid_sigs = [s for s in sigs if isinstance(s, gpg.ValidSig)]
103 if not valid_sigs:
104 raise model.SafeException('No valid signatures found. Signatures:' +
105 ''.join(['\n- ' + str(s) for s in sigs]))
107 domain = trust.domain_from_url(interface.uri)
109 print "\nInterface:", interface.uri
110 print "The interface is correctly signed with the following keys:"
111 for x in valid_sigs:
112 print "-", x
114 if len(valid_sigs) == 1:
115 print "Do you want to trust this key to sign feeds from '%s'?" % domain
116 else:
117 print "Do you want to trust all of these keys to sign feeds from '%s'?" % domain
118 while True:
119 i = raw_input("Trust [Y/N] ")
120 if not i: continue
121 if i in 'Nn':
122 raise model.SafeException('Not signed with a trusted key')
123 if i in 'Yy':
124 break
125 for key in valid_sigs:
126 print "Trusting", key.fingerprint, "for", domain
127 trust.trust_db.trust_key(key.fingerprint, domain)
129 trust.trust_db.notify()
131 def report_error(self, exception):
132 """Report an exception to the user.
133 @param exception: the exception to report
134 @type exception: L{SafeException}
135 @since: 0.25"""
136 if self._loop_errors is None:
137 warn("%s", exception)
138 else:
139 self._loop_errors.append(str(exception))
140 info("%s", exception) # (will get reported later)