diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases
new file mode 100755
index 0000000..ce1a439
--- /dev/null
+++ b/type/__mail_alias/explorer/aliases
@@ -0,0 +1,59 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Find aliases for a given name and print the aliases line separated
+
+aliases_file=$("${__type_explorer}/aliases_file")
+test -r "${aliases_file}" || exit 0
+
+name=$__object_id
+
+awk -F ':[[:blank:]]*' '
+function print_aliases (aliases, matches) {
+ split(aliases, matches, /,[[:blank:]]*/)
+ for (i in matches) {
+ gsub(/^[[:blank:]]*|[[:blank:]]*$/, "", matches[i])
+ print matches[i]
+ }
+}
+
+/^#/ {
+ # comment
+ select = 0; cont = 0; next
+}
+
+{
+ cont = ($0 ~ /\\$/)
+ if (cont) sub(/[[:blank:]]*\\$/, "", $0)
+}
+
+/^[[:blank:]]/ || cont {
+ # continuation line
+ if (select) print_aliases($0)
+ next
+}
+
+$1 == ENVIRON["__object_id"] {
+ select = 1
+ print_aliases($2)
+ next
+}
+
+{ select = 0 }
+' "${aliases_file}"
diff --git a/type/__mail_alias/manifest b/type/__mail_alias/explorer/aliases_file
old mode 100755
new mode 100644
similarity index 56%
rename from type/__mail_alias/manifest
rename to type/__mail_alias/explorer/aliases_file
index e0c809a..f7c4596
--- a/type/__mail_alias/manifest
+++ b/type/__mail_alias/explorer/aliases_file
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -17,14 +17,36 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
+# This explorer tries to find the correct aliases file.
+found() { echo "$*"; exit 0; }
-os=$(cat "$__global/explorer/os")
+check_file() {
+ if test -f "$1"
+ then
+ found "$1"
+ fi
+}
-case "$os" in
- *)
- printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2
- printf "Please contribute an implementation for it if you can.\n" >&2
- exit 1
- ;;
+case $("$__explorer/os")
+in
+ (freebsd|openbsd|solaris)
+ check_file /etc/mail/aliases
+
+ # default
+ found /etc/mail/aliases
+ ;;
+ (debian|devuan|ubuntu)
+ check_file /etc/aliases
+
+ # default
+ found /etc/aliases
+ ;;
+ (*)
+ check_file /etc/mail/aliases
+ check_file /etc/aliases
+
+ # default
+ found /etc/mail/aliases
+ ;;
esac
diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote
index 77ad9d2..a93dff2 100755
--- a/type/__mail_alias/gencode-remote
+++ b/type/__mail_alias/gencode-remote
@@ -18,3 +18,111 @@
# along with cdist. If not, see .
#
+state_should=$(cat "${__object}/parameter/state")
+
+case $state_should
+in
+ (present)
+ if cmp "${__object}/explorer/aliases" "${__object}/parameter/alias"
+ then
+ # all good!
+ exit 0
+ fi
+
+ echo "set aliases" >>"$__messages_out"
+ mode=1
+ ;;
+ (absent)
+ # nothing to do if no aliases found.
+ test -s "${__object}/explorer/aliases" || exit 0
+
+ echo "delete aliases" >>"$__messages_out"
+ mode=0
+ ;;
+ (*)
+ printf 'Invalid --state given: %s\n' "$state_should" >&2
+ exit 1
+esac
+
+aliases_file=$(cat "${__object}/explorer/aliases_file")
+
+if test -z "${aliases_file}"
+then
+ echo 'Could not determine aliases file path.' >&2
+ exit 1
+fi
+
+# "export" variables to remote
+printf 'mode=%u\n' "${mode}"
+printf "aliases_file='%s'\n" "${aliases_file}"
+
+cat <<'EOF'
+awk -F ':[[:blank:]]*' -v mode="${mode}" '
+function sepafter(f, default, _) {
+ _ = substr($0, length($f) + 1, index(substr($0, length($f)+1), $(f+1)) - 1)
+ if (_) return _
+ else return default
+}
+
+function write_aliases() {
+ if (aliases_written) return
+
+ printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ")
+ while ((getline < aliases_should_file) > 0) {
+ if (aliases_written) printf ", "
+ printf "%s", $0
+ aliases_written = 1
+ }
+ printf "\n"
+ close(aliases_should_file)
+}
+
+BEGIN {
+ aliases_should_file = (ENVIRON["__object"] "/parameter/alias")
+}
+
+/^#/ {
+ # comment
+ select = 0; cont = 0
+ print
+ next
+}
+
+{
+ cont = ($0 ~ /\\$/)
+ if (cont) sub(/[[:blank:]]*\\$/, "", $0)
+}
+
+/^[[:blank:]]/ || cont {
+ # continuation line
+ if (select) next
+}
+
+$1 == ENVIRON["__object_id"] {
+ in_list = 1
+ if (mode) write_aliases()
+ next
+}
+
+{
+ in_list = 0
+ print
+}
+
+END {
+ # if the last line as an alias definition, the separator will be reused
+ if (mode && !aliases_written) write_aliases()
+}
+' <"${aliases_file}" >"${aliases_file}.tmp" || {
+ echo 'Generating new aliases file failed!' >&2
+ exit 1
+}
+
+if ! cmp "${aliases_file}" "${aliases_file}.tmp"
+then
+ mv "${aliases_file}.tmp" "${aliases_file}"
+ newaliases
+else
+ rm "${aliases_file}.tmp"
+fi
+EOF
diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst
index d9563a6..d6c7873 100644
--- a/type/__mail_alias/man.rst
+++ b/type/__mail_alias/man.rst
@@ -3,12 +3,12 @@ cdist-type__mail_alias(7)
NAME
----
-cdist-type__mail_alias - TODO
+cdist-type__mail_alias - Manage mail aliases.
DESCRIPTION
-----------
-This space intentionally left blank.
+This cdist type allows you to configure mail aliases (/etc/mail/aliases).
REQUIRED PARAMETERS
@@ -18,7 +18,14 @@ None.
OPTIONAL PARAMETERS
-------------------
-None.
+state
+ 'present' or 'absent', defaults to 'present'
+alias
+ the aliases where mail for the given user should be redirected to.
+ This parameter can be specified multiple times to redirect to more than one
+ recipient.
+ See the `aliases(5)` man page for the different forms this parameter can
+ take..
BOOLEAN PARAMETERS
@@ -31,13 +38,15 @@ EXAMPLES
.. code-block:: sh
- # TODO
- __mail_alias
+ # Redirect root mail to a "real" email address
+ __mail_alias root --alias admin@example.com
+ # Disable redirection of mail for joe
+ __mail_alias joe --state absent
SEE ALSO
--------
-:strong:`TODO`\ (7)
+:strong:`aliases`\ (5)
AUTHORS
diff --git a/type/__mail_alias/parameter/default/state b/type/__mail_alias/parameter/default/state
new file mode 100644
index 0000000..e7f6134
--- /dev/null
+++ b/type/__mail_alias/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/type/__mail_alias/parameter/optional b/type/__mail_alias/parameter/optional
new file mode 100644
index 0000000..ff72b5c
--- /dev/null
+++ b/type/__mail_alias/parameter/optional
@@ -0,0 +1 @@
+state
diff --git a/type/__mail_alias/parameter/optional_multiple b/type/__mail_alias/parameter/optional_multiple
new file mode 100644
index 0000000..d077ed8
--- /dev/null
+++ b/type/__mail_alias/parameter/optional_multiple
@@ -0,0 +1 @@
+alias