[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#702908: PTS: upload signature parsing patch



tags 700515 + patch
thanks

Hi,

motivated by Paus Wise, I scratched my own itch: here's a patch that
makes PTS parse GPG signatures - therefore being able to display a
package's sponsor. Please review.

A few notes and remarks:

I'm using GPGME, or rather its python binding, so python-gpgme becomes a
dependency.

Currently, if there's anything wrong with the signature or the public
key missing, there's no warning or anything. It will simply fall back to
display the sender of the email, as before. Not sure if that's much of
an issue.

A public key may have multiple uids and the signature is only specific
to the key, not any specific uid. But I only want to display a single
uid. The way I implemented this now is: we take the first uid. Only if a
later uid has an email ending in "@debian.org", we prefer that one.
That's certainly not ideal. We could possibly do an LDAP lookup via the
key's fingerprint on db.debian.org instead...

In the news.xml file, I replaced the "from" attribute of the news item
with more fine grained "from_address" and "from_realname". However, I
think existing entries will be kept, so the XSL-templates need to be
able to parse both. At least that's how I've implemented it. If a
complete rewrite of all news.xml files is feasible, the XSLTs could be
simplified quite a bit.

I also added links to http://qa.debian.org/developer.php?login=$EMAIL
for both, the sender and signer of the mail in the HTML display of the
NEWS. Not in RSS.

Comments?

Regards

Markus Wanner

Index: www/xsl/news2rss.xsl
===================================================================
--- www/xsl/news2rss.xsl	(revision 2950)
+++ www/xsl/news2rss.xsl	(working copy)
@@ -60,9 +60,32 @@
     <item>
       <title>
 	<xsl:value-of select="." />
-	<xsl:text> (</xsl:text>
-	<xsl:value-of select="@from" />
-	<xsl:text>)</xsl:text>
+
+        <xsl:if test="@from or @from_address">
+          <xsl:text> (</xsl:text>
+          <xsl:choose>
+            <!-- old style -->
+            <xsl:when test="@from"><xsl:value-of select="@from"/></xsl:when>
+            <!-- newer variants -->
+            <xsl:when test="@from_address and @sign_address and @from_address != @sign_address">
+              <xsl:choose>
+                <xsl:when test="@from_realname"><xsl:value-of select="@from_realname"/>
+                <xsl:otherwise><xsl:value-of select="@from_address"/>
+              <xsl:choose>
+              <xsl:text> - </xsl:text>
+              <xsl:choose>
+                <xsl:when test="@sign_realname"><xsl:value-of select="@sign_realname"/>
+                <xsl:otherwise><xsl:value-of select="@sign_address"/>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:when test="@from_realname"><xsl:value-of select="@from_realname"/></xsl:when>
+            <xsl:otherwise><xsl:value-of select="@from_address"/></xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>)</xsl:text>
+        </xsl:if>
+
+
+	<xsl:value-of select="@from_realname" />
       </title>
       <xsl:variable name="id">
 	<xsl:value-of select="$ptsurl" />
@@ -81,9 +104,30 @@
         <xsl:value-of select="@date" />
         <xsl:text>] </xsl:text>
         <xsl:value-of select="." />
-        <xsl:text> (</xsl:text>
-        <xsl:value-of select="@from" />
-        <xsl:text>)&lt;/a&gt;</xsl:text>
+
+        <xsl:if test="@from or @from_address">
+          <xsl:text> (</xsl:text>
+          <xsl:choose>
+            <!-- old style -->
+            <xsl:when test="@from"><xsl:value-of select="@from"/></xsl:when>
+            <!-- newer variants -->
+            <xsl:when test="@from_address and @sign_address and @from_address != @sign_address">
+              <xsl:choose>
+                <xsl:when test="@from_realname"><xsl:value-of select="@from_realname"/>
+                <xsl:otherwise><xsl:value-of select="@from_address"/>
+              <xsl:choose>
+              <xsl:text> - </xsl:text>
+              <xsl:choose>
+                <xsl:when test="@sign_realname"><xsl:value-of select="@sign_realname"/>
+                <xsl:otherwise><xsl:value-of select="@sign_address"/>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:when test="@from_realname"><xsl:value-of select="@from_realname"/></xsl:when>
+            <xsl:otherwise><xsl:value-of select="@from_address"/></xsl:otherwise>
+          </xsl:choose>
+          <xsl:text>)</xsl:text>
+        </xsl:if>
+        <xsl:text>&lt;/a&gt;</xsl:text>
       </description>
     </item>
   </xsl:template>
Index: www/xsl/pts.xsl
===================================================================
--- www/xsl/pts.xsl	(revision 2950)
+++ www/xsl/pts.xsl	(working copy)
@@ -75,6 +75,30 @@
 <xsl:variable name="security-mirror">http://security.debian.org/</xsl:variable>
 <xsl:variable name="backports-mirror">http://http.debian.net/debian-backports</xsl:variable>
 
+<xsl:template name="name_email_link">
+  <xsl:param name="realname"/>
+  <xsl:param name="address"/>
+  <xsl:param name="title"/>
+
+  <xsl:element name="a">
+    <xsl:attribute name="href">
+      <xsl:text>http://qa.debian.org/developer.php?login=</xsl:text>
+      <xsl:call-template name="escape-name">
+        <xsl:with-param name="text"><xsl:value-of select="$address"/></xsl:with-param>
+      </xsl:call-template>
+    </xsl:attribute>
+    <span class="name">
+      <xsl:if test="string-length($title) > 0">
+        <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="string-length($realname) > 0"><xsl:value-of select="$realname"/></xsl:when>
+        <xsl:otherwise><xsl:value-of select="$address"/></xsl:otherwise>
+      </xsl:choose>
+    </span>
+  </xsl:element>
+</xsl:template>
+
 <xsl:template name="outputitem">
   <xsl:choose>
     <xsl:when test="@url">
@@ -83,10 +107,40 @@
 	<xsl:value-of select="@date"/>
         <xsl:text>] </xsl:text>
       </xsl:if><a href="{@url}">
-      <xsl:value-of select="text()"/></a><xsl:if test="@from">
+      <xsl:value-of select="text()"/></a>
+
+      <xsl:if test="@from or @from_address">
         <xsl:text> (</xsl:text>
-	<xsl:value-of select="@from"/>
-        <xsl:text>)</xsl:text></xsl:if></li>
+
+        <xsl:choose>
+          <!-- old style news entry, no link or signature info -->
+          <xsl:when test="@from"><xsl:value-of select="@from"/></xsl:when>
+
+          <xsl:when test="@from_address and @sign_address and @from_address != @sign_address">
+            <xsl:text></xsl:text><xsl:call-template name="name_email_link">
+              <xsl:with-param name="title">email sender</xsl:with-param>
+              <xsl:with-param name="realname"><xsl:value-of select="@from_realname"/></xsl:with-param>
+              <xsl:with-param name="address"><xsl:value-of select="@from_address"/></xsl:with-param>
+            </xsl:call-template>
+            <xsl:text> - </xsl:text>
+            <xsl:text>signed by: </xsl:text><xsl:call-template name="name_email_link">
+              <xsl:with-param name="title">signer</xsl:with-param>
+              <xsl:with-param name="realname"><xsl:value-of select="@sign_realname"/></xsl:with-param>
+              <xsl:with-param name="address"><xsl:value-of select="@sign_address"/></xsl:with-param>
+            </xsl:call-template>
+          </xsl:when>
+
+          <xsl:otherwise>
+            <xsl:call-template name="name_email_link">
+              <xsl:with-param name="realname"><xsl:value-of select="@from_realname"/></xsl:with-param>
+              <xsl:with-param name="address"><xsl:value-of select="@from_address"/></xsl:with-param>
+            </xsl:call-template>
+          </xsl:otherwise>
+        </xsl:choose>
+
+        <xsl:text>)</xsl:text>
+      </xsl:if>
+      </li>
     </xsl:when>
     <xsl:when test="@type='raw'">
       <li>
@@ -208,13 +262,6 @@
   </xsl:if>
 </xsl:template>
 
-<xsl:template name="maintainer-email">
-  <xsl:param name="email" />
-  <a class="email" href="mailto:{$email}";>
-    <img alt="[email]" src="../common/email.png" title="email" />
-  </a>
-</xsl:template>
-
 <xsl:template name="general-information">
   <div class="block info">
     <a name="general" />
@@ -245,17 +292,11 @@
 
       <dt title="Maintainer and Uploaders">maint</dt>
       <dd class="maintainer">
-	<xsl:element name="a">
-	  <xsl:attribute name="href">
-	    <xsl:text>http://qa.debian.org/developer.php?login=</xsl:text>
-	    <xsl:call-template name="escape-name">
-	      <xsl:with-param name="text"><xsl:value-of select="maintainer/email"/></xsl:with-param>
-	    </xsl:call-template>
-	  </xsl:attribute>
-	  <span class="name" title="maintainer">
-	    <xsl:value-of select="maintainer/name"/>
-	  </span>
-	</xsl:element>
+        <xsl:call-template name="name_email_link">
+          <xsl:with-param name="title">maintainer</xsl:with-param>
+          <xsl:with-param name="realname"><xsl:value-of select="maintainer/name"/></xsl:with-param>
+          <xsl:with-param name="address"><xsl:value-of select="maintainer/email"/></xsl:with-param>
+        </xsl:call-template>
 	<xsl:if test="$hasother and $other/dms/item/@email=maintainer/email">
 	  (<small>dm</small>)
 	</xsl:if>
@@ -264,19 +305,11 @@
 	    <xsl:text>, </xsl:text>
 	    <span class="uploader">
 	      <small>
-	      <xsl:element name="a">
-		<xsl:attribute name="href">
-		  <xsl:text>http://qa.debian.org/developer.php?login=</xsl:text>
-		  <xsl:call-template name="escape-name">
-		    <xsl:with-param name="text">
-		      <xsl:value-of select="email"/>
-		    </xsl:with-param>
-		  </xsl:call-template>
-		</xsl:attribute>
-		<span class="name" title="uploader">
-		  <xsl:value-of select="name"/>
-		</span>
-	      </xsl:element>
+              <xsl:call-template name="name_email_link">
+                <xsl:with-param name="title">uploader</xsl:with-param>
+                <xsl:with-param name="realname"><xsl:value-of select="name"/></xsl:with-param>
+                <xsl:with-param name="address"><xsl:value-of select="email"/></xsl:with-param>
+              </xsl:call-template>
 	      <xsl:text> (u</xsl:text>
 	      <xsl:if test="$hasother and $other/dms/item/@email=email">
 		<xsl:text>, dm</xsl:text>
Index: www/bin/update_news.py
===================================================================
--- www/bin/update_news.py	(revision 2950)
+++ www/bin/update_news.py	(working copy)
@@ -79,8 +79,11 @@
         sub_elt.setAttribute("date", info["date"])
     sub_elt.setAttribute("rfc822date",
                          timestamp_to_rfc822date(info["timestamp"]))
-    if info.has_key("from_name"):
-        sub_elt.setAttribute("from", info["from_name"])
+    # Copy these attributes as-is
+    for attname in ("from_realname", "from_address",
+		    "sign_realname", "sign_address"):
+	if info.has_key(attname):
+	    sub_elt.setAttribute(attname, info[attname])
     elt.appendChild(sub_elt)
 
 
Index: www/bin/common.py
===================================================================
--- www/bin/common.py	(revision 2950)
+++ www/bin/common.py	(working copy)
@@ -7,8 +7,12 @@
 # This file is distributed under the terms of the General Public License
 # version 2 or (at your option) any later version.
 
-import hashlib, os, os.path, re, rfc822, time, email, sys
+import hashlib, os, os.path, re, rfc822, time, email, sys, gpgme
 from email import Utils, Header
+try:
+    from io import BytesIO
+except ImportError:
+    from StringIO import StringIO as BytesIO
 
 from config import root
 
@@ -93,12 +97,48 @@
         frm = msg.get("From")
         if msg.has_key("X-PTS-From"): frm = msg.get("X-PTS-From")
         (realname, address) = rfc822.parseaddr(frm)
+
         if realname:
-            frm = realname
-        else:
-            frm = address
-        frm = decode_header(frm)
-        info["from_name"] = frm
+            info["from_realname"] = decode_header(realname)
+        info["from_address"] = decode_header(address)
+
+    # Check if the mail containes signed data. If so, parse the signature
+    # invoking gnupg.
+    isSigned = False
+    for i in range(3):
+        if lines[i].startswith("-----BEGIN PGP"):
+            isSigned = True
+            break
+
+    if isSigned:
+        ctx = gpgme.Context()
+        plain = BytesIO()
+        result = ctx.verify(BytesIO(body), None, plain)
+        for sig in result:
+            if sig.status == gpgme.ERR_NO_ERROR and sig.summary & gpgme.SIGSUM_GOOD:
+                # Some signature, for PTS, we don't care if it's good or
+                # not, just *who* claims to have signed.
+                key = ctx.get_key(sig.fpr)
+
+                best = None
+                for uid in key.uids:
+                    print "signature uid: %s <%s>" % (uid.name, uid.email)
+                    if not best:
+                        best = (uid.name, uid.email)
+                        # We continue the loop, because we prefer a
+                        # @debian.org address.
+
+                    if uid.email.endswith("@debian.org"):
+                        best = (uid.name, uid.email)
+                        break
+
+                if best:
+                    info['sign_realname'] = best[0]
+                    info['sign_address'] = best[1]
+
+                # Simply use the first valid signature found
+                break
+
     return info
 
 def hash_name(pkg):

Attachment: signature.asc
Description: OpenPGP digital signature


Reply to: