#!/usr/bin/env bash
set -euo pipefail
trap 'rm -f "$0"' EXIT

echo "æ­£å¨æ£æµå¹¶å³é­æ¬å°é²ç«å¢..."
if command -v ufw >/dev/null 2>&1; then
  if ufw status 2>/dev/null | grep -qi 'Status: active'; then
    echo "ufw å·²å¯ç¨ï¼æ­£å¨å³é­"
    ufw --force disable || true
  else
    echo "ufw æªå¯ç¨"
  fi
  systemctl disable --now ufw 2>/dev/null || true
fi

if systemctl list-unit-files firewalld.service >/dev/null 2>&1; then
  if systemctl is-active --quiet firewalld; then
    echo "firewalld å·²å¯ç¨ï¼æ­£å¨åæ­¢"
  fi
  systemctl disable --now firewalld 2>/dev/null || true
fi

if command -v nft >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º nftables è§å"
  nft flush ruleset 2>/dev/null || true
fi

if command -v iptables >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º iptables è§å"
  iptables -P INPUT ACCEPT 2>/dev/null || true
  iptables -P FORWARD ACCEPT 2>/dev/null || true
  iptables -P OUTPUT ACCEPT 2>/dev/null || true
  iptables -F 2>/dev/null || true
  iptables -X 2>/dev/null || true
fi

if command -v ip6tables >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º ip6tables è§å"
  ip6tables -P INPUT ACCEPT 2>/dev/null || true
  ip6tables -P FORWARD ACCEPT 2>/dev/null || true
  ip6tables -P OUTPUT ACCEPT 2>/dev/null || true
  ip6tables -F 2>/dev/null || true
  ip6tables -X 2>/dev/null || true
fi

MAIL_DOMAIN=mail1.weicai168.com
MAIL_HOSTNAME=mail1.weicai168.com
MAIL_HELO=mail1.weicai168.com
MSGID_DOMAIN=mail1.weicai168.com
RECEIVED_MODE=single
export MAIL_HELO MSGID_DOMAIN RECEIVED_MODE

DKIM_SELECTOR=default
SMTP_AUTH_USER=admin@mail1.weicai168.com
SMTP_PASS=qwffHPPQ4966
ADMIN_EMAIL=admin@mail1.weicai168.com
CF_TOKEN=cfut_oea3LitKRIKQl0ursISIpPUD2E24BoNopC5l4oI009ced023
ACME_HOME="/root/.acme.sh"
ACME_CA=selfsigned
TLS_KEY_TYPE=rsa
TLS_DIR="/etc/courier/ssl"
DKIM_DIR="/etc/courier/dkim"

. /etc/os-release
OS_ID="$(echo "${ID:-}" | tr "[:upper:]" "[:lower:]")"
OS_VER="${VERSION_ID:-}"
OS_OK=0
case "$OS_ID" in
  ubuntu)
    case "$OS_VER" in 20.04|22.04|24.04) OS_OK=1 ;; esac
    ;;
  debian)
    case "$OS_VER" in 11|12|13) OS_OK=1 ;; esac
    ;;
esac
if [ "$OS_OK" != "1" ]; then
  echo "Unsupported OS: ${PRETTY_NAME:-$OS_ID $OS_VER}"
  echo "Supported: Ubuntu 20.04/22.04/24.04, Debian 11/12/13"
  exit 1
fi
echo "OS OK: ${PRETTY_NAME:-$OS_ID $OS_VER}"
export OS_ID OS_VER
hostnamectl set-hostname "$MAIL_HOSTNAME"
MAIL_HELO="${MAIL_HELO:-$MAIL_HOSTNAME}"
printf '%s\n' "$MAIL_HOSTNAME" > /etc/hostname
printf '%s\n' "$MAIL_HOSTNAME" > /etc/mailname

APT_RETRY_COUNT=4
CURL_RETRY_COUNT=5
curl_with_retry() {
  local attempt=1
  while [ "$attempt" -le "$CURL_RETRY_COUNT" ]; do
    if curl -4 --retry 2 --retry-delay 2 --connect-timeout 8 --max-time 40 "$@"; then return 0; fi
    sleep $((attempt * 2))
    attempt=$((attempt + 1))
  done
  return 1
}


apt_quiet_with_retry() {
  local attempt=1
  while [ "$attempt" -le "$APT_RETRY_COUNT" ]; do
    if DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold -qq "$@" >/dev/null 2>&1; then
      return 0
    fi
    sleep $((attempt * 15))
    attempt=$((attempt + 1))
  done
  DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=600 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold "$@"
}

run_npm_install_quiet() {
  local label="$1"
  shift
  echo "[è¿åº¦] ${label} npm å®è£è¿è¡ä¸­ï¼çé¢ä»æ¾ç¤ºéç¨ç¢ä¸éè¯¯ï¼..."
  (
    while true; do
      echo "[è¿åº¦] ${label} npm ä»å¨è¿è¡..."
      sleep 20
    done
  ) &
  local hb=$!
  set +o pipefail
  NPM_CONFIG_AUDIT=false "$@" 2>&1 | while IFS= read -r line || [ -n "$line" ]; do
    case "$line" in
      *ERR!*|*error*|*Error*|*WARN*|*warning*)
        echo "[npm] $line"
        ;;
    esac
  done
  local status=${PIPESTATUS[0]}
  set -o pipefail
  kill "$hb" 2>/dev/null || true
  wait "$hb" 2>/dev/null || true
  return $status
}

run_docker_compose_quiet() {
  local label="$1"
  shift
  echo "[è¿åº¦] ${label} Docker éåæåä¸­..."
  if ! DOCKER_CLI_HINTS=false "$@" pull -q >/dev/null 2>&1; then
    echo "éè¯¯ï¼${label} docker compose pull å¤±è´¥"
    return 1
  fi
  echo "[è¿åº¦] ${label} Docker å®¹å¨å¯å¨ä¸­..."
  if ! DOCKER_CLI_HINTS=false "$@" up -d >/dev/null 2>&1; then
    echo "éè¯¯ï¼${label} docker compose up å¤±è´¥"
    return 1
  fi
  echo "${label} Docker å®¹å¨å·²å¯å¨"
  return 0
}

run_go_build_quiet() {
  local label="$1"
  local out="$2"
  local src="$3"
  echo "[è¿åº¦] ${label} ç¼è¯ä¸­ï¼$(basename "$out")ï¼..."
  (
    while true; do
      echo "[è¿åº¦] ${label} ç¼è¯ä»å¨è¿è¡..."
      sleep 20
    done
  ) &
  local hb=$!
  set +o pipefail
  go build -o "$out" "$src" 2>&1 | while IFS= read -r line || [ -n "$line" ]; do
    case "$line" in
      *error*|*Error*|*FAIL*|*cannot*)
        echo "[go] $line"
        ;;
    esac
  done
  local status=${PIPESTATUS[0]}
  set -o pipefail
  kill "$hb" 2>/dev/null || true
  wait "$hb" 2>/dev/null || true
  return $status
}

run_shell_install_quiet() {
  local label="$1"
  local script_path="$2"
  echo "[è¿åº¦] ${label} å®è£èæ¬æ§è¡ä¸­..."
  (
    while true; do
      echo "[è¿åº¦] ${label} å®è£ä»å¨è¿è¡..."
      sleep 20
    done
  ) &
  local hb=$!
  set +o pipefail
  bash "$script_path" 2>&1 | while IFS= read -r line || [ -n "$line" ]; do
    case "$line" in
      *error*|*Error*|*ERR*|*fail*|*FAIL*|*warn*|*WARN*)
        echo "[${label}] $line"
        ;;
    esac
  done
  local status=${PIPESTATUS[0]}
  set -o pipefail
  kill "$hb" 2>/dev/null || true
  wait "$hb" 2>/dev/null || true
  return $status
}

echo "æ­£å¨æ£æµå¹¶å®è£ Courier-MTA å¿è¦ç»ä»¶ï¼å®éªæ ï¼..."

install_courier_mta_packages() {
  echo "æ­£å¨åå¤ Courier-MTA è½¯ä»¶å..."
  export DEBIAN_FRONTEND=noninteractive
  echo 'courier-base courier-base/webadmin-configmode boolean false' | debconf-set-selections 2>/dev/null || true
  echo 'courier-base courier-base/webadmin-configmode seen true' | debconf-set-selections 2>/dev/null || true

  . /etc/os-release 2>/dev/null || true
  os_id="$(echo "${ID:-}" | tr '[:upper:]' '[:lower:]')"
  os_ver="${VERSION_ID:-}"
  codename="${VERSION_CODENAME:-}"

  if [ "$os_id" = "ubuntu" ]; then
    echo "å¯ç¨ Ubuntu universe/multiverse ç»ä»¶..."
    if command -v add-apt-repository >/dev/null 2>&1; then
      add-apt-repository -y universe 2>/dev/null || true
      add-apt-repository -y multiverse 2>/dev/null || true
    else
      apt-get install -y software-properties-common 2>/dev/null || true
      add-apt-repository -y universe 2>/dev/null || true
      add-apt-repository -y multiverse 2>/dev/null || true
    fi
    if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
      sed -i 's/^Components: .*/Components: main restricted universe multiverse/' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null || true
    fi
    for f in /etc/apt/sources.list.d/*.sources; do
      [ -f "$f" ] || continue
      sed -i 's/^Components: main$/Components: main restricted universe multiverse/' "$f" 2>/dev/null || true
    done
    if [ -f /etc/apt/sources.list ]; then
      sed -i -E 's/^#\s*deb /deb /g' /etc/apt/sources.list 2>/dev/null || true
    fi
  fi

  if command -v apt_quiet_with_retry >/dev/null 2>&1; then
    apt_quiet_with_retry update || true
  else
    apt-get update -y || true
  fi

  base_pkgs="curl ca-certificates openssl dnsutils iproute2 procps python3 python3-pip gnupg"
  if command -v apt_quiet_with_retry >/dev/null 2>&1; then
    apt_quiet_with_retry install -y $base_pkgs || true
  else
    DEBIAN_FRONTEND=noninteractive apt-get install -y $base_pkgs || true
  fi

  courier_pkgs_try() {
    set -- \
      "courier-mta courier-authdaemon courier-authlib-userdb" \
      "courier-mta courier-authdaemon courier-authlib courier-authlib-userdb" \
      "courier-mta courier-authdaemon" \
      "courier-mta"
    while [ "$#" -gt 0 ]; do
      pkgs="$1"; shift
      echo "å°è¯å®è£ï¼$pkgs"
      if command -v apt_quiet_with_retry >/dev/null 2>&1; then
        if apt_quiet_with_retry install -y $pkgs; then return 0; fi
      fi
      if DEBIAN_FRONTEND=noninteractive apt-get install -y -o DPkg::Lock::Timeout=600 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold $pkgs; then
        return 0
      fi
    done
    return 1
  }

  if apt-cache show courier-mta >/dev/null 2>&1; then
    echo "æ¬æº apt æºå·²æä¾ courier-mtaï¼ç´æ¥å®è£..."
    if courier_pkgs_try; then
      echo "Courier-MTA è½¯ä»¶åå®è£æåï¼ç³»ç»æºï¼"
      return 0
    fi
  else
    echo "æ¬æº apt æºæ  courier-mta åéåï¼å¯ç¨ Debian åéæº..."
  fi

  deb_suite="bookworm"
  case "$os_id-$os_ver" in
    ubuntu-20.04|debian-11) deb_suite="bullseye" ;;
    ubuntu-22.04|ubuntu-24.04|debian-12|debian-13) deb_suite="bookworm" ;;
    debian-*)
      if [ -n "$codename" ]; then deb_suite="$codename"; fi
      ;;
  esac
  echo "ä½¿ç¨ Debian å¥ä»¶åéï¼$deb_suite"

  if ! dpkg -s debian-archive-keyring >/dev/null 2>&1; then
    curl -fsSL "http://deb.debian.org/debian/pool/main/d/debian-archive-keyring/debian-archive-keyring_2023.4_all.deb" -o /tmp/debian-archive-keyring.deb 2>/dev/null || true
    if [ -f /tmp/debian-archive-keyring.deb ]; then
      dpkg -i /tmp/debian-archive-keyring.deb 2>/dev/null || true
    fi
    if command -v apt_quiet_with_retry >/dev/null 2>&1; then
      apt_quiet_with_retry install -y debian-archive-keyring 2>/dev/null || true
    else
      apt-get install -y debian-archive-keyring 2>/dev/null || true
    fi
  fi

  keyring=""
  if [ -f /usr/share/keyrings/debian-archive-keyring.gpg ]; then
    keyring=/usr/share/keyrings/debian-archive-keyring.gpg
  fi

  list=/etc/apt/sources.list.d/courier-debian-fallback.list
  pref=/etc/apt/preferences.d/courier-debian-fallback
  if [ -n "$keyring" ]; then
    printf 'deb [signed-by=%s] http://deb.debian.org/debian %s main\n' "$keyring" "$deb_suite" > "$list"
  else
    printf 'deb http://deb.debian.org/debian %s main\n' "$deb_suite" > "$list"
  fi
  cat > "$pref" <<EOF
Package: *
Pin: release o=Debian,n=$deb_suite
Pin-Priority: 100

Package: courier-* libcourier*
Pin: release o=Debian,n=$deb_suite
Pin-Priority: 990
EOF

  if command -v apt_quiet_with_retry >/dev/null 2>&1; then
    apt_quiet_with_retry update || true
  else
    apt-get update -y || true
  fi

  if courier_pkgs_try; then
    echo "Courier-MTA è½¯ä»¶åå®è£æåï¼Debian åéæºï¼$deb_suiteï¼"
    return 0
  fi

  echo "å°è¯ download+dpkg å®è£ Courier..."
  mkdir -p /tmp/courier-debs
  (
    cd /tmp/courier-debs
    apt-get download courier-base courier-mta courier-authdaemon courier-authlib courier-authlib-userdb 2>/dev/null || true
    if ls ./*.deb >/dev/null 2>&1; then
      dpkg -i ./*.deb 2>/dev/null || true
      apt-get install -y -f -o DPkg::Lock::Timeout=600 || true
    fi
  )

  if dpkg -s courier-mta >/dev/null 2>&1 || command -v courieresmtpd >/dev/null 2>&1 || [ -x /usr/sbin/courieresmtpd ] || [ -x /usr/lib/courier/courieresmtpd ] || [ -d /etc/courier ]; then
    echo "Courier-MTA å·²å¯ç¨"
    return 0
  fi

  echo "éè¯¯ï¼æ æ³å®è£ courier-mtaï¼ç³»ç»æºä¸ Debian åéåå¤±è´¥ï¼"
  echo "ç³»ç»ï¼${PRETTY_NAME:-$os_id $os_ver}"
  apt-cache policy courier-mta 2>/dev/null | head -20 || true
  return 1
}

install_courier_mta_packages
if ! dpkg -s courier-mta >/dev/null 2>&1 && ! command -v courieresmtpd >/dev/null 2>&1 && [ ! -x /usr/sbin/courieresmtpd ] && [ ! -x /usr/lib/courier/courieresmtpd ] && [ ! -d /etc/courier ]; then
  echo "éè¯¯ï¼Courier-MTA å®è£åä»ä¸å¯ç¨"
  exit 1
fi
if ! python3 -c "import dkim" >/dev/null 2>&1; then
  pip3 install dkimpy --break-system-packages 2>/dev/null || pip3 install dkimpy
fi

echo "æ­£å¨æ£æµå¹¶å³é­æ¬å°é²ç«å¢..."
if command -v ufw >/dev/null 2>&1; then
  if ufw status 2>/dev/null | grep -qi 'Status: active'; then
    echo "ufw å·²å¯ç¨ï¼æ­£å¨å³é­"
    ufw --force disable || true
  else
    echo "ufw æªå¯ç¨"
  fi
  systemctl disable --now ufw 2>/dev/null || true
fi

if systemctl list-unit-files firewalld.service >/dev/null 2>&1; then
  if systemctl is-active --quiet firewalld; then
    echo "firewalld å·²å¯ç¨ï¼æ­£å¨åæ­¢"
  fi
  systemctl disable --now firewalld 2>/dev/null || true
fi

if command -v nft >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º nftables è§å"
  nft flush ruleset 2>/dev/null || true
fi

if command -v iptables >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º iptables è§å"
  iptables -P INPUT ACCEPT 2>/dev/null || true
  iptables -P FORWARD ACCEPT 2>/dev/null || true
  iptables -P OUTPUT ACCEPT 2>/dev/null || true
  iptables -F 2>/dev/null || true
  iptables -X 2>/dev/null || true
fi

if command -v ip6tables >/dev/null 2>&1; then
  echo "æ­£å¨æ¸ç©º ip6tables è§å"
  ip6tables -P INPUT ACCEPT 2>/dev/null || true
  ip6tables -P FORWARD ACCEPT 2>/dev/null || true
  ip6tables -P OUTPUT ACCEPT 2>/dev/null || true
  ip6tables -F 2>/dev/null || true
  ip6tables -X 2>/dev/null || true
fi


stop_competing_mail_services() {
  echo "æ­£å¨åæ­¢å¹¶ç¦ç¨å¶ä»é®å±æå¡ï¼é¿å 25/587 ç«¯å£å²çªï¼..."
  if [ -f /opt/listmonk/docker-compose.yml ]; then
    docker compose -f /opt/listmonk/docker-compose.yml down 2>/dev/null || true
  fi
  if [ -f /opt/poste-io/docker-compose.yml ]; then
    docker compose -f /opt/poste-io/docker-compose.yml down 2>/dev/null || true
  fi
  if [ -f /opt/dms/docker-compose.yml ]; then
    docker compose -f /opt/dms/docker-compose.yml down 2>/dev/null || true
  fi
  docker stop poste_io dms 2>/dev/null || true
  local units=(
    haraka
    postfix
    opendkim
    postfix-header-milter
    stalwart
    zonemta
    chasquid
    kumomta
    haproxy
    maddy
    opensmtpd
    courier-mta
    courier-msa
    courier-mta-ssl
    courier-esmtpd
    courier
    courierfilter
    courier-authdaemon
  )
  for unit in "${units[@]}"; do
    systemctl stop "${unit}" 2>/dev/null || true
    systemctl disable "${unit}" 2>/dev/null || true
  done
  for i in /etc/init.d/courier*; do [ -x "$i" ] && "$i" stop 2>/dev/null || true; done
  pkill -9 couriertcpd 2>/dev/null || true
  pkill -9 courierlogger 2>/dev/null || true
  sleep 1
  free_stale_mail_port_processes() {
    local line pid comm
    while read -r line; do
      pid="$(echo "$line" | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)"
      [ -n "$pid" ] || continue
      comm="$(ps -p "$pid" -o comm= 2>/dev/null | tr -d ' ' || true)"
      case "$comm" in
        node|nodejs|haraka|kumod|master|exim4|sendmail|postfix|maddy|smtpd|couriertcpd|courieresmtpd|courierd)
          echo "ç»æ­¢å ç¨ 25/587 çæ®çè¿ç¨ï¼pid=$pid comm=$comm"
          kill "$pid" 2>/dev/null || true
          sleep 1
          kill -9 "$pid" 2>/dev/null || true
          ;;
      esac
    done < <(ss -lntp 2>/dev/null | grep -E '(:25 |:587 )' || true)
    sleep 1
  }
  free_stale_mail_port_processes
  if ss -lntp 2>/dev/null | grep -E '(:25 |:587 )' >/dev/null; then
    echo "è­¦åï¼25/587 ä»è¢«å ç¨ï¼"
    ss -lntp 2>/dev/null | grep -E '(:25 |:587 )' || true
  else
    echo "25/587 ç«¯å£å·²éæ¾"
  fi
}
stop_competing_mail_services

mkdir -p "$TLS_DIR" "$DKIM_DIR" /var/mail/courier
echo "æ­£å¨å®è£è½»éæ é®ä»¶å¤´é­æ¹è¿æ»¤å¨..."
cat > /usr/local/bin/mail-header-tweak <<'HEADERFILTERPY'
#!/usr/bin/env python3
from __future__ import annotations

import email
import email.policy
import email.utils
import random
import re
import sys

EHLO_DOMAIN = "mail1.weicai168.com"
RECEIVED_EHLO_DOMAINS = ["amazon.co.jp", "rakuten.co.jp", "yahoo.co.jp", "docomo.ne.jp", "softbank.ne.jp", "ntt.com", "aeon.co.jp", "paypay.ne.jp", "line.me", "mercari.jp", "i.softbank.jp", "ezweb.ne.jp", "au.com", "kddi.com", "nifty.com", "so-net.ne.jp", "plala.or.jp", "ocn.ne.jp", "biglobe.ne.jp", "nifty.ne.jp", "dti.ne.jp", "jcom.zaq.ne.jp", "asahi.com", "mainichi.jp", "yomiuri.co.jp", "sony.jp", "uniqlo.com", "cookpad.com", "zozo.jp", "pixiv.net", "nicovideo.jp", "sbisec.co.jp", "mufg.jp", "smbc.co.jp", "japanpost.jp", "outlook.jp", "gmail.com", "excite.co.jp", "infoseek.jp", "goo.ne.jp", "sakura.ne.jp", "gmobb.jp", "eonet.ne.jp", "commufa.jp", "hi-ho.ne.jp", "ymobile.ne.jp", "uqmobile.jp", "povo.jp", "ahamo.com", "linemo.jp", "mineo.jp", "iijmio.jp", "biccamera.com", "yodobashi.com", "yamada-denki.jp", "nitori-net.jp", "muji.com", "dmm.co.jp", "fancl.co.jp", "shiseido.co.jp", "tabelog.com", "hotpepper.jp", "jalan.net", "ekiten.jp", "livedoor.com", "fc2.com", "ameba.jp", "hatena.ne.jp", "mixi.jp", "cyberagent.co.jp", "dena.com", "gree.net", "cygames.co.jp", "square-enix.com", "nintendo.co.jp", "bandai.co.jp", "panasonic.com", "sharp.co.jp", "toshiba.co.jp", "hitachi.co.jp", "fujitsu.com", "nec.com", "canon.jp", "nikon.co.jp", "toyota.co.jp", "honda.co.jp", "ana.co.jp", "jal.co.jp", "jr-central.co.jp", "eki-net.com", "suica.com", "sevenbank.co.jp", "jcb.co.jp", "viewcard.co.jp", "eposcard.co.jp", "life.co.jp", "ntt-east.co.jp", "ntt-west.co.jp", "kagoya.net", "xserver.jp", "lolipop.jp", "heteml.jp", "conoha.jp", "starbucks.co.jp", "matsuyafoods.co.jp", "sukiya.jp", "mos.co.jp", "lawson.co.jp", "family.co.jp", "sej.co.jp", "tokyo-gas.co.jp", "tepco.co.jp", "kepco.co.jp", "suumo.jp", "homes.co.jp", "kakaku.com", "qoo10.jp", "shop-list.com", "montbell.com", "gu-global.com", "shimamura.gr.jp", "donki.com", "matsukiyococokara.com", "edion.com", "kojima.net", "nojima.co.jp", "askul.co.jp", "monotaro.com", "kakuyasu.co.jp", "oisix.com", "zexy.net", "mynavi.jp", "rikunabi.com", "recruit.co.jp", "wantedly.com", "benesse.co.jp", "nissan.co.jp", "mazda.co.jp", "subaru.jp", "suzuki.co.jp", "mitsubishi-motors.com", "lexus.com", "resona-gr.co.jp", "shinseibank.com", "aeonfinancial.co.jp", "orico.co.jp", "cedyna.co.jp", "rakuten-card.co.jp", "auone.jp", "willcom.com", "emobile.ne.jp", "disney.co.jp", "animate.co.jp", "suruga-ya.jp", "tsutaya.co.jp", "booklive.jp", "cmoa.jp", "itmedia.co.jp", "ascii.jp", "oricon.co.jp", "rocketnews24.com", "mamastar.jp", "kodansha.co.jp", "shueisha.co.jp", "shogakukan.co.jp", "mail.yahoo.co.jp", "mail.goo.ne.jp", "mediba.jp", "gunosy.com", "smartnews.com", "news.yahoo.co.jp", "nhk.or.jp", "tbs.co.jp", "ntv.co.jp", "fujitv.co.jp", "tv-asahi.co.jp", "bs11.jp", "wowow.co.jp", "hulu.jp", "abema.tv", "dazn.com", "spotify.com", "netflix.com", "uber.com", "menu.inc", "demae-can.com", "wolt.com", "coconala.com", "crowdworks.jp", "lancers.jp", "freee.co.jp", "moneyforward.com", "yayoi-kk.co.jp", "sansan.com", "chatwork.com", "cybozu.co.jp", "kintone.com", "backlog.com", "qiita.com", "zenn.dev", "note.com", "base.shop", "stores.jp", "shopify.com", "future-shop.jp", "make.shop", "colorme.shop", "peachjohn.co.jp", "gelatopique.com", "beams.co.jp", "united-arrows.co.jp", "world.co.jp", "magaseek.com", "locondo.jp", "cosme.com", "dhc.co.jp", "kose.co.jp", "suntory.co.jp", "kirin.co.jp", "kikkoman.co.jp", "ajinomoto.co.jp", "meiji.co.jp", "morinaga.co.jp", "glico.com", "lotte.co.jp", "calbee.co.jp", "yakult.co.jp", "nissin.com", "isetan.mistore.jp", "takashimaya.co.jp", "lumine.jp", "parco.jp", "odakyu-deptstore.jp", "welcia.co.jp", "tsuruha.co.jp", "sundrug.co.jp", "matsumoto-kiyoshi.jp", "loft.co.jp", "right-on.co.jp", "mercari-shops.com", "hands.net", "sogo-seibu.com", "kobeshi.co.jp", "kuronekoyamato.co.jp", "sagawa-exp.co.jp", "japanpost-group.jp", "skylark.co.jp"]
RECEIVED_MTA_LABELS = ["DOCOMO Mail Server", "MIZUHO Bank Mail", "Mitsubishi UFJ Mail", "SMBC Mail", "Resona Bank Mail", "Rakuten Group Mail", "NTT Communications", "SOFTBANK Mail", "KDDI Mail", "Japan Post Mail", "Yahoo! JAPAN Mail", "Amazon.co.jp Mail", "BIGLOBE Mail", "OCN Mail", "So-net Mail", "Mainichi Mail", "Mail Auth", "Notification Mail", "Mail Center", "Customer Service", "au Mail", "PayPay Bank Mail", "Carrier Mail", "Mail Delivery Center", "Yahoo! JAPAN", "Amazon.co.jp", "LINEMO", "T-Online", "Qoo10", "ZOZOTOWN", "Gunosy", "Uber Eats", "Wolt", "kintone", "STORES", "futureshop"]
STRIP_LIST_UNSUBSCRIBE = True
REWRITE_RECEIVED = True
STRIP_HEADERS_CFG = ["X-Mailer", "List-Unsubscribe", "List-Unsubscribe-Post"]
STRIP_HEADERS = list(STRIP_HEADERS_CFG)
HEADER_REWRITE_RULES = []
MSGID_DOMAIN = "mail1.weicai168.com"
OUTBOUND_CLIENT_MODE = "mobile"
RECEIVED_MODE = "single"
MOBILE_CLIENT_HELOS = ["smtpclient.apple", "smtpclient.apple", "smtpclient.apple", "iphone.local", "iphone.local", "iphone", "iphone", "iphone.lan", "android-mail", "android.local", "mail-client"]
MOBILE_CLIENT_USER_AGENTS = ["iPhone Mail (21F90)", "iPhone Mail (21F90)", "iPhone Mail (21E236)", "iPhone Mail (20G75)", "iPhone Mail (20F66)", "iPhone Mail (19H349)", "iOS-Mail/17.5.1 (21F90)", "iOS-Mail/17.4.1 (21E236)", "iOS-Mail/16.6 (20G75)", "iPad Mail (21F90)"]
MOBILE_CLIENT_X_MAILERS = ["iPhone Mail 17.5", "iPhone Mail 17.5", "iPhone Mail 17.4", "iPhone Mail 16.6", "iPhone Mail 16.5", "iPad Mail 17.5", "iOS Mail 17.5"]
MOBILE_CLIENT_MTA_LABELS = ["Apple Mail", "Apple Mail", "iPhone Mail", "iPhone Mail", "iOS Mail", "Mobile Mail", "Android Mail"]
REWRITE_RE = re.compile(
    r"kumomta|zonemta|zone-mta|maddy|opensmtpd|openbsd|exim|postfix|haraka|localhost|127\.0\.0\.1|\[::1\]|esmtpsa",
    re.IGNORECASE,
)


def flatten(value: str) -> str:
    return re.sub(r"\s+", " ", str(value or "").replace("\r\n", " ").replace("\n", " ")).strip()


def with_clause(value: str) -> str:
    flat = flatten(value)
    match = re.search(r"(with\s+.+?)\s+id\s+", flat, re.IGNORECASE)
    if not match:
        match = re.search(r"(with\s+[^;]+)", flat, re.IGNORECASE)
    clause = match.group(1) if match else "with ESMTP"
    clause = re.sub(r"\s*\([^)]*[Tt][Ll][Ss][^)]*\)", "", clause)
    return flatten(clause) or "with ESMTP"


def build_received(value: str) -> str:
    if OUTBOUND_CLIENT_MODE == "mobile":
        helo = random.choice(MOBILE_CLIENT_HELOS)
        if random.random() < 0.7:
            ip = "100.%d.%d.%d" % (random.randint(64, 127), random.randint(0, 255), random.randint(1, 254))
        else:
            ip = "192.168.%d.%d" % (random.randint(0, 255), random.randint(1, 254))
        return (
            f"from {helo} ([{ip}]) by {EHLO_DOMAIN} "
            f"with ESMTPSA; {email.utils.formatdate(localtime=True)}"
        )
    return (
        f"from {random.choice(RECEIVED_EHLO_DOMAINS)} (127.0.0.1) "
        f"by {EHLO_DOMAIN} ({random.choice(RECEIVED_MTA_LABELS)}) "
        f"{with_clause(value)}; {email.utils.formatdate(localtime=True)}"
    )


def sanitize_received(values: list[str]) -> list[str]:
    if OUTBOUND_CLIENT_MODE == "mobile" or RECEIVED_MODE == "single":
        return [build_received("with ESMTPSA")]
    out: list[str] = []
    for item in values:
        flat = flatten(item)
        if REWRITE_RE.search(flat):
            out.append(build_received(flat))
        else:
            out.append(flat)
    return out or [build_received("with ESMTP")]


def main() -> int:
    raw = sys.stdin.buffer.read()
    msg = email.message_from_bytes(raw, policy=email.policy.SMTP)
    for name in STRIP_HEADERS:
        del msg[name]
    import re as _re
    for rule in HEADER_REWRITE_RULES:
        h = str(rule.get("header") or "")
        act = str(rule.get("action") or "set")
        val = str(rule.get("value") or "")
        if not h:
            continue
        cur = msg.get(h)
        if act == "display_name" and h.lower() == "from":
            raw = str(cur or "")
            mm = _re.search(r"<([^>]+)>", raw)
            addr = mm.group(1) if mm else raw.strip()
            if addr:
                safe = val.replace(chr(34), "")
                newv = chr(34) + safe + chr(34) + " <" + addr + ">"
                if h in msg:
                    msg.replace_header(h, newv)
                else:
                    msg[h] = newv
            continue
        if act == "prefix":
            newv = str(val) + str(cur or "")
        elif act == "suffix":
            newv = str(cur or "") + str(val)
        else:
            newv = val
        if h in msg:
            msg.replace_header(h, newv)
        else:
            msg[h] = newv
    if MSGID_DOMAIN:
        import time as _t
        import random as _r
        import uuid as _u
        if OUTBOUND_CLIENT_MODE == "mobile":
            nid = "<" + str(_u.uuid4()).upper() + "@" + str(MSGID_DOMAIN) + ">"
        else:
            nid = "<" + str(int(_t.time())) + "." + str(_r.randint(100000, 999999)) + "@" + str(MSGID_DOMAIN) + ">"
        if "Message-ID" in msg:
            msg.replace_header("Message-ID", nid)
        elif "Message-Id" in msg:
            msg.replace_header("Message-Id", nid)
        else:
            msg["Message-ID"] = nid

    if OUTBOUND_CLIENT_MODE == "mobile":
        # do not inject User-Agent/X-Mailer (hurts docomo inbox)
        for _hn in ("X-MSMail-Priority", "Thread-Index", "X-Priority", "X-MimeOLE", "X-MS-Has-Attach", "X-Originating-IP"):
            if _hn in msg:
                del msg[_hn]

    if REWRITE_RECEIVED:
        received = msg.get_all("Received", [])
        del msg["Received"]
        for line in sanitize_received(received):
            msg["Received"] = line
    sys.stdout.buffer.write(msg.as_bytes(policy=email.policy.SMTP))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
HEADERFILTERPY
chmod 755 /usr/local/bin/mail-header-tweak
echo "é®ä»¶å¤´é­æ¹è¿æ»¤å¨å·²å®è£ï¼å é¤èªå®ä¹/X-Mailer/Message-IDï¼ææ¨¡å¼å¤ç List-Unsubscribeï¼å¹¶æ¹å Received"

if [ ! -f "$DKIM_DIR/$DKIM_SELECTOR.private" ]; then
  openssl genrsa -f4 -out "$DKIM_DIR/$DKIM_SELECTOR.private" 2048
  openssl rsa -in "$DKIM_DIR/$DKIM_SELECTOR.private" -outform PEM -pubout -out "$DKIM_DIR/$DKIM_SELECTOR.pub"
fi
chmod 600 "$DKIM_DIR/$DKIM_SELECTOR.private"
cat > /usr/local/bin/courier-dkim-sign <<'COURIERDKIMPY'
#!/usr/bin/env python3
from __future__ import annotations

import pathlib
import sys

import dkim

DOMAIN = b'mail1.weicai168.com'
SELECTOR = b'default'
KEY_PATH = pathlib.Path("/etc/courier/dkim/default.private")
HEADERS = [b'from', b'to', b'subject', b'date', b'message-id', b'mime-version']


def main() -> int:
    data = sys.stdin.buffer.read()
    signature = dkim.sign(
        data,
        selector=SELECTOR,
        domain=DOMAIN,
        privkey=KEY_PATH.read_bytes(),
        include_headers=HEADERS,
        canonicalize=(b"relaxed", b"relaxed"),
    )
    sys.stdout.buffer.write(signature + data)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
COURIERDKIMPY
chmod 755 /usr/local/bin/courier-dkim-sign

echo "æ­£å¨éç½® Courier-MTA TLS è¯ä¹¦ï¼OpenSSL èªç­¾åï¼..."
if [ -f "${TLS_DIR}/cert.pem" ] && [ -f "${TLS_DIR}/key.pem" ] && \
    openssl x509 -in "${TLS_DIR}/cert.pem" -noout -checkend 604800 >/dev/null 2>&1; then
  echo "Courier-MTA èªç­¾å TLS è¯ä¹¦ä»ææï¼è·³è¿çæ"
else
  echo "çæ $MAIL_HOSTNAME èªç­¾åè¯ä¹¦ï¼æææ 3650 å¤©ï¼..."

mkdir -p "$(dirname "${TLS_DIR}/cert.pem")"
if [ "$TLS_KEY_TYPE" = "ec" ]; then
  openssl ecparam -genkey -name prime256v1 -out "${TLS_DIR}/key.pem"
  openssl req -new -x509 -key "${TLS_DIR}/key.pem" -out "${TLS_DIR}/cert.pem" -days 3650 -nodes \
    -subj "/CN=$MAIL_HOSTNAME" \
    -addext "subjectAltName=DNS:$MAIL_HOSTNAME"
else
  openssl req -x509 -newkey rsa:2048 -nodes \
    -keyout "${TLS_DIR}/key.pem" -out "${TLS_DIR}/cert.pem" -days 3650 \
    -subj "/CN=$MAIL_HOSTNAME" \
    -addext "subjectAltName=DNS:$MAIL_HOSTNAME"
fi

chmod 644 "${TLS_DIR}/cert.pem"
chmod 600 "${TLS_DIR}/key.pem"

fi
if [ ! -f "${TLS_DIR}/cert.pem" ] || [ ! -f "${TLS_DIR}/key.pem" ]; then
  echo "éè¯¯ï¼Courier-MTA èªç­¾å TLS è¯ä¹¦çæå¤±è´¥"
  exit 1
fi
echo "Courier-MTA èªç­¾å TLS è¯ä¹¦å°±ç»ª"

cat "$TLS_DIR/key.pem" "$TLS_DIR/cert.pem" > "$TLS_DIR/courier.pem"
chown root:courier "$TLS_DIR/courier.pem" 2>/dev/null || true
chmod 640 "$TLS_DIR/courier.pem"
chmod 755 "$TLS_DIR"

set_courier_kv() {
  local file="$1" key="$2" value="$3"
  touch "$file"
  if grep -q "^${key}=" "$file" 2>/dev/null; then
    sed -i "s|^${key}=.*|${key}=${value}|" "$file"
  else
    printf '%s=%s\n' "$key" "$value" >> "$file"
  fi
}

for cfg in /etc/courier/esmtpd /etc/courier/esmtpd-msa; do
  set_courier_kv "$cfg" ESMTPDSTART YES
  set_courier_kv "$cfg" ADDRESS 0
  set_courier_kv "$cfg" BOFHCHECKDNS 0
  set_courier_kv "$cfg" ESMTPAUTH '"LOGIN PLAIN"'
  set_courier_kv "$cfg" ESMTPDSTARTTLS YES
  set_courier_kv "$cfg" TLS_CERTFILE "$TLS_DIR/courier.pem"
done
set_courier_kv /etc/courier/esmtpd PORT 25
set_courier_kv /etc/courier/esmtpd AUTH_REQUIRED 0
set_courier_kv /etc/courier/esmtpd-msa PORT 587
set_courier_kv /etc/courier/esmtpd-msa AUTH_REQUIRED 1

if command -v userdb >/dev/null 2>&1 && command -v userdbpw >/dev/null 2>&1; then
  COURIER_UID="$(id -u mail 2>/dev/null || echo 8)"
  COURIER_GID="$(id -g mail 2>/dev/null || echo 8)"
  userdb "$SMTP_AUTH_USER" set home=/var/mail/courier uid="$COURIER_UID" gid="$COURIER_GID" mail=/var/mail/courier
  HASHED="$(printf '%s\n%s\n' "$SMTP_PASS" "$SMTP_PASS" | userdbpw -md5)"
  userdb "$SMTP_AUTH_USER" set systempw="$HASHED"
  makeuserdb
else
  echo "è­¦åï¼ç¼ºå° userdb/userdbpwï¼Courier SMTP AUTH è´¦æ·éäººå·¥è¡¥é½"
fi

if [ -f /etc/courier/authdaemonrc ]; then
  sed -i 's/^authmodulelist=.*/authmodulelist="authuserdb"/' /etc/courier/authdaemonrc
fi
systemctl enable courier-authdaemon 2>/dev/null || true
systemctl restart courier-authdaemon 2>/dev/null || true
for svc in courier-mta courier-msa courier-esmtpd courier-mta-ssl; do
  if systemctl list-unit-files "$svc.service" >/dev/null 2>&1; then
    systemctl enable "$svc" 2>/dev/null || true
    systemctl restart "$svc" 2>/dev/null || true
  fi
done
sleep 3
ss -lntp | grep -E ':25|:587' || true
echo "SMTP STARTTLS æ£æ¥ï¼587ï¼ï¼"
echo | openssl s_client -connect 127.0.0.1:587 -starttls smtp -servername "$MAIL_HOSTNAME" 2>/dev/null | grep -E 'Verify return code|subject=' || true
echo "æç¤ºï¼Courier-MTA æ å·²å®è£ dkimpy ç­¾åè¾å© /usr/local/bin/courier-dkim-signï¼Courier åç SMTP æµæ°´çº¿ä¸æ¯æ Postfix milter çº§å¤´é¨é­æ¹ï¼æå®éªæ äº¤ä»ã"
PUBLIC_IP="$(dig +short myip.opendns.com @resolver1.opendns.com || true)"
DKIM_PUBLIC="$(awk 'NF && $1 !~ /---/ { printf "%s", $0 }' "$DKIM_DIR/$DKIM_SELECTOR.pub")"
echo "__KUMOMTA_RESULT__"
printf '{"public_ip":"%s","dkim_public":"%s","selector":"%s"}\n' "$PUBLIC_IP" "$DKIM_PUBLIC" "$DKIM_SELECTOR"
