#!/bin/sh

# shellcheck disable=SC2119,SC2120

# shellcheck source=../lib/load-configuration
. "${0%/*}/../lib/load-configuration"

# shellcheck disable=SC2016
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 in the'
  >&2 echo ' list in `gpg_keys`. `allowed_email_actions`'
  >&2 echo ' determines what instructions are allowed.'
  >&2 echo ''
  >&2 echo ' Possible instructions are:'
  >&2 echo ''
  >&2 echo '  - "block: <state-file> <reason>":'
  >&2 echo '    Block the given build assignment for the given reason.'
  >&2 echo ''
  >&2 echo '  - "copy-to-build-support: <arch> <pkgname>":'
  >&2 echo '    Copy the given binary package into [build-support].'
  >&2 echo ''
  >&2 echo '  - "delete:":'
  >&2 echo '    Delete all scheduled, safely deletable packages.'
  >&2 echo ''
  >&2 echo '  - "delete-from-build-support: <arch> <package-file>":'
  >&2 echo '    Delete the given package from <arch>/build-support.'
  >&2 echo ''
  >&2 echo '  - "prioritize: <pkgbase-regex>":'
  >&2 echo '    Increase the priority of matching build assignments.'
  >&2 echo ''
  >&2 echo '  - "schedule: <pkgbase>":'
  >&2 echo '    Put the given package on the build list (again).'
  >&2 echo ''
  >&2 echo '  - "stabilize: <package-file>":'
  >&2 echo '    Mark the given package as tested.'
  >&2 echo ''
  >&2 echo '  - "unblock: <state-file>":'
  >&2 echo '    Unblock the given build assignment.'
  >&2 echo ''
  exit 1
fi

# log $success $action $count [$comment_file]

# shellcheck disable=SC2039
log() {
  local success
  local action
  local count
  local comment
  success="$1"
  action="$2"
  count="$3"
  if [ -z "$4" ]; then
    comment=''
  else
    comment=$(
      base64 -w0 "$4"
    )
  fi
  # shellcheck disable=SC2016
  {
    printf 'INSERT IGNORE INTO `email_log` (`success`,`action`,`count`,`gpg_key`,`comment`)'
    printf ' SELECT '
    if [ "${success}" = '1' ]; then
      printf '1,'
    else
      printf '0,'
    fi
    printf '`email_actions`.`id`,from_base64("%s"),`gpg_keys`.`id`,from_base64("%s")' \
      "$(
        printf '%s' "${count}" | \
          base64 -w0
      )" \
      "${comment}"
    printf ' FROM `email_actions`'
    printf ' JOIN `gpg_keys`'
    printf '%s' "${gpg_keys_filter}"
    printf ' AND `email_actions`.`name`=from_base64("%s");\n' "$(
      printf '%s' "${action}" | \
        base64 -w0
    )"
  } | \
    mysql_run_query
}

# run_and_log_on_error $action

# shellcheck disable=SC2039
run_and_log_on_error() {
  # shellcheck disable=SC2039
  local err
  local action
  action="$1"
  shift
  err=0
  "$@" 2> "${tmp_dir}/stderr" > "${tmp_dir}/stdout" || \
    err=$?
  if [ "${err}" -eq 0 ]; then
    return 0
  fi
  cat "${tmp_dir}/stdout" >> "${tmp_dir}/stderr"
  if [ "${err}" -eq 1 ]; then
    printf '^ temporary error - I keep the message.\n' >> \
      "${tmp_dir}/stderr"
  fi
  log '0' "${action}" '0' "${tmp_dir}/stderr"

  if [ "${err}" -eq 1 ]; then
    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
  # shellcheck disable=SC2016
  {
    printf 'INSERT IGNORE INTO `email_log` (`success`,`comment`)'
    printf ' VALUES (0,"Invalid stamp - ignoring this message.");\n'
  } | \
    mysql_run_query
  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
  # shellcheck disable=SC2016
  {
    printf 'INSERT IGNORE INTO `email_log` (`success`,`comment`)'
    printf ' VALUES (0,from_base64("%s"));\n' \
      "$(
        {
          printf 'Invalid encryption/signature - ignoring this message.\n'
          cat "${tmp_dir}/gpg-status"
        } | \
          base64 -w0
      )"
  } | \
    mysql_run_query
  exit
fi

gpg_keys_filter=$(
  # shellcheck disable=SC2016
  {
    printf 'SELECT DISTINCT `gpg_keys`.`id`'
    printf ' FROM `gpg_keys`'
    printf ' WHERE `gpg_keys`.`fingerprint` IN ('
    grep '^\[GNUPG:] VALIDSIG ' "${tmp_dir}/gpg-status" | \
      cut -d' ' -f3 | \
      sort -u | \
      base64_encode_each | \
      sed '
        s/^/from_base64("/
        s/$/"),/
      '
    printf '"");\n'
  } | \
    mysql_run_query | \
    sed '
      $! s/$/,/
      1  s/^/ WHERE `gpg_keys`.`id` IN (/
      $  s/$/)/
    '
)

if [ -z "${gpg_keys_filter}" ]; then
  # shellcheck disable=SC2016
  {
    printf 'INSERT IGNORE INTO `email_log` (`success`,`comment`)'
    printf ' VALUES (0,from_base64("%s"));\n' \
      "$(
        {
          printf 'No known signature found - I found:\n'
          grep '^\[GNUPG:] VALIDSIG ' "${tmp_dir}/gpg-status" | \
            cut -d' ' -f3 | \
            sort -u | \
            sed 's|^|> |'
          printf 'Ignoring this message.\n'
        } | \
          base64 -w0
      )"
  } | \
    mysql_run_query
  exit
fi

# shellcheck disable=SC2016
{
  printf 'SELECT DISTINCT `email_actions`.`name`'
  printf ' FROM `email_actions`'
  mysql_join_email_actions_allowed_email_actions
  mysql_join_allowed_email_actions_gpg_keys
  printf '%s\n' "${gpg_keys_filter}"
} | \
  mysql_run_query > \
  "${tmp_dir}/allowed-actions"


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 'block' "${base_dir}/bin/modify-package-state" --wait --block "${tmp_dir}/block"; then
    log 1 'block' "$(wc -l < "${tmp_dir}/block")"
  else
    log 0 'block' 0
  fi
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 'copy-to-build-support' "${base_dir}/bin/copy-to-build-support" --wait "${tmp_dir}/copy-to-build-support"; then
    log 1 'copy-to-build-support' "$(wc -l < "${tmp_dir}/copy-to-build-support")"
  else
    log 0 'copy-to-build-support' 0
  fi
fi

if [ -s "${tmp_dir}/delete" ]; then
  if run_and_log_on_error 'delete' "${base_dir}/bin/delete-packages" --wait; then
    log 1 'delete' 1
  else
    log 0 'delete' 0
  fi
fi

if [ -s "${tmp_dir}/delete-from-build-support" ]; then
  if run_and_log_on_error 'delete-from-build-support' "${base_dir}/bin/delete-packages" --wait --build-support "${tmp_dir}/delete-from-build-support"; then
    log 1 'delete-from-build-support' "$(wc -l < "${tmp_dir}/delete-from-build-support")"
  else
    log 0 'delete-from-build-support' 0
  fi
fi

if [ -s "${tmp_dir}/prioritize" ]; then
  if run_and_log_on_error 'prioritize' "${base_dir}/bin/prioritize-build-list" --wait "${tmp_dir}/prioritize"; then
    log 1 'prioritize' "$(cat "${tmp_dir}/prioritize")"
  else
    log 0 'prioritize' 0
  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 1 'schedule' "$(wc -l < "${tmp_dir}/schedule")"
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 'stabilize' "${base_dir}/bin/modify-package-state" --wait --tested "${tmp_dir}/stabilize"; then
    log 1 'stabilize' "$(wc -l < "${tmp_dir}/stabilize")"
  else
    log 0 'stabilize' 0
  fi
fi

if [ -s "${tmp_dir}/unblock" ]; then
  if run_and_log_on_error 'unblock' "${base_dir}/bin/modify-package-state" --wait --unblock "${tmp_dir}/unblock"; then
    log 1 'unblock' "$(wc -l < "${tmp_dir}/unblock")"
  else
    log 0 'unblock' 0
  fi
fi