Added function escape_display_name() to double quote, if not already,
authorDavid Levine <levinedl@acm.org>
Fri, 23 Mar 2012 02:06:47 +0000 (21:06 -0500)
committerDavid Levine <levinedl@acm.org>
Fri, 23 Mar 2012 02:06:47 +0000 (21:06 -0500)
a fullname that contains any of the special characters listed in RFC
5322, and escape unescaped, embedded double quotes.

With this change, nmh should work with no special configuration on
Cygwin, even if the user's fullname is of the form server\name.

MACHINES
Makefile.am
h/prototypes.h
sbr/escape_display_name.c [new file with mode: 0644]
sbr/mts.c
test/format/test-myname
test/getfullname.c

index ab0d2cb..8bae084 100644 (file)
--- a/MACHINES
+++ b/MACHINES
@@ -47,12 +47,6 @@ Libs category.  And libncurses10 or later in the Lib category.
 
 You may notice a few (three) compile warnings:  they can be ignored.
 
-If send, post, and whom fail, the cause might be a \ in your username.
-To avoid this, either add a Signature profile entry (see the
-mh-profile(5) man page) or set/export your SIGNATURE environment
-variable.  The value can be just the short form of your username, such
-as that displayed by "id -nu".
-
 --------------------------------------
 HPUX:
 
index 14d1dd1..c8006a9 100644 (file)
@@ -369,7 +369,7 @@ uip_viamail_SOURCES = uip/viamail.c uip/mhmisc.c uip/mhoutsbr.c uip/sendsbr.c \
                      uip/annosbr.c uip/distsbr.c
 
 test_getfullname_SOURCES = test/getfullname.c
-test_getfullname_LDADD =
+test_getfullname_LDADD = sbr/libmh.a
 
 test_getfqdn_SOURCES = test/getfqdn.c
 test_getfqdn_LDADD =
@@ -452,7 +452,7 @@ sbr_libmh_a_SOURCES = sbr/addrsbr.c sbr/ambigsw.c sbr/atooi.c sbr/brkstring.c \
                      sbr/context_replace.c sbr/context_save.c \
                      sbr/copy.c sbr/copyip.c sbr/cpydata.c \
                      sbr/cpydgst.c sbr/crawl_folders.c sbr/discard.c \
-                     sbr/done.c sbr/dtime.c \
+                     sbr/done.c sbr/dtime.c sbr/escape_display_name.c \
                      sbr/error.c  sbr/ext_hook.c sbr/fdcompare.c \
                      sbr/folder_addmsg.c sbr/folder_delmsgs.c \
                      sbr/folder_free.c sbr/folder_pack.c \
index 38357a8..b4c0048 100644 (file)
@@ -47,6 +47,7 @@ void cpydgst (int, int, char *, char *);
 int decode_rfc2047 (char *, char *, size_t);
 void discard (FILE *);
 int default_done (int);
+void escape_display_name (char *);
 int ext_hook(char *, char *, char *);
 int fdcompare (int, int);
 int folder_addmsg (struct msgs **, char *, int, int, int, int, char *);
diff --git a/sbr/escape_display_name.c b/sbr/escape_display_name.c
new file mode 100644 (file)
index 0000000..0918942
--- /dev/null
@@ -0,0 +1,63 @@
+#include <sys/types.h>
+#include <h/utils.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Escape a display name, hopefully per RFC 5322.
+   The argument is assumed to be a pointer to a character array of
+   one-byte characters with enough space to handle the additional
+   characters. */
+void
+escape_display_name (char *name) {
+    /* Quote and escape name that contains any specials, as necessary. */
+    if (strpbrk("\"(),.:;<>@[\\]", name)) {
+        size_t len = strlen(name);
+        char *destp, *srcp;
+        size_t destpos, srcpos;
+        /* E.g., 2 characters, "", would require 7, "\"\""\0. */
+       char *tmp = malloc (2*len+3);
+
+        for (destp = tmp, srcp = name, destpos = 0, srcpos = 0;
+             *srcp;
+             ++destp, ++srcp, ++destpos, ++srcpos) {
+            if (srcpos == 0) {
+                /* Insert initial double quote, if needed. */
+                if (*srcp != '"') {
+                    *destp++ = '"';
+                    ++destpos;
+                }
+            } else {
+                /* Escape embedded, unescaped ". */
+                if (*srcp == '"'  &&  srcpos < len - 1  &&  *(srcp-1) != '\\') {
+                    *destp++ = '\\';
+                    ++destpos;
+                }
+            }
+
+            *destp = *srcp;
+
+            /* End of name. */
+            if (srcpos == len - 1) {
+                /* Insert final double quote, if needed. */
+                if (*srcp != '"') {
+                    *++destp = '"';
+                    ++destpos;
+                }
+
+                *++destp = '\0';
+                ++destpos;
+            }
+        }
+
+        if (strcmp (tmp, "\"")) {
+            /* assert (strlen(tmp) + 1 == destpos); */
+            strncpy (name, tmp, destpos);
+        } else {
+            /* Handle just " as special case here instead of above. */
+            strcpy (name, "\"\\\"\"");
+        }
+
+        free (tmp);
+    }
+}
index c529156..556d740 100644 (file)
--- a/sbr/mts.c
+++ b/sbr/mts.c
@@ -395,16 +395,10 @@ getuserinfo (void)
     else if ((cp = context_find("Signature")))
        strncpy (fullname, cp, sizeof(fullname));
 
-    if (strchr(fullname, '.')) {               /*  quote any .'s */
-       char tmp[BUFSIZ];
-
-       /* should quote "'s too */
-       snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
-       strncpy (fullname, tmp, sizeof(fullname));
-    }
-
     fullname[sizeof(fullname) - 1] = '\0';
 
+    escape_display_name(fullname);
+
     localmbox[0] = '\0';
 
     return;
index 2b48c2f..d9dca2d 100755 (executable)
@@ -31,4 +31,25 @@ export SIGNATURE="Some Random Name 2"
 run_test "${MH_LIB_DIR}/ap -format %(myname) ignore" \
          "${SIGNATURE}" "SIGNATURE Environment test"
 
+#### Test escaping of display names.
+escape="${MH_OBJ_DIR}/test/getfullname"
+run_test "$escape "'user'           'user'              'no escape'
+run_test "$escape "'first.last'     '"first.last"'      'escape'
+run_test "$escape "'"first.last"'   '"first.last"'      'already escaped'
+run_test "$escape "'embedded"quote' '"embedded\"quote"' 'embedded quote'
+run_test "$escape "'"'              '"\""'              'special "'
+run_test "$escape "'('              '"("'               'special ('
+run_test "$escape "')'              '")"'               'special )'
+#### We stop at the first comma so this one gets eliminated:
+run_test "$escape "','              ''                  'special ,'
+run_test "$escape "'.'              '"."'               'special .'
+run_test "$escape "':'              '":"'               'special :'
+run_test "$escape "';'              '";"'               'special ;'
+run_test "$escape "'<'              '"<"'               'special <'
+run_test "$escape "'>'              '">"'               'special >'
+run_test "$escape "'@'              '"@"'               'special @'
+run_test "$escape "'['              '"["'               'special ['
+run_test "$escape "'\\'             '"\\"'              'special \\'
+run_test "$escape "']'              '"]"'               'special ]'
+
 exit $failed
index d8ff101..ec83939 100644 (file)
 #include <sys/types.h>
 #include <pwd.h>
 
+extern void escape_display_name (char *);
+
 int
 main(int argc, char *argv[])
 {
        struct passwd *pwd;
-       char name[BUFSIZ], *p;
-
-       if (argc > 1) {
-               fprintf (stderr, "usage: %s\n", argv[0]);
-       }
-
-       pwd = getpwuid(getuid());
-
-       if (! pwd) {
-               fprintf(stderr, "Unable to retrieve user info for "
-                       "userid %ld\n", (long) getuid());
-               exit(1);
+       char buf[BUFSIZ], *p;
+       char *name = buf;
+
+       if (argc < 2) {
+               pwd = getpwuid(getuid());
+
+               if (! pwd) {
+                       fprintf(stderr, "Unable to retrieve user info for "
+                               "userid %ld\n", (long) getuid());
+                       exit(1);
+               }
+
+               strncpy(buf, pwd->pw_gecos, sizeof(buf));
+               buf[sizeof(buf) - 1] = '\0';
+       } else if (argc == 2) {
+               name = argv[1];
+       } else if (argc > 2) {
+               fprintf (stderr, "usage: %s [name]\n", argv[0]);
+               return 1;
        }
 
        /*
         * Perform the same processing that getuserinfo() does.
         */
 
-       strncpy(name, pwd->pw_gecos, sizeof(name));
-
-       name[sizeof(name) - 1] = '\0';
-
        /*
-        * Stop at the first comma
+        * Stop at the first comma.
         */
-
        if ((p = strchr(name, ',')))
                *p = '\0';
 
        /*
-        * Quote the entire string if it has a "." in it
+        * Quote the entire string if it has a special character in it.
         */
-
-       if (strchr(name, '.')) {
-               char tmp[BUFSIZ];
-
-               snprintf(tmp, sizeof(tmp), "\"%s\"", name);
-               strncpy(name, tmp, sizeof(name));
-
-               name[sizeof(name) - 2] = '"';
-               name[sizeof(name) - 1] = '\0';
-       }
+       escape_display_name (name);
 
        printf("%s\n", name);