common: Prepare for parsing mail sub-addresses.
authorWerner Koch <wk@gnupg.org>
Mon, 12 Nov 2018 06:44:33 +0000 (07:44 +0100)
committerWerner Koch <wk@gnupg.org>
Mon, 12 Nov 2018 06:44:33 +0000 (07:44 +0100)
* common/mbox-util.c (mailbox_from_userid): Add arg subaddress and
implement.  Change all callers to pass false for it.

* common/t-mbox-util.c (run_mbox_no_sub_test): New.
(run_filter): Add arg no_sub.
(main): Call new test and add option --no-sub.
--

Some stats: In the about 5300000 keys on the SKS servers we found 3055
unique mailboxes with a '+' in it.  After removing leading and
trailing '+' as well as multiple '+' (e.g. "c++" or "foo+bar+baz")
2697 were left which seem to be valid sub-addresses.

To filter mailboxes out from a line delimited list with
user-ids (e.g. an SQL output), the command

   t-mbox-util --verbose --filter

can be used; to output w/o sub-addresses add --no-sub.

GnuPG-bug-id: 4200
Signed-off-by: Werner Koch <wk@gnupg.org>
16 files changed:
common/mbox-util.c
common/mbox-util.h
common/t-mbox-util.c
dirmngr/server.c
g10/export.c
g10/getkey.c
g10/gpg.c
g10/import.c
g10/keylist.c
g10/keyserver.c
g10/sign.c
g10/tofu.c
g10/trustdb.c
tools/gpg-wks-client.c
tools/gpg-wks-server.c
tools/wks-util.c

index 76255ba..a9086a3 100644 (file)
@@ -173,11 +173,12 @@ is_valid_mailbox (const char *name)
 
 
 /* Return the mailbox (local-part@domain) form a standard user id.
-   All plain ASCII characters in the result are converted to
-   lowercase.  Caller must free the result.  Returns NULL if no valid
-   mailbox was found (or we are out of memory). */
+ * All plain ASCII characters in the result are converted to
+ * lowercase.  If SUBADDRESS is 1, '+' denoted sub-addresses are not
+ * included in the result.  Caller must free the result.  Returns NULL
+ * if no valid mailbox was found (or we are out of memory). */
 char *
-mailbox_from_userid (const char *userid)
+mailbox_from_userid (const char *userid, int subaddress)
 {
   const char *s, *s_end;
   size_t len;
@@ -226,6 +227,29 @@ mailbox_from_userid (const char *userid)
   else
     errno = EINVAL;
 
+  if (result && subaddress == 1)
+    {
+      char *atsign, *plus;
+
+      if ((atsign = strchr (result, '@')))
+        {
+          /* We consider a subaddress only if there is a single '+'
+           * in the local part and the '+' is not the first or last
+           * character.  */
+          *atsign = 0;
+          if ((plus = strchr (result, '+'))
+              && !strchr (plus+1, '+')
+              && result != plus
+              && plus[1] )
+            {
+              *atsign = '@';
+              memmove (plus, atsign, strlen (atsign)+1);
+            }
+          else
+            *atsign = '@';
+        }
+    }
+
   return result? ascii_strlwr (result): NULL;
 }
 
index 7355cee..10ff2c4 100644 (file)
@@ -22,7 +22,7 @@
 int has_invalid_email_chars (const void *buffer, size_t length);
 int is_valid_mailbox (const char *name);
 int is_valid_mailbox_mem (const void *buffer, size_t length);
-char *mailbox_from_userid (const char *userid);
+char *mailbox_from_userid (const char *userid, int subaddress);
 int is_valid_user_id (const char *uid);
 int is_valid_domain_name (const char *string);
 
index e9cf412..ae717f9 100644 (file)
@@ -83,7 +83,86 @@ run_mbox_test (void)
 
   for (idx=0; testtbl[idx].userid; idx++)
     {
-      char *mbox = mailbox_from_userid (testtbl[idx].userid);
+      char *mbox = mailbox_from_userid (testtbl[idx].userid, 0);
+
+      if (!testtbl[idx].mbox)
+        {
+          if (mbox)
+            fail (idx);
+        }
+      else if (!mbox)
+        fail (idx);
+      else if (strcmp (mbox, testtbl[idx].mbox))
+        fail (idx);
+
+      xfree (mbox);
+    }
+}
+
+
+static void
+run_mbox_no_sub_test (void)
+{
+  static struct
+  {
+    const char *userid;
+    const char *mbox;
+  } testtbl[] =
+    {
+      { "foo+bar@example.org", "foo@example.org" },
+      { "Werner Koch <wk@gnupg.org>", "wk@gnupg.org" },
+      { "<wk@gnupg.org>", "wk@gnupg.org" },
+      { "wk@gnupg.org", "wk@gnupg.org" },
+      { "wk@gnupg.org ", NULL },
+      { " wk@gnupg.org", NULL },
+      { "Werner Koch (test) <wk@gnupg.org>", "wk@gnupg.org" },
+      { "Werner Koch <wk@gnupg.org> (test)", "wk@gnupg.org" },
+      { "Werner Koch <wk@gnupg.org (test)", NULL },
+      { "Werner Koch <wk@gnupg.org >", NULL },
+      { "Werner Koch <wk@gnupg.org", NULL },
+      { "", NULL },
+      { "@", NULL },
+      { "bar <>", NULL },
+      { "<foo@example.org>", "foo@example.org" },
+      { "<foo.@example.org>", "foo.@example.org" },
+      { "<.foo.@example.org>", ".foo.@example.org" },
+      { "<foo..@example.org>", "foo..@example.org" },
+      { "<foo..bar@example.org>", "foo..bar@example.org" },
+      { "<foo@example.org.>", NULL },
+      { "<foo@example..org>", NULL },
+      { "<foo@.>", NULL },
+      { "<@example.org>", NULL },
+      { "<foo@@example.org>", NULL },
+      { "<@foo@example.org>", NULL },
+      { "<foo@example.org> ()", "foo@example.org" },
+      { "<fo()o@example.org> ()", "fo()o@example.org" },
+      { "<fo()o@example.org> ()", "fo()o@example.org" },
+      { "fo()o@example.org", NULL},
+      { "Mr. Foo <foo@example.org><bar@example.net>", "foo@example.org"},
+      { "foo+bar@example.org", "foo@example.org" },
+      { "foo++bar@example.org", "foo++bar@example.org" },
+      { "foo++@example.org", "foo++@example.org" },
+      { "foo+@example.org", "foo+@example.org" },
+      { "+foo@example.org", "+foo@example.org" },
+      { "++foo@example.org", "++foo@example.org" },
+      { "+foo+@example.org", "+foo+@example.org" },
+      { "+@example.org", "+@example.org" },
+      { "++@example.org", "++@example.org" },
+      { "foo+b@example.org", "foo@example.org" },
+      { "foo+ba@example.org", "foo@example.org" },
+      { "foo+bar@example.org", "foo@example.org" },
+      { "foo+barb@example.org", "foo@example.org" },
+      { "foo+barba@example.org", "foo@example.org" },
+      { "f+b@example.org", "f@example.org" },
+      { "fo+b@example.org", "fo@example.org" },
+
+      { NULL, NULL }
+    };
+  int idx;
+
+  for (idx=0; testtbl[idx].userid; idx++)
+    {
+      char *mbox = mailbox_from_userid (testtbl[idx].userid, 1);
 
       if (!testtbl[idx].mbox)
         {
@@ -151,7 +230,7 @@ run_dns_test (void)
 
 
 static void
-run_filter (void)
+run_filter (int no_sub)
 {
   char buf[4096];
   int c;
@@ -172,7 +251,7 @@ run_filter (void)
         }
       count1++;
       trim_spaces (buf);
-      mbox = mailbox_from_userid (buf);
+      mbox = mailbox_from_userid (buf, no_sub);
       if (mbox)
         {
           printf ("%s\n", mbox);
@@ -190,6 +269,7 @@ main (int argc, char **argv)
 {
   int last_argc = -1;
   int opt_filter = 0;
+  int opt_no_sub = 0;
 
   if (argc)
     { argc--; argv++; }
@@ -208,6 +288,7 @@ main (int argc, char **argv)
                  "  --verbose         Print timings etc.\n"
                  "  --debug           Flyswatter\n"
                  "  --filter          Filter mboxes from input lines\n"
+                 "  --no-sub          Ignore '+'-sub-addresses\n"
                  , stdout);
           exit (0);
         }
@@ -227,6 +308,11 @@ main (int argc, char **argv)
           opt_filter = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--no-sub"))
+        {
+          opt_no_sub = 1;
+          argc--; argv++;
+        }
       else if (!strncmp (*argv, "--", 2))
         {
           fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
@@ -235,10 +321,11 @@ main (int argc, char **argv)
     }
 
   if (opt_filter)
-    run_filter ();
+    run_filter (opt_no_sub);
   else
     {
       run_mbox_test ();
+      run_mbox_no_sub_test ();
       run_dns_test ();
     }
 
index 05a530b..a21e1ab 100644 (file)
@@ -731,7 +731,7 @@ cmd_dns_cert (assuan_context_t ctx, char *line)
       /* We lowercase ascii characters but the DANE I-D does not allow
          this.  FIXME: Check after the release of the RFC whether to
          change this.  */
-      mbox = mailbox_from_userid (line);
+      mbox = mailbox_from_userid (line, 0);
       if (!mbox || !(domain = strchr (mbox, '@')))
         {
           err = set_error (GPG_ERR_INV_USER_ID, "no mailbox in user id");
@@ -855,7 +855,7 @@ proc_wkd_get (ctrl_t ctrl, assuan_context_t ctx, char *line)
   line = skip_options (line);
   is_wkd_query = !(opt_policy_flags || opt_submission_addr);
 
-  mbox = mailbox_from_userid (line);
+  mbox = mailbox_from_userid (line, 0);
   if (!mbox || !(domain = strchr (mbox, '@')))
     {
       err = set_error (GPG_ERR_INV_USER_ID, "no mailbox in user id");
index 9477b75..d53be99 100644 (file)
@@ -1469,7 +1469,7 @@ print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
         continue;
 
       xfree (mbox);
-      mbox = mailbox_from_userid (uid->name);
+      mbox = mailbox_from_userid (uid->name, 0);
       if (!mbox)
         continue;
 
index 75ce9cb..c776a61 100644 (file)
@@ -1373,7 +1373,7 @@ pubkey_cmp (ctrl_t ctrl, const char *name, struct pubkey_cmp_cookie *old,
        n; n = find_next_kbnode (n, PKT_USER_ID))
     {
       PKT_user_id *uid = n->pkt->pkt.user_id;
-      char *mbox = mailbox_from_userid (uid->name);
+      char *mbox = mailbox_from_userid (uid->name, 0);
       int match = mbox ? strcasecmp (name, mbox) == 0 : 0;
 
       xfree (mbox);
index aeb9c6f..1102892 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3130,7 +3130,7 @@ main (int argc, char **argv)
            break;
          case oSender:
             {
-              char *mbox = mailbox_from_userid (pargs.r.ret_str);
+              char *mbox = mailbox_from_userid (pargs.r.ret_str, 0);
               if (!mbox)
                 log_error (_("\"%s\" is not a proper mail address\n"),
                            pargs.r.ret_str);
index 23258a0..8ea5144 100644 (file)
@@ -1264,7 +1264,7 @@ impex_filter_getval (void *cookie, const char *propname)
         {
           if (!uid->mbox)
             {
-              uid->mbox = mailbox_from_userid (uid->name);
+              uid->mbox = mailbox_from_userid (uid->name, 0);
             }
           result = uid->mbox;
         }
index 8b7da76..793f7da 100644 (file)
@@ -1020,7 +1020,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
               char *mbox, *hash, *p;
               char hashbuf[32];
 
-              mbox = mailbox_from_userid (uid->name);
+              mbox = mailbox_from_userid (uid->name, 0);
               if (mbox && (p = strchr (mbox, '@')))
                 {
                   *p++ = 0;
index a8c222d..44870a6 100644 (file)
@@ -2053,7 +2053,7 @@ keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick,
 
   /* We want to work on the mbox.  That is what dirmngr will do anyway
    * and we need the mbox for the import filter anyway.  */
-  mbox = mailbox_from_userid (name);
+  mbox = mailbox_from_userid (name, 0);
   if (!mbox)
     {
       err = gpg_error_from_syserror ();
index df88813..b2d1c18 100644 (file)
@@ -153,7 +153,8 @@ mk_notation_policy_etc (PKT_signature *sig,
       char *mbox;
 
       /* For now we use the uid which was used to locate the key.  */
-      if (pksk->user_id && (mbox = mailbox_from_userid (pksk->user_id->name)))
+      if (pksk->user_id
+          && (mbox = mailbox_from_userid (pksk->user_id->name, 0)))
         {
           if (DBG_LOOKUP)
             log_debug ("setting Signer's UID to '%s'\n", mbox);
index 762b19b..44f3545 100644 (file)
@@ -3292,7 +3292,7 @@ show_warning (const char *fingerprint, strlist_t user_id_list)
 static char *
 email_from_user_id (const char *user_id)
 {
-  char *email = mailbox_from_userid (user_id);
+  char *email = mailbox_from_userid (user_id, 0);
   if (! email)
     {
       /* Hmm, no email address was provided or we are out of core.  Just
index c46dc50..a230a6c 100644 (file)
@@ -1131,7 +1131,7 @@ tdb_get_validity_core (ctrl_t ctrl,
           if (sig && sig->signers_uid)
             /* Make sure the UID matches.  */
             {
-              char *email = mailbox_from_userid (user_id->name);
+              char *email = mailbox_from_userid (user_id->name, 0);
               if (!email || !*email || strcmp (sig->signers_uid, email) != 0)
                 {
                   if (DBG_TRUST)
index bf6b119..0be5ea8 100644 (file)
@@ -627,11 +627,11 @@ command_supported (char *userid)
   if (!strchr (userid, '@'))
     {
       char *tmp = xstrconcat ("foo@", userid, NULL);
-      addrspec = mailbox_from_userid (tmp);
+      addrspec = mailbox_from_userid (tmp, 0);
       xfree (tmp);
     }
   else
-    addrspec = mailbox_from_userid (userid);
+    addrspec = mailbox_from_userid (userid, 0);
   if (!addrspec)
     {
       log_error (_("\"%s\" is not a proper mail address\n"), userid);
@@ -694,7 +694,7 @@ command_check (char *userid)
   uidinfo_list_t sl;
   int found = 0;
 
-  addrspec = mailbox_from_userid (userid);
+  addrspec = mailbox_from_userid (userid, 0);
   if (!addrspec)
     {
       log_error (_("\"%s\" is not a proper mail address\n"), userid);
@@ -805,7 +805,7 @@ command_send (const char *fingerprint, const char *userid)
       goto leave;
     }
 
-  addrspec = mailbox_from_userid (userid);
+  addrspec = mailbox_from_userid (userid, 0);
   if (!addrspec)
     {
       log_error (_("\"%s\" is not a proper mail address\n"), userid);
index 24b3312..1b53312 100644 (file)
@@ -2020,7 +2020,7 @@ command_install_key (const char *fname, const char *userid)
   char *huname = NULL;
   int any;
 
-  addrspec = mailbox_from_userid (userid);
+  addrspec = mailbox_from_userid (userid, 0);
   if (!addrspec)
     {
       log_error ("\"%s\" is not a proper mail address\n", userid);
@@ -2153,7 +2153,7 @@ fname_from_userid (const char *userid, char **r_fname, char **r_addrspec)
   if (r_addrspec)
     *r_addrspec = NULL;
 
-  addrspec = mailbox_from_userid (userid);
+  addrspec = mailbox_from_userid (userid, 0);
   if (!addrspec)
     {
       if (opt.verbose)
index 729098a..cf80a25 100644 (file)
@@ -104,7 +104,7 @@ append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created)
 
   strcpy (sl->uid, uid);
   sl->created = created;
-  sl->mbox = mailbox_from_userid (uid);
+  sl->mbox = mailbox_from_userid (uid, 0);
   sl->next = NULL;
   if (!*list)
     *list = sl;