Wrap local part (username) of address with double quotes if needed.
authorDavid Levine <levinedl@acm.org>
Thu, 10 May 2012 03:05:28 +0000 (22:05 -0500)
committerDavid Levine <levinedl@acm.org>
Thu, 10 May 2012 03:05:28 +0000 (22:05 -0500)
Makefile.am
h/prototypes.h
sbr/escape_addresses.c [moved from sbr/escape_display_name.c with 63% similarity]
sbr/mts.c
test/format/test-mymbox

index 8a384a7..ad6b1e6 100644 (file)
@@ -478,7 +478,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/escape_display_name.c \
+                     sbr/done.c sbr/dtime.c sbr/escape_addresses.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 5304548..914036c 100644 (file)
@@ -48,6 +48,7 @@ int decode_rfc2047 (char *, char *, size_t);
 void discard (FILE *);
 int default_done (int);
 void escape_display_name (char *, size_t);
+void escape_local_part (char *, size_t);
 int ext_hook(char *, char *, char *);
 int fdcompare (int, int);
 int folder_addmsg (struct msgs **, char *, int, int, int, int, char *);
similarity index 63%
rename from sbr/escape_display_name.c
rename to sbr/escape_addresses.c
index 4e98e97..a7dc146 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * escape_display_name.c -- Escape a display name, hopefully per RFC 5322.
+ * escape_addresses.c -- Escape address components, hopefully per RFC 5322.
  *
  * This code is Copyright (c) 2012, by the authors of nmh.  See the
  * COPYRIGHT file in the root directory of the nmh distribution for
 #include <string.h>
 #include <stdlib.h>
 
-/* Escape a display name, hopefully per RFC 5322.  Assumes one-byte
-   characters.  The char array pointed to by the name argument is
-   modified in place.  Its size is specified by the namesize
-   argument. */
+static void
+escape_component (char *name, size_t namesize, char *chars);
+
+
 void
 escape_display_name (char *name, size_t namesize) {
-    /* Quote and escape name that contains any specials, as necessary. */
-    if (strpbrk("\"(),.:;<>@[\\]", name)) {
+  char *specials = "\"(),.:;<>@[\\]";
+  escape_component (name, namesize, specials);
+}
+
+
+void
+escape_local_part (char *name, size_t namesize) {
+  /* wsp (whitespace) is horizontal tab or space, according to
+     RFC 5234. */
+  char *specials_less_dot_plus_wsp = "  \"(),:;<>@[\\]";
+  escape_component (name, namesize, specials_less_dot_plus_wsp);
+}
+
+
+/* Escape an address component, hopefully per RFC 5322.  Assumes
+   one-byte characters.  The char array pointed to by the name
+   argument is modified in place.  Its size is specified by the
+   namesize argument.  The need_escape argument is a string of
+   characters that require that name be escaped. */
+void
+escape_component (char *name, size_t namesize, char *chars_to_escape) {
+    /* If name contains any chars_to_escape:
+       1) enclose it in ""
+       2) escape any embedded "
+     */
+    if (strpbrk(name, chars_to_escape)) {
         char *destp, *srcp;
         /* Maximum space requirement would be if each character had
            to be escaped, plus enclosing double quotes, plus null termintor.
index cb11a77..548731e 100644 (file)
--- a/sbr/mts.c
+++ b/sbr/mts.c
@@ -337,19 +337,6 @@ getlocalmbox (void)
     if (username[0] == '\0')
        getuserinfo();
 
-    if (localmbox[0] == '\0') {
-       char *cp;
-
-       if ((cp = context_find("Local-Mailbox")) != NULL) {
-           strncpy(localmbox, cp, sizeof(localmbox));
-       } else {
-           snprintf(localmbox, sizeof(localmbox), "%s <%s@%s>", fullname,
-                    username, LocalName(0));
-       }
-
-       localmbox[sizeof(localmbox) - 1] = '\0';
-    }
-
     return localmbox;
 }
 
@@ -373,6 +360,39 @@ getuserinfo (void)
        return;
     }
 
+
+    /* username */
+    /* If there's a Local-Mailbox profile component, try to extract
+       the username from it.  But don't try very hard, this assumes
+       the very simple User Name <user@name.com> form.
+       Note that post(8) and whom(1) use context_foil (), so they
+       won't see the profile component. */
+    if ((np = context_find("Local-Mailbox")) != NULL) {
+       char *left_angle_bracket = strchr (np, '<');
+       char *at_sign = strchr (np, '@');
+       char *right_angle_bracket = strchr (np, '>');
+
+       strncpy(localmbox, np, sizeof(localmbox));
+
+       if (left_angle_bracket  &&  at_sign  &&  right_angle_bracket) {
+           if (at_sign > left_angle_bracket  &&
+               at_sign - left_angle_bracket < BUFSIZ) {
+               strncpy(username, left_angle_bracket + 1,
+                       at_sign - left_angle_bracket - 1);
+           }
+       }
+    }
+
+    if (username[0] == '\0') {
+       strncpy (username, pw->pw_name, sizeof(username));
+    }
+
+    username[sizeof(username) - 1] = '\0';
+
+    escape_local_part(username, sizeof(username));
+
+
+    /* fullname */
     np = pw->pw_gecos;
 
     /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
@@ -382,8 +402,6 @@ getuserinfo (void)
        continue;
     *cp = '\0';
 
-    strncpy (username, pw->pw_name, sizeof(username));
-
     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
        your real name. If SIGNATURE isn't set, use the Signature profile
        setting if it exists. */
@@ -396,9 +414,14 @@ getuserinfo (void)
 
     escape_display_name(fullname, sizeof(fullname));
 
-    localmbox[0] = '\0';
 
-    return;
+    /* localmbox, if not using Local-Mailbox */
+    if (localmbox[0] == '\0') {
+       snprintf(localmbox, sizeof(localmbox), "%s <%s@%s>", fullname,
+                username, LocalName(0));
+    }
+
+    localmbox[sizeof(localmbox) - 1] = '\0';
 }
 
 static const char*
index 39ab4c1..786d943 100755 (executable)
@@ -13,13 +13,17 @@ fi
 
 setup_test
 
-user=${LOGNAME:-`id -un`}
+#### Use ap to get the username.  That will either be what's in the
+#### Local-Mailbox profile component, which we don't use in the test
+#### suite, or the user's login name.  ap will escape (quote) it if
+#### needed.
+user=`${MH_LIB_DIR}/ap -format '%(me)' 0`
 host=`${MH_OBJ_DIR}/test/getfqdn`
 
-run_test "${MH_LIB_DIR}/ap -format %(mymbox{text}) ${user}" \
-         1 "Basic user test"
-run_test "${MH_LIB_DIR}/ap -format %(mymbox{text}) ${user}@${host}" \
-         1 "Basic user@host test"
+output=`${MH_LIB_DIR}/ap -format '%(mymbox{text})' "${user}"`
+run_test "echo $output" 1 "Basic user test"
+output=`${MH_LIB_DIR}/ap -format '%(mymbox{text})' "${user}@${host}"`
+run_test "echo $output" 1 "Basic user@host test"
 run_test "${MH_LIB_DIR}/ap -format %(mymbox{text}) nosuchuser@nosuchhost.blah" \
          0 "Basic non-matching test"
 
@@ -34,7 +38,7 @@ echo "Local-Mailbox: ${myname}" >> ${MH}
 run_test "echo `${MH_LIB_DIR}/ap -format '%(mymbox{text})' "${myname}"`" \
          1 "Local-Mailbox test"
 
-run_test "${MH_LIB_DIR}/ap -format %(mymbox{text}) ${user}@${host}" \
-         0 "Local-mailbox overriding user@host test"
+output=`${MH_LIB_DIR}/ap -format '%(mymbox{text})' "${user}@${host}"`
+run_test "echo $output" 0 "Local-mailbox overriding user@host test"
 
 exit $failed