/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5971.1.36 by Jonathan Riddell
update copyright
1
# Copyright (C) 2005, 2011 Canonical Ltd
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
17
18
"""GPG signing and checking logic."""
19
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
20
import os
21
import sys
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
22
from StringIO import StringIO
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
23
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
1442.1.58 by Robert Collins
gpg signing of content
26
import errno
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
27
import subprocess
28
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
29
from bzrlib import (
30
    errors,
31
    trace,
1551.8.11 by Aaron Bentley
Clear terminal before signing
32
    ui,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
33
    )
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
34
""")
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
35
5971.1.79 by Jonathan Riddell
gpg.py uses i18n but bzrlib.i18n is not ready for use, so add a stub class for now
36
class i18n:
37
    """this class is ready to use bzrlib.i18n but bzrlib.i18n is not ready to
38
    use so here is a stub until it is"""
39
    @staticmethod
40
    def gettext(string):
41
        return string
42
        
43
    @staticmethod
44
    def ngettext(single, plural, number):
45
        if number == 1:
46
            return single
47
        else:
48
            return plural
49
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
50
#verification results
5971.1.1 by Jonathan Riddell
add a verify command
51
SIGNATURE_VALID = 0
52
SIGNATURE_KEY_MISSING = 1
53
SIGNATURE_NOT_VALID = 2
54
SIGNATURE_NOT_SIGNED = 3
55
56
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
57
class DisabledGPGStrategy(object):
58
    """A GPG Strategy that makes everything fail."""
59
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
60
    @staticmethod
61
    def verify_signatures_available():
62
        return True
63
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
64
    def __init__(self, ignored):
65
        """Real strategies take a configuration."""
66
67
    def sign(self, content):
68
        raise errors.SigningFailed('Signing is disabled.')
69
5971.1.31 by Jonathan Riddell
and update tests
70
    def verify(self, content, testament):
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
71
        raise errors.SignatureVerificationFailed('Signature verification is \
72
disabled.')
5971.1.6 by Jonathan Riddell
fix methods for dummy gpg strategies
73
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
74
    def set_acceptable_keys(self, command_line_input):
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
75
        pass
76
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
77
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
78
class LoopbackGPGStrategy(object):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
79
    """A GPG Strategy that acts like 'cat' - data is just passed through.
5971.1.86 by Jonathan Riddell
doc string formatting
80
    Used in tests.
81
    """
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
82
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
83
    @staticmethod
84
    def verify_signatures_available():
85
        return True
86
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
87
    def __init__(self, ignored):
88
        """Real strategies take a configuration."""
89
90
    def sign(self, content):
1551.12.15 by Aaron Bentley
add header/trailer to fake clearsigned texts
91
        return ("-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content +
1551.12.52 by Aaron Bentley
speling fix
92
                "-----END PSEUDO-SIGNED CONTENT-----\n")
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
93
5971.1.31 by Jonathan Riddell
and update tests
94
    def verify(self, content, testament):
5971.1.22 by Jonathan Riddell
fix tests
95
        return SIGNATURE_VALID, None
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
96
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
97
    def set_acceptable_keys(self, command_line_input):
98
        if command_line_input is not None:
99
            patterns = command_line_input.split(",")
100
            self.acceptable_keys = []
101
            for pattern in patterns:
102
                if pattern == "unknown":
103
                    pass
104
                else:
105
                    self.acceptable_keys.append(pattern)
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
106
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
107
    def do_verifications(self, revisions, repository):
108
        count = {SIGNATURE_VALID: 0,
109
                 SIGNATURE_KEY_MISSING: 0,
110
                 SIGNATURE_NOT_VALID: 0,
111
                 SIGNATURE_NOT_SIGNED: 0}
112
        result = []
113
        all_verifiable = True
114
        for rev_id in revisions:
115
            verification_result, uid =\
116
                                repository.verify_revision(rev_id,self)
117
            result.append([rev_id, verification_result, uid])
118
            count[verification_result] += 1
119
            if verification_result != SIGNATURE_VALID:
120
                all_verifiable = False
121
        return (count, result, all_verifiable)
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
122
123
    def valid_commits_message(self, count):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
124
        return i18n.gettext(u"{0} commits with valid signatures").format(
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
125
                                        count[SIGNATURE_VALID])            
126
127
    def unknown_key_message(self, count):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
128
        return i18n.ngettext(u"{0} commit with unknown key",
129
                             u"{0} commits with unknown keys",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
130
                             count[SIGNATURE_KEY_MISSING]).format(
131
                                        count[SIGNATURE_KEY_MISSING])
132
133
    def commit_not_valid_message(self, count):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
134
        return i18n.ngettext(u"{0} commit not valid",
135
                             u"{0} commits not valid",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
136
                             count[SIGNATURE_NOT_VALID]).format(
137
                                            count[SIGNATURE_NOT_VALID])
138
139
    def commit_not_signed_message(self, count):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
140
        return i18n.ngettext(u"{0} commit not signed",
141
                             u"{0} commits not signed",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
142
                             count[SIGNATURE_NOT_SIGNED]).format(
143
                                        count[SIGNATURE_NOT_SIGNED])
144
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
145
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
146
def _set_gpg_tty():
147
    tty = os.environ.get('TTY')
148
    if tty is not None:
149
        os.environ['GPG_TTY'] = tty
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
150
        trace.mutter('setting GPG_TTY=%s', tty)
151
    else:
152
        # This is not quite worthy of a warning, because some people
153
        # don't need GPG_TTY to be set. But it is worthy of a big mark
154
        # in ~/.bzr.log, so that people can debug it if it happens to them
155
        trace.mutter('** Env var TTY empty, cannot set GPG_TTY.'
156
                     '  Is TTY exported?')
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
157
158
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
159
class GPGStrategy(object):
160
    """GPG Signing and checking facilities."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
161
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
162
    acceptable_keys = None
163
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
164
    @staticmethod
165
    def verify_signatures_available():
5971.1.82 by Jonathan Riddell
method doc
166
        """
5971.1.86 by Jonathan Riddell
doc string formatting
167
        check if this strategy can verify signatures
168
5971.1.82 by Jonathan Riddell
method doc
169
        :return: boolean if this strategy can verify signatures
170
        """
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
171
        try:
172
            import gpgme
173
            return True
174
        except ImportError, error:
175
            return False
176
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
177
    def _command_line(self):
6012.2.4 by Jonathan Riddell
set default for signing_key to user_email
178
        
6012.2.1 by Jonathan Riddell
sign using gpg key matching config e-mail by default
179
        return [self._config.gpg_signing_command(), '--clearsign', '-u',
6012.2.11 by Jonathan Riddell
rename config option signing_key to gpg_signing_key
180
                                                self._config.gpg_signing_key()]
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
181
182
    def __init__(self, config):
183
        self._config = config
5971.1.61 by Jonathan Riddell
make gpgme context global to class
184
        try:
185
            import gpgme
186
            self.context = gpgme.Context()
187
        except ImportError, error:
188
            pass # can't use verify()
1442.1.58 by Robert Collins
gpg signing of content
189
190
    def sign(self, content):
2273.1.1 by John Arbash Meinel
``GPGStrategy.sign()`` will now raise ``BzrBadParameterUnicode`` if
191
        if isinstance(content, unicode):
192
            raise errors.BzrBadParameterUnicode('content')
1551.8.11 by Aaron Bentley
Clear terminal before signing
193
        ui.ui_factory.clear_term()
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
194
195
        preexec_fn = _set_gpg_tty
196
        if sys.platform == 'win32':
197
            # Win32 doesn't support preexec_fn, but wouldn't support TTY anyway.
198
            preexec_fn = None
1442.1.58 by Robert Collins
gpg signing of content
199
        try:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
200
            process = subprocess.Popen(self._command_line(),
201
                                       stdin=subprocess.PIPE,
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
202
                                       stdout=subprocess.PIPE,
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
203
                                       preexec_fn=preexec_fn)
1442.1.58 by Robert Collins
gpg signing of content
204
            try:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
205
                result = process.communicate(content)[0]
1442.1.58 by Robert Collins
gpg signing of content
206
                if process.returncode is None:
207
                    process.wait()
208
                if process.returncode != 0:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
209
                    raise errors.SigningFailed(self._command_line())
1442.1.58 by Robert Collins
gpg signing of content
210
                return result
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
211
            except OSError, e:
1442.1.58 by Robert Collins
gpg signing of content
212
                if e.errno == errno.EPIPE:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
213
                    raise errors.SigningFailed(self._command_line())
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
214
                else:
215
                    raise
1442.1.58 by Robert Collins
gpg signing of content
216
        except ValueError:
217
            # bad subprocess parameters, should never happen.
218
            raise
219
        except OSError, e:
220
            if e.errno == errno.ENOENT:
221
                # gpg is not installed
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
222
                raise errors.SigningFailed(self._command_line())
1442.1.58 by Robert Collins
gpg signing of content
223
            else:
224
                raise
5971.1.1 by Jonathan Riddell
add a verify command
225
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
226
    def verify(self, content, testament):
5971.1.7 by Jonathan Riddell
add method docs
227
        """Check content has a valid signature.
228
        
229
        :param content: the commit signature
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
230
        :param testament: the valid testament string for the commit
5971.1.7 by Jonathan Riddell
add method docs
231
        
5971.1.18 by Jonathan Riddell
add email to verbose output
232
        :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid
5971.1.7 by Jonathan Riddell
add method docs
233
        """
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
234
        try:
235
            import gpgme
5971.1.41 by Jonathan Riddell
fix calling GpgmeNotInstalled
236
        except ImportError, error:
237
            raise errors.GpgmeNotInstalled(error)
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
238
5971.1.1 by Jonathan Riddell
add a verify command
239
        signature = StringIO(content)
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
240
        plain_output = StringIO()
5971.1.1 by Jonathan Riddell
add a verify command
241
        
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
242
        try:
5971.1.61 by Jonathan Riddell
make gpgme context global to class
243
            result = self.context.verify(signature, None, plain_output)
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
244
        except gpgme.GpgmeError,error:
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
245
            raise errors.SignatureVerificationFailed(error[2])
5971.1.1 by Jonathan Riddell
add a verify command
246
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
247
        #no result if input is invalid
5971.1.9 by Jonathan Riddell
add some tests
248
        if len(result) == 0:
5971.1.17 by Jonathan Riddell
add verbose option
249
            return SIGNATURE_NOT_VALID, None
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
250
        #user has specified a list of acceptable keys, check our result is in it
5971.1.13 by Jonathan Riddell
return missing if not in acceptable keys
251
        fingerprint = result[0].fpr
252
        if self.acceptable_keys is not None:
253
            if not fingerprint in self.acceptable_keys:
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
254
                return SIGNATURE_KEY_MISSING, fingerprint[-8:]
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
255
        #check the signature actually matches the testament
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
256
        if testament != plain_output.getvalue():
257
            return SIGNATURE_NOT_VALID, None
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
258
        #yay gpgme set the valid bit
5971.1.1 by Jonathan Riddell
add a verify command
259
        if result[0].summary & gpgme.SIGSUM_VALID:
5971.1.61 by Jonathan Riddell
make gpgme context global to class
260
            key = self.context.get_key(fingerprint)
5971.1.17 by Jonathan Riddell
add verbose option
261
            name = key.uids[0].name
5971.1.18 by Jonathan Riddell
add email to verbose output
262
            email = key.uids[0].email
263
            return SIGNATURE_VALID, name + " <" + email + ">"
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
264
        #sigsum_red indicates a problem
5971.1.1 by Jonathan Riddell
add a verify command
265
        if result[0].summary & gpgme.SIGSUM_RED:
5971.1.17 by Jonathan Riddell
add verbose option
266
            return SIGNATURE_NOT_VALID, None
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
267
        #gpg does not know this key
5971.1.1 by Jonathan Riddell
add a verify command
268
        if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
269
            return SIGNATURE_KEY_MISSING, fingerprint[-8:]
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
270
        #summary isn't set if sig is valid but key is untrusted
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
271
        #but if user has explicity set the key as acceptable we can validate it
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
272
        if result[0].summary == 0 and self.acceptable_keys is not None:
273
            if fingerprint in self.acceptable_keys:
5971.1.17 by Jonathan Riddell
add verbose option
274
                return SIGNATURE_VALID, None
6043.2.3 by Jonathan Riddell
comment gpgme verify usage so it can be verified by someone who knows what they're talking about, also accept signatures from keys which were valid but have since expired
275
        #if the expired key was not expired at time of signing it's good
276
        if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED:
277
            expires = context.get_key(result[0].fpr).subkeys[0].expires
278
            if expires > result[0].timestamp:
279
                return SIGNATURE_VALID, fingerprint[-8:]
280
            else:
281
                return SIGNATURE_NOT_VALID, None
282
        #other error types such as revokes keys should (I think) be caught by
283
        #SIGSUM_RED so anything else means something is wrong
5971.1.42 by Jonathan Riddell
fix string formatting
284
        raise errors.SignatureVerificationFailed("Unknown GnuPG key "\
285
                                                 "verification result")
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
286
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
287
    def set_acceptable_keys(self, command_line_input):
288
        """sets the acceptable keys for verifying with this GPGStrategy
289
        
290
        :param command_line_input: comma separated list of patterns from
291
                                command line
292
        :return: nothing
293
        """
294
        key_patterns = None
295
        acceptable_keys_config = self._config.acceptable_keys()
296
        try:
297
            if isinstance(acceptable_keys_config, unicode):
298
                acceptable_keys_config = str(acceptable_keys_config)
299
        except UnicodeEncodeError:
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
300
            #gpg Context.keylist(pattern) does not like unicode
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
301
            raise errors.BzrCommandError('Only ASCII permitted in option names')
302
303
        if acceptable_keys_config is not None:
304
            key_patterns = acceptable_keys_config
305
        if command_line_input is not None: #command line overrides config
306
            key_patterns = command_line_input
307
        if key_patterns is not None:
308
            patterns = key_patterns.split(",")
309
310
            self.acceptable_keys = []
311
            for pattern in patterns:
312
                result = self.context.keylist(pattern)
313
                found_key = False
314
                for key in result:
315
                    found_key = True
316
                    self.acceptable_keys.append(key.subkeys[0].fpr)
317
                    trace.mutter("Added acceptable key: " + key.subkeys[0].fpr)
318
                if not found_key:
319
                    trace.note(i18n.gettext(
320
                            "No GnuPG key results for pattern: {}"
321
                                ).format(pattern))
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
322
5971.1.83 by Jonathan Riddell
add a process_events_callback when verifying revisions so qbzr can keep the UI responsive
323
    def do_verifications(self, revisions, repository,
324
                            process_events_callback=None):
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
325
        """do verifications on a set of revisions
326
        
327
        :param revisions: list of revision ids to verify
328
        :param repository: repository object
5971.1.83 by Jonathan Riddell
add a process_events_callback when verifying revisions so qbzr can keep the UI responsive
329
        :param process_events_callback: method to call for GUI frontends that
330
                                                want to keep their UI refreshed
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
331
        
332
        :return: count dictionary of results of each type,
333
                 result list for each revision,
334
                 boolean True if all results are verified successfully
335
        """
336
        count = {SIGNATURE_VALID: 0,
337
                 SIGNATURE_KEY_MISSING: 0,
338
                 SIGNATURE_NOT_VALID: 0,
339
                 SIGNATURE_NOT_SIGNED: 0}
340
        result = []
341
        all_verifiable = True
342
        for rev_id in revisions:
343
            verification_result, uid =\
344
                                repository.verify_revision(rev_id,self)
345
            result.append([rev_id, verification_result, uid])
346
            count[verification_result] += 1
347
            if verification_result != SIGNATURE_VALID:
348
                all_verifiable = False
5971.1.83 by Jonathan Riddell
add a process_events_callback when verifying revisions so qbzr can keep the UI responsive
349
            if process_events_callback is not None:
350
                process_events_callback()
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
351
        return (count, result, all_verifiable)
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
352
353
    def verbose_valid_message(self, result):
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
354
        """takes a verify result and returns list of signed commits strings"""
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
355
        signers = {}
356
        for rev_id, validity, uid in result:
357
            if validity == SIGNATURE_VALID:
358
                signers.setdefault(uid, 0)
359
                signers[uid] += 1
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
360
        result = []
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
361
        for uid, number in signers.items():
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
362
             result.append( i18n.ngettext(u"{0} signed {1} commit", 
363
                             u"{0} signed {1} commits",
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
364
                             number).format(uid, number) )
365
        return result
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
366
367
368
    def verbose_not_valid_message(self, result, repo):
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
369
        """takes a verify result and returns list of not valid commit info"""
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
370
        signers = {}
371
        for rev_id, validity, empty in result:
372
            if validity == SIGNATURE_NOT_VALID:
373
                revision = repo.get_revision(rev_id)
374
                authors = ', '.join(revision.get_apparent_authors())
375
                signers.setdefault(authors, 0)
376
                signers[authors] += 1
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
377
        result = []
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
378
        for authors, number in signers.items():
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
379
            result.append( i18n.ngettext(u"{0} commit by author {1}", 
380
                                 u"{0} commits by author {1}",
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
381
                                 number).format(number, authors) )
382
        return result
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
383
384
    def verbose_not_signed_message(self, result, repo):
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
385
        """takes a verify result and returns list of not signed commit info"""
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
386
        signers = {}
387
        for rev_id, validity, empty in result:
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
388
            if validity == SIGNATURE_NOT_SIGNED:
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
389
                revision = repo.get_revision(rev_id)
390
                authors = ', '.join(revision.get_apparent_authors())
391
                signers.setdefault(authors, 0)
392
                signers[authors] += 1
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
393
        result = []
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
394
        for authors, number in signers.items():
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
395
            result.append( i18n.ngettext(u"{0} commit by author {1}", 
396
                                 u"{0} commits by author {1}",
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
397
                                 number).format(number, authors) )
398
        return result
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
399
400
    def verbose_missing_key_message(self, result):
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
401
        """takes a verify result and returns list of missing key info"""
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
402
        signers = {}
403
        for rev_id, validity, fingerprint in result:
404
            if validity == SIGNATURE_KEY_MISSING:
405
                signers.setdefault(fingerprint, 0)
406
                signers[fingerprint] += 1
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
407
        result = []
5971.1.71 by Jonathan Riddell
move some message code into gpg.py
408
        for fingerprint, number in signers.items():
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
409
            result.append( i18n.ngettext(u"Unknown key {0} signed {1} commit", 
410
                                 u"Unknown key {0} signed {1} commits",
5971.1.75 by Jonathan Riddell
fix verbose messages, now return a list
411
                                 number).format(fingerprint, number) )
412
        return result
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
413
414
    def valid_commits_message(self, count):
5971.1.73 by Jonathan Riddell
document methods
415
        """returns message for number of commits"""
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
416
        return i18n.gettext(u"{0} commits with valid signatures").format(
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
417
                                        count[SIGNATURE_VALID])
418
419
    def unknown_key_message(self, count):
5971.1.73 by Jonathan Riddell
document methods
420
        """returns message for number of commits"""
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
421
        return i18n.ngettext(u"{0} commit with unknown key",
422
                             u"{0} commits with unknown keys",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
423
                             count[SIGNATURE_KEY_MISSING]).format(
424
                                        count[SIGNATURE_KEY_MISSING])
425
426
    def commit_not_valid_message(self, count):
5971.1.73 by Jonathan Riddell
document methods
427
        """returns message for number of commits"""
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
428
        return i18n.ngettext(u"{0} commit not valid",
429
                             u"{0} commits not valid",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
430
                             count[SIGNATURE_NOT_VALID]).format(
431
                                            count[SIGNATURE_NOT_VALID])
432
433
    def commit_not_signed_message(self, count):
5971.1.73 by Jonathan Riddell
document methods
434
        """returns message for number of commits"""
5971.1.81 by Jonathan Riddell
signature messages need to handle unicode names
435
        return i18n.ngettext(u"{0} commit not signed",
436
                             u"{0} commits not signed",
5971.1.72 by Jonathan Riddell
move all messages into gpg.py so they can be reused by other UIs
437
                             count[SIGNATURE_NOT_SIGNED]).format(
438
                                        count[SIGNATURE_NOT_SIGNED])