1 from zeroinstall
import SafeException
2 from zeroinstall
.injector
import gpg
3 import tempfile
, os
, base64
, sys
, shutil
, signal
5 from rox
import tasks
, g
17 while '\n' in self
.data
:
18 command
, self
.data
= self
.data
.split('\n', 1)
21 # (version in ROX-Lib < 2.0.4 is buggy; missing IO_HUP)
22 class InputBlocker(tasks
.Blocker
):
23 """Triggers when os.read(stream) would not block."""
26 def __init__(self
, stream
):
27 tasks
.Blocker
.__init
__(self
)
30 def add_task(self
, task
):
31 tasks
.Blocker
.add_task(self
, task
)
33 self
._tag
= gobject
.io_add_watch(self
._stream
, gobject
.IO_IN | gobject
.IO_HUP
,
34 lambda src
, cond
: self
.trigger())
36 def remove_task(self
, task
):
37 tasks
.Blocker
.remove_task(self
, task
)
38 if not self
._rox
_lib
_tasks
:
39 gobject
.source_remove(self
._tag
)
42 def get_secret_keys():
43 child
= subprocess
.Popen(('gpg', '--list-secret-keys', '--with-colons', '--with-fingerprint'),
44 stdout
= subprocess
.PIPE
)
45 stdout
, _
= child
.communicate()
48 raise Exception("GPG failed with exit code %d" % status
)
50 for line
in stdout
.split('\n'):
51 line
= line
.split(':')
53 keys
.append([None, line
[9]])
54 elif line
[0] == 'fpr':
58 def check_signature(path
):
59 data
= file(path
).read()
60 xml_comment
= data
.rfind('\n<!-- Base64 Signature')
62 data_stream
, sigs
= gpg
.check_stream(file(path
))
64 data
= data
[:xml_comment
+ 1]
66 elif data
.startswith('-----BEGIN'):
67 data_stream
, sigs
= gpg
.check_stream(file(path
))
68 sign_fn
= sign_xml
# Don't support saving as plain
69 data
= data_stream
.read()
71 return data
, sign_unsigned
, None
73 if isinstance(sig
, gpg
.ValidSig
):
74 return data
, sign_fn
, sig
.fingerprint
75 print "ERROR: No valid signatures found!"
78 ok
= raw_input('Ignore and load anyway? (y/N) ').lower()
79 if ok
and 'yes'.startswith(ok
):
81 __main__
.force_save
= True
82 return data
, sign_unsigned
, None
85 def write_tmp(path
, data
):
86 """Create a temporary file in the same directory as 'path' and write data to it."""
87 fd
, tmp
= tempfile
.mkstemp(prefix
= 'tmp-', dir = os
.path
.dirname(path
))
88 stream
= os
.fdopen(fd
, 'w')
93 def run_gpg(default_key
, *arguments
):
94 arguments
= list(arguments
)
95 if default_key
is not None:
96 arguments
= ['--default-key', default_key
] + arguments
97 arguments
.insert(0, 'gpg')
98 if os
.spawnvp(os
.P_WAIT
, 'gpg', arguments
):
99 raise SafeException("Command '%s' failed" % arguments
)
101 def sign_unsigned(path
, data
, key
, callback
):
102 os
.rename(write_tmp(path
, data
), path
)
103 if callback
: callback()
105 def sign_xml(path
, data
, key
, callback
):
107 wTree
= g
.glade
.XML(main
.gladefile
, 'get_passphrase')
108 box
= wTree
.get_widget('get_passphrase')
109 box
.set_default_response(g
.RESPONSE_OK
)
110 entry
= wTree
.get_widget('passphrase')
112 buffer = LineBuffer()
122 tmp
= write_tmp(path
, data
)
124 child
= subprocess
.Popen(('gpg', '--default-key', key
,
125 '--detach-sign', '--status-fd', str(w
),
129 preexec_fn
= setup_child
,
130 stdin
= subprocess
.PIPE
)
135 input = InputBlocker(r
)
137 msg
= os
.read(r
, 100)
140 for command
in buffer:
141 if command
.startswith('[GNUPG:] NEED_PASSPHRASE '):
146 if resp
== g
.RESPONSE_OK
:
147 child
.stdin
.write(entry
.get_text() + '\n')
150 os
.kill(child
.pid
, signal
.SIGTERM
)
153 status
= child
.wait()
155 raise Exception("GPG failed with exit code %d" % status
)
157 # No generator finally blocks in Python 2.4...
160 if r
is not None: os
.close(r
)
161 if w
is not None: os
.close(w
)
162 if tmp
is not None: os
.unlink(tmp
)
168 encoded
= base64
.encodestring(file(tmp
).read())
170 sig
= "<!-- Base64 Signature\n" + encoded
+ "\n-->\n"
171 os
.rename(write_tmp(path
, data
+ sig
), path
)
173 if callback
: callback()
175 def export_key(dir, fingerprint
):
176 assert fingerprint
is not None
177 # Convert fingerprint to key ID
178 stream
= os
.popen('gpg --with-colons --list-keys %s' % fingerprint
)
182 parts
= line
.split(':')
183 if parts
[0] == 'pub':
185 raise Exception('Two key IDs returned from GPG!')
189 key_file
= os
.path
.join(dir, keyID
+ '.gpg')
190 if os
.path
.isfile(key_file
):
192 key_stream
= file(key_file
, 'w')
193 stream
= os
.popen("gpg -a --export '%s'" % fingerprint
)
194 shutil
.copyfileobj(stream
, key_stream
)
197 print "Exported public key as '%s'" % key_file