summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-08-01 17:21:39 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2016-08-01 17:21:39 +0200
commit78fec302cb21c2ecb1abc613eaa3f1fb21a32c76 (patch)
treef3315322c9094eb68ee2b48f803b82bdabffcb87
parent400c6793d673c7a978178ae01d718053cdf23cb3 (diff)
downloadmblaze-78fec302cb21c2ecb1abc613eaa3f1fb21a32c76.tar.gz
mblaze-78fec302cb21c2ecb1abc613eaa3f1fb21a32c76.tar.xz
mblaze-78fec302cb21c2ecb1abc613eaa3f1fb21a32c76.zip
add mgenmid
-rw-r--r--Makefile3
-rw-r--r--man/mgenmid.158
-rw-r--r--mgenmid.c115
3 files changed, 175 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 2a2ecec..37701d0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 CFLAGS=-g -O1 -Wall -Wno-switch -Wextra -fstack-protector-strong -D_FORTIFY_SOURCE=2
 
-ALL = maddr mdeliver mdirs mflag mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread
+ALL = maddr mdeliver mdirs mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread
 
 all: $(ALL)
 
@@ -8,6 +8,7 @@ maddr: maddr.o blaze822.o seq.o rfc2047.o mymemmem.o
 mdeliver: mdeliver.o blaze822.o mymemmem.o
 mdirs: mdirs.o
 mflag: mflag.o blaze822.o seq.o mymemmem.o
+mgenmid: mgenmid.o blaze822.o seq.o mymemmem.o
 mhdr: mhdr.o blaze822.o seq.o rfc2047.o mymemmem.o
 minc: minc.o
 mlist: mlist.o
diff --git a/man/mgenmid.1 b/man/mgenmid.1
new file mode 100644
index 0000000..8cf2d96
--- /dev/null
+++ b/man/mgenmid.1
@@ -0,0 +1,58 @@
+.Dd August 1, 2016
+.Dt MGENMID 1
+.Os
+.Sh NAME
+.Nm mgenmid
+.Nd generate a Message-ID
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+generates a fresh Message-ID and prints it.
+The Message-ID consists of a timestamp,
+a random value,
+and a fully qualified domain name.
+.Pp
+The fully qualified domain name is computed by:
+.Bl -enum
+.It
+Using
+.Sq Li "FQDN:"
+from
+.Pa "~/.santoku/profile"
+(if set).
+.It
+Resolving the current hostname.
+.It
+Using the host part of the address in
+.Sq Li "Local-Mailbox:"
+from
+.Pa "~/.santoku/profile"
+(if set).
+.El
+.Pp
+If these steps don't result in a fully qualified domain name,
+.Nm
+fails.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Rs
+.%A M. Curtin
+.%A J. Zawinski
+.%D July 1998
+.%R draft-ietf-usefor-message-id-01.txt
+.%T Recommendations for generating Message IDs
+.Re
+.Sh AUTHORS
+.An Christian Neukirchen Aq Mt chneukirchen@gmail.com
+.Sh LICENSE
+.Nm
+is in the public domain.
+.Pp
+To the extent possible under law,
+the creator of this work
+has waived all copyright and related or
+neighboring rights to this work.
+.Pp
+.Lk http://creativecommons.org/publicdomain/zero/1.0/
diff --git a/mgenmid.c b/mgenmid.c
new file mode 100644
index 0000000..6122519
--- /dev/null
+++ b/mgenmid.c
@@ -0,0 +1,115 @@
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "blaze822.h"
+
+void
+printb36(uint64_t x)
+{
+	static char const base36[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+	char outbuf[16];
+	char *o = outbuf + sizeof outbuf;
+	*o = 0;
+
+	do { *--o = base36[x % 36]; } while (x /= 36);
+	fputs(o, stdout);
+}
+
+int main()
+{
+	char hostbuf[1024];
+	char *host = 0;
+
+	char *f = blaze822_home_file(".santoku/profile");
+	struct message *config = blaze822(f);
+
+	if (config) // try FQDN: first
+		host = blaze822_hdr(config, "fqdn");
+
+	if (!host && gethostname(hostbuf, sizeof hostbuf) == 0) {
+		// termination not posix guaranteed
+		hostbuf[sizeof hostbuf - 1] = 0;
+
+		struct addrinfo hints = { .ai_family = AF_UNSPEC,
+					  .ai_socktype = SOCK_STREAM,
+					  .ai_flags = AI_CANONNAME };
+		struct addrinfo *info;
+		if (getaddrinfo(hostbuf, 0, &hints, &info) == 0) {
+			// sanity checks: no (null), at least one dot,
+			// doesn't start with localhost.
+
+			if (info &&
+			    info->ai_canonname &&
+			    strchr(info->ai_canonname, '.') &&
+			    strncmp(info->ai_canonname, "localhost.", 10) != 0)
+				host = info->ai_canonname;
+		}
+	}
+
+	if (!host && config) {
+		// get address part of Local-Mailbox:
+		char *disp, *addr;
+		char *from = blaze822_hdr(config, "local-mailbox");
+		while (from && (from = blaze822_addr(from, &disp, &addr)))
+			if (addr) {
+				host = strchr(addr, '@');
+				if (host) {
+					host++;
+					break;
+				}
+			}
+	}
+
+	if (!host) {
+		fprintf(stderr,
+		    "mgenmid: failed to find a FQDN for the Message-ID.\n"
+		    " Define 'FQDN:' or 'Local-Mailbox:' in ~/.santoku/profile\n"
+		    " or add a FQDN to /etc/hosts.\n");
+		exit(1);
+	}
+		
+	struct timespec tp;
+	clock_gettime(CLOCK_REALTIME, &tp);
+
+	uint64_t rnd;
+
+	int rndfd = open("/dev/urandom", O_RDONLY);
+	if (rndfd >= 0) {
+		unsigned char rndb[8];
+		if (read(rndfd, rndb, sizeof rndb) != sizeof rndb)
+			goto fallback;
+		close(rndfd);
+
+		int i;
+		for (i = 0, rnd = 0; i < 8; i++)
+			rnd = rnd*256 + rndb[i];
+	} else {
+fallback:
+		srand48(tp.tv_sec ^ tp.tv_nsec/1000 ^ getpid());
+		rnd = (lrand48() << 32) + lrand48();
+	}
+
+	rnd |= (1LL << 63);  // set highest bit to force full width
+
+        putchar('<');
+	printb36(((uint64_t)tp.tv_sec << 16) + (tp.tv_nsec >> 16));
+        putchar('.');
+	printb36(rnd);
+        putchar('@');
+	fputs(host, stdout);
+        putchar('>');
+        putchar('\n');
+
+        return 0;
+}