gpg: Add function to extract the mailbox.
authorWerner Koch <wk@gnupg.org>
Tue, 24 Feb 2015 16:43:57 +0000 (17:43 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 24 Feb 2015 16:46:22 +0000 (17:46 +0100)
* g10/misc.c (has_invalid_email_chars, is_valid_mailbox)
(is_valid_user_id): Move to ...
* g10/mailbox.c: new file.
(string_has_ctrl_or_space, has_dotdot_after_at): New.
(has_invalid_email_chars): New.

* g10/t-mailbox.c: New.
* g10/Makefile.am (module_tests): Add t-mailbox.
(t_mailbox_SOURCES, t_mailbox_LDADD): New.
--

Signed-off-by: Werner Koch <wk@gnupg.org>
g10/Makefile.am
g10/mailbox.c [new file with mode: 0644]
g10/main.h
g10/misc.c
g10/t-mailbox.c [new file with mode: 0644]

index 0a02119..0704924 100644 (file)
@@ -83,6 +83,7 @@ common_source =  \
              textfilter.c      \
              progress.c        \
              misc.c            \
+             mailbox.c         \
               rmd160.c rmd160.h \
              options.h         \
              openfile.c        \
@@ -154,9 +155,11 @@ gpgv2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
 gpgv2_LDFLAGS = $(extra_bin_ldflags)
 
 t_common_ldadd =
-module_tests = t-rmd160
+module_tests = t-rmd160 t-mailbox
 t_rmd160_SOURCES = t-rmd160.c rmd160.c
 t_rmd160_LDADD = $(t_common_ldadd)
+t_mailbox_SOURCES = t-mailbox.c mailbox.c
+t_mailbox_LDADD = $(t_common_ldadd)
 
 
 $(PROGRAMS): $(needed_libs) ../common/libgpgrl.a
diff --git a/g10/mailbox.c b/g10/mailbox.c
new file mode 100644 (file)
index 0000000..64b818f
--- /dev/null
@@ -0,0 +1,184 @@
+/* mailbox.c - Mail address helper functions
+ * Copyright (C) 1998-2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "gpg.h"
+#include "util.h"
+#include "main.h"
+
+
+static int
+string_count_chr (const char *string, int c)
+{
+  int count;
+
+  for (count=0; *string; string++ )
+    if ( *string == c )
+      count++;
+  return count;
+}
+
+
+static int
+string_has_ctrl_or_space (const char *string)
+{
+  for (; *string; string++ )
+    if (!(*string & 0x80) && *string <= 0x20)
+      return 1;
+  return 0;
+}
+
+
+/* Return true if STRING has two consecutive '.' after an '@'
+   sign.  */
+static int
+has_dotdot_after_at (const char *string)
+{
+  string = strchr (string, '@');
+  if (!string)
+    return 0; /* No at-sign.  */
+  string++;
+  return !!strstr (string, "..");
+}
+
+
+/* Check whether the string has characters not valid in an RFC-822
+   address.  To cope with OpenPGP we ignore non-ascii characters
+   so that for example umlauts are legal in an email address.  An
+   OpenPGP user ID must be utf-8 encoded but there is no strict
+   requirement for RFC-822.  Thus to avoid IDNA encoding we put the
+   address verbatim as utf-8 into the user ID under the assumption
+   that mail programs handle IDNA at a lower level and take OpenPGP
+   user IDs as utf-8.  Note that we can't do an utf-8 encoding
+   checking here because in keygen.c this function is called with the
+   native encoding and native to utf-8 encoding is only done  later.  */
+int
+has_invalid_email_chars (const char *s)
+{
+  int at_seen=0;
+  const char *valid_chars=
+    "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+  for ( ; *s; s++ )
+    {
+      if ( (*s & 0x80) )
+        continue; /* We only care about ASCII.  */
+      if ( *s == '@' )
+        at_seen=1;
+      else if ( !at_seen && !(strchr (valid_chars, *s)
+                              || strchr ("!#$%&'*+/=?^`{|}~", *s)))
+        return 1;
+      else if ( at_seen && !strchr( valid_chars, *s ) )
+        return 1;
+    }
+  return 0;
+}
+
+
+/* Check whether NAME represents a valid mailbox according to
+   RFC822. Returns true if so. */
+int
+is_valid_mailbox (const char *name)
+{
+  return !( !name
+            || !*name
+            || has_invalid_email_chars (name)
+            || string_count_chr (name,'@') != 1
+            || *name == '@'
+            || name[strlen(name)-1] == '@'
+            || name[strlen(name)-1] == '.'
+            || strstr (name, "..") );
+}
+
+
+/* Return the mailbox (local-part@domain) form a standard user id.
+   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)
+{
+  const char *s, *s_end;
+  size_t len;
+  char *result = NULL;
+
+  s = strchr (userid, '<');
+  if (s)
+    {
+      /* Seems to be a standard user id.  */
+      s++;
+      s_end = strchr (s, '>');
+      if (s_end && s_end > s)
+        {
+          len = s_end - s;
+          result = xtrymalloc (len + 1);
+          if (!result)
+            return NULL; /* Ooops - out of core.  */
+          strncpy (result, s, len);
+          result[len] = 0;
+          /* Apply some basic checks on the address.  We do not use
+             is_valid_mailbox because those checks are too strict.  */
+          if (string_count_chr (result, '@') != 1  /* Need exactly one '@.  */
+              || *result == '@'           /* local-part missing.  */
+              || result[len-1] == '@'     /* domain missing.  */
+              || result[len-1] == '.'     /* ends with a dot.  */
+              || string_has_ctrl_or_space (result)
+              || has_dotdot_after_at (result))
+            {
+              xfree (result);
+              result = NULL;
+              errno = EINVAL;
+            }
+        }
+      else
+        errno = EINVAL;
+    }
+  else if (is_valid_mailbox (userid))
+    {
+      /* The entire user id is a mailbox.  Return that one.  Note that
+         this fallback method has some restrictions on the valid
+         syntax of the mailbox.  However, those who want weird
+         addresses should know about it and use the regular <...>
+         syntax.  */
+      result = xtrystrdup (userid);
+    }
+  else
+    errno = EINVAL;
+
+  return result;
+}
+
+
+/* Check whether UID is a valid standard user id of the form
+     "Heinrich Heine <heinrichh@duesseldorf.de>"
+   and return true if this is the case. */
+int
+is_valid_user_id (const char *uid)
+{
+  if (!uid || !*uid)
+    return 0;
+
+  return 1;
+}
index d313afb..8c326f6 100644 (file)
@@ -162,9 +162,6 @@ char *optsep(char **stringp);
 char *argsplit(char *string);
 int parse_options(char *str,unsigned int *options,
                  struct parse_options *opts,int noisy);
-int has_invalid_email_chars (const char *s);
-int is_valid_mailbox (const char *name);
-int is_valid_user_id (const char *uid);
 const char *get_libexecdir (void);
 int path_access(const char *file,int mode);
 
@@ -179,6 +176,13 @@ int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
 unsigned int ecdsa_qbits_from_Q (unsigned int qbits);
 
 
+/*-- mailbox.c --*/
+int has_invalid_email_chars (const char *s);
+int is_valid_mailbox (const char *name);
+char *mailbox_from_userid (const char *userid);
+int is_valid_user_id (const char *uid);
+
+
 /*-- status.c --*/
 void set_status_fd ( int fd );
 int  is_status_enabled ( void );
index 276ff0a..4cff2dc 100644 (file)
 
 #include <assert.h>
 
-static int
-string_count_chr (const char *string, int c)
-{
-  int count;
-
-  for (count=0; *string; string++ )
-    if ( *string == c )
-      count++;
-  return count;
-}
-
-
 
 #ifdef ENABLE_SELINUX_HACKS
 /* A object and a global variable to keep track of files marked as
@@ -1464,69 +1452,6 @@ parse_options(char *str,unsigned int *options,
 }
 
 
-/* Check whether the string has characters not valid in an RFC-822
-   address.  To cope with OpenPGP we ignore non-ascii characters
-   so that for example umlauts are legal in an email address.  An
-   OpenPGP user ID must be utf-8 encoded but there is no strict
-   requirement for RFC-822.  Thus to avoid IDNA encoding we put the
-   address verbatim as utf-8 into the user ID under the assumption
-   that mail programs handle IDNA at a lower level and take OpenPGP
-   user IDs as utf-8.  Note that we can't do an utf-8 encoding
-   checking here because in keygen.c this function is called with the
-   native encoding and native to utf-8 encoding is only done  later.  */
-int
-has_invalid_email_chars (const char *s)
-{
-  int at_seen=0;
-  const char *valid_chars=
-    "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-  for ( ; *s; s++ )
-    {
-      if ( (*s & 0x80) )
-        continue; /* We only care about ASCII.  */
-      if ( *s == '@' )
-        at_seen=1;
-      else if ( !at_seen && !(strchr (valid_chars, *s)
-                              || strchr ("!#$%&'*+/=?^`{|}~", *s)))
-        return 1;
-      else if ( at_seen && !strchr( valid_chars, *s ) )
-        return 1;
-    }
-  return 0;
-}
-
-
-/* Check whether NAME represents a valid mailbox according to
-   RFC822. Returns true if so. */
-int
-is_valid_mailbox (const char *name)
-{
-  return !( !name
-            || !*name
-            || has_invalid_email_chars (name)
-            || string_count_chr (name,'@') != 1
-            || *name == '@'
-            || name[strlen(name)-1] == '@'
-            || name[strlen(name)-1] == '.'
-            || strstr (name, "..") );
-}
-
-
-/* Check whether UID is a valid standard user id of the form
-     "Heinrich Heine <heinrichh@duesseldorf.de>"
-   and return true if this is the case. */
-int
-is_valid_user_id (const char *uid)
-{
-  if (!uid || !*uid)
-    return 0;
-
-  return 1;
-}
-
-
-
 /* Similar to access(2), but uses PATH to find the file. */
 int
 path_access(const char *file,int mode)
diff --git a/g10/t-mailbox.c b/g10/t-mailbox.c
new file mode 100644 (file)
index 0000000..aa7cf33
--- /dev/null
@@ -0,0 +1,127 @@
+/* t-mailbox.c - Module test for mailbox.c
+ * Copyright (C) 2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpg.h"
+#include "util.h"
+#include "main.h"
+
+#define pass()  do { ; } while(0)
+#define fail(a)  do { fprintf (stderr, "%s:%d: test %d failed\n",\
+                               __FILE__,__LINE__, (a));          \
+                       exit (1);                                 \
+                    } while(0)
+
+
+void *
+gcry_malloc (size_t n)
+{
+  return malloc (n);
+}
+
+
+char *
+gcry_strdup (const char *string)
+{
+  return strdup (string);
+}
+
+
+void
+gcry_free (void *a)
+{
+  if (a)
+    free (a);
+}
+
+
+
+static void
+run_test (void)
+{
+  static struct
+  {
+    const char *userid;
+    const char *mbox;
+  } testtbl[] =
+    {
+      { "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"},
+      { NULL, NULL }
+    };
+  int idx;
+
+  for (idx=0; testtbl[idx].userid; idx++)
+    {
+      char *mbox = mailbox_from_userid (testtbl[idx].userid);
+
+      if (!testtbl[idx].mbox)
+        {
+          if (mbox)
+            fail (idx);
+        }
+      else if (!mbox)
+        fail (idx);
+      else if (strcmp (mbox, testtbl[idx].mbox))
+        fail (idx);
+    }
+}
+
+
+int
+main (int argc, char **argv)
+{
+  (void)argc;
+  (void)argv;
+
+  run_test ();
+
+  return 0;
+}