2020-05-27 16:34:06 +00:00
|
|
|
#!/bin/sh -e
|
|
|
|
|
2020-06-01 15:07:35 +00:00
|
|
|
CONF_PATH=/etc/dma # set in Makefile
|
|
|
|
|
2020-06-01 17:21:41 +00:00
|
|
|
# Determine mailname
|
|
|
|
if test -f "${__object}/parameter/mailname"
|
|
|
|
then
|
|
|
|
mailname=$(cat "${__object}/parameter/mailname")
|
|
|
|
else
|
|
|
|
case $(cat "${__global}/explorer/os")
|
|
|
|
in
|
|
|
|
(debian|devuan|ubuntu)
|
|
|
|
# On Debian-like systems use /etc/mailname unless --mailname is used
|
|
|
|
mailname='/etc/mailname'
|
|
|
|
;;
|
|
|
|
(*)
|
|
|
|
mailname=$__target_fqdn
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
# Generate "should" values for config
|
2020-06-01 15:07:35 +00:00
|
|
|
conf_should=$(
|
2020-06-09 12:44:54 +00:00
|
|
|
if test -s "${__object}/parameter/smarthost"
|
2020-06-01 15:07:35 +00:00
|
|
|
then
|
2020-06-09 12:44:54 +00:00
|
|
|
printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smarthost")"
|
2020-06-01 17:21:41 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
printf 'MAILNAME %s\n' "${mailname}"
|
|
|
|
|
|
|
|
if test -s "${__object}/explorer/auth_conf"
|
|
|
|
then
|
|
|
|
printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")"
|
2020-06-01 15:07:35 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
case $(cat "${__object}/parameter/security")
|
|
|
|
in
|
|
|
|
(ssl|tls)
|
|
|
|
default_smtp_port=465
|
|
|
|
echo 'SECURETRANSFER'
|
|
|
|
;;
|
|
|
|
(starttls)
|
|
|
|
default_smtp_port=587
|
|
|
|
echo 'SECURETRANSFER'
|
|
|
|
echo 'STARTTLS'
|
|
|
|
;;
|
|
|
|
(opportunistic)
|
|
|
|
default_smtp_port=25 # XXX: correct?
|
|
|
|
echo 'SECURETRANSFER'
|
|
|
|
echo 'STARTTLS'
|
|
|
|
echo 'OPPORTUNISTIC_TLS'
|
|
|
|
;;
|
|
|
|
(insecure)
|
|
|
|
default_smtp_port=25
|
|
|
|
echo 'INSECURE'
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
|
|
|
|
if test -s "${__object}/parameter/port"
|
|
|
|
then
|
|
|
|
printf 'PORT %u\n' "$(cat "${__object}/parameter/port")"
|
2020-06-11 15:01:22 +00:00
|
|
|
elif test "${default_smtp_port}" -ne 25 # DMA uses port 25 by default
|
2020-06-01 15:07:35 +00:00
|
|
|
then
|
|
|
|
printf 'PORT %u\n' "${default_smtp_port}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if test -f "${__object}/parameter/masquerade"
|
|
|
|
then
|
|
|
|
while read -r line
|
|
|
|
do
|
|
|
|
printf 'MASQUERADE %s\n' "${line}"
|
|
|
|
done <"${__object}/parameter/masquerade"
|
|
|
|
fi
|
|
|
|
|
2020-06-01 15:23:31 +00:00
|
|
|
if test -f "${__object}/parameter/defer"
|
|
|
|
then
|
|
|
|
echo 'DEFER'
|
|
|
|
fi
|
|
|
|
|
2020-06-09 12:51:11 +00:00
|
|
|
if test -f "${__object}/parameter/fullbounce"
|
2020-06-01 15:23:31 +00:00
|
|
|
then
|
|
|
|
echo 'FULLBOUNCE'
|
|
|
|
fi
|
|
|
|
|
2020-06-09 12:51:11 +00:00
|
|
|
if test -f "${__object}/parameter/nullclient"
|
2020-06-01 15:23:31 +00:00
|
|
|
then
|
2020-06-09 12:44:54 +00:00
|
|
|
test -s "${__object}/parameter/smarthost" || {
|
2020-06-09 12:51:11 +00:00
|
|
|
echo '--nullclient requires a --smarthost to be defined' >&2
|
2020-06-01 15:23:31 +00:00
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
|
|
|
echo 'NULLCLIENT'
|
|
|
|
fi
|
2020-06-01 15:07:35 +00:00
|
|
|
)
|
2020-06-11 15:01:22 +00:00
|
|
|
# Sort conf_should to compare against "conf_is"
|
2020-06-01 15:07:35 +00:00
|
|
|
conf_should=$(echo "$conf_should" | sort -s -k 1,1)
|
|
|
|
|
|
|
|
config_updated=false
|
|
|
|
if ! echo "$conf_should" | cmp -s "${__object}/explorer/conf" -
|
|
|
|
then
|
|
|
|
# config needs to be updated
|
|
|
|
echo "dma_conf='${CONF_PATH:?}/dma.conf'"
|
2020-06-11 15:01:22 +00:00
|
|
|
|
|
|
|
# The following AWK script will output the new config file to be stored on
|
|
|
|
# disk. To do so it reads the current dma.conf file and the config options
|
|
|
|
# that should be set (from stdin).
|
|
|
|
# Note that the path to the current dma.conf is passed to AWK twice, because
|
|
|
|
# the new file cannot be generated in one pass.
|
|
|
|
|
|
|
|
# The logic tries to place options at a sensible location, that is:
|
|
|
|
# a) if the option is already used in the config file:
|
|
|
|
# group all similar options (e.g. MASQUERADE) at one place in the order
|
|
|
|
# they are listed in stdin.
|
|
|
|
# b) if it is a new option and a "default comment" (e.g. "#PORT 25") exists:
|
|
|
|
# place options grouped directly after the comment (the comment is left
|
|
|
|
# alone)
|
|
|
|
# c) otherwise:
|
|
|
|
# options are grouped by word (the first word in the line) and appended
|
|
|
|
# at the end of the file.
|
|
|
|
|
2020-06-01 15:07:35 +00:00
|
|
|
cat <<'EOF'
|
|
|
|
awk -F '\n' '
|
2020-06-11 15:01:22 +00:00
|
|
|
function comment_line(line) {
|
|
|
|
# returns the position in line at which the comment's text starts
|
|
|
|
# (0 if the line is not a comment)
|
|
|
|
match(line, /^[ \t]*\#+[ \t]*/)
|
|
|
|
return RSTART ? (RLENGTH + 1) : 0
|
|
|
|
}
|
|
|
|
function empty_line(line) { return line ~ /^[ \t]*$/ }
|
|
|
|
function is_word(s) { return s ~ /^[A-Z_]+$/ } # "looks like a plausible word"
|
2020-06-01 15:07:35 +00:00
|
|
|
|
|
|
|
function first(line, sep) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# returns the part of the line until sep is found
|
|
|
|
# (or the whole line if sep is not found)
|
2020-06-01 15:07:35 +00:00
|
|
|
if (!sep) sep = SUBSEP
|
2020-06-01 17:24:32 +00:00
|
|
|
return index(line, sep) ? substr(line, 1, index(line, sep) - 1) : line
|
2020-06-01 15:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function rest(line, sep) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# returns the part of the line after the first occurrence of sep is found.
|
|
|
|
# (or nothing if sep is not found)
|
2020-06-01 15:07:35 +00:00
|
|
|
if (!sep) sep = SUBSEP
|
|
|
|
if (index(line, sep))
|
|
|
|
return substr(line, index(line, sep) + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
function conf_pop(word, value) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# returns the next value for the config `word` and delete it from the list.
|
|
|
|
# if value is set, this function will only return value if it is the first
|
|
|
|
# option in the list, otherwise it returns 0.
|
|
|
|
|
2020-06-01 15:07:35 +00:00
|
|
|
if (!(word in conf)) return 0
|
|
|
|
if (!value) {
|
|
|
|
if (index(conf[word], SUBSEP)) # more than one element?
|
2020-06-01 17:24:32 +00:00
|
|
|
value = substr(conf[word], 1, index(conf[word], SUBSEP) - 1)
|
2020-06-01 15:07:35 +00:00
|
|
|
else
|
|
|
|
value = conf[word]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index(conf[word], SUBSEP)) {
|
|
|
|
if (index(conf[word], value SUBSEP) != 1) return 0
|
|
|
|
conf[word] = substr(conf[word], length(value) + 2)
|
|
|
|
} else {
|
|
|
|
if (conf[word] != value) return 0
|
|
|
|
delete conf[word]
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
function print_conf(word, value) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# print a config line with the given parameters
|
2020-06-01 15:07:35 +00:00
|
|
|
printf "%s", word
|
|
|
|
if (value) printf " %s", value
|
|
|
|
printf "\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
function print_confs(word, value) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# print config lines for all values stored in conf[word].
|
2020-06-01 15:07:35 +00:00
|
|
|
if (!(word in conf)) return
|
|
|
|
if (conf[word]) {
|
|
|
|
while (value = conf_pop(word))
|
|
|
|
print_conf(word, value)
|
|
|
|
} else {
|
|
|
|
print_conf(word)
|
|
|
|
delete conf[word]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BEGIN {
|
2020-06-11 15:01:22 +00:00
|
|
|
# read the "should" state into the `conf` array.
|
2020-06-01 15:07:35 +00:00
|
|
|
while (getline < "/dev/stdin") {
|
|
|
|
word = first($0, " ")
|
|
|
|
if ((word in conf))
|
|
|
|
conf[word] = conf[word] SUBSEP rest($0, " ")
|
|
|
|
else
|
|
|
|
conf[word] = rest($0, " ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
# first pass, gather information about where which information is stored in the
|
|
|
|
# current config file. This information will be used in the second pass.
|
2020-06-01 15:07:35 +00:00
|
|
|
NR == FNR {
|
|
|
|
if (comment_line($0)) {
|
|
|
|
# comment line
|
2020-06-11 15:01:22 +00:00
|
|
|
word = first(substr($0, comment_line($0) + 1), " ")
|
2020-06-01 15:07:35 +00:00
|
|
|
if (is_word(word)) last_occ["#" word] = FNR
|
|
|
|
} else {
|
|
|
|
word = first($0, " ")
|
|
|
|
if (is_word(word)) last_occ[word] = FNR
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
# before second pass prepare hashes containing location information to be used
|
|
|
|
# in the second pass.
|
2020-06-01 15:07:35 +00:00
|
|
|
NR > FNR && FNR == 1 {
|
2020-06-11 15:01:22 +00:00
|
|
|
# First we drop the locations of commented-out options if a non-commented
|
|
|
|
# option is available. If a non-commented option is available, we will
|
|
|
|
# append new config options there to have them all at one place.
|
2020-06-01 15:07:35 +00:00
|
|
|
for (k in last_occ)
|
|
|
|
if (k ~ /^\#/ && (substr(k, 2) in last_occ))
|
|
|
|
delete last_occ[k]
|
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
# Reverse the option => line mapping. The line_map allows for easier lookups
|
|
|
|
# in the second pass.
|
|
|
|
for (k in last_occ) line_map[last_occ[k]] = k
|
2020-06-01 15:07:35 +00:00
|
|
|
}
|
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
# second pass, generate and output new config
|
2020-06-01 15:07:35 +00:00
|
|
|
NR > FNR {
|
|
|
|
if (comment_line($0) || empty_line($0)) {
|
|
|
|
# comment or empty line
|
|
|
|
print
|
|
|
|
|
|
|
|
if ((FNR in line_map)) {
|
|
|
|
if (line_map[FNR] ~ /^\#/) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# This line contains a commented config option. If the conf hash
|
|
|
|
# contains options to be set, we output them here because this
|
|
|
|
# option is not used in the current config.
|
2020-06-01 15:07:35 +00:00
|
|
|
k = substr(line_map[FNR], 2)
|
|
|
|
if ((k in conf)) print_confs(k)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (("INSECURE" in conf) && line_map[FNR] ~ /^\#?SECURE$/) {
|
2020-06-11 15:01:22 +00:00
|
|
|
# INSECURE goes where SECURE comment is.
|
2020-06-01 15:07:35 +00:00
|
|
|
print_confs("INSECURE")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
word = first($0, " ")
|
2020-06-11 15:01:22 +00:00
|
|
|
value = rest($0, " ")
|
|
|
|
sub(/[ \t]*\#.*$/, "", value) # ignore comments in value
|
2020-06-01 15:07:35 +00:00
|
|
|
|
2020-06-11 15:01:22 +00:00
|
|
|
if ((word in conf) && value == first(conf[word])) {
|
2020-06-01 15:07:35 +00:00
|
|
|
# keep config options we want
|
|
|
|
conf_pop(word)
|
|
|
|
print
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((FNR in line_map) && line_map[FNR] == word) {
|
|
|
|
# rest of config options should be here
|
|
|
|
print_confs(word)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
END {
|
2020-06-11 15:01:22 +00:00
|
|
|
# print rest of config options (
|
2020-06-01 15:07:35 +00:00
|
|
|
for (word in conf) print_confs(word)
|
|
|
|
}
|
|
|
|
' "${dma_conf}" "${dma_conf}" <<'EOF' >"${dma_conf}.tmp" \
|
|
|
|
&& mv "${dma_conf}.tmp" "${dma_conf}"
|
|
|
|
EOF
|
2020-06-11 15:01:22 +00:00
|
|
|
# Pass in "conf_should" via stdin
|
2020-06-01 15:07:35 +00:00
|
|
|
echo "${conf_should}"
|
|
|
|
echo 'EOF'
|
|
|
|
|
|
|
|
config_updated=true
|
|
|
|
echo 'config updated' >>"${__messages_out}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
2020-05-31 13:18:11 +00:00
|
|
|
if test -f "${__object}/parameter/send-test-email"
|
|
|
|
then
|
2020-06-11 15:01:22 +00:00
|
|
|
if grep -q '^__mail_alias/root:' "${__messages_in}" \
|
|
|
|
|| grep -q '^__dma_auth/' "${__messages_in}" \
|
|
|
|
|| $config_updated
|
2020-05-31 13:18:11 +00:00
|
|
|
then
|
|
|
|
cat <<-EOF
|
|
|
|
sendmail root <<EOM
|
2020-06-01 17:24:32 +00:00
|
|
|
Subject: [cdist] Test mail from '${__target_fqdn}'
|
2020-05-27 16:34:06 +00:00
|
|
|
|
2020-06-01 17:24:32 +00:00
|
|
|
Hi,
|
|
|
|
|
|
|
|
you can ignore this message.
|
|
|
|
Its sole purpose is to notify you that root mail on ${__target_fqdn}
|
|
|
|
will be redirected to you.
|
2020-05-27 16:34:06 +00:00
|
|
|
|
2020-05-31 13:18:11 +00:00
|
|
|
Enjoy!
|
|
|
|
EOM
|
|
|
|
EOF
|
|
|
|
fi
|
|
|
|
fi
|