/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6609.1.1 by Vincent Ladeuil
Fix the failing gpg test on wily.
1
# Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012, 2013, 2016 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
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
18
"""GPG signing and checking logic."""
19
6379.6.3 by Jelmer Vernooij
Use absolute_import.
20
from __future__ import absolute_import
21
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
22
import os
23
import sys
24
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
25
from breezy.lazy_import import lazy_import
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
26
lazy_import(globals(), """
1442.1.58 by Robert Collins
gpg signing of content
27
import errno
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
28
import subprocess
29
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
30
from breezy import (
6372.1.3 by Vincent Ladeuil
Fix gpg_signing_ket default values handling
31
    config,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
32
    trace,
1551.8.11 by Aaron Bentley
Clear terminal before signing
33
    ui,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
34
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
35
from breezy.i18n import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
36
    gettext,
6092.2.3 by Jonathan Riddell
improve formatting
37
    ngettext,
38
    )
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
39
""")
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
40
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
41
from . import (
42
    errors,
43
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
44
from .sixish import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
45
    BytesIO,
46
    )
6491.1.4 by Jelmer Vernooij
Deprecate GPGStrategy.do_verifications.
47
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
48
#verification results
5971.1.1 by Jonathan Riddell
add a verify command
49
SIGNATURE_VALID = 0
50
SIGNATURE_KEY_MISSING = 1
51
SIGNATURE_NOT_VALID = 2
52
SIGNATURE_NOT_SIGNED = 3
6043.3.1 by Jonathan Riddell
Report commits signed with expired keys in "verify-signatures".
53
SIGNATURE_EXPIRED = 4
5971.1.1 by Jonathan Riddell
add a verify command
54
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
55
MODE_NORMAL = 0
56
MODE_DETACH = 1
57
MODE_CLEAR = 2
58
5971.1.1 by Jonathan Riddell
add a verify command
59
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
60
class GpgNotInstalled(errors.DependencyNotPresent):
61
62
    _fmt = 'python-gpg is not installed, it is needed to verify signatures'
63
64
    def __init__(self, error):
65
        errors.DependencyNotPresent.__init__(self, 'gpg', error)
66
67
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
68
class SigningFailed(errors.BzrError):
69
70
    _fmt = 'Failed to GPG sign data: "%(error)s"'
71
72
    def __init__(self, error):
73
        errors.BzrError.__init__(self, error=error)
74
75
76
class SignatureVerificationFailed(errors.BzrError):
77
78
    _fmt = 'Failed to verify GPG signature data with error "%(error)s"'
79
80
    def __init__(self, error):
81
        errors.BzrError.__init__(self, error=error)
82
83
6491.1.3 by Jelmer Vernooij
Make 'bzr verify-signatures' show a progress bar.
84
def bulk_verify_signatures(repository, revids, strategy,
85
        process_events_callback=None):
86
    """Do verifications on a set of revisions
87
88
    :param repository: repository object
89
    :param revids: list of revision ids to verify
90
    :param strategy: GPG strategy to use
91
    :param process_events_callback: method to call for GUI frontends that
92
        want to keep their UI refreshed
93
94
    :return: count dictionary of results of each type,
95
             result list for each revision,
96
             boolean True if all results are verified successfully
97
    """
98
    count = {SIGNATURE_VALID: 0,
99
             SIGNATURE_KEY_MISSING: 0,
100
             SIGNATURE_NOT_VALID: 0,
101
             SIGNATURE_NOT_SIGNED: 0,
102
             SIGNATURE_EXPIRED: 0}
103
    result = []
104
    all_verifiable = True
105
    total = len(revids)
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
106
    with ui.ui_factory.nested_progress_bar() as pb:
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
107
        for i, (rev_id, verification_result, uid) in enumerate(
108
                repository.verify_revision_signatures(
109
                    revids, strategy)):
6491.1.3 by Jelmer Vernooij
Make 'bzr verify-signatures' show a progress bar.
110
            pb.update("verifying signatures", i, total)
111
            result.append([rev_id, verification_result, uid])
112
            count[verification_result] += 1
113
            if verification_result != SIGNATURE_VALID:
114
                all_verifiable = False
115
            if process_events_callback is not None:
116
                process_events_callback()
117
    return (count, result, all_verifiable)
118
119
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
120
class DisabledGPGStrategy(object):
121
    """A GPG Strategy that makes everything fail."""
122
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
123
    @staticmethod
124
    def verify_signatures_available():
125
        return True
126
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
127
    def __init__(self, ignored):
128
        """Real strategies take a configuration."""
129
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
130
    def sign(self, content, mode):
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
131
        raise SigningFailed('Signing is disabled.')
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
132
6883.11.2 by Jelmer Vernooij
Change signature API.
133
    def verify(self, signed_data, signature=None):
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
134
        raise SignatureVerificationFailed('Signature verification is \
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
135
disabled.')
5971.1.6 by Jonathan Riddell
fix methods for dummy gpg strategies
136
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
137
    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
138
        pass
139
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
140
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
141
class LoopbackGPGStrategy(object):
5971.1.85 by Jonathan Riddell
use unicode strings for UI
142
    """A GPG Strategy that acts like 'cat' - data is just passed through.
5971.1.86 by Jonathan Riddell
doc string formatting
143
    Used in tests.
144
    """
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
145
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
146
    @staticmethod
147
    def verify_signatures_available():
148
        return True
149
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
150
    def __init__(self, ignored):
151
        """Real strategies take a configuration."""
152
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
153
    def sign(self, content, mode):
1551.12.15 by Aaron Bentley
add header/trailer to fake clearsigned texts
154
        return ("-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content +
1551.12.52 by Aaron Bentley
speling fix
155
                "-----END PSEUDO-SIGNED CONTENT-----\n")
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
156
6883.11.2 by Jelmer Vernooij
Change signature API.
157
    def verify(self, signed_data, signature=None):
6883.11.3 by Jelmer Vernooij
Fix tests.
158
        plain_text = signed_data.replace("-----BEGIN PSEUDO-SIGNED CONTENT-----\n", "")
159
        plain_text = plain_text.replace("-----END PSEUDO-SIGNED CONTENT-----\n", "")
160
        return SIGNATURE_VALID, None, plain_text
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
161
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
162
    def set_acceptable_keys(self, command_line_input):
163
        if command_line_input is not None:
164
            patterns = command_line_input.split(",")
165
            self.acceptable_keys = []
166
            for pattern in patterns:
167
                if pattern == "unknown":
168
                    pass
169
                else:
170
                    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
171
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
172
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
173
def _set_gpg_tty():
174
    tty = os.environ.get('TTY')
175
    if tty is not None:
176
        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.
177
        trace.mutter('setting GPG_TTY=%s', tty)
178
    else:
179
        # This is not quite worthy of a warning, because some people
180
        # don't need GPG_TTY to be set. But it is worthy of a big mark
6622.1.33 by Jelmer Vernooij
Fix more tests (all?)
181
        # in ~/.brz.log, so that people can debug it if it happens to them
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
182
        trace.mutter('** Env var TTY empty, cannot set GPG_TTY.'
183
                     '  Is TTY exported?')
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
184
185
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
186
class GPGStrategy(object):
187
    """GPG Signing and checking facilities."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
188
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
189
    acceptable_keys = None
190
6351.3.9 by Vincent Ladeuil
Move __init__ at the beginning of the class since that's the current idiom.
191
    def __init__(self, config_stack):
192
        self._config_stack = config_stack
193
        try:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
194
            import gpg
195
            self.context = gpg.Context()
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
196
            self.context.armor = True
6883.11.6 by Jelmer Vernooij
Support tests on machines without gpg.
197
            self.context.signers = self._get_signing_keys()
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
198
        except ImportError as error:
6351.3.9 by Vincent Ladeuil
Move __init__ at the beginning of the class since that's the current idiom.
199
            pass # can't use verify()
200
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
201
    def _get_signing_keys(self):
202
        import gpg
203
        keyname = self._config_stack.get('gpg_signing_key')
204
        if keyname:
205
            try:
206
                return [self.context.get_key(keyname)]
207
            except gpg.errors.KeyNotFound:
208
                pass
209
210
        if keyname is None or keyname == 'default':
211
            # 'default' or not setting gpg_signing_key at all means we should
212
            # use the user email address
213
            keyname = config.extract_email_address(self._config_stack.get('email'))
214
        possible_keys = self.context.keylist(keyname, secret=True)
215
        try:
216
            return [possible_keys.next()]
217
        except StopIteration:
218
            return []
219
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
220
    @staticmethod
221
    def verify_signatures_available():
5971.1.82 by Jonathan Riddell
method doc
222
        """
5971.1.86 by Jonathan Riddell
doc string formatting
223
        check if this strategy can verify signatures
224
5971.1.82 by Jonathan Riddell
method doc
225
        :return: boolean if this strategy can verify signatures
226
        """
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
227
        try:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
228
            import gpg
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
229
            return True
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
230
        except ImportError as error:
5971.1.60 by Jonathan Riddell
move checking for gpgme availability into gpg.py
231
            return False
232
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
233
    def sign(self, content, mode):
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
234
        import gpg
2273.1.1 by John Arbash Meinel
``GPGStrategy.sign()`` will now raise ``BzrBadParameterUnicode`` if
235
        if isinstance(content, unicode):
236
            raise errors.BzrBadParameterUnicode('content')
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
237
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
238
        plain_text = gpg.Data(content)
1442.1.58 by Robert Collins
gpg signing of content
239
        try:
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
240
            output, result = self.context.sign(
6883.11.1 by Jelmer Vernooij
Add support for a mode argument to GPGStrategy.sign.
241
                plain_text, mode={
242
                    MODE_DETACH: gpg.constants.sig.mode.DETACH,
243
                    MODE_CLEAR: gpg.constants.sig.mode.CLEAR,
244
                    MODE_NORMAL: gpg.constants.sig.mode.NORMAL,
245
                    }[mode])
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
246
        except gpg.errors.GPGMEError as error:
247
            raise SigningFailed(str(error))
248
249
        return output
5971.1.1 by Jonathan Riddell
add a verify command
250
6883.11.2 by Jelmer Vernooij
Change signature API.
251
    def verify(self, signed_data, signature=None):
5971.1.7 by Jonathan Riddell
add method docs
252
        """Check content has a valid signature.
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
253
6883.11.2 by Jelmer Vernooij
Change signature API.
254
        :param signed_data; Signed data
255
        :param signature: optional signature (if detached)
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
256
6883.11.2 by Jelmer Vernooij
Change signature API.
257
        :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid, plain text
5971.1.7 by Jonathan Riddell
add method docs
258
        """
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
259
        try:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
260
            import gpg
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
261
        except ImportError as error:
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
262
            raise errors.GpgNotInstalled(error)
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
263
6883.11.2 by Jelmer Vernooij
Change signature API.
264
        signed_data = gpg.Data(signed_data)
265
        if signature:
266
            signature = gpg.Data(signature)
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
267
        try:
6883.11.2 by Jelmer Vernooij
Change signature API.
268
            plain_output, result = self.context.verify(signed_data, signature)
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
269
        except gpg.errors.BadSignatures as error:
270
            fingerprint = error.result.signatures[0].fpr
271
            if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_EXPIRED:
272
                expires = self.context.get_key(error.result.signatures[0].fpr).subkeys[0].expires
273
                if expires > error.result.signatures[0].timestamp:
274
                    # The expired key was not expired at time of signing.
275
                    # test_verify_expired_but_valid()
6883.11.2 by Jelmer Vernooij
Change signature API.
276
                    return SIGNATURE_EXPIRED, fingerprint[-8:], None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
277
                else:
278
                    # I can't work out how to create a test where the signature
279
                    # was expired at the time of signing.
6883.11.2 by Jelmer Vernooij
Change signature API.
280
                    return SIGNATURE_NOT_VALID, None, None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
281
282
            # GPG does not know this key.
283
            # test_verify_unknown_key()
284
            if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_MISSING:
6883.11.2 by Jelmer Vernooij
Change signature API.
285
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
286
6883.11.2 by Jelmer Vernooij
Change signature API.
287
            return SIGNATURE_NOT_VALID, None, None
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
288
        except gpg.errors.GPGMEError as error:
6883.11.2 by Jelmer Vernooij
Change signature API.
289
            raise SignatureVerificationFailed(error)
5971.1.1 by Jonathan Riddell
add a verify command
290
6883.11.3 by Jelmer Vernooij
Fix tests.
291
        # No result if input is invalid.
292
        # test_verify_invalid()
293
        if len(result.signatures) == 0:
294
            return SIGNATURE_NOT_VALID, None, plain_output
295
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
296
        # User has specified a list of acceptable keys, check our result is in
297
        # it.  test_verify_unacceptable_key()
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
298
        fingerprint = result.signatures[0].fpr
5971.1.13 by Jonathan Riddell
return missing if not in acceptable keys
299
        if self.acceptable_keys is not None:
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
300
            if not fingerprint in self.acceptable_keys:
6883.11.2 by Jelmer Vernooij
Change signature API.
301
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], plain_output
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
302
        # Yay gpg set the valid bit.
6043.2.15 by Jonathan Riddell
turn comments into sentences
303
        # Can't write a test for this one as you can't set a key to be
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
304
        # trusted using gpg.
305
        if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
5971.1.61 by Jonathan Riddell
make gpgme context global to class
306
            key = self.context.get_key(fingerprint)
5971.1.17 by Jonathan Riddell
add verbose option
307
            name = key.uids[0].name
5971.1.18 by Jonathan Riddell
add email to verbose output
308
            email = key.uids[0].email
6883.11.4 by Jelmer Vernooij
Decode unicode fields.
309
            return SIGNATURE_VALID, name.decode('utf-8') + u" <" + email.decode('utf-8') + u">", plain_output
6043.2.15 by Jonathan Riddell
turn comments into sentences
310
        # Sigsum_red indicates a problem, unfortunatly I have not been able
311
        # to write any tests which actually set this.
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
312
        if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
6883.11.2 by Jelmer Vernooij
Change signature API.
313
            return SIGNATURE_NOT_VALID, None, plain_output
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
314
        # Summary isn't set if sig is valid but key is untrusted but if user
315
        # has explicity set the key as acceptable we can validate it.
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
316
        if result.signatures[0].summary == 0 and self.acceptable_keys is not 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
317
            if fingerprint in self.acceptable_keys:
6043.2.15 by Jonathan Riddell
turn comments into sentences
318
                # test_verify_untrusted_but_accepted()
6883.11.2 by Jelmer Vernooij
Change signature API.
319
                return SIGNATURE_VALID, None, plain_output
6043.2.15 by Jonathan Riddell
turn comments into sentences
320
        # test_verify_valid_but_untrusted()
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
321
        if result.signatures[0].summary == 0 and self.acceptable_keys is None:
6883.11.2 by Jelmer Vernooij
Change signature API.
322
            return SIGNATURE_NOT_VALID, None, plain_output
6043.2.15 by Jonathan Riddell
turn comments into sentences
323
        # Other error types such as revoked keys should (I think) be caught by
324
        # SIGSUM_RED so anything else means something is buggy.
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
325
        raise SignatureVerificationFailed(
6609.1.1 by Vincent Ladeuil
Fix the failing gpg test on wily.
326
            "Unknown GnuPG key verification result")
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
327
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
328
    def set_acceptable_keys(self, command_line_input):
6351.3.2 by Jelmer Vernooij
Convert some gpg options to config stacks.
329
        """Set the acceptable keys for verifying with this GPGStrategy.
6491.1.1 by Jelmer Vernooij
Various cleanups related to GPG.
330
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
331
        :param command_line_input: comma separated list of patterns from
332
                                command line
333
        :return: nothing
334
        """
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
335
        patterns = None
6351.3.2 by Jelmer Vernooij
Convert some gpg options to config stacks.
336
        acceptable_keys_config = self._config_stack.get('acceptable_keys')
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
337
        if acceptable_keys_config is not None:
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
338
            patterns = acceptable_keys_config
6372.1.1 by Vincent Ladeuil
Remove spurious spaces.
339
        if command_line_input is not None: # command line overrides config
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
340
            patterns = command_line_input.split(',')
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
341
6589.3.1 by Vincent Ladeuil
Fix command line override handling for acceptable_keys
342
        if patterns:
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
343
            self.acceptable_keys = []
344
            for pattern in patterns:
345
                result = self.context.keylist(pattern)
346
                found_key = False
347
                for key in result:
348
                    found_key = True
349
                    self.acceptable_keys.append(key.subkeys[0].fpr)
350
                    trace.mutter("Added acceptable key: " + key.subkeys[0].fpr)
351
                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
352
                    trace.note(gettext(
6123.1.15 by Jelmer Vernooij
fix format string
353
                            "No GnuPG key results for pattern: {0}"
5971.1.69 by Jonathan Riddell
move some code from cmd_verify to gpg.set_acceptable_keys
354
                                ).format(pattern))
5971.1.70 by Jonathan Riddell
move code which does verifications of revisions from cmd_verify_signatures to gpg.do_verifications
355
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
356
357
def valid_commits_message(count):
358
    """returns message for number of commits"""
359
    return gettext(u"{0} commits with valid signatures").format(
360
                                    count[SIGNATURE_VALID])
361
362
363
def unknown_key_message(count):
364
    """returns message for number of commits"""
365
    return ngettext(u"{0} commit with unknown key",
366
                    u"{0} commits with unknown keys",
367
                    count[SIGNATURE_KEY_MISSING]).format(
368
                                    count[SIGNATURE_KEY_MISSING])
369
370
371
def commit_not_valid_message(count):
372
    """returns message for number of commits"""
373
    return ngettext(u"{0} commit not valid",
374
                    u"{0} commits not valid",
375
                    count[SIGNATURE_NOT_VALID]).format(
376
                                        count[SIGNATURE_NOT_VALID])
377
378
379
def commit_not_signed_message(count):
380
    """returns message for number of commits"""
381
    return ngettext(u"{0} commit not signed",
382
                    u"{0} commits not signed",
383
                    count[SIGNATURE_NOT_SIGNED]).format(
384
                                    count[SIGNATURE_NOT_SIGNED])
385
386
387
def expired_commit_message(count):
388
    """returns message for number of commits"""
389
    return ngettext(u"{0} commit with key now expired",
390
                    u"{0} commits with key now expired",
391
                    count[SIGNATURE_EXPIRED]).format(
392
                                count[SIGNATURE_EXPIRED])
393
394
395
def verbose_expired_key_message(result, repo):
396
    """takes a verify result and returns list of expired key info"""
397
    signers = {}
398
    fingerprint_to_authors = {}
399
    for rev_id, validity, fingerprint in result:
400
        if validity == SIGNATURE_EXPIRED:
401
            revision = repo.get_revision(rev_id)
402
            authors = ', '.join(revision.get_apparent_authors())
403
            signers.setdefault(fingerprint, 0)
404
            signers[fingerprint] += 1
405
            fingerprint_to_authors[fingerprint] = authors
406
    result = []
407
    for fingerprint, number in signers.items():
408
        result.append(
409
            ngettext(u"{0} commit by author {1} with key {2} now expired",
410
                     u"{0} commits by author {1} with key {2} now expired",
411
                     number).format(
412
                number, fingerprint_to_authors[fingerprint], fingerprint))
413
    return result
414
415
416
def verbose_valid_message(result):
417
    """takes a verify result and returns list of signed commits strings"""
418
    signers = {}
419
    for rev_id, validity, uid in result:
420
        if validity == SIGNATURE_VALID:
421
            signers.setdefault(uid, 0)
422
            signers[uid] += 1
423
    result = []
424
    for uid, number in signers.items():
425
         result.append(ngettext(u"{0} signed {1} commit",
426
                                u"{0} signed {1} commits",
427
                                number).format(uid, number))
428
    return result
429
430
431
def verbose_not_valid_message(result, repo):
432
    """takes a verify result and returns list of not valid commit info"""
433
    signers = {}
434
    for rev_id, validity, empty in result:
435
        if validity == SIGNATURE_NOT_VALID:
436
            revision = repo.get_revision(rev_id)
437
            authors = ', '.join(revision.get_apparent_authors())
438
            signers.setdefault(authors, 0)
439
            signers[authors] += 1
440
    result = []
441
    for authors, number in signers.items():
442
        result.append(ngettext(u"{0} commit by author {1}",
443
                               u"{0} commits by author {1}",
444
                               number).format(number, authors))
445
    return result
446
447
448
def verbose_not_signed_message(result, repo):
449
    """takes a verify result and returns list of not signed commit info"""
450
    signers = {}
451
    for rev_id, validity, empty in result:
452
        if validity == SIGNATURE_NOT_SIGNED:
453
            revision = repo.get_revision(rev_id)
454
            authors = ', '.join(revision.get_apparent_authors())
455
            signers.setdefault(authors, 0)
456
            signers[authors] += 1
457
    result = []
458
    for authors, number in signers.items():
459
        result.append(ngettext(u"{0} commit by author {1}",
460
                               u"{0} commits by author {1}",
461
                               number).format(number, authors))
462
    return result
463
464
465
def verbose_missing_key_message(result):
466
    """takes a verify result and returns list of missing key info"""
467
    signers = {}
468
    for rev_id, validity, fingerprint in result:
469
        if validity == SIGNATURE_KEY_MISSING:
470
            signers.setdefault(fingerprint, 0)
471
            signers[fingerprint] += 1
472
    result = []
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
473
    for fingerprint, number in list(signers.items()):
6491.1.5 by Jelmer Vernooij
Add Repository.verify_revision_signatures.
474
        result.append(ngettext(u"Unknown key {0} signed {1} commit",
475
                               u"Unknown key {0} signed {1} commits",
476
                               number).format(fingerprint, number))
477
    return result