]> diplodocus.org Git - nmh/blobdiff - sbr/message_id.c
mhshow/test-charset: Use octal, not hex, in printf(1) format string.
[nmh] / sbr / message_id.c
index d0a6dd9dbd2da36dc2183124fbc88c4bef24cd42..8decfa844d875257ab11232c06b4fb2054015527 100644 (file)
@@ -1,5 +1,4 @@
-/*
- * message-id.c -- construct the body of a Message-ID or Content-ID
+/* message_id.c -- construct the body of a Message-ID or Content-ID
  *                 header field
  *
  * This code is Copyright (c) 2012, by the authors of nmh.  See the
  */
 
 #include <h/mh.h>
-#include <unistd.h>   /* for getpid() */
-#include <sys/time.h> /* for gettimeofday() */
-#include <stdio.h>
+#include "m_rand.h"
+#include "message_id.h"
+#include <sys/time.h>  /* for gettimeofday() */
+#include "base64.h"
 
 
-#define NMH_MESSAGE_ID_LOCALNAME 0
-#define NMH_MESSAGE_ID_RANDOM 1
-
-static int message_id_style = NMH_MESSAGE_ID_LOCALNAME;
+static enum {
+  NMH_MESSAGE_ID_LOCALNAME,
+  NMH_MESSAGE_ID_RANDOM
+} message_id_style = NMH_MESSAGE_ID_LOCALNAME;
 static char message_id_[BUFSIZ];
 
 
 /* Convert name of message id style to integer value and store it. */
 int
 save_message_id_style (const char *value) {
-  if (! mh_strcasecmp (value, "localname")) {
+  if (! strcasecmp (value, "localname")) {
     message_id_style = NMH_MESSAGE_ID_LOCALNAME;
     return 0;
-  } else if (! mh_strcasecmp (value, "random")) {
+  }
+  if (! strcasecmp (value, "random")) {
     message_id_style = NMH_MESSAGE_ID_RANDOM;
     return 0;
-  } else {
-    return 1;
   }
+  return 1;
 }
 
 
 char *
 message_id (time_t tclock, int content_id) {
-  if (message_id_style == NMH_MESSAGE_ID_LOCALNAME) {
-    char *format = content_id  ?  "<%d.%ld.%%d@%s>"  :  "<%d.%ld@%s>";
-
-    snprintf (message_id_, sizeof message_id_, format,
-              (int) getpid (), (long) tclock, LocalName (1));
-  } else if  (message_id_style == NMH_MESSAGE_ID_RANDOM) {
-    char *format = content_id ? "<%d-%ld.%06ld%%d@%s>" : "<%d-%ld.%06ld@%s>";
-    /* Use a sequence of digits divisible by 3 because that will
-       expand to base64 without any waste.  Must be shorter than 58,
-       see below. */
-    unsigned char rnd[12];
-
-    if (m_rand (rnd, sizeof rnd) == 0) {
-      pid_t pid;
-      struct timeval now;
-      /* All we really need is 4 * [sizeof rnd/3] + 2, as long as the
-         base64 encoding stays shorter than 76 bytes so embedded
-         newlines aren't necessary.  But use double the sizeof rnd
-         just to be safe. */
-      unsigned char rnd_base64[2 * sizeof rnd];
-      int i;
-
-      writeBase64 (rnd, sizeof rnd, rnd_base64);
-
-      for (i = strlen ((const char *) rnd_base64) - 1;
-           i > 0  &&  iscntrl (rnd_base64[i]);
-           --i) {
-        /* Remove trailing newline.  rnd_base64 had better be shorter
-           than 76 characters, so don't bother to look for embedded
-           newlines. */
-        rnd_base64[i] = '\0';
-      }
+  switch (message_id_style) {
+    case NMH_MESSAGE_ID_LOCALNAME: {
+#define P(fmt) \
+    snprintf(message_id_, sizeof message_id_, \
+        (fmt), (int)getpid(), (long)tclock, LocalName(1))
+
+            if (content_id)
+                P("<%d.%ld.%%d@%s>");
+            else
+                P("<%d.%ld@%s>");
+#undef P
+      break;
+    }
 
-      /* Neither of these can fail according to the POSIX spec. */
-      pid = getpid ();
-      gettimeofday (&now, 0);
+    case NMH_MESSAGE_ID_RANDOM: {
+      /* Use a sequence of digits divisible by 3 because that will
+         expand to base64 without any waste.  Must be shorter than 58,
+         see below. */
+      unsigned char rnd[9];
+      /* The part after the '@' is divided into thirds.  The base64
+         encoded string will be 4/3 the size of rnd. */
+      size_t one_third = sizeof rnd * 4/3/3;
+
+      if (m_rand (rnd, sizeof rnd) == 0) {
+        struct timeval now;
+        /* All we really need is 4 * [sizeof rnd/3] + 2, as long as
+           the base64 encoding stays shorter than 76 bytes so embedded
+           newlines aren't necessary.  But use double the sizeof rnd
+           just to be safe. */
+        unsigned char rnd_base64[2 * sizeof rnd];
+        unsigned char *cp;
+        int i;
+
+        writeBase64 (rnd, sizeof rnd, rnd_base64);
+
+        for (i = strlen ((const char *) rnd_base64) - 1;
+             i > 0  &&  iscntrl (rnd_base64[i]);
+             --i) {
+          /* Remove trailing newline.  rnd_base64 had better be
+             shorter than 76 characters, so don't bother to look for
+             embedded newlines. */
+          rnd_base64[i] = '\0';
+        }
+
+        /* Try to make the base64 string look a little more like a
+           hostname by replacing + with - and / with _. */
+        for (cp = rnd_base64; *cp; ++cp) {
+          if (*cp == '+') {
+            *cp = '-';
+          } else if (*cp == '/') {
+            *cp = '_';
+          }
+        }
+
+        /* gettimeofday() and getpid() shouldn't fail on POSIX platforms. */
+        gettimeofday (&now, 0);
+
+        /* The format string inserts a couple of dots, for the benefit
+           of spam filters that want to see a message id with a final
+           part that resembles a hostname. */
+#define P(fmt) \
+    snprintf(message_id_, sizeof message_id_, (fmt), \
+        getpid(), (long)now.tv_sec, (long)now.tv_usec, \
+        (int)one_third, rnd_base64, \
+        (int)one_third, &rnd_base64[one_third], \
+        (int)one_third, &rnd_base64[2*one_third])
+
+            if (content_id)
+                P("<%d-%ld.%06ld%%d@%.*s.%.*s.%.*s>");
+            else
+                P("<%d-%ld.%06ld@%.*s.%.*s.%.*s>");
+#undef P
+      }
 
-      snprintf (message_id_, sizeof message_id_, format,
-                pid, (long) now.tv_sec, (long) now.tv_usec, rnd_base64);
+      break;
     }
-  } else {
-    /* Should never get here. */
-    adios (0, "invalid message id style \"%s\"", message_id_style);
   }
 
-
   return message_id_;
 }