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