changeset 1681:98eef041f9c7

fixes for gpg.py extension - add copyright and license - add i18n - add 'sigs' command behaving like 'hg tags' - change 'role' to 'comment' and output it only if there is one - refactoring
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Wed, 01 Feb 2006 19:37:26 +0100
parents c21b54f7f7b8
children ca1cda9220d5
files hgext/gpg.py
diffstat 1 files changed, 133 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/gpg.py	Wed Feb 01 19:18:15 2006 +0100
+++ b/hgext/gpg.py	Wed Feb 01 19:37:26 2006 +0100
@@ -1,6 +1,14 @@
-import os, tempfile, binascii, errno
+# GnuPG signing extension for Mercurial
+#
+# Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import os, tempfile, binascii
 from mercurial import util
 from mercurial import node as hgnode
+from mercurial.i18n import gettext as _
 
 class gpg:
     def __init__(self, path, key=None):
@@ -14,6 +22,7 @@
     def verify(self, data, sig):
         """ returns of the good and bad signatures"""
         try:
+            # create temporary files
             fd, sigfile = tempfile.mkstemp(prefix="hggpgsig")
             fp = os.fdopen(fd, 'wb')
             fp.write(sig)
@@ -22,8 +31,8 @@
             fp = os.fdopen(fd, 'wb')
             fp.write(data)
             fp.close()
-            gpgcmd = "%s --logger-fd 1 --status-fd 1 --verify \"%s\" \"%s\"" % (self.path, sigfile, datafile)
-            #gpgcmd = "%s --status-fd 1 --verify \"%s\" \"%s\"" % (self.path, sigfile, datafile)
+            gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
+                      "\"%s\" \"%s\"" % (self.path, sigfile, datafile))
             ret = util.filter("", gpgcmd)
         except:
             for f in (sigfile, datafile):
@@ -41,7 +50,7 @@
                 continue
             l = l[9:]
             if l.startswith("ERRSIG"):
-                err = "error while verifying signature"
+                err = _("error while verifying signature")
                 break
             elif l.startswith("VALIDSIG"):
                 # fingerprint of the primary key
@@ -61,12 +70,97 @@
         return err, keys
 
 def newgpg(ui, **opts):
+    """create a new gpg instance"""
     gpgpath = ui.config("gpg", "cmd", "gpg")
     gpgkey = opts.get('key')
     if not gpgkey:
         gpgkey = ui.config("gpg", "key", None)
     return gpg(gpgpath, gpgkey)
 
+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))
+            ln +=1
+
+    fl = repo.file(".hgsigs")
+    h = fl.heads()
+    h.reverse()
+    # read the heads
+    for r in h:
+        fn = ".hgsigs|%s" % hgnode.short(r)
+        for item in parsefile(fl.read(r).splitlines(), fn):
+            yield item
+    try:
+        # read local signatures
+        fn = "localsigs"
+        for item in parsefile(repo.opener(fn), fn):
+            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)
+    err, keys = mygpg.verify(data, sig)
+    if err:
+        ui.warn("%s:%d %s\n" % (fn, ln , err))
+        return None
+
+    validkeys = []
+    # warn for expired key and/or sigs
+    for key in keys:
+        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
+
+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)
+    nodes = list(revs)
+    nodes.reverse()
+    for r in nodes:
+        for k in revs[r]:
+            r = "%5d:%s" % (r, hgnode.hex(repo.changelog.node(r)))
+            ui.write("%-30s %s\n" % (keystr(ui, k), r))
+
 def check(ui, repo, rev):
     """verify all the signatures there may be for a particular revision"""
     mygpg = newgpg(ui)
@@ -74,63 +168,30 @@
     hexrev = hgnode.hex(rev)
     keys = []
 
-    def addsig(fn, ln, l):
-        if not l: return
-        n, v, sig = l.split(" ", 2)
-        if n == hexrev:
-            data = node2txt(repo, rev, v)
-            sig = binascii.a2b_base64(sig)
-            err, k = mygpg.verify(data, sig)
-            if not err:
-                keys.append((k, fn, ln))
-            else:
-                ui.warn("%s:%d %s\n" % (fn, ln , err))
-
-    fl = repo.file(".hgsigs")
-    h = fl.heads()
-    h.reverse()
-    # read the heads
-    for r in h:
-        ln = 1
-        for l in fl.read(r).splitlines():
-            addsig(".hgsigs|%s" % hgnode.short(r), ln, l)
-            ln +=1
-    try:
-        # read local signatures
-        ln = 1
-        f = repo.opener("localsigs")
-        for l in f:
-            addsig("localsigs", ln, l)
-            ln +=1
-    except IOError:
-        pass
+    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)
 
     if not keys:
-        ui.write("%s not signed\n" % hgnode.short(rev))
+        ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
         return
-    valid = []
-    # warn for expired key and/or sigs
-    for k, fn, ln in keys:
-        prefix = "%s:%d" % (fn, ln)
-        for key in k:
-            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]))
-            valid.append((key[1], key[2], key[3]))
+
     # print summary
     ui.write("%s is signed by:\n" % hgnode.short(rev))
-    for keyid, user, fingerprint in valid:
-        role = getrole(ui, fingerprint)
-        ui.write("  %s (%s)\n" % (user, role))
+    for key in keys:
+        ui.write(" %s\n" % keystr(ui, key))
 
-def getrole(ui, fingerprint):
-    return ui.config("gpg", fingerprint, "no role defined")
+def keystr(ui, key):
+    """associate a string to a key (username, comment)"""
+    keyid, user, fingerprint = key
+    comment = ui.config("gpg", fingerprint, None)
+    if comment:
+        return "%s (%s)" % (user, comment)
+    else:
+        return user
 
 def sign(ui, repo, *revs, **opts):
     """add a signature for the current tip or a given revision"""
@@ -150,7 +211,7 @@
         data = node2txt(repo, n, sigver)
         sig = mygpg.sign(data)
         if not sig:
-            raise util.Abort("Error while signing")
+            raise util.Abort(_("Error while signing"))
         sig = binascii.b2a_base64(sig)
         sig = sig.replace("\n", "")
         sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
@@ -162,9 +223,9 @@
 
     for x in repo.changes():
         if ".hgsigs" in x and not opts["force"]:
-            raise util.Abort("working copy of .hgsigs is changed "
-                             "(please commit .hgsigs manually "
-                             "or use --force)")
+            raise util.Abort(_("working copy of .hgsigs is changed "
+                               "(please commit .hgsigs manually "
+                               "or use --force)"))
 
     repo.wfile(".hgsigs", "ab").write(sigmessage)
 
@@ -176,7 +237,8 @@
 
     message = opts['message']
     if not message:
-        message = "\n".join(["Added signature for changeset %s" % hgnode.hex(n)
+        message = "\n".join([_("Added signature for changeset %s")
+                             % hgnode.hex(n)
                              for n in nodes])
     try:
         repo.commit([".hgsigs"], message, opts['user'], opts['date'])
@@ -188,19 +250,20 @@
     if ver == "0":
         return "%s\n" % hgnode.hex(node)
     else:
-        util.Abort("unknown signature version")
+        util.Abort(_("unknown signature version"))
 
 cmdtable = {
     "sign":
         (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"),
-          ('m', 'message', "", "commit message"),
-          ('d', 'date', "", "date code"),
-          ('u', 'user', "", "user"),
-          ('k', 'key', "", "the key id to sign with")],
-         "hg sign [OPTION]... REVISIONS"),
-    "sigcheck": (check, [], 'hg sigcheck REVISION')
+         [('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")),
+          ('m', 'message', "", _("commit message")),
+          ('d', 'date', "", _("date code")),
+          ('u', 'user', "", _("user")),
+          ('k', 'key', "", _("the key id to sign with"))],
+         _("hg sign [OPTION]... REVISIONS")),
+    "sigcheck": (check, [], _('hg sigcheck REVISION')),
+    "sigs": (sigs, [], _('hg sigs')),
 }