##// END OF EJS Templates
lfs: use byteskwargs() on some **kwargs for python 3 compat...
lfs: use byteskwargs() on some **kwargs for python 3 compat Differential Revision: https://phab.mercurial-scm.org/D2566

File last commit:

r36051:f3d8f61c default
r36620:e30be4d2 default
Show More
gpg.py
327 lines | 10.2 KiB | text/x-python | PythonLexer
Benoit Boissinot
fixes for gpg.py extension...
r1681 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Benoit Boissinot
fixes for gpg.py extension...
r1681
Dirkjan Ochtman
extensions: change descriptions for extensions providing a few commands
r8934 '''commands to sign and verify changesets'''
Dirkjan Ochtman
help: add/fix docstrings for a bunch of extensions
r8873
Pulkit Goyal
py3: make hgext/gpg.py use absolute_import
r29124 from __future__ import absolute_import
import binascii
import os
import tempfile
Yuya Nishihara
py3: move up symbol imports to enforce import-checker rules...
r29205
from mercurial.i18n import _
Pulkit Goyal
py3: make hgext/gpg.py use absolute_import
r29124 from mercurial import (
cmdutil,
error,
match,
node as hgnode,
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (2 of 2)
r30925 pycompat,
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 registrar,
Pulkit Goyal
py3: make hgext/gpg.py use absolute_import
r29124 util,
)
Benoit Boissinot
gpg signing extension for hg...
r1592
Martin Geisler
gpg: use cmdutil.command decorator
r14299 cmdtable = {}
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 command = registrar.command(cmdtable)
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 testedwith = 'ships-with-hg-core'
Martin Geisler
gpg: use cmdutil.command decorator
r14299
Boris Feld
configitems: register the 'gpg.cmd' config
r34502 configtable = {}
configitem = registrar.configitem(configtable)
configitem('gpg', 'cmd',
default='gpg',
)
Boris Feld
configitems: register the 'gpg.key' config
r34503 configitem('gpg', 'key',
default=None,
)
Boris Feld
configitems: register the 'gpg' arbitraty key section
r34771 configitem('gpg', '.*',
default=None,
generic=True,
)
Boris Feld
configitems: register the 'gpg.cmd' config
r34502
Benoit Boissinot
use new style classes
r8778 class gpg(object):
Benoit Boissinot
gpg signing extension for hg...
r1592 def __init__(self, path, key=None):
self.path = path
self.key = (key and " --local-user \"%s\"" % key) or ""
def sign(self, data):
gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key)
return util.filter(data, gpgcmd)
def verify(self, data, sig):
""" returns of the good and bad signatures"""
Thomas Arendsen Hein
gpg extension: Always remove temporary files created by 'hg sigcheck'.
r2231 sigfile = datafile = None
Benoit Boissinot
gpg signing extension for hg...
r1592 try:
Benoit Boissinot
fixes for gpg.py extension...
r1681 # create temporary files
Thomas Arendsen Hein
Use better names (hg-{usage}-{random}.{suffix}) for temporary files.
r2165 fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig")
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (2 of 2)
r30925 fp = os.fdopen(fd, pycompat.sysstr('wb'))
Benoit Boissinot
gpg signing extension for hg...
r1592 fp.write(sig)
fp.close()
Thomas Arendsen Hein
Use better names (hg-{usage}-{random}.{suffix}) for temporary files.
r2165 fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt")
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (2 of 2)
r30925 fp = os.fdopen(fd, pycompat.sysstr('wb'))
Benoit Boissinot
gpg signing extension for hg...
r1592 fp.write(data)
fp.close()
Benoit Boissinot
fixes for gpg.py extension...
r1681 gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
"\"%s\" \"%s\"" % (self.path, sigfile, datafile))
Benoit Boissinot
gpg signing extension for hg...
r1592 ret = util.filter("", gpgcmd)
Thomas Arendsen Hein
gpg extension: Always remove temporary files created by 'hg sigcheck'.
r2231 finally:
Benoit Boissinot
gpg signing extension for hg...
r1592 for f in (sigfile, datafile):
try:
Matt Mackall
many, many trivial check-code fixups
r10282 if f:
os.unlink(f)
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except OSError:
Matt Mackall
many, many trivial check-code fixups
r10282 pass
Benoit Boissinot
gpg signing extension for hg...
r1592 keys = []
key, fingerprint = None, None
for l in ret.splitlines():
# see DETAILS in the gnupg documentation
# filter the logger output
if not l.startswith("[GNUPG:]"):
continue
l = l[9:]
Wei, Elson
gpg: treat "ERRSIG" as a valid key id but no fingerprint
r19441 if l.startswith("VALIDSIG"):
Benoit Boissinot
gpg signing extension for hg...
r1592 # fingerprint of the primary key
fingerprint = l.split()[10]
Wei, Elson
gpg: treat "ERRSIG" as a valid key id but no fingerprint
r19441 elif l.startswith("ERRSIG"):
key = l.split(" ", 3)[:2]
key.append("")
fingerprint = None
Benoit Boissinot
gpg signing extension for hg...
r1592 elif (l.startswith("GOODSIG") or
l.startswith("EXPSIG") or
l.startswith("EXPKEYSIG") or
l.startswith("BADSIG")):
if key is not None:
keys.append(key + [fingerprint])
key = l.split(" ", 2)
fingerprint = None
if key is not None:
keys.append(key + [fingerprint])
Wei, Elson
gpg: getkeys() removes unused returning value "err"
r19442 return keys
Benoit Boissinot
gpg signing extension for hg...
r1592
def newgpg(ui, **opts):
Benoit Boissinot
fixes for gpg.py extension...
r1681 """create a new gpg instance"""
Boris Feld
configitems: register the 'gpg.cmd' config
r34502 gpgpath = ui.config("gpg", "cmd")
Pulkit Goyal
py3: handle keyword arguments in hgext/gpg.py...
r34979 gpgkey = opts.get(r'key')
Benoit Boissinot
gpg signing extension for hg...
r1592 if not gpgkey:
Boris Feld
configitems: register the 'gpg.key' config
r34503 gpgkey = ui.config("gpg", "key")
Benoit Boissinot
gpg signing extension for hg...
r1592 return gpg(gpgpath, gpgkey)
Benoit Boissinot
fixes for gpg.py extension...
r1681 def sigwalk(repo):
"""
walk over every sigs, yields a couple
((node, version, sig), (filename, linenumber))
"""
def parsefile(fileiter, context):
ln = 1
for l in fileiter:
if not l:
continue
yield (l.split(" ", 2), (context, ln))
Benoit Boissinot
fix coding style (reported by pylint)
r10394 ln += 1
Benoit Boissinot
fixes for gpg.py extension...
r1681
Matt Mackall
replace various uses of list.reverse()
r8210 # read the heads
Benoit Boissinot
fixes for gpg.py extension...
r1681 fl = repo.file(".hgsigs")
Matt Mackall
replace various uses of list.reverse()
r8210 for r in reversed(fl.heads()):
Benoit Boissinot
fixes for gpg.py extension...
r1681 fn = ".hgsigs|%s" % hgnode.short(r)
for item in parsefile(fl.read(r).splitlines(), fn):
yield item
try:
# read local signatures
fn = "localsigs"
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 for item in parsefile(repo.vfs(fn), fn):
Benoit Boissinot
fixes for gpg.py extension...
r1681 yield item
except IOError:
pass
def getkeys(ui, repo, mygpg, sigdata, context):
"""get the keys who signed a data"""
fn, ln = context
node, version, sig = sigdata
prefix = "%s:%d" % (fn, ln)
node = hgnode.bin(node)
data = node2txt(repo, node, version)
sig = binascii.a2b_base64(sig)
Wei, Elson
gpg: getkeys() removes unused returning value "err"
r19442 keys = mygpg.verify(data, sig)
Benoit Boissinot
fixes for gpg.py extension...
r1681
validkeys = []
# warn for expired key and/or sigs
for key in keys:
Wei, Elson
gpg: show "Unknown key ID xxxxxxxx" when the status is ERRSIG
r19444 if key[0] == "ERRSIG":
Josef 'Jeff' Sipek
gpg: print unknown key IDs in their entirety...
r36051 ui.write(_("%s Unknown key ID \"%s\"\n") % (prefix, key[1]))
Wei, Elson
gpg: show "Unknown key ID xxxxxxxx" when the status is ERRSIG
r19444 continue
Benoit Boissinot
fixes for gpg.py extension...
r1681 if key[0] == "BADSIG":
ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
continue
if key[0] == "EXPSIG":
ui.write(_("%s Note: Signature has expired"
" (signed by: \"%s\")\n") % (prefix, key[2]))
elif key[0] == "EXPKEYSIG":
ui.write(_("%s Note: This key has expired"
" (signed by: \"%s\")\n") % (prefix, key[2]))
validkeys.append((key[1], key[2], key[3]))
return validkeys
Martin Geisler
gpg: use cmdutil.command decorator
r14299 @command("sigs", [], _('hg sigs'))
Benoit Boissinot
fixes for gpg.py extension...
r1681 def sigs(ui, repo):
"""list signed changesets"""
mygpg = newgpg(ui)
revs = {}
for data, context in sigwalk(repo):
node, version, sig = data
fn, ln = context
try:
n = repo.lookup(node)
except KeyError:
ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
continue
r = repo.changelog.rev(n)
keys = getkeys(ui, repo, mygpg, data, context)
if not keys:
continue
revs.setdefault(r, [])
revs[r].extend(keys)
Martin Geisler
gpg: use reverse kwarg to sort sigs in reversed order
r8303 for rev in sorted(revs, reverse=True):
Benoit Boissinot
fix an exception in gpg.py with multiples sigs for the same cset...
r1682 for k in revs[rev]:
r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
Benoit Boissinot
fixes for gpg.py extension...
r1681 ui.write("%-30s %s\n" % (keystr(ui, k), r))
Thomas Arendsen Hein
consistency: use REV instead of REVISION
r16991 @command("sigcheck", [], _('hg sigcheck REV'))
timeless
gpg: rename sigcheck function...
r27117 def sigcheck(ui, repo, rev):
Benoit Boissinot
gpg signing extension for hg...
r1592 """verify all the signatures there may be for a particular revision"""
mygpg = newgpg(ui)
rev = repo.lookup(rev)
hexrev = hgnode.hex(rev)
keys = []
Benoit Boissinot
fixes for gpg.py extension...
r1681 for data, context in sigwalk(repo):
node, version, sig = data
if node == hexrev:
k = getkeys(ui, repo, mygpg, data, context)
if k:
keys.extend(k)
Benoit Boissinot
gpg signing extension for hg...
r1592
if not keys:
Martin Geisler
gpg: lowercase messages
r16927 ui.write(_("no valid signature for %s\n") % hgnode.short(rev))
Benoit Boissinot
gpg signing extension for hg...
r1592 return
Benoit Boissinot
fixes for gpg.py extension...
r1681
Benoit Boissinot
gpg signing extension for hg...
r1592 # print summary
FUJIWARA Katsunori
gpg: make a message translatable...
r29239 ui.write(_("%s is signed by:\n") % hgnode.short(rev))
Benoit Boissinot
fixes for gpg.py extension...
r1681 for key in keys:
ui.write(" %s\n" % keystr(ui, key))
Benoit Boissinot
gpg signing extension for hg...
r1592
Benoit Boissinot
fixes for gpg.py extension...
r1681 def keystr(ui, key):
"""associate a string to a key (username, comment)"""
keyid, user, fingerprint = key
Boris Feld
configitems: register the 'gpg' arbitraty key section
r34771 comment = ui.config("gpg", fingerprint)
Benoit Boissinot
fixes for gpg.py extension...
r1681 if comment:
return "%s (%s)" % (user, comment)
else:
return user
Benoit Boissinot
gpg signing extension for hg...
r1592
Martin Geisler
gpg: use cmdutil.command decorator
r14299 @command("sign",
[('l', 'local', None, _('make the signature local')),
('f', 'force', None, _('sign even if the sigfile is modified')),
('', 'no-commit', None, _('do not commit the sigfile after signing')),
('k', 'key', '',
_('the key id to sign with'), _('ID')),
('m', 'message', '',
FUJIWARA Katsunori
doc: unify help text for "--message" option...
r21951 _('use text as commit message'), _('TEXT')),
FUJIWARA Katsunori
gpg: accept '--edit' like other commands creating new changeset...
r21711 ('e', 'edit', False, _('invoke editor on commit messages')),
Yuya Nishihara
commands: move templates of common command options to cmdutil (API)...
r32375 ] + cmdutil.commitopts2,
Thomas Arendsen Hein
consistency: use REV instead of REVISION
r16991 _('hg sign [OPTION]... [REV]...'))
Benoit Boissinot
gpg signing extension for hg...
r1592 def sign(ui, repo, *revs, **opts):
Thomas Arendsen Hein
Make 'hg sign' behave like other commands: Default to current parent.
r3916 """add a signature for the current or given revision
If no revision is given, the parent of the working directory is used,
or tip if no revision is checked out.
Thomas Arendsen Hein
Document log date ranges and mention 'hg help dates' for all commands (issue998)
r6163
Matt Mackall
gpg: mention undocumented options
r25791 The ``gpg.cmd`` config setting can be used to specify the command
to run. A default key can be specified with ``gpg.key``.
Martin Geisler
Use our custom hg reStructuredText role some more...
r11193 See :hg:`help dates` for a list of formats valid for -d/--date.
Thomas Arendsen Hein
Make 'hg sign' behave like other commands: Default to current parent.
r3916 """
Bryan O'Sullivan
with: use context manager for wlock in sign
r27814 with repo.wlock():
FUJIWARA Katsunori
gpg: make sign acquire wlock before processing...
r27196 return _dosign(ui, repo, *revs, **opts)
Thomas Arendsen Hein
Make 'hg sign' behave like other commands: Default to current parent.
r3916
FUJIWARA Katsunori
gpg: make sign acquire wlock before processing...
r27196 def _dosign(ui, repo, *revs, **opts):
Benoit Boissinot
gpg signing extension for hg...
r1592 mygpg = newgpg(ui, **opts)
Pulkit Goyal
py3: handle keyword arguments in hgext/gpg.py...
r34979 opts = pycompat.byteskwargs(opts)
Benoit Boissinot
gpg signing extension for hg...
r1592 sigver = "0"
sigmessage = ""
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139
date = opts.get('date')
if date:
opts['date'] = util.parsedate(date)
Benoit Boissinot
gpg signing extension for hg...
r1592 if revs:
nodes = [repo.lookup(n) for n in revs]
else:
Thomas Arendsen Hein
Make 'hg sign' behave like other commands: Default to current parent.
r3916 nodes = [node for node in repo.dirstate.parents()
if node != hgnode.nullid]
if len(nodes) > 1:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('uncommitted merge - please provide a '
Thomas Arendsen Hein
Make 'hg sign' behave like other commands: Default to current parent.
r3916 'specific revision'))
if not nodes:
nodes = [repo.changelog.tip()]
Benoit Boissinot
gpg signing extension for hg...
r1592
for n in nodes:
hexnode = hgnode.hex(n)
Martin Geisler
gpg: lowercase messages
r16927 ui.write(_("signing %d:%s\n") % (repo.changelog.rev(n),
Benoit Boissinot
i18n: mark more strings for translation
r10510 hgnode.short(n)))
Benoit Boissinot
gpg signing extension for hg...
r1592 # build data
data = node2txt(repo, n, sigver)
sig = mygpg.sign(data)
if not sig:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("error while signing"))
Benoit Boissinot
gpg signing extension for hg...
r1592 sig = binascii.b2a_base64(sig)
sig = sig.replace("\n", "")
sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
# write it
if opts['local']:
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 repo.vfs.append("localsigs", sigmessage)
Benoit Boissinot
gpg signing extension for hg...
r1592 return
Matt Mackall
gpg: move test of force before status call
r22682 if not opts["force"]:
msigs = match.exact(repo.root, '', ['.hgsigs'])
Augie Fackler
cleanup: use __builtins__.any instead of util.any...
r25149 if any(repo.status(match=msigs, unknown=True, ignored=True)):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("working copy of .hgsigs is changed "),
Matt Mackall
gpg: use an abort hint and don't mention --force
r22683 hint=_("please commit .hgsigs manually"))
Benoit Boissinot
gpg signing extension for hg...
r1592
Pierre-Yves David
gpg: use 'wvfs' instead of 'wfile'...
r31414 sigsfile = repo.wvfs(".hgsigs", "ab")
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 sigsfile.write(sigmessage)
sigsfile.close()
Benoit Boissinot
gpg signing extension for hg...
r1592
Matt Mackall
dirstate: add __contains__ and make __getitem__ more useful...
r4906 if '.hgsigs' not in repo.dirstate:
Dirkjan Ochtman
move working dir/dirstate methods from localrepo to workingctx
r11303 repo[None].add([".hgsigs"])
Benoit Boissinot
gpg signing extension for hg...
r1592
if opts["no_commit"]:
return
message = opts['message']
if not message:
Martin Geisler
do not translate commit messages...
r9183 # we don't translate commit messages
message = "\n".join(["Added signature for changeset %s"
Benoit Boissinot
gpg: use the same log message format as hg tag
r5475 % hgnode.short(n)
Benoit Boissinot
gpg signing extension for hg...
r1592 for n in nodes])
try:
Pulkit Goyal
py3: handle keyword arguments in hgext/gpg.py...
r34979 editor = cmdutil.getcommiteditor(editform='gpg.sign',
**pycompat.strkwargs(opts))
FUJIWARA Katsunori
gpg: accept '--edit' like other commands creating new changeset...
r21711 repo.commit(message, opts['user'], opts['date'], match=msigs,
FUJIWARA Katsunori
gpg: pass 'editform' argument to 'cmdutil.getcommiteditor'...
r22001 editor=editor)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except ValueError as inst:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(str(inst))
Benoit Boissinot
gpg signing extension for hg...
r1592
def node2txt(repo, node, ver):
"""map a manifest into some text"""
if ver == "0":
return "%s\n" % hgnode.hex(node)
else:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("unknown signature version"))