Hi Kevin,

On Friday, 2015-02-13 18:53:17 -0800, Kevin J. McCarthy wrote:

> If you are okay with my modifications to your patches, I'll push these
> after some more testing and giving others a chance to review them.

I tried now, works fine.

If you didn't push yet, attached is a slightly modified version of my
resulting patch that changes the (s1-pf) use to a hexdigits counter and
adds a comment about the exlusive return pointers to the
crypt_get_fingerprint_or_id() declaration.

  Eike

-- 
OpenPGP/GnuPG encrypted mail preferred in all private communication.
Key "ID" 0x65632D3A - 2265 D7F3 A7B0 95CC 3918  630B 6A6C D5B7 6563 2D3A
Better use 64-bit 0x6A6CD5B765632D3A here is why: https://evil32.com/
Care about Free Software, support the FSFE https://fsfe.org/support/?erack
Use LibreOffice! https://www.libreoffice.org/
# HG changeset patch
# User Eike Rathke <er...@erack.de>
# Date 1423687117 -3600
# Node ID f0b0e80f5e36671bf36e283b3c7680f6202b2dbc
# Parent  6213c21607b79b1faab74ce39573c17a08011fb8
Allow fingerprint user input for key selection. (see #3695)

Accept and check input of a fingerprint and find the matching key.

Note that for both to work, match against and display of fingerprint, the
pgp_list_pubring_command and pgp_list_secring_command need to contain the
--with-fingerprint option, or have with-fingerprint in ~/.gnupg/gpg.conf.

diff --git a/crypt-gpgme.c b/crypt-gpgme.c
--- a/crypt-gpgme.c
+++ b/crypt-gpgme.c
@@ -4188,32 +4188,23 @@
   crypt_key_t *matches = NULL;
   crypt_key_t **matches_endp = &matches;
   crypt_key_t *k;
-  const char *ps, *pl;
+  const char *ps, *pl, *pfcopy, *phint;
 
   mutt_message (_("Looking for keys matching \"%s\"..."), p);
 
   *forced_valid = 0;
 
-  hints = crypt_add_string_to_hints (hints, p);
+  pfcopy = crypt_get_fingerprint_or_id (p, &phint, &pl, &ps);
+  hints = crypt_add_string_to_hints (hints, phint);
   keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
   mutt_free_list (&hints);
 
   if (!keys)
+  {
+    FREE (&pfcopy);
     return NULL;
-
-  /* User input may be short or long key ID, independent of OPTPGPLONGIDS.
-   * crypt_key_t->keyid should always contain a long key ID without 0x.
-   * Strip leading "0x" before loops so it doesn't have to be done over and
-   * over again, and prepare pl and ps to simplify logic in the loop's inner
-   * condition.
-   */
-  pl = (!mutt_strncasecmp (p, "0x", 2) ? p + 2 : p);
-  ps = (mutt_strlen (pl) == 16 ? pl + 8 : pl);
-
-  /* If ps != pl it means a long ID (or name of 16 characters) was given, do
-   * not attempt to match short IDs then. Also, it is unnecessary to try to
-   * match pl against long IDs if ps == pl as pl could not be a long ID. */
-  
+  }
+
   for (k = keys; k; k = k->next)
     {
       if (abilities && !(k->flags & abilities))
@@ -4223,8 +4214,9 @@
                   "key %s, \"%s\": ",  p, crypt_long_keyid (k), k->uid));
 
       if (!*p
-          || (ps != pl && mutt_strcasecmp (pl, crypt_long_keyid (k)) == 0)
-          || (ps == pl && mutt_strcasecmp (ps, crypt_short_keyid (k)) == 0)
+          || (pfcopy && mutt_strcasecmp (pfcopy, crypt_fpr (k)) == 0)
+          || (pl && mutt_strcasecmp (pl, crypt_long_keyid (k)) == 0)
+          || (ps && mutt_strcasecmp (ps, crypt_short_keyid (k)) == 0)
           || mutt_stristr (k->uid, p))
         {
           crypt_key_t *tmp;
@@ -4236,6 +4228,7 @@
         }
     }
   
+  FREE (&pfcopy);
   crypt_free_key (&keys);
   
   if (matches)
diff --git a/crypt.c b/crypt.c
--- a/crypt.c
+++ b/crypt.c
@@ -900,3 +900,83 @@
 }
 
 
+/* Obtain pointers to fingerprint or short or long key ID, if any.
+ * See mutt_crypt.h for details.
+ */
+const char* crypt_get_fingerprint_or_id (char *p, const char **pphint,
+    const char **ppl, const char **pps)
+{
+  const char *ps, *pl, *phint;
+  char *pfcopy, *pf, *s1, *s2;
+  char c;
+  int isid;
+  size_t hexdigits;
+
+  /* User input may be partial name, fingerprint or short or long key ID,
+   * independent of OPTPGPLONGIDS.
+   * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5).
+   * Strip leading "0x" for key ID detection and prepare pl and ps to indicate
+   * if an ID was found and to simplify logic in the key loop's inner
+   * condition of the caller. */
+
+  pf = mutt_skip_whitespace (p);
+  if (!mutt_strncasecmp (pf, "0x", 2))
+    pf += 2;
+
+  /* Check if a fingerprint is given, must be hex digits only, blanks
+   * separating groups of 4 hex digits are allowed. Also pre-check for ID. */
+  isid = 2;             /* unknown */
+  hexdigits = 0;
+  s1 = pf;
+  do
+  {
+    c = *(s1++);
+    if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))
+    {
+      ++hexdigits;
+      if (isid == 2)
+        isid = 1;       /* it is an ID so far */
+    }
+    else if (c)
+    {
+      isid = 0;         /* not an ID */
+      if (c == ' ' && ((hexdigits % 4) == 0))
+        ;               /* skip blank before or after 4 hex digits */
+      else
+        break;          /* any other character or position */
+    }
+  } while (c);
+
+  /* If at end of input, check for correct fingerprint length and copy if. */
+  pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? safe_strdup (pf) : NULL);
+
+  if (pfcopy)
+  {
+    /* Use pfcopy to strip all spaces from fingerprint and as hint. */
+    s1 = s2 = pfcopy;
+    do
+    {
+      *(s1++) = *(s2 = mutt_skip_whitespace (s2));
+    } while (*(s2++));
+
+    phint = pfcopy;
+    ps = pl = NULL;
+  }
+  else
+  {
+    phint = p;
+    ps = pl = NULL;
+    if (isid == 1)
+    {
+      if (mutt_strlen (pf) == 16)
+        pl = pf;        /* long key ID */
+      else if (mutt_strlen (pf) == 8)
+        ps = pf;        /* short key ID */
+    }
+  }
+
+  *pphint = phint;
+  *ppl = pl;
+  *pps = ps;
+  return pfcopy;
+}
diff --git a/mutt_crypt.h b/mutt_crypt.h
--- a/mutt_crypt.h
+++ b/mutt_crypt.h
@@ -153,6 +153,20 @@
    TEMPFILE.  */
 int crypt_write_signed(BODY *a, STATE *s, const char *tempf);
 
+/* Obtain pointers to fingerprint or short or long key ID, if any.
+ 
+   Upon return, at most one of return, *ppl and *pps pointers is non-NULL,
+   indicating the longest fingerprint or ID found, if any.
+
+   Return:  Copy of fingerprint, if any, stripped of all spaces, else NULL.
+            Must be FREE'd by caller.
+   *pphint  Start of string to be passed to pgp_add_string_to_hints() or 
+            crypt_add_string_to_hints().
+   *ppl     Start of long key ID if detected, else NULL.
+   *pps     Start of short key ID if detected, else NULL. */
+const char* crypt_get_fingerprint_or_id (char *p, const char **pphint,
+    const char **ppl, const char **pps);
+
 
 
 /*-- cryptglue.c --*/
diff --git a/pgpkey.c b/pgpkey.c
--- a/pgpkey.c
+++ b/pgpkey.c
@@ -932,29 +932,21 @@
   pgp_uid_t *a;
   short match;
   size_t l;
-  const char *ps, *pl;
+  const char *ps, *pl, *pfcopy, *phint;
 
   if ((l = mutt_strlen (p)) && p[l-1] == '!')
     p[l-1] = 0;
 
   mutt_message (_("Looking for keys matching \"%s\"..."), p);
 
-  hints = pgp_add_string_to_hints (hints, p);
+  pfcopy = crypt_get_fingerprint_or_id (p, &phint, &pl, &ps);
+  hints = pgp_add_string_to_hints (hints, phint);
   keys = pgp_get_candidates (keyring, hints);
   mutt_free_list (&hints);
 
   if (!keys)
     goto out;
 
-  /* User input may be short or long key ID, independent of OPTPGPLONGIDS.
-   * pgp_key_t->keyid should always contain a long key ID without 0x.
-   * Strip leading "0x" before loops so it doesn't have to be done over and
-   * over again, and prepare pl and ps to simplify logic in the loop's inner
-   * condition.
-   */
-  pl = (!mutt_strncasecmp (p, "0x", 2) ? p + 2 : p);
-  ps = (mutt_strlen (pl) == 16 ? pl + 8 : pl);
-
   for (k = keys; k; k = kn)
   {
     kn = k->next;
@@ -972,12 +964,10 @@
     dprint (5, (debugfile, "pgp_getkeybystr: matching \"%s\" against key %s:\n",
                 p, pgp_long_keyid (k)));
 
-    /* If ps != pl it means a long ID (or name of 16 characters) was given, do
-     * not attempt to match short IDs then. Also, it is unnecessary to try to
-     * match pl against long IDs if ps == pl as pl could not be a long ID. */
     if (!*p ||
-        (ps != pl && mutt_strcasecmp (pl, pgp_long_keyid (k)) == 0) ||
-        (ps == pl && mutt_strcasecmp (ps, pgp_short_keyid (k)) == 0))
+        (pfcopy && mutt_strcasecmp (pfcopy, k->fingerprint) == 0) ||
+        (pl && mutt_strcasecmp (pl, pgp_long_keyid (k)) == 0) ||
+        (ps && mutt_strcasecmp (ps, pgp_short_keyid (k)) == 0))
     {
       dprint (5, (debugfile, "\t\tmatch.\n"));
       match = 1;
@@ -1013,12 +1003,14 @@
       pgp_remove_key (&matches, k);
 
     pgp_free_key (&matches);
+    FREE (&pfcopy);
     if (l && !p[l-1])
       p[l-1] = '!';
     return k;
   }
 
 out:
+  FREE (&pfcopy);
   if (l && !p[l-1])
     p[l-1] = '!';
   return NULL;

Attachment: signature.asc
Description: Digital signature

Reply via email to