diff --git a/contrib/dma/BSDmakefile b/contrib/dma/BSDmakefile
new file mode 100644
index 000000000000..af3a0ac7c34f
--- /dev/null
+++ b/contrib/dma/BSDmakefile
@@ -0,0 +1,28 @@
+# $DragonFly: src/libexec/dma/Makefile,v 1.5 2008/09/19 00:36:57 corecode Exp $
+#
+
+version!=	sh get-version.sh
+
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME
+CFLAGS+= -DLIBEXEC_PATH='"${LIBEXEC}"' -DDMA_VERSION='"${version}"'
+CFLAGS+= -DCONF_PATH='"${CONFDIR}"'
+
+DPADD=  ${LIBSSL} ${LIBCRYPTO}
+LDADD=  -lssl -lcrypto
+
+PROG=	dma
+SRCS=	aliases_parse.y aliases_scan.l base64.c conf.c crypto.c
+SRCS+=	dma.c dns.c local.c mail.c net.c spool.c util.c
+MAN=	dma.8
+
+PREFIX?=	/usr/local
+LIBEXEC?=	${PREFIX}/libexec
+CONFDIR?=	${PREFIX}/etc/dma
+
+BINOWN= root
+BINGRP= mail
+BINMODE=2555
+WARNS?=	6
+
+.include <bsd.prog.mk>
diff --git a/contrib/dma/INSTALL b/contrib/dma/INSTALL
new file mode 100644
index 000000000000..3c52dc8e8c7f
--- /dev/null
+++ b/contrib/dma/INSTALL
@@ -0,0 +1,28 @@
+Installing DMA:
+===============
+
+On most systems (with a development environment installed) you should be able to compile DMA with:
+    make
+
+Once it have compiled it successfully, you can install it with:
+    make install sendmail-link mailq-link install-spool-dirs install-etc
+
+Troubleshooting:
+----------------
+On systems that do not default to a compatible "make" version, try using "gmake" or "pmake" instead of "make". Some known examples of this:
+* Solaris 9
+* Solaris 10
+
+Check that you have the following commands installed:
+* cc - gcc is known to work
+* lex - flex is known to work
+* yacc - bison is kjnown to work
+* make - BSD make and GNU make is knwon to work
+* sh - Need to be POSIX compliant, dash, bash known to work
+* install - GNU and BSD versions known to work
+* openssl - Add the header location to C_INCLUDE_PATH if you get errors about "err.h"
+
+If you have all of these tools installed, set the CC, YACC, INSTALL, LEX and SH variable to point to the relevant location and command.
+
+Example:
+    make CC=gcc YACC=bison LEX=/usr/bin/flex SH=/bin/bash INSTALL=/usr/bin/install
diff --git a/contrib/dma/LICENSE b/contrib/dma/LICENSE
new file mode 100644
index 000000000000..b89e5bcd16f3
--- /dev/null
+++ b/contrib/dma/LICENSE
@@ -0,0 +1,109 @@
+Copyright (c) 2008 The DragonFly Project.
+Copyright (c) 2008-2011, Simon Schubert <2@0x2c.org>.
+All rights reserved.
+
+This code is derived from software contributed to The DragonFly Project
+by Simon Schubert <2@0x2c.org>.
+
+This code is derived from software contributed to The DragonFly Project
+by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+Germany.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+3. Neither the name of The DragonFly Project nor the names of its
+   contributors may be used to endorse or promote products derived
+   from this software without specific, prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1995-2001 Kungliga Tekniska H�gskolan
+(Royal Institute of Technology, Stockholm, Sweden).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Institute nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (c) 1998, M. Warner Losh <imp@freebsd.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/contrib/dma/Makefile b/contrib/dma/Makefile
new file mode 100644
index 000000000000..aed2ef7246cf
--- /dev/null
+++ b/contrib/dma/Makefile
@@ -0,0 +1,111 @@
+#
+# Depending on your operating system, you might want to influence
+# the conditional inclusion of some helper functions:
+#
+# Define HAVE_* (in caps) if your system already provides:
+#   reallocf
+#   strlcpy
+#   getprogname
+#
+
+SH?=		sh
+
+version=	$(shell ${SH} get-version.sh)
+debversion=	$(shell ${SH} get-version.sh | sed -Ee 's/^v//;s/[.]([[:digit:]]+)[.](g[[:xdigit:]]+)$$/+\1+\2/')
+
+CC?=		gcc
+CFLAGS?=	-O -pipe
+LDADD?=		-lssl -lcrypto -lresolv
+
+CFLAGS+=	-Wall -DDMA_VERSION='"${version}"' -DLIBEXEC_PATH='"${LIBEXEC}"' -DCONF_PATH='"${CONFDIR}"'
+
+INSTALL?=	install -p
+CHGRP?=		chgrp
+CHMOD?=		chmod
+
+PREFIX?=	/usr/local
+SBIN?=		${PREFIX}/sbin
+LIBEXEC?=	${PREFIX}/lib
+CONFDIR?=	/etc/dma
+MAN?=		${PREFIX}/share/man
+VAR?=		/var
+DMASPOOL?=	${VAR}/spool/dma
+VARMAIL?=	${VAR}/mail
+SYMLINK?=	-s # or empty to create hard link
+
+YACC?=		yacc
+LEX?=		lex
+LN?=		ln
+
+OBJS=	aliases_parse.o aliases_scan.o base64.o conf.o crypto.o
+OBJS+=	dma.o dns.o local.o mail.o net.o spool.o util.o
+OBJS+=	dfcompat.o
+
+all: dma dma-mbox-create
+
+clean:
+	-rm -f .depend dma dma-mbox-create *.[do]
+	-rm -f aliases_parse.[ch] aliases_scan.c
+
+install: all
+	${INSTALL} -d ${DESTDIR}${SBIN}
+	${INSTALL} -d ${DESTDIR}${MAN}/man8 ${DESTDIR}${LIBEXEC}
+	${INSTALL} -m 2755 -o root -g mail dma ${DESTDIR}${SBIN}
+	${INSTALL} -m 4754 -o root -g mail dma-mbox-create ${DESTDIR}${LIBEXEC}
+	${INSTALL} -m 0644 dma.8 ${DESTDIR}${MAN}/man8/
+
+sendmail-link:
+	cd ${DESTDIR}${SBIN} && ${LN} ${SYMLINK} dma sendmail
+
+mailq-link:
+	cd ${DESTDIR}${SBIN} && ${LN} ${SYMLINK} dma mailq
+
+install-spool-dirs:
+	${INSTALL} -d -m 2775 -o root -g mail ${DESTDIR}${DMASPOOL}
+	${INSTALL} -d -m 2775 -o root -g mail ${DESTDIR}${VARMAIL}
+
+permissions:
+	-${CHGRP} mail ${DESTDIR}${VARMAIL}/*
+	-${CHMOD} g+w ${DESTDIR}${VARMAIL}/*
+	-${CHMOD} 660 ${DESTDIR}${DMASPOOL}/flush
+
+install-etc:
+	${INSTALL} -d ${DESTDIR}${CONFDIR}
+	@if [ -e ${DESTDIR}${CONFDIR}/dma.conf ]; then \
+		echo "Not overwriting ${DESTDIR}${CONFDIR}/dma.conf."; \
+	else \
+		echo ${INSTALL} -m 644 -o root -g mail dma.conf ${DESTDIR}${CONFDIR}; \
+		${INSTALL} -m 644 -o root -g mail dma.conf ${DESTDIR}${CONFDIR}; \
+	fi
+	@if [ -e ${DESTDIR}${CONFDIR}/auth.conf ]; then \
+		echo "Not overwriting ${DESTDIR}${CONFDIR}/auth.conf."; \
+	else \
+		echo ${INSTALL} -m 640 -o root -g mail auth.conf ${DESTDIR}${CONFDIR}; \
+		${INSTALL} -m 640 -o root -g mail auth.conf ${DESTDIR}${CONFDIR}; \
+	fi
+
+aliases_parse.c: aliases_parse.y
+	${YACC} -d -o aliases_parse.c aliases_parse.y
+
+aliases_scan.c: aliases_scan.l
+	${LEX} -t aliases_scan.l > aliases_scan.c
+
+.SUFFIXES: .c .o
+
+.c.o:
+	${CC} ${CFLAGS} ${CPPFLAGS} -include dfcompat.h -o $@ -c $<
+
+dma: ${OBJS}
+	${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+
+dch:
+	dch --release-heuristic changelog -v ${debversion}
+
+
+ppa:
+	@if [ -z '${DEB_DIST}' ]; then echo "please set DEB_DIST to build"; exit 1; fi
+	dch -v "${debversion}~${DEB_DIST}" -D ${DEB_DIST} "${DEB_DIST} build" -b
+	debuild -S -sa
+	ver=$$(dpkg-parsechangelog -n1 | awk '$$1 == "Version:" { print $$2 }'); \
+	dput ppa:corecode/dma ../dma_$${ver}_source.changes
diff --git a/contrib/dma/Makefile.etc b/contrib/dma/Makefile.etc
new file mode 100644
index 000000000000..fca23b94a604
--- /dev/null
+++ b/contrib/dma/Makefile.etc
@@ -0,0 +1,15 @@
+# $DragonFly: src/etc/dma/Makefile,v 1.3 2008/02/12 22:10:20 matthias Exp $
+
+FILESDIR=	/etc/dma
+SHAREOWN=	root
+SHAREGRP=	mail
+FILESMODE=	640
+
+.if !exists(${DESTDIR}/etc/dma/auth.conf)
+FILES+=		auth.conf
+.endif
+.if !exists(${DESTDIR}/etc/dma/dma.conf)
+FILES+=		dma.conf
+.endif
+
+.include <bsd.prog.mk>
diff --git a/contrib/dma/README.markdown b/contrib/dma/README.markdown
new file mode 100644
index 000000000000..13ff20c9aa41
--- /dev/null
+++ b/contrib/dma/README.markdown
@@ -0,0 +1,32 @@
+dma -- DragonFly Mail Agent
+===========================
+
+dma is a small Mail Transport Agent (MTA), designed for home and
+office use.  It accepts mails from locally installed Mail User Agents (MUA)
+and delivers the mails either locally or to a remote destination.
+Remote delivery includes several features like TLS/SSL support and
+SMTP authentication.
+
+dma is not intended as a replacement for real, big MTAs like sendmail(8)
+or postfix(1).  Consequently, dma does not listen on port 25 for
+incoming connections.
+
+
+Building
+--------
+
+	make
+
+
+Installation
+------------
+
+	make install sendmail-link mailq-link install-spool-dirs install-etc
+
+See INSTALL for requirements and configuration options.
+
+
+Contact
+-------
+
+Simon Schubert <2@0x2c.org>
diff --git a/contrib/dma/TODO b/contrib/dma/TODO
new file mode 100644
index 000000000000..01de465d66af
--- /dev/null
+++ b/contrib/dma/TODO
@@ -0,0 +1,5 @@
+- unquote/handle quoted local recipients
+- use proper sysexit codes
+- handle/use ESMTP extensions
+- .forward support
+- suggest way to run a queue flush on boot
diff --git a/contrib/dma/VERSION b/contrib/dma/VERSION
new file mode 100644
index 000000000000..490a0cdcc139
--- /dev/null
+++ b/contrib/dma/VERSION
@@ -0,0 +1 @@
+v0.9
diff --git a/contrib/dma/aliases_parse.y b/contrib/dma/aliases_parse.y
new file mode 100644
index 000000000000..a5a9e7b32f07
--- /dev/null
+++ b/contrib/dma/aliases_parse.y
@@ -0,0 +1,112 @@
+%{
+
+#include <err.h>
+#include <string.h>
+#include <syslog.h>
+#include "dma.h"
+
+extern int yylineno;
+static void yyerror(const char *);
+int yywrap(void);
+int yylex(void);
+
+static void
+yyerror(const char *msg)
+{
+	/**
+	 * Because we do error '\n' below, we need to report the error
+	 * one line above of what yylineno points to.
+	 */
+	syslog(LOG_CRIT, "aliases line %d: %s", yylineno - 1, msg);
+	fprintf(stderr, "aliases line %d: %s\n", yylineno - 1, msg);
+}
+
+int
+yywrap(void)
+{
+	return (1);
+}
+
+%}
+
+%union {
+	char *ident;
+	struct stritem *strit;
+	struct alias *alias;
+}
+
+%token <ident> T_IDENT
+%token T_ERROR
+%token T_EOF 0
+
+%type <strit> dests
+%type <alias> alias aliases
+
+%%
+
+start	: aliases T_EOF
+		{
+			LIST_FIRST(&aliases) = $1;
+		}
+
+aliases	: /* EMPTY */
+		{
+			$$ = NULL;
+		}
+	| alias aliases
+		{
+			if ($2 != NULL && $1 != NULL)
+				LIST_INSERT_AFTER($2, $1, next);
+			else if ($2 == NULL)
+				$2 = $1;
+			$$ = $2;
+		}
+       	;
+
+alias	: T_IDENT ':' dests '\n'
+		{
+			struct alias *al;
+
+			if ($1 == NULL)
+				YYABORT;
+			al = calloc(1, sizeof(*al));
+			if (al == NULL)
+				YYABORT;
+			al->alias = $1;
+			SLIST_FIRST(&al->dests) = $3;
+			$$ = al;
+		}
+	| error '\n'
+		{
+			YYABORT;
+		}
+     	;
+
+dests	: T_IDENT
+		{
+			struct stritem *it;
+
+			if ($1 == NULL)
+				YYABORT;
+			it = calloc(1, sizeof(*it));
+			if (it == NULL)
+				YYABORT;
+			it->str = $1;
+			$$ = it;
+		}
+	| T_IDENT ',' dests
+		{
+			struct stritem *it;
+
+			if ($1 == NULL)
+				YYABORT;
+			it = calloc(1, sizeof(*it));
+			if (it == NULL)
+				YYABORT;
+			it->str = $1;
+			SLIST_NEXT(it, next) = $3;
+			$$ = it;
+		}
+	;
+
+%%
diff --git a/contrib/dma/aliases_scan.l b/contrib/dma/aliases_scan.l
new file mode 100644
index 000000000000..809d1e168731
--- /dev/null
+++ b/contrib/dma/aliases_scan.l
@@ -0,0 +1,24 @@
+%{
+
+#include <string.h>
+#include "aliases_parse.h"
+
+#define YY_NO_INPUT
+
+int yylex(void);
+%}
+
+%option yylineno
+%option nounput
+
+%%
+
+[^:,#[:space:][:cntrl:]]+	{yylval.ident = strdup(yytext); return T_IDENT;}
+^([[:blank:]]*(#.*)?\n)+	;/* ignore empty lines */
+[:,\n]				return yytext[0];
+(\n?[[:blank:]]+|#.*)+		;/* ignore whitespace and continuation */
+\\\n				;/* ignore continuation.  not allowed in comments */
+.				return T_ERROR;
+<<EOF>>				return T_EOF;
+
+%%
diff --git a/contrib/dma/auth.conf b/contrib/dma/auth.conf
new file mode 100644
index 000000000000..393a80a8ab5f
--- /dev/null
+++ b/contrib/dma/auth.conf
@@ -0,0 +1,4 @@
+# $DragonFly: src/etc/dma/auth.conf,v 1.1 2008/02/02 18:24:00 matthias Exp $
+#
+# SMTP authentication entries (currently AUTH LOGIN only)
+# Format: user|my.smarthost.example.com:password
diff --git a/contrib/dma/base64.c b/contrib/dma/base64.c
new file mode 100644
index 000000000000..663763e8c773
--- /dev/null
+++ b/contrib/dma/base64.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska H�gskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dma.h"
+
+static char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+pos(char c)
+{
+	char *p;
+	for (p = base64_chars; *p; p++)
+		if (*p == c)
+			return p - base64_chars;
+	return -1;
+}
+
+
+int
+base64_encode(const void *data, int size, char **str)
+{
+    char *s, *p;
+    int i;
+    int c;
+    const unsigned char *q;
+
+    p = s = (char *) malloc(size * 4 / 3 + 4);
+    if (p == NULL)
+	return -1;
+    q = (const unsigned char *) data;
+    i = 0;
+    for (i = 0; i < size;) {
+	c = q[i++];
+	c *= 256;
+	if (i < size)
+	    c += q[i];
+	i++;
+	c *= 256;
+	if (i < size)
+	    c += q[i];
+	i++;
+	p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+	p[1] = base64_chars[(c & 0x0003f000) >> 12];
+	p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+	p[3] = base64_chars[(c & 0x0000003f) >> 0];
+	if (i > size)
+	    p[3] = '=';
+	if (i > size + 1)
+	    p[2] = '=';
+	p += 4;
+    }
+    *p = 0;
+    *str = s;
+    return strlen(s);
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static unsigned int
+token_decode(const char *token)
+{
+	int i;
+	unsigned int val = 0;
+	int marker = 0;
+	if (strlen(token) < 4)
+		return DECODE_ERROR;
+	for (i = 0; i < 4; i++) {
+		val *= 64;
+		if (token[i] == '=')
+			marker++;
+		else if (marker > 0)
+			return DECODE_ERROR;
+		else
+			val += pos(token[i]);
+	}
+	if (marker > 2)
+		return DECODE_ERROR;
+	return (marker << 24) | val;
+}
+
+int
+base64_decode(const char *str, void *data)
+{
+	const char *p;
+	unsigned char *q;
+
+	q = data;
+	for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+		unsigned int val = token_decode(p);
+		unsigned int marker = (val >> 24) & 0xff;
+		if (val == DECODE_ERROR)
+			return -1;
+		*q++ = (val >> 16) & 0xff;
+		if (marker < 2)
+			*q++ = (val >> 8) & 0xff;
+		if (marker < 1)
+			*q++ = val & 0xff;
+	}
+	return q - (unsigned char *) data;
+}
+
diff --git a/contrib/dma/conf.c b/contrib/dma/conf.c
new file mode 100644
index 000000000000..919ab7c4d77e
--- /dev/null
+++ b/contrib/dma/conf.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "dma.h"
+
+#define DP	": \t"
+#define EQS	" \t"
+
+
+/*
+ * Remove trailing \n's
+ */
+void
+trim_line(char *line)
+{
+	size_t linelen;
+	char *p;
+
+	if ((p = strchr(line, '\n')))
+		*p = (char)0;
+
+	/* Escape leading dot in every case */
+	linelen = strlen(line);
+	if (line[0] == '.') {
+		if ((linelen + 2) > 1000) {
+			syslog(LOG_CRIT, "Cannot escape leading dot.  Buffer overflow");
+			exit(1);
+		}
+		memmove((line + 1), line, (linelen + 1));
+		line[0] = '.';
+	}
+}
+
+static void
+chomp(char *str)
+{
+	size_t len = strlen(str);
+
+	if (len == 0)
+		return;
+	if (str[len - 1] == '\n')
+		str[len - 1] = 0;
+}
+
+/*
+ * Read the SMTP authentication config file
+ *
+ * file format is:
+ * user|host:password
+ *
+ * A line starting with # is treated as comment and ignored.
+ */
+void
+parse_authfile(const char *path)
+{
+	char line[2048];
+	struct authuser *au;
+	FILE *a;
+	char *data;
+	int lineno = 0;
+
+	a = fopen(path, "r");
+	if (a == NULL) {
+		errlog(1, "can not open auth file `%s'", path);
+		/* NOTREACHED */
+	}
+
+	while (!feof(a)) {
+		if (fgets(line, sizeof(line), a) == NULL)
+			break;
+		lineno++;
+
+		chomp(line);
+
+		/* We hit a comment */
+		if (*line == '#')
+			continue;
+		/* Ignore empty lines */
+		if (*line == 0)
+			continue;
+
+		au = calloc(1, sizeof(*au));
+		if (au == NULL)
+			errlog(1, NULL);
+
+		data = strdup(line);
+		au->login = strsep(&data, "|");
+		au->host = strsep(&data, DP);
+		au->password = data;
+
+		if (au->login == NULL ||
+		    au->host == NULL ||
+		    au->password == NULL) {
+			errlogx(1, "syntax error in authfile %s:%d",
+				path, lineno);
+			/* NOTREACHED */
+		}
+
+		SLIST_INSERT_HEAD(&authusers, au, next);
+	}
+
+	fclose(a);
+}
+
+/*
+ * XXX TODO
+ * Check for bad things[TM]
+ */
+void
+parse_conf(const char *config_path)
+{
+	char *word;
+	char *data;
+	FILE *conf;
+	char line[2048];
+	int lineno = 0;
+
+	conf = fopen(config_path, "r");
+	if (conf == NULL) {
+		/* Don't treat a non-existing config file as error */
+		if (errno == ENOENT)
+			return;
+		errlog(1, "can not open config `%s'", config_path);
+		/* NOTREACHED */
+	}
+
+	while (!feof(conf)) {
+		if (fgets(line, sizeof(line), conf) == NULL)
+			break;
+		lineno++;
+
+		chomp(line);
+
+		/* We hit a comment */
+		if (strchr(line, '#'))
+			*strchr(line, '#') = 0;
+
+		data = line;
+		word = strsep(&data, EQS);
+
+		/* Ignore empty lines */
+		if (word == NULL || *word == 0)
+			continue;
+
+		if (data != NULL && *data != 0)
+			data = strdup(data);
+		else
+			data = NULL;
+
+		if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
+			config.smarthost = data;
+		else if (strcmp(word, "PORT") == 0 && data != NULL)
+			config.port = atoi(data);
+		else if (strcmp(word, "ALIASES") == 0 && data != NULL)
+			config.aliases = data;
+		else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
+			config.spooldir = data;
+		else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
+			config.authpath= data;
+		else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
+			config.certfile = data;
+		else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
+			config.mailname = data;
+		else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) {
+			char *user = NULL, *host = NULL;
+			if (strrchr(data, '@')) {
+				host = strrchr(data, '@');
+				*host = 0;
+				host++;
+				user = data;
+			} else {
+				host = data;
+			}
+ 			if (host && *host == 0)
+				host = NULL;
+                        if (user && *user == 0)
+                                user = NULL;
+			config.masquerade_host = host;
+			config.masquerade_user = user;
+		} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
+			config.features |= STARTTLS;
+		else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
+			config.features |= TLS_OPP;
+		else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
+			config.features |= SECURETRANS;
+		else if (strcmp(word, "DEFER") == 0 && data == NULL)
+			config.features |= DEFER;
+		else if (strcmp(word, "INSECURE") == 0 && data == NULL)
+			config.features |= INSECURE;
+		else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
+			config.features |= FULLBOUNCE;
+		else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL)
+			config.features |= NULLCLIENT;
+		else {
+			errlogx(1, "syntax error in %s:%d", config_path, lineno);
+			/* NOTREACHED */
+		}
+	}
+
+	if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
+		errlogx(1, "%s: NULLCLIENT requires SMARTHOST", config_path);
+		/* NOTREACHED */
+	}
+
+	fclose(conf);
+}
diff --git a/contrib/dma/crypto.c b/contrib/dma/crypto.c
new file mode 100644
index 000000000000..897b55bfdcfc
--- /dev/null
+++ b/contrib/dma/crypto.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <openssl/x509.h>
+#include <openssl/md5.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+#include <syslog.h>
+
+#include "dma.h"
+
+static int
+init_cert_file(SSL_CTX *ctx, const char *path)
+{
+	int error;
+
+	/* Load certificate into ctx */
+	error = SSL_CTX_use_certificate_chain_file(ctx, path);
+	if (error < 1) {
+		syslog(LOG_ERR, "SSL: Cannot load certificate `%s': %s", path, ssl_errstr());
+		return (-1);
+	}
+
+	/* Add private key to ctx */
+	error = SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM);
+	if (error < 1) {
+		syslog(LOG_ERR, "SSL: Cannot load private key `%s': %s", path, ssl_errstr());
+		return (-1);
+	}
+
+	/*
+	 * Check the consistency of a private key with the corresponding
+         * certificate
+	 */
+	error = SSL_CTX_check_private_key(ctx);
+	if (error < 1) {
+		syslog(LOG_ERR, "SSL: Cannot check private key: %s", ssl_errstr());
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+smtp_init_crypto(int fd, int feature)
+{
+	SSL_CTX *ctx = NULL;
+#if (OPENSSL_VERSION_NUMBER >= 0x00909000L)
+	const SSL_METHOD *meth = NULL;
+#else
+	SSL_METHOD *meth = NULL;
+#endif
+	X509 *cert;
+	int error;
+
+	/* XXX clean up on error/close */
+	/* Init SSL library */
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	meth = TLSv1_client_method();
+
+	ctx = SSL_CTX_new(meth);
+	if (ctx == NULL) {
+		syslog(LOG_WARNING, "remote delivery deferred: SSL init failed: %s", ssl_errstr());
+		return (1);
+	}
+
+	/* User supplied a certificate */
+	if (config.certfile != NULL) {
+		error = init_cert_file(ctx, config.certfile);
+		if (error) {
+			syslog(LOG_WARNING, "remote delivery deferred");
+			return (1);
+		}
+	}
+
+	/*
+	 * If the user wants STARTTLS, we have to send EHLO here
+	 */
+	if (((feature & SECURETRANS) != 0) &&
+	     (feature & STARTTLS) != 0) {
+		/* TLS init phase, disable SSL_write */
+		config.features |= NOSSL;
+
+		send_remote_command(fd, "EHLO %s", hostname());
+		if (read_remote(fd, 0, NULL) == 2) {
+			send_remote_command(fd, "STARTTLS");
+			if (read_remote(fd, 0, NULL) != 2) {
+				if ((feature & TLS_OPP) == 0) {
+					syslog(LOG_ERR, "remote delivery deferred: STARTTLS not available: %s", neterr);
+					return (1);
+				} else {
+					syslog(LOG_INFO, "in opportunistic TLS mode, STARTTLS not available: %s", neterr);
+					return (0);
+				}
+			}
+		}
+		/* End of TLS init phase, enable SSL_write/read */
+		config.features &= ~NOSSL;
+	}
+
+	config.ssl = SSL_new(ctx);
+	if (config.ssl == NULL) {
+		syslog(LOG_NOTICE, "remote delivery deferred: SSL struct creation failed: %s",
+		       ssl_errstr());
+		return (1);
+	}
+
+	/* Set ssl to work in client mode */
+	SSL_set_connect_state(config.ssl);
+
+	/* Set fd for SSL in/output */
+	error = SSL_set_fd(config.ssl, fd);
+	if (error == 0) {
+		syslog(LOG_NOTICE, "remote delivery deferred: SSL set fd failed: %s",
+		       ssl_errstr());
+		return (1);
+	}
+
+	/* Open SSL connection */
+	error = SSL_connect(config.ssl);
+	if (error < 0) {
+		syslog(LOG_ERR, "remote delivery deferred: SSL handshake failed fatally: %s",
+		       ssl_errstr());
+		return (1);
+	}
+
+	/* Get peer certificate */
+	cert = SSL_get_peer_certificate(config.ssl);
+	if (cert == NULL) {
+		syslog(LOG_WARNING, "remote delivery deferred: Peer did not provide certificate: %s",
+		       ssl_errstr());
+	}
+	X509_free(cert);
+
+	return (0);
+}
+
+/*
+ * hmac_md5() taken out of RFC 2104.  This RFC was written by H. Krawczyk,
+ * M. Bellare and R. Canetti.
+ *
+ * text      pointer to data stream
+ * text_len  length of data stream
+ * key       pointer to authentication key
+ * key_len   length of authentication key
+ * digest    caller digest to be filled int
+ */
+void
+hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
+    unsigned char* digest)
+{
+        MD5_CTX context;
+        unsigned char k_ipad[65];    /* inner padding -
+                                      * key XORd with ipad
+                                      */
+        unsigned char k_opad[65];    /* outer padding -
+                                      * key XORd with opad
+                                      */
+        unsigned char tk[16];
+        int i;
+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
+        if (key_len > 64) {
+
+                MD5_CTX      tctx;
+
+                MD5_Init(&tctx);
+                MD5_Update(&tctx, key, key_len);
+                MD5_Final(tk, &tctx);
+
+                key = tk;
+                key_len = 16;
+        }
+
+        /*
+         * the HMAC_MD5 transform looks like:
+         *
+         * MD5(K XOR opad, MD5(K XOR ipad, text))
+         *
+         * where K is an n byte key
+         * ipad is the byte 0x36 repeated 64 times
+	 *
+         * opad is the byte 0x5c repeated 64 times
+         * and text is the data being protected
+         */
+
+        /* start out by storing key in pads */
+        bzero( k_ipad, sizeof k_ipad);
+        bzero( k_opad, sizeof k_opad);
+        bcopy( key, k_ipad, key_len);
+        bcopy( key, k_opad, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++) {
+                k_ipad[i] ^= 0x36;
+                k_opad[i] ^= 0x5c;
+        }
+        /*
+         * perform inner MD5
+         */
+        MD5_Init(&context);                   /* init context for 1st
+                                              * pass */
+        MD5_Update(&context, k_ipad, 64);     /* start with inner pad */
+        MD5_Update(&context, text, text_len); /* then text of datagram */
+        MD5_Final(digest, &context);          /* finish up 1st pass */
+        /*
+         * perform outer MD5
+         */
+        MD5_Init(&context);                   /* init context for 2nd
+                                              * pass */
+        MD5_Update(&context, k_opad, 64);     /* start with outer pad */
+        MD5_Update(&context, digest, 16);     /* then results of 1st
+                                              * hash */
+        MD5_Final(digest, &context);          /* finish up 2nd pass */
+}
+
+/*
+ * CRAM-MD5 authentication
+ */
+int
+smtp_auth_md5(int fd, char *login, char *password)
+{
+	unsigned char digest[BUF_SIZE];
+	char buffer[BUF_SIZE], ascii_digest[33];
+	char *temp;
+	int len, i;
+	static char hextab[] = "0123456789abcdef";
+
+	temp = calloc(BUF_SIZE, 1);
+	memset(buffer, 0, sizeof(buffer));
+	memset(digest, 0, sizeof(digest));
+	memset(ascii_digest, 0, sizeof(ascii_digest));
+
+	/* Send AUTH command according to RFC 2554 */
+	send_remote_command(fd, "AUTH CRAM-MD5");
+	if (read_remote(fd, sizeof(buffer), buffer) != 3) {
+		syslog(LOG_DEBUG, "smarthost authentication:"
+		       " AUTH cram-md5 not available: %s", neterr);
+		/* if cram-md5 is not available */
+		free(temp);
+		return (-1);
+	}
+
+	/* skip 3 char status + 1 char space */
+	base64_decode(buffer + 4, temp);
+	hmac_md5((unsigned char *)temp, strlen(temp),
+		 (unsigned char *)password, strlen(password), digest);
+	free(temp);
+
+	ascii_digest[32] = 0;
+	for (i = 0; i < 16; i++) {
+		ascii_digest[2*i] = hextab[digest[i] >> 4];
+		ascii_digest[2*i+1] = hextab[digest[i] & 15];
+	}
+
+	/* prepare answer */
+	snprintf(buffer, BUF_SIZE, "%s %s", login, ascii_digest);
+
+	/* encode answer */
+	len = base64_encode(buffer, strlen(buffer), &temp);
+	if (len < 0) {
+		syslog(LOG_ERR, "can not encode auth reply: %m");
+		return (-1);
+	}
+
+	/* send answer */
+	send_remote_command(fd, "%s", temp);
+	free(temp);
+	if (read_remote(fd, 0, NULL) != 2) {
+		syslog(LOG_WARNING, "remote delivery deferred:"
+				" AUTH cram-md5 failed: %s", neterr);
+		return (-2);
+	}
+
+	return (0);
+}
diff --git a/contrib/dma/debian/NEWS b/contrib/dma/debian/NEWS
new file mode 100644
index 000000000000..36ed11a44026
--- /dev/null
+++ b/contrib/dma/debian/NEWS
@@ -0,0 +1,29 @@
+dma (0.0.2010.06.17-3) unstable; urgency=low
+
+   The default delivery mode has been changed to immediate, as it is in
+   the upstream version of dma; the DEFER keyword is now disabled by default
+   in dma.conf.
+
+ -- Peter Pentchev <roam@ringlet.net>  Tue, 27 Jul 2010 13:26:48 +0300
+
+dma (0.0.2010.06.17-1) unstable; urgency=low
+
+   The dma spool directory format has changed.  The Debian package of dma now
+   recommends a separate package containing the dma-migrate utility; if it is
+   present, it will be invoked at each periodic dma queue flush and attempt to
+   convert the existing old-style queued messages to the new format.  In most
+   cases, this should not incur any performance penalties in normal operation,
+   since dma-migrate will scan the spool directory and ignore any new messages
+   (they should already be in the new format); however, if it appears that
+   the periodic queue flush runs take longer than usual to start up, you may
+   remove the dma-migrate package once you have ascertained that your queue
+   directory (/var/spool/dma) only contains files with names beginning with
+   the letters M or Q.
+
+   This version of dma knows how to perform MX lookups, so remote delivery is
+   now possible directly, not through a smarthost.  However, a smarthost setup
+   might still be preferred on many systems for various reasons - e.g. dynamic
+   address assignment, a central outgoing mailserver, a roaming laptop, etc.
+
+ -- Peter Pentchev <roam@ringlet.net>  Mon, 21 Jun 2010 11:03:57 +0300
+
diff --git a/contrib/dma/debian/README.Debian b/contrib/dma/debian/README.Debian
new file mode 100644
index 000000000000..564aaa75be70
--- /dev/null
+++ b/contrib/dma/debian/README.Debian
@@ -0,0 +1,12 @@
+dma for Debian
+--------------
+
+Smarthost operation by default - needs to be configured!
+
+After first installing dma, you need to configure it for proper operation -
+whether it should deliver all outgoing e-mail messages through a single
+smarthost or attempt to contact the remote mail servers directly.  This should
+be configured through the debconf questions, but you may change the setting
+using the SMARTHOST directive in the /etc/dma/dma.conf file.
+
+ -- Simon Schubert <2@0x2c.org>  Fri, 29 Oct 2010 00:25:48 +0200
diff --git a/contrib/dma/debian/changelog b/contrib/dma/debian/changelog
new file mode 100644
index 000000000000..4d5530043529
--- /dev/null
+++ b/contrib/dma/debian/changelog
@@ -0,0 +1,297 @@
+dma (0.9) unstable; urgency=low
+
+  [ Ed Maste ]
+  * Be explicit about missing user.
+  * Allow DMA_ROOT_USER & DMA_GROUP to be overridden.
+  * Add compat #ifdef for older OpenSSL
+  * Add CONF_DIR, as in Makefile
+  * More detailed error message for tmp file failure.
+
+  [ Simon Schubert ]
+  * spool.c: bzero contents of pointer
+
+ -- Simon Schubert <2@0x2c.org>  Mon, 03 Jun 2013 15:58:44 +0200
+
+dma (0.8) unstable; urgency=low
+
+  [ Gert van den Berg ]
+  * Added some more documentation on compiling
+  * Make Makefile and README consistent with INSTALL
+
+  [ Sascha Wildner ]
+  * dma.8: Fix a few small issues.
+
+  [ Simon Schubert ]
+  * dma.8: we only have 2 config files at the moment
+  * Merge pull request #2 from mohag/master
+  * don't treat -options following -q as argument to it
+  * deliver_remote: propagate back DNS errors
+  * don't complain when we can't lock a queue file during flush
+  * implement queue flushing prod
+
+ -- Simon Schubert <2@0x2c.org>  Fri, 30 Mar 2012 12:03:54 +0200
+
+dma (0.7) unstable; urgency=low
+
+  [ Simon Schubert ]
+  * add semicolon before date in Received: header
+  * parse_conf: fix bug with masqueraded domains
+  * clear up warnings found by clang static analysis
+  * mark printf-alike functions
+
+ -- Simon Schubert <2@0x2c.org>  Tue, 03 Jan 2012 14:53:43 +0100
+
+dma (0.6) unstable; urgency=low
+
+  [ Simon Schubert ]
+  * deliver_local: quote "From " more liberally
+
+ -- Simon Schubert <2@0x2c.org>  Wed, 07 Dec 2011 12:42:22 +0100
+
+dma (0.5) unstable; urgency=low
+
+  [ Simon Schubert ]
+  * implement masquerading using the MASQUERADE config option
+  * access config files at CONF_PATH, add makefile target to install conf files
+  * implement the "*" catch-all alias
+
+ -- Simon Schubert <2@0x2c.org>  Wed, 16 Nov 2011 13:34:43 +0100
+
+dma (0.4) unstable; urgency=low
+
+  [ Simon Schubert ]
+  * Makefile: put libraries at the end when linking
+  * LICENSE: add
+  * Merge commit 'refs/merge-requests/3' of git://gitorious.org/dma/dma
+  * Merge commit 'refs/merge-requests/4' of git://gitorious.org/dma/dma
+  * Add symlink for sendmail which is expected by many packages
+  * Makefile: create spool directories in a separate target
+  * Makefile: add symlink for mailq
+  * README: elaborate, use markdown
+
+  [ Peter Pentchev ]
+  * Fix straight SSL/TLS delivery to remote MX's.
+  * Fix a minor memory leak discovered by cppcheck.
+
+ -- Simon Schubert <2@0x2c.org>  Wed, 16 Nov 2011 00:08:28 +0100
+
+dma (0.3) unstable; urgency=low
+
+  [ Simon Schubert ]
+  * todo: we create mboxes properly now
+  * dma-mbox-create: group mail only needs to write to mboxes
+  * errlog: preserve errno
+  * dma-mbox-create: add error/status logging
+  * dns_get_mx_list: handle errors properly
+
+  [ Peter Pentchev ]
+  * Make add_host() really return an error code.
+
+  [ Simon Schubert ]
+  * readmail: accept mail without newline at the end
+
+  [ Peter Pentchev ]
+  * In OpenSSL 1.0, TLSv1_client_method() returns a const pointer.
+
+  [ Simon Schubert ]
+  * make dma compile again on DragonFly
+  * parse_addrs: fix parsing for multiple <addresses>
+
+ -- Simon Schubert <2@0x2c.org>  Sat, 09 Jul 2011 02:38:05 +0200
+
+dma (0.2) unstable; urgency=low
+
+  * Update to dma 0.2
+    - debian: better mark as UNRELEASED
+    - add ppa makefile recipe
+    - TODO: suggest way to run a queue flush on boot
+    - partially adopt 34-manpage-defaults.patch: AUTHPATH is not set by default
+    - Revert "debian: better mark as UNRELEASED"
+    - setlogident: openlog() wants a static variable
+    - writequeuef: create files with g+rw
+    - drop privileges when run by root
+    - implement mbox creation via setuid helper
+    - debian: build with consistent flags
+    - debian: remove unused files
+    - debian: fix lintian warnings and errors
+    - make ppa: force lower version number
+    - make ppa: proper name
+
+ -- Simon Schubert <2@0x2c.org>  Sun, 31 Oct 2010 23:57:50 +0100
+
+dma (0.1) unstable; urgency=low
+
+  * Update dma to 0.1
+
+ -- Simon Schubert <2@0x2c.org>  Fri, 29 Oct 2010 00:57:26 +0200
+
+dma (0.0.2010.06.17-6) unstable; urgency=low
+
+  * Add the 37-gnu-hurd patch to really fix the FTBFS on GNU/Hurd.
+  * Convert several shell output assignments from = to :=
+  * Switch to bzip2 compression for the Debian tarball.
+
+ -- Peter Pentchev <roam@ringlet.net>  Sun, 17 Oct 2010 00:08:33 +0300
+
+dma (0.0.2010.06.17-5) unstable; urgency=low
+
+  * Only use SA_NOCLDWAIT if available to fix the Hurd FTBFS.
+
+ -- Peter Pentchev <roam@ringlet.net>  Thu, 07 Oct 2010 11:42:23 +0300
+
+dma (0.0.2010.06.17-4) unstable; urgency=low
+
+  * Fix an infinite loop in dma-migrate if char is unsigned.
+    Closes: #599172
+  * Switch to Git and point the Vcs-* fields to Gitorious.
+
+ -- Peter Pentchev <roam@ringlet.net>  Wed, 06 Oct 2010 17:30:29 +0300
+
+dma (0.0.2010.06.17-3) unstable; urgency=low
+
+  * Update the debconf translations:
+    - French by Steve Petruzzello; Closes: #587883
+  * Bump Standards-Version to 3.9.1 with no changes.
+  * Disable deferred delivery by default, as in the upstream version:
+    - in the 03-debian-locations patch, comment the DEFER keyword in dma.conf,
+      as it is upstream
+    - refresh the 11-double-bounce.patch
+    - reword the README.Debian section on deferred delivery a bit
+    - add a news blurb
+
+ -- Peter Pentchev <roam@ringlet.net>  Tue, 27 Jul 2010 13:34:27 +0300
+
+dma (0.0.2010.06.17-2) unstable; urgency=low
+
+  * Quick on the heels of -1 to fix a momentary lapse of reason on my part:
+    in the 03-debian-locations patch, revert part of the 0.0.2010.06.17-1
+    change: do not set a un-overrideable default for the deferred delivery!
+    Closes: #587593
+  * Update the debconf translations:
+    - Japanese by Hideki Yamane; Closes: #587543
+
+ -- Peter Pentchev <roam@ringlet.net>  Wed, 30 Jun 2010 11:59:46 +0300
+
+dma (0.0.2010.06.17-1) unstable; urgency=low
+
+  * New upstream version:
+    - no longer reports the remote port number; Closes: #544820
+    - fixes some queue locking problems; Closes: #582593
+    - adapt the rules file to use the GNU Makefile instead of the BSD one
+    - drop pmake from Build-Depends
+    - remove the 01-debian-build patch, overtaken by upstream changes
+    - in the 03-debian-locations patch, make the Debian defaults actual
+      defaults for the dma binary, not just in the dma.conf file
+    - adapt the 04-debian-setgid patch for the GNU Makefile
+    - in the 10-liblockfile patch, change the GNU Makefile, too
+    - enhance the 11-double-bounce patch a bit:
+      - use dma's own delqueue() function instead of a naive unlink() so
+        all the queue files are cleaned up
+      - document the Debian default for DBOUNCEPROG in the manual page
+    - resurrect the 13-hardening patch, correcting a couple of
+      unchecked asprintf(3) and vasprintf(3) invocations
+    - the functionality of the 20-parse-recipient patch was implemented
+      upstream in a different way, so replace it with a small bugfix
+    - remove the 22-openfiles patch, overtaken by upstream changes
+    - in the 24-random-message-id patch, change the GNU Makefile, too
+    - add the 27-int-size patch to cast a variable to the correct type
+    - add the 28-valid-recipient patch to fix parsing recipients out of
+      the message body
+    - add the 29-double-free patch to fix a double-free error
+    - add the 30-ldflags patch to honor LDFLAGS if specified
+    - refresh the 09-typos, 17-mailname, 23-dirent-d_type, and
+      25-unsupported-starttls patches
+    - teach the dbounce-simple-safecat handler about the M*/Q* spool
+      files scheme
+  * Bump Standards-Version to 3.9.0 with no changes.
+  * Update the copyright file:
+    - bring it up to the latest revision of the DEP 5
+    - update the upstream copyright notices
+    - bump the year on my copyright notice
+  * Remove the diffsrc rules target which was intended for my own
+    internal use, but has outlived its time.
+  * Use dpkg-buildflags from dpkg-dev >= 1.15.7~ to get the default
+    values for CFLAGS, CPPFLAGS, and LDFLAGS; no longer rely on
+    dpkg-buildpackage to set them by default.
+  * Add the dma-migrate utility (in a separate binary package) to convert
+    spool files to the new queue format (M* and Q* files for each message)
+  * Add a dma-migrate invocation to dma.cron.d
+  * Shorten the Vcs-Browser URL.
+  * Add the 31-sigalrm-backoff patch to force a delivery attempt on SIGALRM.
+  * Properly substitute the debconf-supplied values for DBOUNCEPROG and
+    SMARTHOST into dma.conf even if they are empty without generating
+    an invalid dma.conf file.
+  * Remove the smarthost default; dma does MX lookups now, so it doesn't
+    really *need* one anymore.
+  * Reword the debconf relayhost question - dma does not really need
+    a smarthost anymore.
+  * Update the debconf translations:
+    - Bulgarian
+    - Portuguese by Américo Monteiro
+    - German by Helge Kreutzmann; Closes: #586531
+    - Russian by Yuri Kozlov; Closes: #586579
+    - Czech by Michal Simunek; Closes: #586791
+    - Swedish by Martin Bagge; Closes: #586825
+    - Spanish by Francisco Javier Cuadrado; Closes: #587088
+  * Update the smarthost configuration information in README.Debian.
+  * Add the 32-comment-uncomment patch to correct the manual page wording.
+  * Add the 33-opportunistic-tls patch to allow remote connections to proceed
+    even if the STARTTLS negotiation fails.
+  * Fix the 25-unsupported-starttls patch to actually error out if the SSL
+    negotiation fails.
+  * Forward all the non-Debian-specific patches upstream to Simon Schubert.
+  * Add the 34-manpage-defaults patch to properly document what dma will use
+    as default values if not specified in the config file instead of what
+    the default config file specifies.  Closes: #544748
+  * Add the 35-delivery-retry patch to try local deliveries a bit more often
+    and to randomize the delivery timeout to avoid locking issues.
+
+ -- Peter Pentchev <roam@ringlet.net>  Mon, 28 Jun 2010 23:26:36 +0300
+
+dma (0.0.2009.07.17-3) unstable; urgency=low
+
+  * Really install the files in /etc/dma/ as root/mail/640 and
+    change the ownership of the existing files in the postinst script
+    when upgrading from older versions.  Closes: #544664
+  * Install the /usr/bin/mailq and /usr/bin/newaliases symlinks.
+    Closes: #558421
+  * Switch to the 3.0 (quilt) source format.
+  * Update the debconf translations:
+    - add German.  Closes: #552754
+    - add Japanese.  Closes: #554515
+    - remove a double space and unfuzzy the translations.  Closes: #552586
+  * Fix a crash when the SMTP server does not support STARTTLS.
+    Closes: #547594
+  * Always use the user-supplied value from the debconf query for
+    the smarthost and the double-bounce program.  This may result in
+    debconf overriding a manually-edited config file, so add a note to
+    dma.conf stating that these values are handled via debconf.
+    Closes: #544663
+  * Fix a misspelling of dma/dbounceprog as dma/defer in the debconf
+    configuration script.  Oops.
+
+ -- Peter Pentchev <roam@ringlet.net>  Sat, 19 Dec 2009 14:35:10 +0200
+
+dma (0.0.2009.07.17-2) unstable; urgency=low
+
+  * Allow the spool directory to live on a filesystem that does not
+    set the d_type member of the dirent structure, like XFS.
+    Closes: #544357
+  * Randomize the Message-Id a bit more.  Closes: #544475
+  * Bump Standards-Version to 3.8.3 with no changes.
+  * Only enable the build hardening wrapper if the "hardening" build
+    option is specified.
+  * Switch the copyright file header from the Wiki to DEP 5.
+  * Remove the manual page ".Dx" patch - the groff version in Squeeze
+    knows about the .Dx mdoc macro.  Add a lintian override for
+    the "Unknown DragonFly version" error.
+  * Convert the patch file headers to the DEP 3 format.
+
+ -- Peter Pentchev <roam@ringlet.net>  Tue, 01 Sep 2009 13:36:33 +0300
+
+dma (0.0.2009.07.17-1) unstable; urgency=low
+
+  * Initial release
+    (Closes: #511410, #533458, #533614, #533890, #534101, #534860)
+
+ -- Peter Pentchev <roam@ringlet.net>  Tue, 11 Aug 2009 16:08:41 +0300
diff --git a/contrib/dma/debian/compat b/contrib/dma/debian/compat
new file mode 100644
index 000000000000..7f8f011eb73d
--- /dev/null
+++ b/contrib/dma/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/contrib/dma/debian/control b/contrib/dma/debian/control
new file mode 100644
index 000000000000..6eeea08aa7c0
--- /dev/null
+++ b/contrib/dma/debian/control
@@ -0,0 +1,37 @@
+Source: dma
+Section: mail
+Priority: optional
+Maintainer: Peter Pentchev <roam@ringlet.net>
+DM-Upload-Allowed: yes
+Build-Depends: debhelper (>= 7.0.50), byacc, dpkg-dev (>= 1.15.7~), flex, hardening-wrapper, libssl-dev, po-debconf
+Standards-Version: 3.9.1
+Homepage: http://devel.ringlet.net/mail/dma/
+Vcs-Git: git://gitorious.org/dma-roam/pkg-debian.git
+Vcs-Browser: http://gitorious.org/dma-roam/pkg-debian
+
+Package: dma
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Provides: mail-transport-agent
+Conflicts: mail-transport-agent
+Replaces: mail-transport-agent
+Recommends: dma-migrate
+Description: lightweight mail transport agent
+ The DragonFly Mail Agent is a small Mail Transport Agent (MTA),
+ designed for home and office use.  It accepts mails from local Mail
+ User Agents (MUA) and delivers them either to local mailboxes or
+ remote SMTP servers.  Remote delivery includes support for features
+ such as TLS/SSL and SMTP authentication.
+ .
+ dma is not intended as a replacement for full-featured MTAs like
+ Sendmail, Postfix, or Exim.  Consequently, dma does not listen on
+ port 25 for incoming connections.
+
+Package: dma-migrate
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Enhances: dma
+Description: migration utility for the DragonFly Mail Agent's spool files
+ The dma-migrate utility examines the DragonFly Mail Agent's mail queue
+ and performs any conversions from old message file formats to the most
+ recent one as needed.
diff --git a/contrib/dma/debian/copyright b/contrib/dma/debian/copyright
new file mode 100644
index 000000000000..4ad8fbb7a80f
--- /dev/null
+++ b/contrib/dma/debian/copyright
@@ -0,0 +1,91 @@
+Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
+Name: dma
+
+Files: base64.c
+Copyright: Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ (Royal Institute of Technology, Stockholm, Sweden).
+ All rights reserved.
+License: BSD-3
+
+Files: conf.c crypto.c net.c
+Copyright: Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+  This code is derived from software contributed to The DragonFly Project
+  by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+  Germany.
+License: BSD-3
+
+Files: dfcompat.c
+Copyright: Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ Copyright (c) 1998, M. Warner Losh <imp@freebsd.org>  All rights reserved.
+License: BSD-1 BSD-2
+
+Files: dma.8
+Copyright: Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+License: BSD-3
+
+Files: dma.c dns.c mail.c spool.c util.c
+Copyright: Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+  This code is derived from software contributed to The DragonFly Project
+  by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+License: BSD-3
+
+Files: dma.h
+Copyright: Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+  This code is derived from software contributed to The DragonFly Project
+  by Simon 'corecode' Schubert <corecode@fs.ei.tum.de> and
+  Matthias Schmidt <matthias@dragonflybsd.org>.
+License: BSD-3
+
+Files: debian/*
+Copyright: Copyright (c) 2009, 2010 Peter Pentchev.
+License: BSD-3
+
+License: BSD-3
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  .
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. Neither the name of The DragonFly Project nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific, prior written permission.
+
+License: BSD-1
+  Permission to use, copy, modify, and distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+  .
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+License: BSD-2
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  .
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
diff --git a/contrib/dma/debian/dma-migrate.dirs b/contrib/dma/debian/dma-migrate.dirs
new file mode 100644
index 000000000000..b174b445af42
--- /dev/null
+++ b/contrib/dma/debian/dma-migrate.dirs
@@ -0,0 +1,2 @@
+usr/sbin
+usr/share/man/man8
diff --git a/contrib/dma/debian/dma-migrate.install b/contrib/dma/debian/dma-migrate.install
new file mode 100644
index 000000000000..1c35b0afb178
--- /dev/null
+++ b/contrib/dma/debian/dma-migrate.install
@@ -0,0 +1 @@
+debian/migrate/dma-migrate usr/sbin
diff --git a/contrib/dma/debian/dma-migrate.manpages b/contrib/dma/debian/dma-migrate.manpages
new file mode 100644
index 000000000000..3629dbe3a288
--- /dev/null
+++ b/contrib/dma/debian/dma-migrate.manpages
@@ -0,0 +1 @@
+debian/migrate/dma-migrate.8
diff --git a/contrib/dma/debian/dma.dirs b/contrib/dma/debian/dma.dirs
new file mode 100644
index 000000000000..f10c0054ee33
--- /dev/null
+++ b/contrib/dma/debian/dma.dirs
@@ -0,0 +1,4 @@
+etc/dma
+usr/sbin
+usr/share/lintian/overrides
+usr/share/man/man8
diff --git a/contrib/dma/debian/dma.links b/contrib/dma/debian/dma.links
new file mode 100644
index 000000000000..d9ac22a2cdc2
--- /dev/null
+++ b/contrib/dma/debian/dma.links
@@ -0,0 +1,6 @@
+usr/sbin/dma usr/bin/mailq
+usr/sbin/dma usr/bin/newaliases
+usr/sbin/dma usr/sbin/sendmail
+usr/share/man/man8/dma.8 usr/share/man/man8/mailq.8
+usr/share/man/man8/dma.8 usr/share/man/man8/newaliases.8
+usr/share/man/man8/dma.8 usr/share/man/man8/sendmail.8
diff --git a/contrib/dma/debian/dma.lintian-overrides b/contrib/dma/debian/dma.lintian-overrides
new file mode 100644
index 000000000000..03f5d6eb8369
--- /dev/null
+++ b/contrib/dma/debian/dma.lintian-overrides
@@ -0,0 +1,8 @@
+dma: manpage-has-errors-from-man *Unknown DragonFly version*
+dma: non-standard-dir-in-var var/mail/
+dma: non-standard-dir-perm var/spool/dma/ 0770 != 0755
+dma: non-standard-dir-perm var/spool/dma/ 2775 != 0755
+dma: non-standard-file-perm etc/dma/auth.conf 0640 != 0644
+dma: no-upstream-changelog
+dma: setgid-binary usr/sbin/dma 2755 root/mail
+dma: setuid-binary usr/lib/dma-mbox-create 4754 root/mail
diff --git a/contrib/dma/debian/migrate/Makefile b/contrib/dma/debian/migrate/Makefile
new file mode 100644
index 000000000000..cc7d3476c008
--- /dev/null
+++ b/contrib/dma/debian/migrate/Makefile
@@ -0,0 +1,4 @@
+all:	dma-migrate
+
+clean:
+	rm -f dma-migrate dma-migrate.o
diff --git a/contrib/dma/debian/migrate/NEWS b/contrib/dma/debian/migrate/NEWS
new file mode 100644
index 000000000000..29dca0dd335b
--- /dev/null
+++ b/contrib/dma/debian/migrate/NEWS
@@ -0,0 +1,6 @@
+Change log for dma-migrate, the DragonFly Mail Agent queue migration utility.
+
+0.01	not yet ;)
+	- first public release
+
+Comments: Peter Pentchev <roam@ringlet.net>
diff --git a/contrib/dma/debian/migrate/dma-migrate.8 b/contrib/dma/debian/migrate/dma-migrate.8
new file mode 100644
index 000000000000..e40acef5b13d
--- /dev/null
+++ b/contrib/dma/debian/migrate/dma-migrate.8
@@ -0,0 +1,98 @@
+.\" Copyright (c) 2010  Peter Pentchev
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 11, 2009
+.Dt dma-migrate 8
+.Os
+.Sh NAME
+.Nm dma-migrate
+.Nd convert the DragonFly Mail Agent's queue files
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl d Ar spooldir
+.Nm
+.Op Fl h | Fl V
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to convert the mail queue files in the
+.Xr dma 8
+spool directory to the latest spool directory format supported by
+the installed version of
+.Xr dma 8 .
+Currently it only handles the conversion from a single file containing
+both message and delivery metadata to the M/Q format.
+.Pp
+The following command-line options are available:
+.Bl -tag -width indent
+.It Fl d
+Specify the location of the
+.Xr dma 8
+spool directory, default
+.Pa /var/spool/dma .
+.It Fl h
+Display usage information and exit.
+.It Fl V
+Display program version information and exit.
+.It Fl v
+Verbose output - display diagnostic messages.
+.El
+.Sh ENVIRONMENT
+The operation of the
+.Nm
+utility is currently not influenced by environment variables.
+.Sh FILES
+The
+.Nm
+utility looks for the
+.Xr dma 8
+mail queue files in the
+.Pa /var/spool/dma
+directory, unless another location is specified by the
+.Fl d
+command-line option.
+.Sh EXIT STATUS
+The
+.Nm
+utility will scan the whole spool directory and attempt to convert all
+suitable files there.
+If there are no files to be converted, or if all the conversions are
+successful, it will complete with an exit code of zero.
+If any conversion errors are encountered, a message will be displayed
+to the standard error stream and
+.Nm
+will exit with an exit code of 1 after attempting to convert the rest of
+the suitable files in the spool directory.
+.Sh SEE ALSO
+.Xr dma 8
+.Sh STANDARDS
+No standards documentation was harmed in the process of creating
+.Nm .
+.Sh BUGS
+Please report any bugs in
+.Nm
+to the author.
+.Sh AUTHOR
+.An Peter Pentchev Aq roam@ringlet.net
diff --git a/contrib/dma/debian/migrate/dma-migrate.c b/contrib/dma/debian/migrate/dma-migrate.c
new file mode 100644
index 000000000000..56bfa2adbf87
--- /dev/null
+++ b/contrib/dma/debian/migrate/dma-migrate.c
@@ -0,0 +1,413 @@
+/*-
+ * Copyright (c) 2010  Peter Pentchev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef __printflike
+#ifdef __GNUC__
+#define __printflike(fmtarg, firstvararg)	\
+	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define __printflike(fmtarg, firstvararg)
+#endif
+#endif
+
+#define DEFAULT_SPOOLDIR	"/var/spool/dma"
+
+static int	 verbose = 0;
+static char	 copybuf[BUFSIZ];
+
+static int	 dma_migrate(int, const char *);
+
+static int	 open_locked(const char *, int, ...);
+static void	 cleanup_file(int, char *);
+
+static void	 usage(int);
+static void	 version(void);
+static void	 debug(const char *, ...) __printflike(1, 2);
+
+int
+main(int argc, char **argv)
+{
+	const char *spooldir;
+	int hflag, Vflag, errs, fd, res;
+	int ch;
+	DIR *d;
+	struct dirent *e;
+	struct stat sb;
+
+        srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv)));
+
+	hflag = Vflag = 0;
+	spooldir = DEFAULT_SPOOLDIR;
+	while (ch = getopt(argc, argv, "d:hVv"), ch != -1)
+		switch (ch) {
+			case 'd':
+				spooldir = optarg;
+				break;
+
+			case 'h':
+				hflag = 1;
+				break;
+
+			case 'V':
+				Vflag = 1;
+				break;
+
+			case 'v':
+				verbose = 1;
+				break;
+
+			case '?':
+			default:
+				usage(1);
+				/* NOTREACHED */
+		}
+	if (Vflag)
+		version();
+	if (hflag)
+		usage(0);
+	if (hflag || Vflag)
+		exit(0);
+
+	argc -= optind;
+	argv += optind;
+
+	/* Let's roll! */
+	if (chdir(spooldir) == -1)
+		err(1, "Could not change into spool directory %s", spooldir);
+	if (d = opendir("."), d == NULL)
+		err(1, "Could not read spool directory %s", spooldir);
+	errs = 0;
+	while (e = readdir(d), e != NULL) {
+		/* Do we care about this entry? */
+		debug("Read a directory entry: %s\n", e->d_name);
+		if (strncmp(e->d_name, "tmp_", 4) == 0 ||
+		    e->d_name[0] == 'M' || e->d_name[0] == 'Q' ||
+		    (e->d_type != DT_REG && e->d_type != DT_UNKNOWN))
+			continue;
+		if (e->d_type == DT_UNKNOWN)
+			if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode))
+				continue;
+		debug("- want to process it\n");
+
+		/* Try to lock it - skip it if dma is delivering the message */
+		if (fd = open_locked(e->d_name, O_RDONLY|O_NDELAY), fd == -1) {
+			debug("- seems to be locked, skipping\n");
+			continue;
+		}
+
+		/* Okay, convert it to the M/Q schema */
+		res = dma_migrate(fd, e->d_name);
+		close(fd);
+		if (res == -1)
+			errs++;
+	}
+	if (errs)
+		debug("Finished, %d conversion errors\n", errs);
+	else
+		debug("Everything seems to be all right\n");
+	return (errs && 1);
+}
+
+static int
+dma_migrate(int fd, const char *fname)
+{
+	const char *id;
+	char *mname, *qname, *tempname, *sender, *recp, *line, *recpline;
+	int mfd, qfd, tempfd;
+	struct stat sb;
+	FILE *fp, *qfp, *mfp;
+	size_t sz, len;
+	static regex_t *qidreg = NULL;
+
+	mfd = tempfd = qfd = -1;
+	mname = qname = sender = recp = line = NULL;
+	fp = qfp = NULL;
+
+	if (fstat(fd, &sb) == -1) {
+		warn("Could not fstat(%s)", fname);
+		return (-1);
+	}
+	/*
+	 * Let's just blithely assume that the queue ID *is* the filename,
+	 * since that's the way dma did things so far.
+	 * Well, okay, let's check it.
+	 */
+	if (qidreg == NULL) {
+		regex_t *nreg;
+
+		if ((nreg = malloc(sizeof(*qidreg))) == NULL) {
+			warn("Could not allocate memory for a regex");
+			return (-1);
+		}
+		if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0)
+		    != 0) {
+			warnx("Could not compile a dma queue ID regex");
+			free(nreg);
+			return (-1);
+		}
+		qidreg = nreg;
+	}
+	if (regexec(qidreg, fname, 0, NULL, 0) != 0) {
+		warnx("The name '%s' is not a valid dma queue ID", fname);
+		return (-1);
+	}
+	id = fname;
+	debug("  - queue ID %s\n", id);
+	if (asprintf(&mname, "M%s", id) == -1 ||
+	    asprintf(&tempname, "tmp_%s", id) == -1 ||
+	    asprintf(&qname, "Q%s", id) == -1 ||
+	    mname == NULL || tempname == NULL || qname == NULL)
+		goto fail;
+
+	/* Create the message placeholder early to avoid races */
+	mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600);
+	if (mfd == -1) {
+		warn("Could not create temporary file %s", mname);
+		goto fail;
+	}
+	if (stat(qname, &sb) != -1 || errno != ENOENT ||
+	    stat(tempname, &sb) != -1 || errno != ENOENT) {
+		warnx("Some of the queue files for %s already exist", fname);
+		goto fail;
+	}
+	debug("  - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname);
+
+	fp = fdopen(fd, "r");
+	if (fp == NULL) {
+		warn("Could not reopen the descriptor for %s", fname);
+		goto fail;
+	}
+
+	/* Parse the header of the old-format message file */
+	/* ...sender... */
+	if (getline(&sender, &sz, fp) == -1) {
+		warn("Could not read the initial line from %s", fname);
+		goto fail;
+	}
+	sz = strlen(sender);
+	while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r'))
+		sender[--sz] = '\0';
+	if (sz == 0) {
+		warnx("Empty sender line in %s", fname);
+		goto fail;
+	}
+	debug("  - sender %s\n", sender);
+	/* ...recipient(s)... */
+	len = strlen(fname);
+	recpline = NULL;
+	while (1) {
+		if (getline(&line, &sz, fp) == -1) {
+			warn("Could not read a recipient line from %s", fname);
+			goto fail;
+		}
+		sz = strlen(line);
+		while (sz > 0 &&
+		    (line[sz - 1] == '\n' || line[sz - 1] == '\r'))
+			line[--sz] = '\0';
+		if (sz == 0) {
+			free(line);
+			line = NULL;
+			break;
+		}
+		if (recp == NULL &&
+		    strncmp(line, fname, len) == 0 && line[len] == ' ') {
+			recp = line + len + 1;
+			recpline = line;
+		} else {
+			free(line);
+		}
+		line = NULL;
+	}
+	if (recp == NULL) {
+		warnx("Could not find its own recipient line in %s", fname);
+		goto fail;
+	}
+	/* ..phew, finished with the header. */
+
+	tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600);
+	if (tempfd == -1) {
+		warn("Could not create a queue file for %s", fname);
+		goto fail;
+	}
+	qfp = fdopen(tempfd, "w");
+	if (qfp == NULL) {
+		warn("Could not fdopen(%s) for %s", tempname, fname);
+		goto fail;
+	}
+	mfp = fdopen(mfd, "w");
+	if (mfp == NULL) {
+		warn("Could not fdopen(%s) for %s", mname, fname);
+		goto fail;
+	}
+	fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp);
+	fflush(qfp);
+	fsync(tempfd);
+
+	/* Copy the message file over to mname */
+	while ((sz = fread(copybuf, 1, sizeof(copybuf), fp)) > 0)
+		if (fwrite(copybuf, 1, sz, mfp) != sz) {
+			warn("Could not copy the message from %s to %s",
+			    fname, mname);
+			goto fail;
+		}
+	if (ferror(fp)) {
+		warn("Could not read the full message from %s", fname);
+		goto fail;
+	}
+	fflush(mfp);
+	fsync(mfd);
+
+	if (rename(tempname, qname) == -1) {
+		warn("Could not rename the queue file for %s", fname);
+		goto fail;
+	}
+	qfd = tempfd;
+	tempfd = -1;
+	if (unlink(fname) == -1) {
+		warn("Could not remove the old converted file %s", fname);
+		goto fail;
+	}
+
+	fclose(fp);
+	fclose(qfp);
+	free(sender);
+	free(line);
+	free(recpline);
+	free(mname);
+	free(qname);
+	free(tempname);
+	return (0);
+
+fail:
+	if (fp != NULL)
+		fclose(fp);
+	if (qfp != NULL)
+		fclose(qfp);
+	if (sender != NULL)
+		free(sender);
+	if (line != NULL)
+		free(line);
+	if (recpline != NULL)
+		free(recpline);
+	cleanup_file(mfd, mname);
+	cleanup_file(qfd, qname);
+	cleanup_file(tempfd, tempname);
+	return (-1);
+}
+
+static void
+cleanup_file(int fd, char *fname)
+{
+	if (fd != -1) {
+		close(fd);
+		unlink(fname);
+	}
+	if (fname != NULL)
+		free(fname);
+}
+
+static void
+usage(int ferr)
+{
+	const char *s =
+	    "Usage:\tdma-migrate [-hVv] [-d spooldir]\n"
+	    "\t-d\tspecify the spool directory (" DEFAULT_SPOOLDIR ")\n"
+	    "\t-h\tdisplay program usage information and exit\n"
+	    "\t-V\tdisplay program version information and exit\n"
+	    "\t-v\tverbose operation - display diagnostic messages";
+
+	if (ferr)
+		errx(1, "%s", s);
+	puts(s);
+}
+
+static void
+version(void)
+{
+	printf("dma-migrate 0.01 (dma 0.0.2010.06.17)\n");
+}
+
+static void
+debug(const char *fmt, ...)
+{
+	va_list v;
+
+	if (verbose < 1)
+		return;
+	va_start(v, fmt);
+	vfprintf(stderr, fmt, v);
+	va_end(v);
+}
+
+static int
+open_locked(const char *fname, int flags, ...)
+{
+	int mode = 0;
+#ifndef O_EXLOCK
+	int fd, save_errno;
+#endif
+
+	if (flags & O_CREAT) {
+		va_list ap;
+		va_start(ap, flags);
+		mode = va_arg(ap, int);
+		va_end(ap);
+	}
+
+#ifndef O_EXLOCK
+	fd = open(fname, flags, mode);
+	if (fd < 0)
+		return(fd);
+	if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
+		save_errno = errno;
+		close(fd);
+		errno = save_errno;
+		return(-1);
+	}
+	return(fd);
+#else
+	return(open(fname, flags|O_EXLOCK, mode));
+#endif
+}
diff --git a/contrib/dma/debian/rules b/contrib/dma/debian/rules
new file mode 100755
index 000000000000..461a0a8503f6
--- /dev/null
+++ b/contrib/dma/debian/rules
@@ -0,0 +1,49 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Debian build rules for dma, the DragonFly mail agent
+
+DDIR=		$(CURDIR)/debian
+D=		$(DDIR)/dma
+
+BUILDDEFS=	DESTDIR=$D PREFIX=/usr
+
+CFLAGS:=	$(shell dpkg-buildflags --get CFLAGS)
+CPPFLAGS:=	$(shell dpkg-buildflags --get CPPFLAGS)
+LDFLAGS:=	$(shell dpkg-buildflags --get LDFLAGS)
+
+CONFFILES=	dma.conf auth.conf
+
+ifneq (,$(filter werror,$(DEB_BUILD_OPTIONS)))
+	CFLAGS+=	-Werror
+endif
+ifneq (,$(filter nostrip,$(DEB_BUILD_OPTIONS)))
+	export STRIPFLAG=
+endif
+ifneq (,$(filter hardening,$(DEB_BUILD_OPTIONS)))
+export DEB_BUILD_HARDENING=1
+else
+export DEB_BUILD_HARDENING=0
+endif
+
+export CFLAGS CPPFLAGS LDFLAGS
+
+override_dh_auto_build:
+	$(MAKE) -f Makefile ${BUILDDEFS}
+	$(MAKE) -C $(DDIR)/migrate
+
+override_dh_auto_clean:
+	$(MAKE) -f Makefile clean
+	$(MAKE) -C $(DDIR)/migrate clean
+
+override_dh_auto_install:
+	$(MAKE) -f Makefile ${BUILDDEFS} install sendmail-link mailq-link install-spool-dirs install-etc
+
+override_dh_fixperms:
+	dh_fixperms -Xusr/sbin/dma -Xusr/lib/dma-mbox-create -Xvar/spool/dma -Xetc/dma
+
+override_dh_installchangelogs:
+	dh_installchangelogs -p dma
+	dh_installchangelogs -p dma-migrate debian/migrate/NEWS
+
+%:
+	dh $@
diff --git a/contrib/dma/debian/source/format b/contrib/dma/debian/source/format
new file mode 100644
index 000000000000..89ae9db8f88b
--- /dev/null
+++ b/contrib/dma/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/contrib/dma/debian/source/options b/contrib/dma/debian/source/options
new file mode 100644
index 000000000000..779dbfd0ca3a
--- /dev/null
+++ b/contrib/dma/debian/source/options
@@ -0,0 +1,2 @@
+compression = "bzip2"
+compression-level = 9
diff --git a/contrib/dma/dfcompat.c b/contrib/dma/dfcompat.c
new file mode 100644
index 000000000000..014fa88b8d49
--- /dev/null
+++ b/contrib/dma/dfcompat.c
@@ -0,0 +1,122 @@
+#ifndef HAVE_STRLCPY
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $
+ * $FreeBSD$
+ * $DragonFly: src/lib/libc/string/strlcpy.c,v 1.4 2005/09/18 16:32:34 asmodai Exp $
+ */
+
+#include "dfcompat.h"
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCPY */
+
+#ifndef HAVE_REALLOCF
+
+/*-
+ * Copyright (c) 1998, M. Warner Losh <imp@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $DragonFly: src/lib/libc/stdlib/reallocf.c,v 1.2 2003/06/17 04:26:46 dillon Exp $
+ */
+#include <stdlib.h>
+
+void *
+reallocf(void *ptr, size_t size)
+{
+	void *nptr;
+
+	nptr = realloc(ptr, size);
+	if (!nptr && ptr)
+		free(ptr);
+	return (nptr);
+}
+
+#endif /* !HAVE_REALLOCF */
+
+#ifndef HAVE_GETPROGNAME
+
+#ifdef __GLIBC__
+
+#include <errno.h>
+
+const char *
+getprogname(void)
+{
+	return (program_invocation_short_name);
+}
+
+#else /* __GLIBC__ */
+#error "no getprogname implementation available"
+#endif
+
+#endif /* !HAVE_GETPROGNAME */
diff --git a/contrib/dma/dfcompat.h b/contrib/dma/dfcompat.h
new file mode 100644
index 000000000000..bb0a0bbd0d5e
--- /dev/null
+++ b/contrib/dma/dfcompat.h
@@ -0,0 +1,24 @@
+#ifndef DFCOMPAT_H
+#define DFCOMPAT_H
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+
+#ifndef __DECONST
+#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_REALLOCF
+void *reallocf(void *, size_t);
+#endif
+
+#ifndef HAVE_GETPROGNAME
+const char *getprogname(void);
+#endif
+
+#endif /* DFCOMPAT_H */
diff --git a/contrib/dma/dma-mbox-create.c b/contrib/dma/dma-mbox-create.c
new file mode 100644
index 000000000000..c84652b74d47
--- /dev/null
+++ b/contrib/dma/dma-mbox-create.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2010 Simon Schubert <2@0x2c.org>
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This binary is setuid root.  Use extreme caution when touching
+ * user-supplied information.  Keep the root window as small as possible.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+
+static void
+logfail(const char *fmt, ...)
+{
+	int oerrno = errno;
+	va_list ap;
+	char outs[1024];
+
+	outs[0] = 0;
+	if (fmt != NULL) {
+		va_start(ap, fmt);
+		vsnprintf(outs, sizeof(outs), fmt, ap);
+		va_end(ap);
+	}
+
+	errno = oerrno;
+	if (*outs != 0)
+		syslog(LOG_ERR, errno ? "%s: %m" : "%s", outs);
+	else
+		syslog(LOG_ERR, errno ? "%m" : "unknown error");
+
+	exit(1);
+}
+
+/*
+ * Create a mbox in /var/mail for a given user, or make sure
+ * the permissions are correct for dma.
+ */
+
+int
+main(int argc, char **argv)
+{
+	const char *user;
+	struct passwd *pw;
+	struct group *gr;
+	uid_t user_uid;
+	gid_t mail_gid;
+	int error;
+	char fn[PATH_MAX+1];
+	int f;
+
+	openlog("dma-mbox-create", 0, LOG_MAIL);
+
+	errno = 0;
+	gr = getgrnam(DMA_GROUP);
+	if (!gr)
+		logfail("cannot find dma group `%s'", DMA_GROUP);
+
+	mail_gid = gr->gr_gid;
+
+	if (setgid(mail_gid) != 0)
+		logfail("cannot set gid to %d (%s)", mail_gid, DMA_GROUP);
+	if (getegid() != mail_gid)
+		logfail("cannot set gid to %d (%s), still at %d", mail_gid, DMA_GROUP, getegid());
+
+	/*
+	 * We take exactly one argument: the username.
+	 */
+	if (argc != 2) {
+		errno = 0;
+		logfail("no arguments");
+	}
+	user = argv[1];
+
+	syslog(LOG_NOTICE, "creating mbox for `%s'", user);
+
+	/* the username may not contain a pathname separator */
+	if (strchr(user, '/')) {
+		errno = 0;
+		logfail("path separator in username `%s'", user);
+		exit(1);
+	}
+
+	/* verify the user exists */
+	errno = 0;
+	pw = getpwnam(user);
+	if (!pw)
+		logfail("cannot find user `%s'", user);
+
+	user_uid = pw->pw_uid;
+
+	error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, user);
+	if (error < 0 || (size_t)error >= sizeof(fn)) {
+		if (error >= 0) {
+			errno = 0;
+			logfail("mbox path too long");
+		}
+		logfail("cannot build mbox path for `%s/%s'", _PATH_MAILDIR, user);
+	}
+
+	f = open(fn, O_RDONLY|O_CREAT, 0600);
+	if (f < 0)
+		logfail("cannot open mbox `%s'", fn);
+
+	if (fchown(f, user_uid, mail_gid))
+		logfail("cannot change owner of mbox `%s'", fn);
+
+	if (fchmod(f, 0620))
+		logfail("cannot change permissions of mbox `%s'", fn);
+
+	/* file should be present with the right owner and permissions */
+
+	syslog(LOG_NOTICE, "successfully created mbox for `%s'", user);
+
+	return (0);
+}
diff --git a/contrib/dma/dma.8 b/contrib/dma/dma.8
new file mode 100644
index 000000000000..5d786c6be2ca
--- /dev/null
+++ b/contrib/dma/dma.8
@@ -0,0 +1,363 @@
+.\"
+.\" Copyright (c) 2008
+.\"	The DragonFly Project.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 13, 2014
+.Dt DMA 8
+.Os
+.Sh NAME
+.Nm dma
+.Nd DragonFly Mail Agent
+.Sh SYNOPSIS
+.Nm
+.Op Fl DiOt
+.Op Fl A Ns Ar mode
+.Op Fl b Ns Ar mode
+.Op Fl f Ar sender
+.Op Fl L Ar tag
+.Op Fl o Ns Ar option
+.Op Fl r Ar sender
+.Op Fl q Ns Op Ar arg
+.Op Ar recipient ...
+.Sh DESCRIPTION
+.Nm
+is a small Mail Transport Agent (MTA), designed for home and office use.
+It accepts mails from locally installed Mail User Agents (MUA) and
+delivers the mails either locally or to a remote destination.
+Remote delivery includes several features like TLS/SSL support and SMTP
+authentication.
+.Pp
+.Nm
+is not intended as a replacement for real, big MTAs like
+.Xr sendmail 8
+or
+.Xr postfix 1 .
+Consequently,
+.Nm
+does not listen on port 25 for incoming connections.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl A Ns Ar mode
+.Fl \&Ac
+acts as a compatibility option for sendmail.
+.It Fl b Ns Ar mode
+.Bl -tag -width indent
+.It Fl bp
+List all mails currently stored in the mail queue.
+.It Fl bq
+Queue the mail, but don't attempt to deliver it.
+See also the
+.Sq DEFER
+config file setting below.
+.El
+.Pp
+All other
+.Ar mode Ns
+s are are ignored.
+.It Fl D
+Don't run in the background.
+Useful for debugging.
+.It Fl f Ar sender
+Set sender address (envelope-from) to
+.Ar sender .
+This overrides the value of the environment variable
+.Ev EMAIL .
+.It Fl i
+Ignore dots alone on lines by themselves in incoming messages.
+This should be set if you are reading data from a file.
+.It Fl L Ar tag
+Set the identifier used in syslog messages to the supplied
+.Ar tag .
+This is a compatibility option for sendmail.
+.It Fl O
+This is a compatibility option for sendmail.
+.It Fl o Ns Ar option
+Specifying
+.Fl oi
+is synonymous to
+.Fl i .
+All other options are ignored.
+.It Fl q Ns Op Ar arg
+Process saved messages in the queue.
+The argument is optional and ignored.
+.It Fl r Ar sender
+Same as
+.Fl f .
+.It Fl t
+Obtain recipient addresses from the message header.
+.Nm
+will parse the
+.Li To: ,
+.Li Cc: ,
+and
+.Li Bcc:
+headers.
+The
+.Li Bcc:
+header will be removed independent of whether
+.Fl t
+is specified or not.
+.El
+.Sh CONFIGURATION
+.Nm
+can be configured with two config files:
+.Pp
+.Bl -bullet -compact
+.It
+auth.conf
+.It
+dma.conf
+.El
+.Pp
+These two files are stored per default in
+.Pa /etc/dma .
+.Sh FILE FORMAT
+Every file contains parameters of the form
+.Sq name value .
+Lines containing boolean values are set to
+.Sq NO
+if the line is commented and to
+.Sq YES
+if the line is uncommented.
+Empty lines or lines beginning with a
+.Sq #
+are ignored.
+Parameter names and their values are case sensitive.
+.Sh PARAMETERS
+.Ss auth.conf
+SMTP authentication can be configured in
+.Pa auth.conf .
+Each line has the format
+.Dq Li user|smarthost:password .
+.Ss dma.conf
+Most of the behaviour of
+.Nm
+can be configured in
+.Pa dma.conf .
+.Bl -tag -width 4n
+.It Ic SMARTHOST Xo
+(string, default=empty)
+.Xc
+If you want to send outgoing mails via a smarthost, set this variable to
+your smarthosts address.
+.It Ic PORT Xo
+(numeric, default=25)
+.Xc
+Use this port to deliver remote emails.
+Only useful together with the
+.Sq SMARTHOST
+option, because
+.Nm
+will deliver all mails to this port, regardless of whether a smarthost is set
+or not.
+.It Ic ALIASES Xo
+(string, default=/etc/aliases)
+.Xc
+Path to the local aliases file.
+Just stick with the default.
+The aliases file is of the format
+.Dl nam: dest1 dest2 ...
+In this case, mails to
+.Li nam
+will instead be delivered to
+.Li dest1
+and
+.Li dest2 ,
+which in turn could be entries in
+.Pa /etc/aliases .
+The special name
+.Ql *
+can be used to create a catch-all alias, which gets used if no other
+matching alias is found.
+Use the catch-all alias only if you don't want any local mail to be
+delivered.
+.It Ic SPOOLDIR Xo
+(string, default=/var/spool/dma)
+.Xc
+Path to
+.Nm Ap s
+spool directory.
+Just stick with the default.
+.It Ic AUTHPATH Xo
+(string, default=not set)
+.Xc
+Path to the
+.Sq auth.conf
+file.
+.It Ic SECURETRANS Xo
+(boolean, default=commented)
+.Xc
+Uncomment if you want TLS/SSL secured transfer.
+.It Ic STARTTLS Xo
+(boolean, default=commented)
+.Xc
+Uncomment if you want to use STARTTLS.
+Only useful together with
+.Sq SECURETRANS .
+.It Ic OPPORTUNISTIC_TLS Xo
+(boolean, default=commented)
+.Xc
+Uncomment if you want to allow the STARTTLS negotiation to fail.
+Most useful when
+.Nm
+is used without a smarthost, delivering remote messages directly to
+the outside mail exchangers; in opportunistic TLS mode, the connection will
+be encrypted if the remote server supports STARTTLS, but an unencrypted
+delivery will still be made if the negotiation fails.
+Only useful together with
+.Sq SECURETRANS
+and
+.Sq STARTTLS .
+.It Ic CERTFILE Xo
+(string, default=empty)
+.Xc
+Path to your SSL certificate file.
+.It Ic SECURE Xo
+(boolean, default=commented)
+.Xc
+Uncomment this entry and change it to
+.Sq INSECURE
+to use plain text SMTP login over an insecure connection.
+You have to rename this variable manually to prevent that you send your
+password accidentally over an insecure connection.
+.It Ic DEFER Xo
+(boolean, default=commented)
+.Xc
+Uncomment if you want that
+.Nm
+defers your mail.
+You have to flush your mail queue manually with the
+.Fl q
+option.
+This option is handy if you are behind a dialup line.
+.It Ic FULLBOUNCE Xo
+(boolean, default=commented)
+.Xc
+Uncomment if you want the bounce message to include the complete original
+message, not just the headers.
+.It Ic MAILNAME Xo
+(string, default=empty)
+.Xc
+The internet hostname
+.Nm
+uses to identify the host.
+If not set or empty, the result of
+.Xr gethostname 3
+is used.
+If
+.Sq MAILNAME
+is an absolute path to a file, the first line of this file will be used
+as the hostname.
+.It Ic MASQUERADE Xo
+(string, default=empty)
+.Xc
+Masquerade the envelope-from addresses with this address/hostname.
+Use this setting if mails are not accepted by destination mail servers
+because your sender domain is invalid.
+This setting is overridden by the
+.Fl f
+flag and the
+.Ev EMAIL
+environment variable.
+.Pp
+If
+.Sq MASQUERADE
+does not contain a
+.Li @
+sign, the string is interpreted as a host name.
+For example, setting
+.Sq MASQUERADE
+to
+.Ql john@
+on host
+.Ql hamlet
+will send all mails as
+.Ql john@hamlet ;
+setting it to
+.Ql percolator
+will send all mails as
+.Ql Sm off Va username @percolator .
+.Sm on
+.It Ic NULLCLIENT Xo
+.Xc
+Bypass aliases and local delivery, and instead forward all mails to
+the defined
+.Sq SMARTHOST .
+.Sq NULLCLIENT
+requires
+.Sq SMARTHOST
+to be set.
+.El
+.Ss Environment variables
+The behavior of
+.Nm
+can be influenced by some environment variables.
+.Bl -tag -width 4n
+.It Ev EMAIL Xo
+.Xc
+Used to set the sender address (envelope-from).
+Use a plain address, in the form of
+.Li user@example.com .
+This value will be overridden when the
+.Fl f
+flag is used.
+.El
+.Sh SEE ALSO
+.Xr mailaddr 7 ,
+.Xr mailwrapper 8 ,
+.Xr sendmail 8
+.Rs
+.%A "J. B. Postel"
+.%T "Simple Mail Transfer Protocol"
+.%O RFC 821
+.Re
+.Rs
+.%A "J. Myers"
+.%T "SMTP Service Extension for Authentication"
+.%O RFC 2554
+.Re
+.Rs
+.%A "P. Hoffman"
+.%T "SMTP Service Extension for Secure SMTP over TLS"
+.%O RFC 2487
+.Re
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Dx 1.11 .
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was written by
+.An Matthias Schmidt Aq Mt matthias@dragonflybsd.org
+and
+.An Simon Schubert Aq Mt 2@0x2c.org .
diff --git a/contrib/dma/dma.c b/contrib/dma/dma.c
new file mode 100644
index 000000000000..e6462f22544c
--- /dev/null
+++ b/contrib/dma/dma.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "dfcompat.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+
+static void deliver(struct qitem *);
+
+struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
+struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
+struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
+char username[USERNAME_SIZE];
+uid_t useruid;
+const char *logident_base;
+char errmsg[ERRMSG_SIZE];
+
+static int daemonize = 1;
+static int doqueue = 0;
+
+struct config config = {
+	.smarthost	= NULL,
+	.port		= 25,
+	.aliases	= "/etc/aliases",
+	.spooldir	= "/var/spool/dma",
+	.authpath	= NULL,
+	.certfile	= NULL,
+	.features	= 0,
+	.mailname	= NULL,
+	.masquerade_host = NULL,
+	.masquerade_user = NULL,
+};
+
+
+static void
+sighup_handler(int signo)
+{
+	(void)signo;	/* so that gcc doesn't complain */
+}
+
+static char *
+set_from(struct queue *queue, const char *osender)
+{
+	const char *addr;
+	char *sender;
+
+	if (osender) {
+		addr = osender;
+	} else if (getenv("EMAIL") != NULL) {
+		addr = getenv("EMAIL");
+	} else {
+		if (config.masquerade_user)
+			addr = config.masquerade_user;
+		else
+			addr = username;
+	}
+
+	if (!strchr(addr, '@')) {
+		const char *from_host = hostname();
+
+		if (config.masquerade_host)
+			from_host = config.masquerade_host;
+
+		if (asprintf(&sender, "%s@%s", addr, from_host) <= 0)
+			return (NULL);
+	} else {
+		sender = strdup(addr);
+		if (sender == NULL)
+			return (NULL);
+	}
+
+	if (strchr(sender, '\n') != NULL) {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	queue->sender = sender;
+	return (sender);
+}
+
+static int
+read_aliases(void)
+{
+	yyin = fopen(config.aliases, "r");
+	if (yyin == NULL) {
+		/*
+		 * Non-existing aliases file is not a fatal error
+		 */
+		if (errno == ENOENT)
+			return (0);
+		/* Other problems are. */
+		return (-1);
+	}
+	if (yyparse())
+		return (-1);	/* fatal error, probably malloc() */
+	fclose(yyin);
+	return (0);
+}
+
+static int
+do_alias(struct queue *queue, const char *addr)
+{
+	struct alias *al;
+        struct stritem *sit;
+	int aliased = 0;
+
+        LIST_FOREACH(al, &aliases, next) {
+                if (strcmp(al->alias, addr) != 0)
+                        continue;
+		SLIST_FOREACH(sit, &al->dests, next) {
+			if (add_recp(queue, sit->str, EXPAND_ADDR) != 0)
+				return (-1);
+		}
+		aliased = 1;
+        }
+
+        return (aliased);
+}
+
+int
+add_recp(struct queue *queue, const char *str, int expand)
+{
+	struct qitem *it, *tit;
+	struct passwd *pw;
+	char *host;
+	int aliased = 0;
+
+	it = calloc(1, sizeof(*it));
+	if (it == NULL)
+		return (-1);
+	it->addr = strdup(str);
+	if (it->addr == NULL)
+		return (-1);
+
+	it->sender = queue->sender;
+	host = strrchr(it->addr, '@');
+	if (host != NULL &&
+	    (strcmp(host + 1, hostname()) == 0 ||
+	     strcmp(host + 1, "localhost") == 0)) {
+		*host = 0;
+	}
+	LIST_FOREACH(tit, &queue->queue, next) {
+		/* weed out duplicate dests */
+		if (strcmp(tit->addr, it->addr) == 0) {
+			free(it->addr);
+			free(it);
+			return (0);
+		}
+	}
+	LIST_INSERT_HEAD(&queue->queue, it, next);
+
+	/**
+	 * Do local delivery if there is no @.
+	 * Do not do local delivery when NULLCLIENT is set.
+	 */
+	if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) {
+		it->remote = 0;
+		if (expand) {
+			aliased = do_alias(queue, it->addr);
+			if (!aliased && expand == EXPAND_WILDCARD)
+				aliased = do_alias(queue, "*");
+			if (aliased < 0)
+				return (-1);
+			if (aliased) {
+				LIST_REMOVE(it, next);
+			} else {
+				/* Local destination, check */
+				pw = getpwnam(it->addr);
+				if (pw == NULL)
+					goto out;
+				/* XXX read .forward */
+				endpwent();
+			}
+		}
+	} else {
+		it->remote = 1;
+	}
+
+	return (0);
+
+out:
+	free(it->addr);
+	free(it);
+	return (-1);
+}
+
+static struct qitem *
+go_background(struct queue *queue)
+{
+	struct sigaction sa;
+	struct qitem *it;
+	pid_t pid;
+
+	if (daemonize && daemon(0, 0) != 0) {
+		syslog(LOG_ERR, "can not daemonize: %m");
+		exit(1);
+	}
+	daemonize = 0;
+
+	bzero(&sa, sizeof(sa));
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	LIST_FOREACH(it, &queue->queue, next) {
+		/* No need to fork for the last dest */
+		if (LIST_NEXT(it, next) == NULL)
+			goto retit;
+
+		pid = fork();
+		switch (pid) {
+		case -1:
+			syslog(LOG_ERR, "can not fork: %m");
+			exit(1);
+			break;
+
+		case 0:
+			/*
+			 * Child:
+			 *
+			 * return and deliver mail
+			 */
+retit:
+			/*
+			 * If necessary, acquire the queue and * mail files.
+			 * If this fails, we probably were raced by another
+			 * process.  It is okay to be raced if we're supposed
+			 * to flush the queue.
+			 */
+			setlogident("%s", it->queueid);
+			switch (acquirespool(it)) {
+			case 0:
+				break;
+			case 1:
+				if (doqueue)
+					exit(0);
+				syslog(LOG_WARNING, "could not lock queue file");
+				exit(1);
+			default:
+				exit(1);
+			}
+			dropspool(queue, it);
+			return (it);
+
+		default:
+			/*
+			 * Parent:
+			 *
+			 * fork next child
+			 */
+			break;
+		}
+	}
+
+	syslog(LOG_CRIT, "reached dead code");
+	exit(1);
+}
+
+static void
+deliver(struct qitem *it)
+{
+	int error;
+	unsigned int backoff = MIN_RETRY, slept;
+	struct timeval now;
+	struct stat st;
+
+	snprintf(errmsg, sizeof(errmsg), "unknown bounce reason");
+
+retry:
+	syslog(LOG_INFO, "trying delivery");
+
+	if (it->remote)
+		error = deliver_remote(it);
+	else
+		error = deliver_local(it);
+
+	switch (error) {
+	case 0:
+		delqueue(it);
+		syslog(LOG_INFO, "delivery successful");
+		exit(0);
+
+	case 1:
+		if (stat(it->queuefn, &st) != 0) {
+			syslog(LOG_ERR, "lost queue file `%s'", it->queuefn);
+			exit(1);
+		}
+		if (gettimeofday(&now, NULL) == 0 &&
+		    (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
+			snprintf(errmsg, sizeof(errmsg),
+				 "Could not deliver for the last %d seconds. Giving up.",
+				 MAX_TIMEOUT);
+			goto bounce;
+		}
+		for (slept = 0; slept < backoff;) {
+			slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT);
+			if (flushqueue_since(slept)) {
+				backoff = MIN_RETRY;
+				goto retry;
+			}
+		}
+		if (slept >= backoff) {
+			/* pick the next backoff between [1.5, 2.5) times backoff */
+			backoff = backoff + backoff / 2 + random() % backoff;
+			if (backoff > MAX_RETRY)
+				backoff = MAX_RETRY;
+		}
+		goto retry;
+
+	case -1:
+	default:
+		break;
+	}
+
+bounce:
+	bounce(it, errmsg);
+	/* NOTREACHED */
+}
+
+void
+run_queue(struct queue *queue)
+{
+	struct qitem *it;
+
+	if (LIST_EMPTY(&queue->queue))
+		return;
+
+	it = go_background(queue);
+	deliver(it);
+	/* NOTREACHED */
+}
+
+static void
+show_queue(struct queue *queue)
+{
+	struct qitem *it;
+	int locked = 0;	/* XXX */
+
+	if (LIST_EMPTY(&queue->queue)) {
+		printf("Mail queue is empty\n");
+		return;
+	}
+
+	LIST_FOREACH(it, &queue->queue, next) {
+		printf("ID\t: %s%s\n"
+		       "From\t: %s\n"
+		       "To\t: %s\n",
+		       it->queueid,
+		       locked ? "*" : "",
+		       it->sender, it->addr);
+
+		if (LIST_NEXT(it, next) != NULL)
+			printf("--\n");
+	}
+}
+
+/*
+ * TODO:
+ *
+ * - alias processing
+ * - use group permissions
+ * - proper sysexit codes
+ */
+
+int
+main(int argc, char **argv)
+{
+	struct sigaction act;
+	char *sender = NULL;
+	struct queue queue;
+	int i, ch;
+	int nodot = 0, showq = 0, queue_only = 0;
+	int recp_from_header = 0;
+
+	set_username();
+
+	/*
+	 * We never run as root.  If called by root, drop permissions
+	 * to the mail user.
+	 */
+	if (geteuid() == 0 || getuid() == 0) {
+		struct passwd *pw;
+
+		errno = 0;
+		pw = getpwnam(DMA_ROOT_USER);
+		if (pw == NULL) {
+			if (errno == 0)
+				errx(1, "user '%s' not found", DMA_ROOT_USER);
+			else
+				err(1, "cannot drop root privileges");
+		}
+
+		if (setuid(pw->pw_uid) != 0)
+			err(1, "cannot drop root privileges");
+
+		if (geteuid() == 0 || getuid() == 0)
+			errx(1, "cannot drop root privileges");
+	}
+
+	atexit(deltmp);
+	init_random();
+
+	bzero(&queue, sizeof(queue));
+	LIST_INIT(&queue.queue);
+
+	if (strcmp(argv[0], "mailq") == 0) {
+		argv++; argc--;
+		showq = 1;
+		if (argc != 0)
+			errx(1, "invalid arguments");
+		goto skipopts;
+	} else if (strcmp(argv[0], "newaliases") == 0) {
+		logident_base = "dma";
+		setlogident(NULL);
+
+		if (read_aliases() != 0)
+			errx(1, "could not parse aliases file `%s'", config.aliases);
+		exit(0);
+	}
+
+	opterr = 0;
+	while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) {
+		switch (ch) {
+		case 'A':
+			/* -AX is being ignored, except for -A{c,m} */
+			if (optarg[0] == 'c' || optarg[0] == 'm') {
+				break;
+			}
+			/* else FALLTRHOUGH */
+		case 'b':
+			/* -bX is being ignored, except for -bp */
+			if (optarg[0] == 'p') {
+				showq = 1;
+				break;
+			} else if (optarg[0] == 'q') {
+				queue_only = 1;
+				break;
+			}
+			/* else FALLTRHOUGH */
+		case 'D':
+			daemonize = 0;
+			break;
+		case 'L':
+			logident_base = optarg;
+			break;
+		case 'f':
+		case 'r':
+			sender = optarg;
+			break;
+
+		case 't':
+			recp_from_header = 1;
+			break;
+
+		case 'o':
+			/* -oX is being ignored, except for -oi */
+			if (optarg[0] != 'i')
+				break;
+			/* else FALLTRHOUGH */
+		case 'O':
+			break;
+		case 'i':
+			nodot = 1;
+			break;
+
+		case 'q':
+			/* Don't let getopt slup up other arguments */
+			if (optarg && *optarg == '-')
+				optind--;
+			doqueue = 1;
+			break;
+
+		/* Ignored options */
+		case 'B':
+		case 'C':
+		case 'd':
+		case 'F':
+		case 'h':
+		case 'N':
+		case 'n':
+		case 'R':
+		case 'U':
+		case 'V':
+		case 'v':
+		case 'X':
+			break;
+
+		case ':':
+			if (optopt == 'q') {
+				doqueue = 1;
+				break;
+			}
+			/* FALLTHROUGH */
+
+		default:
+			fprintf(stderr, "invalid argument: `-%c'\n", optopt);
+			exit(1);
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	opterr = 1;
+
+	if (argc != 0 && (showq || doqueue))
+		errx(1, "sending mail and queue operations are mutually exclusive");
+
+	if (showq + doqueue > 1)
+		errx(1, "conflicting queue operations");
+
+skipopts:
+	if (logident_base == NULL)
+		logident_base = "dma";
+	setlogident(NULL);
+
+	act.sa_handler = sighup_handler;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	if (sigaction(SIGHUP, &act, NULL) != 0)
+		syslog(LOG_WARNING, "can not set signal handler: %m");
+
+	parse_conf(CONF_PATH "/dma.conf");
+
+	if (config.authpath != NULL)
+		parse_authfile(config.authpath);
+
+	if (showq) {
+		if (load_queue(&queue) < 0)
+			errlog(1, "can not load queue");
+		show_queue(&queue);
+		return (0);
+	}
+
+	if (doqueue) {
+		flushqueue_signal();
+		if (load_queue(&queue) < 0)
+			errlog(1, "can not load queue");
+		run_queue(&queue);
+		return (0);
+	}
+
+	if (read_aliases() != 0)
+		errlog(1, "could not parse aliases file `%s'", config.aliases);
+
+	if ((sender = set_from(&queue, sender)) == NULL)
+		errlog(1, NULL);
+
+	if (newspoolf(&queue) != 0)
+		errlog(1, "can not create temp file in `%s'", config.spooldir);
+
+	setlogident("%s", queue.id);
+
+	for (i = 0; i < argc; i++) {
+		if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0)
+			errlogx(1, "invalid recipient `%s'", argv[i]);
+	}
+
+	if (LIST_EMPTY(&queue.queue) && !recp_from_header)
+		errlogx(1, "no recipients");
+
+	if (readmail(&queue, nodot, recp_from_header) != 0)
+		errlog(1, "can not read mail");
+
+	if (LIST_EMPTY(&queue.queue))
+		errlogx(1, "no recipients");
+
+	if (linkspool(&queue) != 0)
+		errlog(1, "can not create spools");
+
+	/* From here on the mail is safe. */
+
+	if (config.features & DEFER || queue_only)
+		return (0);
+
+	run_queue(&queue);
+
+	/* NOTREACHED */
+	return (0);
+}
diff --git a/contrib/dma/dma.conf b/contrib/dma/dma.conf
new file mode 100644
index 000000000000..1cc2bf5bc843
--- /dev/null
+++ b/contrib/dma/dma.conf
@@ -0,0 +1,66 @@
+# $DragonFly: src/etc/dma/dma.conf,v 1.2 2008/02/04 10:11:41 matthias Exp $
+#
+# Your smarthost (also called relayhost).  Leave blank if you don't want
+# smarthost support.
+# NOTE: on Debian systems this is handled via debconf!
+# Please use dpkg-reconfigure dma to change this value.
+#SMARTHOST
+
+# Use this SMTP port.  Most users will be fine with the default (25)
+#PORT 25
+
+# Path to your alias file.  Just stay with the default.
+#ALIASES /etc/aliases
+
+# Path to your spooldir.  Just stay with the default.
+#SPOOLDIR /var/spool/dma
+
+# SMTP authentication
+#AUTHPATH /etc/dma/auth.conf
+
+# Uncomment if yout want TLS/SSL support
+#SECURETRANSFER
+
+# Uncomment if you want STARTTLS support (only used in combination with
+# SECURETRANSFER)
+#STARTTLS
+
+# Uncomment if you have specified STARTTLS above and it should be allowed
+# to fail ("opportunistic TLS", use an encrypted connection when available
+# but allow an unencrypted one to servers that do not support it)
+#OPPORTUNISTIC_TLS
+
+# Path to your local SSL certificate
+#CERTFILE
+
+# If you want to use plain text SMTP login without using encryption, change
+# the SECURE entry below to INSECURE.  Otherwise plain login will only work
+# over a secure connection.  Use this option with caution.
+#SECURE
+
+# Uncomment if you want to defer your mails.  This is useful if you are
+# behind a dialup line.  You have to submit your mails manually with dma -q
+#DEFER
+
+# Uncomment if you want the bounce message to include the complete original
+# message, not just the headers.
+#FULLBOUNCE
+
+# The internet hostname dma uses to identify the host.
+# If not set or empty, the result of gethostname(2) is used.
+# If MAILNAME is an absolute path to a file, the first line of this file
+# will be used as the hostname.
+#MAILNAME mail.example.net
+
+# Masquerade envelope from addresses with this address/hostname.
+# Use this if mails are not accepted by destination mail servers because
+# your sender domain is invalid.
+# By default, MASQUERADE is not set.
+# Format: MASQUERADE [user@][host]
+# Examples:
+# MASQUERADE john@  on host "hamlet" will send all mails as john@hamlet
+# MASQUERADE percolator  will send mails as $username@percolator, e.g. fish@percolator
+# MASQUERADE herb@ert  will send all mails as herb@ert
+
+# Directly forward the mail to the SMARTHOST bypassing aliases and local delivery
+#NULLCLIENT
diff --git a/contrib/dma/dma.h b/contrib/dma/dma.h
new file mode 100644
index 000000000000..4ce00944a5ee
--- /dev/null
+++ b/contrib/dma/dma.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de> and
+ * Matthias Schmidt <matthias@dragonflybsd.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DMA_H
+#define DMA_H
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <openssl/ssl.h>
+#include <netdb.h>
+
+#define VERSION	"DragonFly Mail Agent " DMA_VERSION
+
+#define BUF_SIZE	2048
+#define ERRMSG_SIZE	200
+#define USERNAME_SIZE	50
+#define MIN_RETRY	300		/* 5 minutes */
+#define MAX_RETRY	(3*60*60)	/* retry at least every 3 hours */
+#define MAX_TIMEOUT	(5*24*60*60)	/* give up after 5 days */
+#define SLEEP_TIMEOUT	30		/* check for queue flush every 30 seconds */
+#ifndef PATH_MAX
+#define PATH_MAX	1024		/* Max path len */
+#endif
+#define	SMTP_PORT	25		/* Default SMTP port */
+#define CON_TIMEOUT	(5*60)		/* Connection timeout per RFC5321 */
+
+#define STARTTLS	0x002		/* StartTLS support */
+#define SECURETRANS	0x004		/* SSL/TLS in general */
+#define NOSSL		0x008		/* Do not use SSL */
+#define DEFER		0x010		/* Defer mails */
+#define INSECURE	0x020		/* Allow plain login w/o encryption */
+#define FULLBOUNCE	0x040		/* Bounce the full message */
+#define TLS_OPP		0x080		/* Opportunistic STARTTLS */
+#define NULLCLIENT	0x100		/* Nullclient support */
+
+#ifndef CONF_PATH
+#error Please define CONF_PATH
+#endif
+
+#ifndef LIBEXEC_PATH
+#error Please define LIBEXEC_PATH
+#endif
+
+#define SPOOL_FLUSHFILE	"flush"
+
+#ifndef DMA_ROOT_USER
+#define DMA_ROOT_USER	"mail"
+#endif
+#ifndef DMA_GROUP
+#define DMA_GROUP	"mail"
+#endif
+
+#ifndef MBOX_STRICT
+#define MBOX_STRICT	0
+#endif
+
+
+struct stritem {
+	SLIST_ENTRY(stritem) next;
+	char *str;
+};
+SLIST_HEAD(strlist, stritem);
+
+struct alias {
+	LIST_ENTRY(alias) next;
+	char *alias;
+	struct strlist dests;
+};
+LIST_HEAD(aliases, alias);
+
+struct qitem {
+	LIST_ENTRY(qitem) next;
+	const char *sender;
+	char *addr;
+	char *queuefn;
+	char *mailfn;
+	char *queueid;
+	FILE *queuef;
+	FILE *mailf;
+	int remote;
+};
+LIST_HEAD(queueh, qitem);
+
+struct queue {
+	struct queueh queue;
+	char *id;
+	FILE *mailf;
+	char *tmpf;
+	const char *sender;
+};
+
+struct config {
+	const char *smarthost;
+	int port;
+	const char *aliases;
+	const char *spooldir;
+	const char *authpath;
+	const char *certfile;
+	int features;
+	const char *mailname;
+	const char *masquerade_host;
+	const char *masquerade_user;
+
+	/* XXX does not belong into config */
+	SSL *ssl;
+};
+
+
+struct authuser {
+	SLIST_ENTRY(authuser) next;
+	char *login;
+	char *password;
+	char *host;
+};
+SLIST_HEAD(authusers, authuser);
+
+
+struct mx_hostentry {
+	char		host[MAXDNAME];
+	char		addr[INET6_ADDRSTRLEN];
+	int		pref;
+	struct addrinfo	ai;
+	struct sockaddr_storage	sa;
+};
+
+
+/* global variables */
+extern struct aliases aliases;
+extern struct config config;
+extern struct strlist tmpfs;
+extern struct authusers authusers;
+extern char username[USERNAME_SIZE];
+extern uid_t useruid;
+extern const char *logident_base;
+
+extern char neterr[ERRMSG_SIZE];
+extern char errmsg[ERRMSG_SIZE];
+
+/* aliases_parse.y */
+int yyparse(void);
+extern FILE *yyin;
+
+/* conf.c */
+void trim_line(char *);
+void parse_conf(const char *);
+void parse_authfile(const char *);
+
+/* crypto.c */
+void hmac_md5(unsigned char *, int, unsigned char *, int, unsigned char *);
+int smtp_auth_md5(int, char *, char *);
+int smtp_init_crypto(int, int);
+
+/* dns.c */
+int dns_get_mx_list(const char *, int, struct mx_hostentry **, int);
+
+/* net.c */
+char *ssl_errstr(void);
+int read_remote(int, int, char *);
+ssize_t send_remote_command(int, const char*, ...)  __attribute__((__nonnull__(2), __format__ (__printf__, 2, 3)));
+int deliver_remote(struct qitem *);
+
+/* base64.c */
+int base64_encode(const void *, int, char **);
+int base64_decode(const char *, void *);
+
+/* dma.c */
+#define EXPAND_ADDR	1
+#define EXPAND_WILDCARD	2
+int add_recp(struct queue *, const char *, int);
+void run_queue(struct queue *);
+
+/* spool.c */
+int newspoolf(struct queue *);
+int linkspool(struct queue *);
+int load_queue(struct queue *);
+void delqueue(struct qitem *);
+int acquirespool(struct qitem *);
+void dropspool(struct queue *, struct qitem *);
+int flushqueue_since(unsigned int);
+int flushqueue_signal(void);
+
+/* local.c */
+int deliver_local(struct qitem *);
+
+/* mail.c */
+void bounce(struct qitem *, const char *);
+int readmail(struct queue *, int, int);
+
+/* util.c */
+const char *hostname(void);
+void setlogident(const char *, ...) __attribute__((__format__ (__printf__, 1, 2)));
+void errlog(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
+void errlogx(int, const char *, ...) __attribute__((__format__ (__printf__, 2, 3)));
+void set_username(void);
+void deltmp(void);
+int do_timeout(int, int);
+int open_locked(const char *, int, ...);
+char *rfc822date(void);
+int strprefixcmp(const char *, const char *);
+void init_random(void);
+
+#endif
diff --git a/contrib/dma/dns.c b/contrib/dma/dns.c
new file mode 100644
index 000000000000..fc5213f1ca60
--- /dev/null
+++ b/contrib/dma/dns.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "dma.h"
+
+static int
+sort_pref(const void *a, const void *b)
+{
+	const struct mx_hostentry *ha = a, *hb = b;
+	int v;
+
+	/* sort increasing by preference primarily */
+	v = ha->pref - hb->pref;
+	if (v != 0)
+		return (v);
+
+	/* sort PF_INET6 before PF_INET */
+	v = - (ha->ai.ai_family - hb->ai.ai_family);
+	return (v);
+}
+
+static int
+add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps)
+{
+	struct addrinfo hints, *res, *res0 = NULL;
+	char servname[10];
+	struct mx_hostentry *p;
+	const int count_inc = 10;
+	int err;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+
+	snprintf(servname, sizeof(servname), "%d", port);
+	err = getaddrinfo(host, servname, &hints, &res0);
+	if (err)
+		return (err == EAI_AGAIN ? 1 : -1);
+
+	for (res = res0; res != NULL; res = res->ai_next) {
+		if (*ps + 1 >= roundup(*ps, count_inc)) {
+			size_t newsz = roundup(*ps + 2, count_inc);
+			*he = reallocf(*he, newsz * sizeof(**he));
+			if (*he == NULL)
+				goto out;
+		}
+
+		p = &(*he)[*ps];
+		strlcpy(p->host, host, sizeof(p->host));
+		p->pref = pref;
+		p->ai = *res;
+		p->ai.ai_addr = NULL;
+		bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen);
+
+		getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen,
+			    p->addr, sizeof(p->addr),
+			    NULL, 0, NI_NUMERICHOST);
+
+		(*ps)++;
+	}
+	freeaddrinfo(res0);
+
+	return (0);
+
+out:
+	if (res0 != NULL)
+		freeaddrinfo(res0);
+	return (1);
+}
+
+int
+dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx)
+{
+	char outname[MAXDNAME];
+	ns_msg msg;
+	ns_rr rr;
+	const char *searchhost;
+	const unsigned char *cp;
+	unsigned char *ans;
+	struct mx_hostentry *hosts = NULL;
+	size_t nhosts = 0;
+	size_t anssz;
+	int pref;
+	int cname_recurse;
+	int have_mx = 0;
+	int err;
+	int i;
+
+	res_init();
+	searchhost = host;
+	cname_recurse = 0;
+
+	anssz = 65536;
+	ans = malloc(anssz);
+	if (ans == NULL)
+		return (1);
+
+	if (no_mx)
+		goto out;
+
+repeat:
+	err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz);
+	if (err < 0) {
+		switch (h_errno) {
+		case NO_DATA:
+			/*
+			 * Host exists, but no MX (or CNAME) entry.
+			 * Not an error, use host name instead.
+			 */
+			goto out;
+		case TRY_AGAIN:
+			/* transient error */
+			goto transerr;
+		case NO_RECOVERY:
+		case HOST_NOT_FOUND:
+		default:
+			errno = ENOENT;
+			goto err;
+		}
+	}
+
+	if (!ns_initparse(ans, anssz, &msg))
+		goto transerr;
+
+	switch (ns_msg_getflag(msg, ns_f_rcode)) {
+	case ns_r_noerror:
+		break;
+	case ns_r_nxdomain:
+		goto err;
+	default:
+		goto transerr;
+	}
+
+	for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) {
+		if (ns_parserr(&msg, ns_s_an, i, &rr))
+			goto transerr;
+
+		cp = ns_rr_rdata(rr);
+
+		switch (ns_rr_type(rr)) {
+		case ns_t_mx:
+			have_mx = 1;
+			pref = ns_get16(cp);
+			cp += 2;
+			err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
+						 cp, outname, sizeof(outname));
+			if (err < 0)
+				goto transerr;
+
+			err = add_host(pref, outname, port, &hosts, &nhosts);
+			if (err == -1)
+				goto err;
+			break;
+
+		case ns_t_cname:
+			err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg),
+						 cp, outname, sizeof(outname));
+			if (err < 0)
+				goto transerr;
+
+			/* Prevent a CNAME loop */
+			if (cname_recurse++ > 10)
+				goto err;
+
+			searchhost = outname;
+			goto repeat;
+
+		default:
+			break;
+		}
+	}
+
+out:
+	err = 0;
+	if (0) {
+transerr:
+		if (nhosts == 0)
+			err = 1;
+	}
+	if (0) {
+err:
+		err = -1;
+	}
+
+	free(ans);
+
+	if (err == 0) {
+		if (!have_mx) {
+			/*
+			 * If we didn't find any MX, use the hostname instead.
+			 */
+			err = add_host(0, host, port, &hosts, &nhosts);
+		} else if (nhosts == 0) {
+			/*
+			 * We did get MX, but couldn't resolve any of them
+			 * due to transient errors.
+			 */
+			err = 1;
+		}
+	}
+
+	if (nhosts > 0) {
+		qsort(hosts, nhosts, sizeof(*hosts), sort_pref);
+		/* terminate list */
+		*hosts[nhosts].host = 0;
+	} else {
+		if (hosts != NULL)
+			free(hosts);
+		hosts = NULL;
+	}
+
+	*he = hosts;
+	return (err);
+
+	free(ans);
+	if (hosts != NULL)
+		free(hosts);
+	return (err);
+}
+
+#if defined(TESTING)
+int
+main(int argc, char **argv)
+{
+	struct mx_hostentry *he, *p;
+	int err;
+
+	err = dns_get_mx_list(argv[1], 53, &he, 0);
+	if (err)
+		return (err);
+
+	for (p = he; *p->host != 0; p++) {
+		printf("%d\t%s\t%s\n", p->pref, p->host, p->addr);
+	}
+
+	return (0);
+}
+#endif
diff --git a/contrib/dma/get-version.sh b/contrib/dma/get-version.sh
new file mode 100755
index 000000000000..aecc3331f446
--- /dev/null
+++ b/contrib/dma/get-version.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+gitver=$(git describe 2>/dev/null | tr - .)
+filever=$(cat VERSION)
+
+version=${gitver}
+: ${version:=$filever}
+
+echo "$version"
diff --git a/contrib/dma/local.c b/contrib/dma/local.c
new file mode 100644
index 000000000000..6a6407ef8652
--- /dev/null
+++ b/contrib/dma/local.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+static int
+create_mbox(const char *name)
+{
+	struct sigaction sa, osa;
+	pid_t child, waitchild;
+	int status;
+	int i;
+	long maxfd;
+	int e;
+	int r = -1;
+
+	/*
+	 * We need to enable SIGCHLD temporarily so that waitpid works.
+	 */
+	bzero(&sa, sizeof(sa));
+	sa.sa_handler = SIG_DFL;
+	sigaction(SIGCHLD, &sa, &osa);
+
+	do_timeout(100, 0);
+
+	child = fork();
+	switch (child) {
+	case 0:
+		/* child */
+		maxfd = sysconf(_SC_OPEN_MAX);
+		if (maxfd == -1)
+			maxfd = 1024;	/* what can we do... */
+
+		for (i = 3; i <= maxfd; ++i)
+			close(i);
+
+		execl(LIBEXEC_PATH "/dma-mbox-create", "dma-mbox-create", name, NULL);
+		syslog(LOG_ERR, "cannot execute "LIBEXEC_PATH"/dma-mbox-create: %m");
+		exit(1);
+
+	default:
+		/* parent */
+		waitchild = waitpid(child, &status, 0);
+
+		e = errno;
+
+		do_timeout(0, 0);
+
+		if (waitchild == -1 && e == EINTR) {
+			syslog(LOG_ERR, "hung child while creating mbox `%s': %m", name);
+			break;
+		}
+
+		if (waitchild == -1) {
+			syslog(LOG_ERR, "child disappeared while creating mbox `%s': %m", name);
+			break;
+		}
+
+		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+			syslog(LOG_ERR, "error creating mbox `%s'", name);
+			break;
+		}
+
+		/* success */
+		r = 0;
+		break;
+
+	case -1:
+		/* error */
+		syslog(LOG_ERR, "error creating mbox");
+		break;
+	}
+
+	sigaction(SIGCHLD, &osa, NULL);
+
+	return (r);
+}
+
+int
+deliver_local(struct qitem *it)
+{
+	char fn[PATH_MAX+1];
+	char line[1000];
+	const char *sender;
+	const char *newline = "\n";
+	size_t linelen;
+	int tries = 0;
+	int mbox;
+	int error;
+	int hadnl = 0;
+	off_t mboxlen;
+	time_t now = time(NULL);
+
+	error = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, it->addr);
+	if (error < 0 || (size_t)error >= sizeof(fn)) {
+		syslog(LOG_NOTICE, "local delivery deferred: %m");
+		return (1);
+	}
+
+retry:
+	/* wait for a maximum of 100s to get the lock to the file */
+	do_timeout(100, 0);
+
+	/* don't use O_CREAT here, because we might be running as the wrong user. */
+	mbox = open_locked(fn, O_WRONLY|O_APPEND);
+	if (mbox < 0) {
+		int e = errno;
+
+		do_timeout(0, 0);
+
+		switch (e) {
+		case EACCES:
+		case ENOENT:
+			/*
+			 * The file does not exist or we can't access it.
+			 * Call dma-mbox-create to create it and fix permissions.
+			 */
+			if (tries > 0 || create_mbox(it->addr) != 0) {
+				syslog(LOG_ERR, "local delivery deferred: can not create `%s'", fn);
+				return (1);
+			}
+			++tries;
+			goto retry;
+
+		case EINTR:
+			syslog(LOG_NOTICE, "local delivery deferred: can not lock `%s'", fn);
+			break;
+
+		default:
+			syslog(LOG_NOTICE, "local delivery deferred: can not open `%s': %m", fn);
+			break;
+		}
+		return (1);
+	}
+	do_timeout(0, 0);
+
+	mboxlen = lseek(mbox, 0, SEEK_END);
+
+	/* New mails start with \nFrom ...., unless we're at the beginning of the mbox */
+	if (mboxlen == 0)
+		newline = "";
+
+	/* If we're bouncing a message, claim it comes from MAILER-DAEMON */
+	sender = it->sender;
+	if (strcmp(sender, "") == 0)
+		sender = "MAILER-DAEMON";
+
+	if (fseek(it->mailf, 0, SEEK_SET) != 0) {
+		syslog(LOG_NOTICE, "local delivery deferred: can not seek: %m");
+		goto out;
+	}
+
+	error = snprintf(line, sizeof(line), "%sFrom %s\t%s", newline, sender, ctime(&now));
+	if (error < 0 || (size_t)error >= sizeof(line)) {
+		syslog(LOG_NOTICE, "local delivery deferred: can not write header: %m");
+		goto out;
+	}
+	if (write(mbox, line, error) != error)
+		goto wrerror;
+
+	while (!feof(it->mailf)) {
+		if (fgets(line, sizeof(line), it->mailf) == NULL)
+			break;
+		linelen = strlen(line);
+		if (linelen == 0 || line[linelen - 1] != '\n') {
+			syslog(LOG_CRIT, "local delivery failed: corrupted queue file");
+			snprintf(errmsg, sizeof(errmsg), "corrupted queue file");
+			error = -1;
+			goto chop;
+		}
+
+		/*
+		 * mboxro processing:
+		 * - escape lines that start with "From " with a > sign.
+		 * - be reversable by escaping lines that contain an arbitrary
+		 *   number of > signs, followed by "From ", i.e. />*From / in regexp.
+		 * - strict mbox processing only requires escaping after empty lines,
+		 *   yet most MUAs seem to relax this requirement and will treat any
+		 *   line starting with "From " as the beginning of a new mail.
+		 */
+		if ((!MBOX_STRICT || hadnl) &&
+		    strncmp(&line[strspn(line, ">")], "From ", 5) == 0) {
+			const char *gt = ">";
+
+			if (write(mbox, gt, 1) != 1)
+				goto wrerror;
+			hadnl = 0;
+		} else if (strcmp(line, "\n") == 0) {
+			hadnl = 1;
+		} else {
+			hadnl = 0;
+		}
+		if ((size_t)write(mbox, line, linelen) != linelen)
+			goto wrerror;
+	}
+	close(mbox);
+	return (0);
+
+wrerror:
+	syslog(LOG_ERR, "local delivery failed: write error: %m");
+	error = 1;
+chop:
+	if (ftruncate(mbox, mboxlen) != 0)
+		syslog(LOG_WARNING, "error recovering mbox `%s': %m", fn);
+out:
+	close(mbox);
+	return (error);
+}
diff --git a/contrib/dma/mail.c b/contrib/dma/mail.c
new file mode 100644
index 000000000000..9cbde41d98f1
--- /dev/null
+++ b/contrib/dma/mail.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+void
+bounce(struct qitem *it, const char *reason)
+{
+	struct queue bounceq;
+	char line[1000];
+	size_t pos;
+	int error;
+
+	/* Don't bounce bounced mails */
+	if (it->sender[0] == 0) {
+		syslog(LOG_INFO, "can not bounce a bounce message, discarding");
+		exit(1);
+	}
+
+	bzero(&bounceq, sizeof(bounceq));
+	LIST_INIT(&bounceq.queue);
+	bounceq.sender = "";
+	if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0)
+		goto fail;
+
+	if (newspoolf(&bounceq) != 0)
+		goto fail;
+
+	syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
+	setlogident("%s", bounceq.id);
+
+	error = fprintf(bounceq.mailf,
+		"Received: from MAILER-DAEMON\n"
+		"\tid %s\n"
+		"\tby %s (%s);\n"
+		"\t%s\n"
+		"X-Original-To: <%s>\n"
+		"From: MAILER-DAEMON <>\n"
+		"To: %s\n"
+		"Subject: Mail delivery failed\n"
+		"Message-Id: <%s@%s>\n"
+		"Date: %s\n"
+		"\n"
+		"This is the %s at %s.\n"
+		"\n"
+		"There was an error delivering your mail to <%s>.\n"
+		"\n"
+		"%s\n"
+		"\n"
+		"%s\n"
+		"\n",
+		bounceq.id,
+		hostname(), VERSION,
+		rfc822date(),
+		it->addr,
+		it->sender,
+		bounceq.id, hostname(),
+		rfc822date(),
+		VERSION, hostname(),
+		it->addr,
+		reason,
+		config.features & FULLBOUNCE ?
+		    "Original message follows." :
+		    "Message headers follow.");
+	if (error < 0)
+		goto fail;
+
+	if (fseek(it->mailf, 0, SEEK_SET) != 0)
+		goto fail;
+	if (config.features & FULLBOUNCE) {
+		while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
+			if (fwrite(line, 1, pos, bounceq.mailf) != pos)
+				goto fail;
+		}
+	} else {
+		while (!feof(it->mailf)) {
+			if (fgets(line, sizeof(line), it->mailf) == NULL)
+				break;
+			if (line[0] == '\n')
+				break;
+			if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
+				goto fail;
+		}
+	}
+
+	if (linkspool(&bounceq) != 0)
+		goto fail;
+	/* bounce is safe */
+
+	delqueue(it);
+
+	run_queue(&bounceq);
+	/* NOTREACHED */
+
+fail:
+	syslog(LOG_CRIT, "error creating bounce: %m");
+	delqueue(it);
+	exit(1);
+}
+
+struct parse_state {
+	char addr[1000];
+	int pos;
+
+	enum {
+		NONE = 0,
+		START,
+		MAIN,
+		EOL,
+		QUIT
+	} state;
+	int comment;
+	int quote;
+	int brackets;
+	int esc;
+};
+
+/*
+ * Simplified RFC2822 header/address parsing.
+ * We copy escapes and quoted strings directly, since
+ * we have to pass them like this to the mail server anyways.
+ * XXX local addresses will need treatment
+ */
+static int
+parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
+{
+	char *addr;
+
+again:
+	switch (ps->state) {
+	case NONE:
+		return (-1);
+
+	case START:
+		/* init our data */
+		bzero(ps, sizeof(*ps));
+
+		/* skip over header name */
+		while (*s != ':')
+			s++;
+		s++;
+		ps->state = MAIN;
+		break;
+
+	case MAIN:
+		/* all fine */
+		break;
+
+	case EOL:
+		switch (*s) {
+		case ' ':
+		case '\t':
+			s++;
+			/* continue */
+			break;
+
+		default:
+			ps->state = QUIT;
+			if (ps->pos != 0)
+				goto newaddr;
+			return (0);
+		}
+
+	case QUIT:
+		return (0);
+	}
+
+	for (; *s != 0; s++) {
+		if (ps->esc) {
+			ps->esc = 0;
+
+			switch (*s) {
+			case '\r':
+			case '\n':
+				goto err;
+
+			default:
+				goto copy;
+			}
+		}
+
+		if (ps->quote) {
+			switch (*s) {
+			case '"':
+				ps->quote = 0;
+				goto copy;
+
+			case '\\':
+				ps->esc = 1;
+				goto copy;
+
+			case '\r':
+			case '\n':
+				goto eol;
+
+			default:
+				goto copy;
+			}
+		}
+
+		switch (*s) {
+		case '(':
+			ps->comment++;
+			break;
+
+		case ')':
+			if (ps->comment)
+				ps->comment--;
+			else
+				goto err;
+			goto skip;
+
+		case '"':
+			ps->quote = 1;
+			goto copy;
+
+		case '\\':
+			ps->esc = 1;
+			goto copy;
+
+		case '\r':
+		case '\n':
+			goto eol;
+		}
+
+		if (ps->comment)
+			goto skip;
+
+		switch (*s) {
+		case ' ':
+		case '\t':
+			/* ignore whitespace */
+			goto skip;
+
+		case '<':
+			/* this is the real address now */
+			ps->brackets = 1;
+			ps->pos = 0;
+			goto skip;
+
+		case '>':
+			if (!ps->brackets)
+				goto err;
+			ps->brackets = 0;
+
+			s++;
+			goto newaddr;
+
+		case ':':
+			/* group - ignore */
+			ps->pos = 0;
+			goto skip;
+
+		case ',':
+		case ';':
+			/*
+			 * Next address, copy previous one.
+			 * However, we might be directly after
+			 * a <address>, or have two consecutive
+			 * commas.
+			 * Skip the comma unless there is
+			 * really something to copy.
+			 */
+			if (ps->pos == 0)
+				goto skip;
+			s++;
+			goto newaddr;
+
+		default:
+			goto copy;
+		}
+
+copy:
+		if (ps->comment)
+			goto skip;
+
+		if (ps->pos + 1 == sizeof(ps->addr))
+			goto err;
+		ps->addr[ps->pos++] = *s;
+
+skip:
+		;
+	}
+
+eol:
+	ps->state = EOL;
+	return (0);
+
+err:
+	ps->state = QUIT;
+	return (-1);
+
+newaddr:
+	ps->addr[ps->pos] = 0;
+	ps->pos = 0;
+	addr = strdup(ps->addr);
+	if (addr == NULL)
+		errlog(1, NULL);
+
+	if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
+		errlogx(1, "invalid recipient `%s'", addr);
+
+	goto again;
+}
+
+int
+readmail(struct queue *queue, int nodot, int recp_from_header)
+{
+	struct parse_state parse_state;
+	char line[1000];	/* by RFC2822 */
+	size_t linelen;
+	size_t error;
+	int had_headers = 0;
+	int had_from = 0;
+	int had_messagid = 0;
+	int had_date = 0;
+	int had_last_line = 0;
+	int nocopy = 0;
+
+	parse_state.state = NONE;
+
+	error = fprintf(queue->mailf,
+		"Received: from %s (uid %d)\n"
+		"\t(envelope-from %s)\n"
+		"\tid %s\n"
+		"\tby %s (%s);\n"
+		"\t%s\n",
+		username, useruid,
+		queue->sender,
+		queue->id,
+		hostname(), VERSION,
+		rfc822date());
+	if ((ssize_t)error < 0)
+		return (-1);
+
+	while (!feof(stdin)) {
+		if (fgets(line, sizeof(line) - 1, stdin) == NULL)
+			break;
+		if (had_last_line)
+			errlogx(1, "bad mail input format");
+		linelen = strlen(line);
+		if (linelen == 0 || line[linelen - 1] != '\n') {
+			/*
+			 * This line did not end with a newline character.
+			 * If we fix it, it better be the last line of
+			 * the file.
+			 */
+			line[linelen] = '\n';
+			line[linelen + 1] = 0;
+			had_last_line = 1;
+		}
+		if (!had_headers) {
+			/*
+			 * Unless this is a continuation, switch of
+			 * the Bcc: nocopy flag.
+			 */
+			if (!(line[0] == ' ' || line[0] == '\t'))
+				nocopy = 0;
+
+			if (strprefixcmp(line, "Date:") == 0)
+				had_date = 1;
+			else if (strprefixcmp(line, "Message-Id:") == 0)
+				had_messagid = 1;
+			else if (strprefixcmp(line, "From:") == 0)
+				had_from = 1;
+			else if (strprefixcmp(line, "Bcc:") == 0)
+				nocopy = 1;
+
+			if (parse_state.state != NONE) {
+				if (parse_addrs(&parse_state, line, queue) < 0) {
+					errlogx(1, "invalid address in header\n");
+					/* NOTREACHED */
+				}
+			}
+
+			if (recp_from_header && (
+					strprefixcmp(line, "To:") == 0 ||
+					strprefixcmp(line, "Cc:") == 0 ||
+					strprefixcmp(line, "Bcc:") == 0)) {
+				parse_state.state = START;
+				if (parse_addrs(&parse_state, line, queue) < 0) {
+					errlogx(1, "invalid address in header\n");
+					/* NOTREACHED */
+				}
+			}
+		}
+
+		if (strcmp(line, "\n") == 0 && !had_headers) {
+			had_headers = 1;
+			while (!had_date || !had_messagid || !had_from) {
+				if (!had_date) {
+					had_date = 1;
+					snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
+				} else if (!had_messagid) {
+					/* XXX msgid, assign earlier and log? */
+					had_messagid = 1;
+					snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
+						 (uintmax_t)time(NULL),
+						 queue->id,
+						 (uintmax_t)random(),
+						 hostname());
+				} else if (!had_from) {
+					had_from = 1;
+					snprintf(line, sizeof(line), "From: <%s>\n", queue->sender);
+				}
+				if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
+					return (-1);
+			}
+			strcpy(line, "\n");
+		}
+		if (!nodot && linelen == 2 && line[0] == '.')
+			break;
+		if (!nocopy) {
+			if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
+				return (-1);
+		}
+	}
+
+	return (0);
+}
diff --git a/contrib/dma/net.c b/contrib/dma/net.c
new file mode 100644
index 000000000000..d95a7884b87e
--- /dev/null
+++ b/contrib/dma/net.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
+ * Germany.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "dfcompat.h"
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+char neterr[ERRMSG_SIZE];
+
+char *
+ssl_errstr(void)
+{
+	long oerr, nerr;
+
+	oerr = 0;
+	while ((nerr = ERR_get_error()) != 0)
+		oerr = nerr;
+
+	return (ERR_error_string(oerr, NULL));
+}
+
+ssize_t
+send_remote_command(int fd, const char* fmt, ...)
+{
+	va_list va;
+	char cmd[4096];
+	size_t len, pos;
+	int s;
+	ssize_t n;
+
+	va_start(va, fmt);
+	s = vsnprintf(cmd, sizeof(cmd) - 2, fmt, va);
+	va_end(va);
+	if (s == sizeof(cmd) - 2 || s < 0) {
+		strcpy(neterr, "Internal error: oversized command string");
+		return (-1);
+	}
+
+	/* We *know* there are at least two more bytes available */
+	strcat(cmd, "\r\n");
+	len = strlen(cmd);
+
+	if (((config.features & SECURETRANS) != 0) &&
+	    ((config.features & NOSSL) == 0)) {
+		while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) {
+			s = SSL_get_error(config.ssl, s);
+			if (s != SSL_ERROR_WANT_READ &&
+			    s != SSL_ERROR_WANT_WRITE) {
+				strncpy(neterr, ssl_errstr(), sizeof(neterr));
+				return (-1);
+			}
+		}
+	}
+	else {
+		pos = 0;
+		while (pos < len) {
+			n = write(fd, cmd + pos, len - pos);
+			if (n < 0)
+				return (-1);
+			pos += n;
+		}
+	}
+
+	return (len);
+}
+
+int
+read_remote(int fd, int extbufsize, char *extbuf)
+{
+	ssize_t rlen = 0;
+	size_t pos, len, copysize;
+	char buff[BUF_SIZE];
+	int done = 0, status = 0, status_running = 0, extbufpos = 0;
+	enum { parse_status, parse_spacedash, parse_rest } parsestate;
+
+	if (do_timeout(CON_TIMEOUT, 1) != 0) {
+		snprintf(neterr, sizeof(neterr), "Timeout reached");
+		return (-1);
+	}
+
+	/*
+	 * Remote reading code from femail.c written by Henning Brauer of
+	 * OpenBSD and released under a BSD style license.
+	 */
+	len = 0;
+	pos = 0;
+	parsestate = parse_status;
+	neterr[0] = 0;
+	while (!(done && parsestate == parse_status)) {
+		rlen = 0;
+		if (pos == 0 ||
+		    (pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) {
+			memmove(buff, buff + pos, len - pos);
+			len -= pos;
+			pos = 0;
+			if (((config.features & SECURETRANS) != 0) &&
+			    (config.features & NOSSL) == 0) {
+				if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) {
+					strncpy(neterr, ssl_errstr(), sizeof(neterr));
+					goto error;
+				}
+			} else {
+				if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) {
+					strncpy(neterr, strerror(errno), sizeof(neterr));
+					goto error;
+				}
+			}
+			len += rlen;
+
+			copysize = sizeof(neterr) - strlen(neterr) - 1;
+			if (copysize > len)
+				copysize = len;
+			strncat(neterr, buff, copysize);
+		}
+		/*
+		 * If there is an external buffer with a size bigger than zero
+		 * and as long as there is space in the external buffer and
+		 * there are new characters read from the mailserver
+		 * copy them to the external buffer
+		 */
+		if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) {
+			/* do not write over the bounds of the buffer */
+			if(extbufpos + rlen > (extbufsize - 1)) {
+				rlen = extbufsize - extbufpos;
+			}
+			memcpy(extbuf + extbufpos, buff + len - rlen, rlen);
+			extbufpos += rlen;
+		}
+
+		if (pos == len)
+			continue;
+
+		switch (parsestate) {
+		case parse_status:
+			for (; pos < len; pos++) {
+				if (isdigit(buff[pos])) {
+					status_running = status_running * 10 + (buff[pos] - '0');
+				} else {
+					status = status_running;
+					status_running = 0;
+					parsestate = parse_spacedash;
+					break;
+				}
+			}
+			continue;
+
+		case parse_spacedash:
+			switch (buff[pos]) {
+			case ' ':
+				done = 1;
+				break;
+
+			case '-':
+				/* ignore */
+				/* XXX read capabilities */
+				break;
+
+			default:
+				strcpy(neterr, "invalid syntax in reply from server");
+				goto error;
+			}
+
+			pos++;
+			parsestate = parse_rest;
+			continue;
+
+		case parse_rest:
+			/* skip up to \n */
+			for (; pos < len; pos++) {
+				if (buff[pos] == '\n') {
+					pos++;
+					parsestate = parse_status;
+					break;
+				}
+			}
+		}
+
+	}
+
+	do_timeout(0, 0);
+
+	/* chop off trailing newlines */
+	while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0)
+		neterr[strlen(neterr) - 1] = 0;
+
+	return (status/100);
+
+error:
+	do_timeout(0, 0);
+	return (-1);
+}
+
+/*
+ * Handle SMTP authentication
+ */
+static int
+smtp_login(int fd, char *login, char* password)
+{
+	char *temp;
+	int len, res = 0;
+
+	res = smtp_auth_md5(fd, login, password);
+	if (res == 0) {
+		return (0);
+	} else if (res == -2) {
+	/*
+	 * If the return code is -2, then then the login attempt failed,
+	 * do not try other login mechanisms
+	 */
+		return (1);
+	}
+
+	if ((config.features & INSECURE) != 0 ||
+	    (config.features & SECURETRANS) != 0) {
+		/* Send AUTH command according to RFC 2554 */
+		send_remote_command(fd, "AUTH LOGIN");
+		if (read_remote(fd, 0, NULL) != 3) {
+			syslog(LOG_NOTICE, "remote delivery deferred:"
+					" AUTH login not available: %s",
+					neterr);
+			return (1);
+		}
+
+		len = base64_encode(login, strlen(login), &temp);
+		if (len < 0) {
+encerr:
+			syslog(LOG_ERR, "can not encode auth reply: %m");
+			return (1);
+		}
+
+		send_remote_command(fd, "%s", temp);
+		free(temp);
+		res = read_remote(fd, 0, NULL);
+		if (res != 3) {
+			syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s",
+			       res == 5 ? "failed" : "deferred", neterr);
+			return (res == 5 ? -1 : 1);
+		}
+
+		len = base64_encode(password, strlen(password), &temp);
+		if (len < 0)
+			goto encerr;
+
+		send_remote_command(fd, "%s", temp);
+		free(temp);
+		res = read_remote(fd, 0, NULL);
+		if (res != 2) {
+			syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s",
+					res == 5 ? "failed" : "deferred", neterr);
+			return (res == 5 ? -1 : 1);
+		}
+	} else {
+		syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. ");
+		return (1);
+	}
+
+	return (0);
+}
+
+static int
+open_connection(struct mx_hostentry *h)
+{
+	int fd;
+
+	syslog(LOG_INFO, "trying remote delivery to %s [%s] pref %d",
+	       h->host, h->addr, h->pref);
+
+	fd = socket(h->ai.ai_family, h->ai.ai_socktype, h->ai.ai_protocol);
+	if (fd < 0) {
+		syslog(LOG_INFO, "socket for %s [%s] failed: %m",
+		       h->host, h->addr);
+		return (-1);
+	}
+
+	if (connect(fd, (struct sockaddr *)&h->sa, h->ai.ai_addrlen) < 0) {
+		syslog(LOG_INFO, "connect to %s [%s] failed: %m",
+		       h->host, h->addr);
+		close(fd);
+		return (-1);
+	}
+
+	return (fd);
+}
+
+static void
+close_connection(int fd)
+{
+	if (config.ssl != NULL) {
+		if (((config.features & SECURETRANS) != 0) &&
+		    ((config.features & NOSSL) == 0))
+			SSL_shutdown(config.ssl);
+		SSL_free(config.ssl);
+	}
+
+	close(fd);
+}
+
+static int
+deliver_to_host(struct qitem *it, struct mx_hostentry *host)
+{
+	struct authuser *a;
+	char line[1000];
+	size_t linelen;
+	int fd, error = 0, do_auth = 0, res = 0;
+
+	if (fseek(it->mailf, 0, SEEK_SET) != 0) {
+		snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno));
+		return (-1);
+	}
+
+	fd = open_connection(host);
+	if (fd < 0)
+		return (1);
+
+#define READ_REMOTE_CHECK(c, exp)	\
+	res = read_remote(fd, 0, NULL); \
+	if (res == 5) { \
+		syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \
+		       host->host, host->addr, c, neterr); \
+		snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \
+			 host->host, host->addr, c, neterr); \
+		return (-1); \
+	} else if (res != exp) { \
+		syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \
+		       host->host, host->addr, c, neterr); \
+		return (1); \
+	}
+
+	/* Check first reply from remote host */
+	if ((config.features & SECURETRANS) == 0 ||
+	    (config.features & STARTTLS) != 0) {
+		config.features |= NOSSL;
+		READ_REMOTE_CHECK("connect", 2);
+
+		config.features &= ~NOSSL;
+	}
+
+	if ((config.features & SECURETRANS) != 0) {
+		error = smtp_init_crypto(fd, config.features);
+		if (error == 0)
+			syslog(LOG_DEBUG, "SSL initialization successful");
+		else
+			goto out;
+
+		if ((config.features & STARTTLS) == 0)
+			READ_REMOTE_CHECK("connect", 2);
+	}
+
+	/* XXX allow HELO fallback */
+	/* XXX record ESMTP keywords */
+	send_remote_command(fd, "EHLO %s", hostname());
+	READ_REMOTE_CHECK("EHLO", 2);
+
+	/*
+	 * Use SMTP authentication if the user defined an entry for the remote
+	 * or smarthost
+	 */
+	SLIST_FOREACH(a, &authusers, next) {
+		if (strcmp(a->host, host->host) == 0) {
+			do_auth = 1;
+			break;
+		}
+	}
+
+	if (do_auth == 1) {
+		/*
+		 * Check if the user wants plain text login without using
+		 * encryption.
+		 */
+		syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
+		error = smtp_login(fd, a->login, a->password);
+		if (error < 0) {
+			syslog(LOG_ERR, "remote delivery failed:"
+					" SMTP login failed: %m");
+			snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host);
+			return (-1);
+		}
+		/* SMTP login is not available, so try without */
+		else if (error > 0) {
+			syslog(LOG_WARNING, "SMTP login not available. Trying without.");
+		}
+	}
+
+	/* XXX send ESMTP ENVID, RET (FULL/HDRS) and 8BITMIME */
+	send_remote_command(fd, "MAIL FROM:<%s>", it->sender);
+	READ_REMOTE_CHECK("MAIL FROM", 2);
+
+	/* XXX send ESMTP ORCPT */
+	send_remote_command(fd, "RCPT TO:<%s>", it->addr);
+	READ_REMOTE_CHECK("RCPT TO", 2);
+
+	send_remote_command(fd, "DATA");
+	READ_REMOTE_CHECK("DATA", 3);
+
+	error = 0;
+	while (!feof(it->mailf)) {
+		if (fgets(line, sizeof(line), it->mailf) == NULL)
+			break;
+		linelen = strlen(line);
+		if (linelen == 0 || line[linelen - 1] != '\n') {
+			syslog(LOG_CRIT, "remote delivery failed: corrupted queue file");
+			snprintf(errmsg, sizeof(errmsg), "corrupted queue file");
+			error = -1;
+			goto out;
+		}
+
+		/* Remove trailing \n's and escape leading dots */
+		trim_line(line);
+
+		/*
+		 * If the first character is a dot, we escape it so the line
+		 * length increases
+		*/
+		if (line[0] == '.')
+			linelen++;
+
+		if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) {
+			syslog(LOG_NOTICE, "remote delivery deferred: write error");
+			error = 1;
+			goto out;
+		}
+	}
+
+	send_remote_command(fd, ".");
+	READ_REMOTE_CHECK("final DATA", 2);
+
+	send_remote_command(fd, "QUIT");
+	if (read_remote(fd, 0, NULL) != 2)
+		syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
+out:
+
+	close_connection(fd);
+	return (error);
+}
+
+int
+deliver_remote(struct qitem *it)
+{
+	struct mx_hostentry *hosts, *h;
+	const char *host;
+	int port;
+	int error = 1, smarthost = 0;
+
+	port = SMTP_PORT;
+
+	/* Smarthost support? */
+	if (config.smarthost != NULL) {
+		host = config.smarthost;
+		port = config.port;
+		syslog(LOG_INFO, "using smarthost (%s:%i)", host, port);
+		smarthost = 1;
+	} else {
+		host = strrchr(it->addr, '@');
+		/* Should not happen */
+		if (host == NULL) {
+			snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s",
+				 it->addr);
+			return(-1);
+		} else {
+			/* Step over the @ */
+			host++;
+		}
+	}
+
+	error = dns_get_mx_list(host, port, &hosts, smarthost);
+	if (error) {
+		snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host);
+		syslog(LOG_NOTICE, "remote delivery %s: DNS lookup failure: host %s not found",
+		       error < 0 ? "failed" : "deferred",
+		       host);
+		return (error);
+	}
+
+	for (h = hosts; *h->host != 0; h++) {
+		switch (deliver_to_host(it, h)) {
+		case 0:
+			/* success */
+			error = 0;
+			goto out;
+		case 1:
+			/* temp failure */
+			error = 1;
+			break;
+		default:
+			/* perm failure */
+			error = -1;
+			goto out;
+		}
+	}
+out:
+	free(hosts);
+
+	return (error);
+}
diff --git a/contrib/dma/spool.c b/contrib/dma/spool.c
new file mode 100644
index 000000000000..416b5fa8f0ee
--- /dev/null
+++ b/contrib/dma/spool.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "dfcompat.h"
+
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "dma.h"
+
+/*
+ * Spool file format:
+ *
+ * 'Q'id files (queue):
+ *   Organized like an RFC822 header, field: value.  Ignores unknown fields.
+ *   ID: id
+ *   Sender: envelope-from
+ *   Recipient: envelope-to
+ *
+ * 'M'id files (data):
+ *   mail data
+ *
+ * Each queue file needs to have a corresponding data file.
+ * One data file might be shared by linking it several times.
+ *
+ * Queue ids are unique, formed from the inode of the data file
+ * and a unique identifier.
+ */
+
+int
+newspoolf(struct queue *queue)
+{
+	char fn[PATH_MAX+1];
+	struct stat st;
+	struct stritem *t;
+	int fd;
+
+	if (snprintf(fn, sizeof(fn), "%s/%s", config.spooldir, "tmp_XXXXXXXXXX") <= 0)
+		return (-1);
+
+	fd = mkstemp(fn);
+	if (fd < 0)
+		return (-1);
+	/* XXX group rights */
+	if (fchmod(fd, 0660) < 0)
+		goto fail;
+	if (flock(fd, LOCK_EX) == -1)
+		goto fail;
+	queue->tmpf = strdup(fn);
+	if (queue->tmpf == NULL)
+		goto fail;
+
+	/*
+	 * Assign queue id
+	 */
+	if (fstat(fd, &st) != 0)
+		goto fail;
+	if (asprintf(&queue->id, "%"PRIxMAX, (uintmax_t)st.st_ino) < 0)
+		goto fail;
+
+	queue->mailf = fdopen(fd, "r+");
+	if (queue->mailf == NULL)
+		goto fail;
+
+	t = malloc(sizeof(*t));
+	if (t != NULL) {
+		t->str = queue->tmpf;
+		SLIST_INSERT_HEAD(&tmpfs, t, next);
+	}
+	return (0);
+
+fail:
+	if (queue->mailf != NULL)
+		fclose(queue->mailf);
+	close(fd);
+	unlink(fn);
+	return (-1);
+}
+
+static int
+writequeuef(struct qitem *it)
+{
+	int error;
+	int queuefd;
+
+	queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0660);
+	if (queuefd == -1)
+		return (-1);
+	if (fchmod(queuefd, 0660) < 0)
+		return (-1);
+	it->queuef = fdopen(queuefd, "w+");
+	if (it->queuef == NULL)
+		return (-1);
+
+	error = fprintf(it->queuef,
+			"ID: %s\n"
+			"Sender: %s\n"
+			"Recipient: %s\n",
+			 it->queueid,
+			 it->sender,
+			 it->addr);
+
+	if (error <= 0)
+		return (-1);
+
+	if (fflush(it->queuef) != 0 || fsync(fileno(it->queuef)) != 0)
+		return (-1);
+
+	return (0);
+}
+
+static struct qitem *
+readqueuef(struct queue *queue, char *queuefn)
+{
+	char line[1000];
+	struct queue itmqueue;
+	FILE *queuef = NULL;
+	char *s;
+	char *queueid = NULL, *sender = NULL, *addr = NULL;
+	struct qitem *it = NULL;
+
+	bzero(&itmqueue, sizeof(itmqueue));
+	LIST_INIT(&itmqueue.queue);
+
+	queuef = fopen(queuefn, "r");
+	if (queuef == NULL)
+		goto out;
+
+	while (!feof(queuef)) {
+		if (fgets(line, sizeof(line), queuef) == NULL || line[0] == 0)
+			break;
+		line[strlen(line) - 1] = 0;	/* chop newline */
+
+		s = strchr(line, ':');
+		if (s == NULL)
+			goto malformed;
+		*s = 0;
+
+		s++;
+		while (isspace(*s))
+			s++;
+
+		s = strdup(s);
+		if (s == NULL)
+			goto malformed;
+
+		if (strcmp(line, "ID") == 0) {
+			queueid = s;
+		} else if (strcmp(line, "Sender") == 0) {
+			sender = s;
+		} else if (strcmp(line, "Recipient") == 0) {
+			addr = s;
+		} else {
+			syslog(LOG_DEBUG, "ignoring unknown queue info `%s' in `%s'",
+			       line, queuefn);
+			free(s);
+		}
+	}
+
+	if (queueid == NULL || sender == NULL || addr == NULL ||
+	    *queueid == 0 || *addr == 0) {
+malformed:
+		errno = EINVAL;
+		syslog(LOG_ERR, "malformed queue file `%s'", queuefn);
+		goto out;
+	}
+
+	if (add_recp(&itmqueue, addr, 0) != 0)
+		goto out;
+
+	it = LIST_FIRST(&itmqueue.queue);
+	it->sender = sender; sender = NULL;
+	it->queueid = queueid; queueid = NULL;
+	it->queuefn = queuefn; queuefn = NULL;
+	LIST_INSERT_HEAD(&queue->queue, it, next);
+
+out:
+	if (sender != NULL)
+		free(sender);
+	if (queueid != NULL)
+		free(queueid);
+	if (addr != NULL)
+		free(addr);
+	if (queuef != NULL)
+		fclose(queuef);
+
+	return (it);
+}
+
+int
+linkspool(struct queue *queue)
+{
+	struct stat st;
+	struct qitem *it;
+
+	if (fflush(queue->mailf) != 0 || fsync(fileno(queue->mailf)) != 0)
+		goto delfiles;
+
+	syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
+	       username, getuid(), queue->sender);
+
+	LIST_FOREACH(it, &queue->queue, next) {
+		if (asprintf(&it->queueid, "%s.%"PRIxPTR, queue->id, (uintptr_t)it) <= 0)
+			goto delfiles;
+		if (asprintf(&it->queuefn, "%s/Q%s", config.spooldir, it->queueid) <= 0)
+			goto delfiles;
+		if (asprintf(&it->mailfn, "%s/M%s", config.spooldir, it->queueid) <= 0)
+			goto delfiles;
+
+		/* Neither file may not exist yet */
+		if (stat(it->queuefn, &st) == 0 || stat(it->mailfn, &st) == 0)
+			goto delfiles;
+
+		if (writequeuef(it) != 0)
+			goto delfiles;
+
+		if (link(queue->tmpf, it->mailfn) != 0)
+			goto delfiles;
+	}
+
+	LIST_FOREACH(it, &queue->queue, next) {
+		syslog(LOG_INFO, "mail to=<%s> queued as %s",
+		       it->addr, it->queueid);
+	}
+
+	unlink(queue->tmpf);
+	return (0);
+
+delfiles:
+	LIST_FOREACH(it, &queue->queue, next) {
+		unlink(it->mailfn);
+		unlink(it->queuefn);
+	}
+	return (-1);
+}
+
+int
+load_queue(struct queue *queue)
+{
+	struct stat sb;
+	struct qitem *it;
+	DIR *spooldir;
+	struct dirent *de;
+	char *queuefn;
+	char *mailfn;
+
+	bzero(queue, sizeof(*queue));
+	LIST_INIT(&queue->queue);
+
+	spooldir = opendir(config.spooldir);
+	if (spooldir == NULL)
+		err(1, "reading queue");
+
+	while ((de = readdir(spooldir)) != NULL) {
+		queuefn = NULL;
+		mailfn = NULL;
+
+		/* ignore non-queue files */
+		if (de->d_name[0] != 'Q')
+			continue;
+		if (asprintf(&queuefn, "%s/Q%s", config.spooldir, de->d_name + 1) < 0)
+			goto fail;
+		if (asprintf(&mailfn, "%s/M%s", config.spooldir, de->d_name + 1) < 0)
+			goto fail;
+
+		/*
+		 * Some file systems don't provide a de->d_type, so we have to
+		 * do an explicit stat on the queue file.
+		 * Move on if it turns out to be something else than a file.
+		 */
+		if (stat(queuefn, &sb) != 0)
+			goto skip_item;
+		if (!S_ISREG(sb.st_mode)) {
+			errno = EINVAL;
+			goto skip_item;
+		}
+
+		if (stat(mailfn, &sb) != 0)
+			goto skip_item;
+
+		it = readqueuef(queue, queuefn);
+		if (it == NULL)
+			goto skip_item;
+
+		it->mailfn = mailfn;
+		continue;
+
+skip_item:
+		syslog(LOG_INFO, "could not pick up queue file: `%s'/`%s': %m", queuefn, mailfn);
+		if (queuefn != NULL)
+			free(queuefn);
+		if (mailfn != NULL)
+			free(mailfn);
+	}
+	closedir(spooldir);
+	return (0);
+
+fail:
+	return (-1);
+}
+
+void
+delqueue(struct qitem *it)
+{
+	unlink(it->mailfn);
+	unlink(it->queuefn);
+	if (it->queuef != NULL)
+		fclose(it->queuef);
+	if (it->mailf != NULL)
+		fclose(it->mailf);
+	free(it);
+}
+
+int
+acquirespool(struct qitem *it)
+{
+	int queuefd;
+
+	if (it->queuef == NULL) {
+		queuefd = open_locked(it->queuefn, O_RDWR|O_NONBLOCK);
+		if (queuefd < 0)
+			goto fail;
+		it->queuef = fdopen(queuefd, "r+");
+		if (it->queuef == NULL)
+			goto fail;
+	}
+
+	if (it->mailf == NULL) {
+		it->mailf = fopen(it->mailfn, "r");
+		if (it->mailf == NULL)
+			goto fail;
+	}
+
+	return (0);
+
+fail:
+	if (errno == EWOULDBLOCK)
+		return (1);
+	syslog(LOG_INFO, "could not acquire queue file: %m");
+	return (-1);
+}
+
+void
+dropspool(struct queue *queue, struct qitem *keep)
+{
+	struct qitem *it;
+
+	LIST_FOREACH(it, &queue->queue, next) {
+		if (it == keep)
+			continue;
+
+		if (it->queuef != NULL)
+			fclose(it->queuef);
+		if (it->mailf != NULL)
+			fclose(it->mailf);
+	}
+}
+
+int
+flushqueue_since(unsigned int period)
+{
+        struct stat st;
+	struct timeval now;
+        char *flushfn = NULL;
+
+	if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
+		return (0);
+	if (stat(flushfn, &st) < 0) {
+		free(flushfn);
+		return (0);
+	}
+	free(flushfn);
+	flushfn = NULL;
+	if (gettimeofday(&now, 0) != 0)
+		return (0);
+
+	/* Did the flush file get touched within the last period seconds? */
+	if (st.st_mtim.tv_sec + period >= now.tv_sec)
+		return (1);
+	else
+		return (0);
+}
+
+int
+flushqueue_signal(void)
+{
+        char *flushfn = NULL;
+	int fd;
+
+        if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
+		return (-1);
+	fd = open(flushfn, O_CREAT|O_WRONLY|O_TRUNC, 0660);
+	free(flushfn);
+	if (fd < 0) {
+		syslog(LOG_ERR, "could not open flush file: %m");
+		return (-1);
+	}
+        close(fd);
+	return (0);
+}
diff --git a/contrib/dma/test/quote.rfc2822 b/contrib/dma/test/quote.rfc2822
new file mode 100644
index 000000000000..f0f52fd1e202
--- /dev/null
+++ b/contrib/dma/test/quote.rfc2822
@@ -0,0 +1,23 @@
+From: test <test@example.org>
+To: test <test@example.org>
+Subject: testing!
+
+From what i can see...
+
+He came along,
+from far away.
+
+The rabbit comes out
+From his rabbit hole.
+
+Fromtal nudity!
+
+> From the previous mail
+
+>From the quote
+
+From here
+From there
+>>>From everywhere.
+
+last line.
diff --git a/contrib/dma/util.c b/contrib/dma/util.c
new file mode 100644
index 000000000000..a139b20d3831
--- /dev/null
+++ b/contrib/dma/util.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "dma.h"
+
+const char *
+hostname(void)
+{
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX	255
+#endif
+	static char name[HOST_NAME_MAX+1];
+	static int initialized = 0;
+	char *s;
+
+	if (initialized)
+		return (name);
+
+	if (config.mailname == NULL || !*config.mailname)
+		goto local;
+
+	if (config.mailname[0] == '/') {
+		/*
+		 * If the mailname looks like an absolute path,
+		 * treat it as a file.
+		 */
+		FILE *fp;
+
+		fp = fopen(config.mailname, "r");
+		if (fp == NULL)
+			goto local;
+
+		s = fgets(name, sizeof(name), fp);
+		fclose(fp);
+		if (s == NULL)
+			goto local;
+
+		for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
+			/* NOTHING */;
+		*s = 0;
+
+		if (!*name)
+			goto local;
+
+		initialized = 1;
+		return (name);
+	} else {
+		snprintf(name, sizeof(name), "%s", config.mailname);
+		initialized = 1;
+		return (name);
+	}
+
+local:
+	if (gethostname(name, sizeof(name)) != 0)
+		*name = 0;
+	/*
+	 * gethostname() is allowed to truncate name without NUL-termination
+	 * and at the same time not return an error.
+	 */
+	name[sizeof(name) - 1] = 0;
+
+	for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
+		/* NOTHING */;
+	*s = 0;
+
+	if (!*name)
+		snprintf(name, sizeof(name), "unknown-hostname");
+
+	initialized = 1;
+	return (name);
+}
+
+void
+setlogident(const char *fmt, ...)
+{
+	static char tag[50];
+
+	snprintf(tag, sizeof(tag), "%s", logident_base);
+	if (fmt != NULL) {
+		va_list ap;
+		char sufx[50];
+
+		va_start(ap, fmt);
+		vsnprintf(sufx, sizeof(sufx), fmt, ap);
+		va_end(ap);
+		snprintf(tag, sizeof(tag), "%s[%s]", logident_base, sufx);
+	}
+	closelog();
+	openlog(tag, 0, LOG_MAIL);
+}
+
+void
+errlog(int exitcode, const char *fmt, ...)
+{
+	int oerrno = errno;
+	va_list ap;
+	char outs[ERRMSG_SIZE];
+
+	outs[0] = 0;
+	if (fmt != NULL) {
+		va_start(ap, fmt);
+		vsnprintf(outs, sizeof(outs), fmt, ap);
+		va_end(ap);
+	}
+
+	errno = oerrno;
+	if (*outs != 0) {
+		syslog(LOG_ERR, "%s: %m", outs);
+		fprintf(stderr, "%s: %s: %s\n", getprogname(), outs, strerror(oerrno));
+	} else {
+		syslog(LOG_ERR, "%m");
+		fprintf(stderr, "%s: %s\n", getprogname(), strerror(oerrno));
+	}
+
+	exit(exitcode);
+}
+
+void
+errlogx(int exitcode, const char *fmt, ...)
+{
+	va_list ap;
+	char outs[ERRMSG_SIZE];
+
+	outs[0] = 0;
+	if (fmt != NULL) {
+		va_start(ap, fmt);
+		vsnprintf(outs, sizeof(outs), fmt, ap);
+		va_end(ap);
+	}
+
+	if (*outs != 0) {
+		syslog(LOG_ERR, "%s", outs);
+		fprintf(stderr, "%s: %s\n", getprogname(), outs);
+	} else {
+		syslog(LOG_ERR, "Unknown error");
+		fprintf(stderr, "%s: Unknown error\n", getprogname());
+	}
+
+	exit(exitcode);
+}
+
+static int
+check_username(const char *name, uid_t ckuid)
+{
+	struct passwd *pwd;
+
+	if (name == NULL)
+		return (0);
+	pwd = getpwnam(name);
+	if (pwd == NULL || pwd->pw_uid != ckuid)
+		return (0);
+	snprintf(username, sizeof(username), "%s", name);
+	return (1);
+}
+
+void
+set_username(void)
+{
+	struct passwd *pwd;
+
+	useruid = getuid();
+	if (check_username(getlogin(), useruid))
+		return;
+	if (check_username(getenv("LOGNAME"), useruid))
+		return;
+	if (check_username(getenv("USER"), useruid))
+		return;
+	pwd = getpwuid(useruid);
+	if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0') {
+		if (check_username(pwd->pw_name, useruid))
+			return;
+	}
+	snprintf(username, sizeof(username), "uid=%ld", (long)useruid);
+}
+
+void
+deltmp(void)
+{
+	struct stritem *t;
+
+	SLIST_FOREACH(t, &tmpfs, next) {
+		unlink(t->str);
+	}
+}
+
+static sigjmp_buf sigbuf;
+static int sigbuf_valid;
+
+static void
+sigalrm_handler(int signo)
+{
+	(void)signo;	/* so that gcc doesn't complain */
+	if (sigbuf_valid)
+		siglongjmp(sigbuf, 1);
+}
+
+int
+do_timeout(int timeout, int dojmp)
+{
+	struct sigaction act;
+	int ret = 0;
+
+	sigemptyset(&act.sa_mask);
+	act.sa_flags = 0;
+
+	if (timeout) {
+		act.sa_handler = sigalrm_handler;
+		if (sigaction(SIGALRM, &act, NULL) != 0)
+			syslog(LOG_WARNING, "can not set signal handler: %m");
+		if (dojmp) {
+			ret = sigsetjmp(sigbuf, 1);
+			if (ret)
+				goto disable;
+			/* else just programmed */
+			sigbuf_valid = 1;
+		}
+
+		alarm(timeout);
+	} else {
+disable:
+		alarm(0);
+
+		act.sa_handler = SIG_IGN;
+		if (sigaction(SIGALRM, &act, NULL) != 0)
+			syslog(LOG_WARNING, "can not remove signal handler: %m");
+		sigbuf_valid = 0;
+	}
+
+	return (ret);
+}
+
+int
+open_locked(const char *fname, int flags, ...)
+{
+	int mode = 0;
+
+	if (flags & O_CREAT) {
+		va_list ap;
+		va_start(ap, flags);
+		mode = va_arg(ap, int);
+		va_end(ap);
+	}
+
+#ifndef O_EXLOCK
+	int fd, save_errno;
+
+	fd = open(fname, flags, mode);
+	if (fd < 0)
+		return(fd);
+	if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
+		save_errno = errno;
+		close(fd);
+		errno = save_errno;
+		return(-1);
+	}
+	return(fd);
+#else
+	return(open(fname, flags|O_EXLOCK, mode));
+#endif
+}
+
+char *
+rfc822date(void)
+{
+	static char str[50];
+	size_t error;
+	time_t now;
+
+	now = time(NULL);
+	error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
+		       localtime(&now));
+	if (error == 0)
+		strcpy(str, "(date fail)");
+	return (str);
+}
+
+int
+strprefixcmp(const char *str, const char *prefix)
+{
+	return (strncasecmp(str, prefix, strlen(prefix)));
+}
+
+void
+init_random(void)
+{
+	unsigned int seed;
+	int rf;
+
+	rf = open("/dev/urandom", O_RDONLY);
+	if (rf == -1)
+		rf = open("/dev/random", O_RDONLY);
+
+	if (!(rf != -1 && read(rf, &seed, sizeof(seed)) == sizeof(seed)))
+		seed = (time(NULL) ^ getpid()) + (uintptr_t)&seed;
+
+	srandom(seed);
+
+	if (rf != -1)
+		close(rf);
+}
diff --git a/etc/mtree/BSD.root.dist b/etc/mtree/BSD.root.dist
index 5acd4b2adadc..d1617c645a6c 100644
--- a/etc/mtree/BSD.root.dist
+++ b/etc/mtree/BSD.root.dist
@@ -32,6 +32,8 @@
         ..
         devd
         ..
+        dma
+        ..
         gnats
         ..
         gss
diff --git a/etc/mtree/BSD.var.dist b/etc/mtree/BSD.var.dist
index 10a256c7d82a..51932ea3bc40 100644
--- a/etc/mtree/BSD.var.dist
+++ b/etc/mtree/BSD.var.dist
@@ -74,6 +74,8 @@
     rwho            gname=daemon mode=0775
     ..
     spool
+        dma             name=root gname=mail mode=0770
+        ..
         lock            uname=uucp gname=dialer mode=0775
         ..
 /set gname=daemon
diff --git a/libexec/Makefile b/libexec/Makefile
index e4781b82a7e9..8463b9075ca5 100644
--- a/libexec/Makefile
+++ b/libexec/Makefile
@@ -8,6 +8,8 @@ SUBDIR=	${_atf} \
 	bootpd \
 	${_casper} \
 	${_comsat} \
+	${_dma} \
+	${_dma-mbox-create} \
 	fingerd \
 	ftpd \
 	getty \
@@ -47,6 +49,11 @@ _casper=	casper
 _comsat=	comsat
 .endif
 
+.if ${MK_DMA} != "no"
+_dma=		dma
+_dma-mbox-create=	dma-mbox-create
+.endif
+
 .if ${MK_NIS} != "no"
 _mknetid=	mknetid
 _ypxfr=		ypxfr
diff --git a/libexec/dma/Makefile b/libexec/dma/Makefile
new file mode 100644
index 000000000000..d28d2036e197
--- /dev/null
+++ b/libexec/dma/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.PATH:	${.CURDIR}/../../contrib/dma
+
+CFLAGS=	-I${.CURDIR}/../../contrib/dma \
+	-DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME \
+	-DCONF_PATH='"/etc/dma"' \
+	-DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.9+"'
+DPADD=	${LIBSSL} ${LIBCRYPTO}
+LDADD=	-lssl -lcrypto
+
+PROG=	dma
+SRCS=	aliases_parse.y \
+	aliases_scan.l \
+	base64.c \
+	conf.c \
+	crypto.c \
+	dma.c \
+	dns.c \
+	local.c \
+	mail.c \
+	net.c \
+	spool.c \
+	util.c
+MAN8=	dma.8
+YFLAGS+=	-i
+CLEANFILES=	aliases_parse.i
+
+BINGRP=	mail
+BINMODE=	2555
+
+.include <bsd.prog.mk>
diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk
index 5e5eab9783fc..03f6c0b60372 100644
--- a/share/mk/bsd.own.mk
+++ b/share/mk/bsd.own.mk
@@ -271,6 +271,7 @@ __DEFAULT_YES_OPTIONS = \
     CTM \
     CXX \
     DICT \
+    DMA \
     DYNAMICROOT \
     ED_CRYPTO \
     EXAMPLES \
@@ -520,6 +521,7 @@ MK_GROFF:=	no
 .if ${MK_MAIL} == "no"
 MK_MAILWRAPPER:= no
 MK_SENDMAIL:=	no
+MK_DMA:=	no
 .endif
 
 .if ${MK_NETGRAPH} == "no"
diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc
index 44002c2f7f69..b124ab942d57 100644
--- a/tools/build/mk/OptionalObsoleteFiles.inc
+++ b/tools/build/mk/OptionalObsoleteFiles.inc
@@ -4306,3 +4306,9 @@ OLD_FILES+=usr/bin/svnserve
 OLD_FILES+=usr/bin/svnsync
 OLD_FILES+=usr/bin/svnversion
 .endif
+
+.if ${MK_DMA} == no
+OLD_FILES+=usr/libexec/dma
+OLD_FILES+=usr/libexec/dma-mbox-create
+OLD_FILES+=usr/share/man/man8/dma.8.gz
+.endif
diff --git a/tools/build/options/WITHOUT_DMA b/tools/build/options/WITHOUT_DMA
new file mode 100644
index 000000000000..6e66bd8432ba
--- /dev/null
+++ b/tools/build/options/WITHOUT_DMA
@@ -0,0 +1,2 @@
+.\" $FreeBSD$
+Set to not build dma Mail Transport Agent