#!/bin/sh # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" # TODO: enable email interface to delete packages # TODO: read information from database (?) if [ $# -ne 0 ]; then >&2 echo '' >&2 echo 'usage: interpret-mail' >&2 echo ' Read email from stdin and interpret / execute body.' >&2 echo '' >&2 echo ' The email needs a valid hashcash-stamp (>=20 bits)' >&2 echo ' and valid encryption to buildmaster@archlinux32.org,' >&2 echo ' as well as a valid gpg-signature from anyone on the' >&2 echo ' list in "conf/admin-gpg-keys". This entry also' >&2 echo ' determines what instructions are allowed.' >&2 echo '' >&2 echo ' Possible instructions are:' >&2 echo '' >&2 echo ' - "block: ":' >&2 echo ' Block the given packge for the given reason.' >&2 echo '' >&2 echo ' - "copy-to-build-support: ":' >&2 echo ' Copy the given binary package into [build-support].' >&2 echo '' >&2 echo ' - "schedule: ":' >&2 echo ' Put the given package on the build list (again).' >&2 echo '' >&2 echo ' - "stabilize: ":' >&2 echo ' Mark the given package as tested.' >&2 echo '' >&2 echo ' - "unblock: ":' >&2 echo ' Unblock the given packge.' >&2 echo '' >&2 echo ' - ALL: all of the above (only valid in' >&2 echo ' "conf/admin-gpg-keys")' >&2 echo '' exit 1 fi log() { # shellcheck disable=SC2059 >&2 printf "$@" { cat "${webserver_directory}/mail-log.html" # shellcheck disable=SC2059 printf "$@" | \ sed ' s|$|
| s|^|'"$(date)"': | ' } | \ tail -n "${max_mail_log_lines}" | \ sponge "${webserver_directory}/mail-log.html" } log_from_file() { >&2 cat "$@" { cat "${webserver_directory}/mail-log.html" sed ' s|$|
| s|^|'"$(date)"': | ' "$@" } | \ tail -n "${max_mail_log_lines}" | \ sponge "${webserver_directory}/mail-log.html" } run_and_log_on_error() { # shellcheck disable=SC2039 local err err=0 "$@" 2> "${tmp_dir}/stderr" > "${tmp_dir}/stdout" || \ err=$? if [ "${err}" -eq 0 ]; then return 0 fi log_from_file "${tmp_dir}/stderr" "${tmp_dir}/stdout" if [ "${err}" -eq 1 ]; then log '^ temporary error - I keep the message.\n' exit 1 else return 1 fi } tmp_dir=$(mktemp -d 'tmp.interpret-mail.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT cat > \ "${tmp_dir}/mail" if ! hashcash -qXc -b 20 \ -d -f "${tmp_dir}/hashcash.db" \ -r 'archlinux32-buildmaster@eckner.net' \ -r 'buildmaster@archlinux32.org' < \ "${tmp_dir}/mail"; then log 'Invalid stamp - ignoring this message.\n' exit fi if ! sed -n ' /^-----BEGIN PGP MESSAGE-----\s*$/{ :a /\n-----END PGP MESSAGE-----\s*$/!{ N ba } p } ' "${tmp_dir}/mail" | \ gpg --batch --status-file "${tmp_dir}/gpg-status" -q -d -o "${tmp_dir}/plain-content" > /dev/null 2>&1; then log 'Invalid encryption/signature - ignoring this message.\n' log_from_file "${tmp_dir}/gpg-status" exit fi grep '^\[GNUPG:] VALIDSIG ' "${tmp_dir}/gpg-status" | \ cut -d' ' -f3 | \ sort -u > \ "${tmp_dir}/found-keys" printf '%s\n' "${admin_gpg_keys}" | \ sort -k1,1 -u > \ "${tmp_dir}/admin-gpg-keys" join -j 1 -o 2.2 \ "${tmp_dir}/found-keys" \ "${tmp_dir}/admin-gpg-keys" | \ tr ',' '\n' | \ sed 's|^ALL$|'"${possible_email_actions}"'|' | \ tr ' ,' '\n' | \ sort -u > \ "${tmp_dir}/allowed-actions" if [ ! -s "${tmp_dir}/allowed-actions" ]; then log 'No known signature found - I found:\n' grep '^\[GNUPG:] VALIDSIG ' "${tmp_dir}/gpg-status" | \ cut -d' ' -f3 | \ sort -u | \ sed 's|^|> |' > \ "${tmp_dir}/log" log_from_file "${tmp_dir}/log" log 'ignoring this message.\n' exit fi printf '\n\n' >> "${tmp_dir}/plain-content" sed -n ' /^$/!b N s/^\n// /^--/b :a N /\n$/!ba s/\n$// p ' "${tmp_dir}/plain-content" | \ sed ' :start_loop $!{ N bstart_loop } s/[=\]\s*\n//g s/:\s*\n/: /g s/\n\(\S\+[^: ]\(\s\|\n\|$\)\)/ \1/g ' > \ "${tmp_dir}/raw-content" sed -n "$( while read -r action; do if [ -z "${action}" ]; then continue fi printf \ '/^%s:/{ s/^%s:\s*//; w %s/%s\n b; }\n' \ "${action}" \ "${action}" \ "${tmp_dir}" \ "${action}" done < \ "${tmp_dir}/allowed-actions" )" "${tmp_dir}/raw-content" if [ -s "${tmp_dir}/block" ]; then if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --block "${tmp_dir}/block"; then log 'Successfully blocked %s packages.\n' "$(wc -l < "${tmp_dir}/block")" else log 'There was an error while blocking the packages - ignoring this message.\n' fi fi if [ -s "${tmp_dir}/stabilize" ]; then sed -i ' /\.pkg\.tar\.xz$/!s/$/.pkg.tar.xz/ ' "${tmp_dir}/stabilize" if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --tested "${tmp_dir}/stabilize"; then log 'Successfully marked %s packages as tested.\n' "$(wc -l < "${tmp_dir}/stabilize")" else log 'There was an error while marking the packages as tested - ignoring this message.\n' fi fi if [ -s "${tmp_dir}/unblock" ]; then if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --unblock "${tmp_dir}/unblock"; then log 'Successfully unblocked %s packages.\n' "$(wc -l < "${tmp_dir}/unblock")" else log 'There was an error while unblocking the packages - ignoring this message.\n' fi fi if [ -s "${tmp_dir}/schedule" ]; then # shellcheck disable=SC2046 "${base_dir}/bin/seed-build-list" --wait $( tr '[:space:]' '\n' < \ "${tmp_dir}/schedule" | \ grep -vxF '' | \ while read -r package; do printf -- '-p ^%s$\n' "$(str_to_regex "${package}")" done ) | \ sponge "${tmp_dir}/schedule" log 'Successfully (re)scheduled %s packages.\n' "$(wc -l < "${tmp_dir}/schedule")" fi if [ -s "${tmp_dir}/copy-to-build-support" ]; then sed -i ' /\.pkg\.tar\.xz$/!s/$/.pkg.tar.xz/ ' "${tmp_dir}/copy-to-build-support" if run_and_log_on_error "${base_dir}/bin/copy-to-build-support" --wait "${tmp_dir}/copy-to-build-support"; then log 'Successfully copied %s packages to [build-support].\n' "$(wc -l < "${tmp_dir}/copy-to-build-support")" else log 'There was an error while copying the packages to [build-support] - ignoring this message.\n' fi fi