first commit
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
# kerio-queue-clean.sh
|
||||||
|
|
||||||
|
Entfernt Nachrichten aus der Kerio-Connect-Queue (`/opt/kerio/mailserver/store/queue`)
|
||||||
|
anhand von Filterregeln, z. B. wenn ein kompromittiertes Konto die Queue mit Spam flutet.
|
||||||
|
|
||||||
|
- durchsucht die Queue rekursiv (inkl. Unterverzeichnisse)
|
||||||
|
- schaut in `<name>.env` (Umschlag) und `<name>.eml` (Header/Inhalt)
|
||||||
|
- entfernt immer alle Dateien `<name>.*` einer Nachricht
|
||||||
|
- Kommandozeile **oder** ncurses-Oberfläche (`dialog` bzw. `whiptail`)
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ncurses-Oberfläche
|
||||||
|
sudo ./kerio-queue-clean.sh
|
||||||
|
|
||||||
|
# erst nur ansehen
|
||||||
|
sudo ./kerio-queue-clean.sh -s hacked@firma.de -n
|
||||||
|
|
||||||
|
# dann entfernen: Dienst kurz stoppen, Backup + Log
|
||||||
|
sudo ./kerio-queue-clean.sh -s hacked@firma.de -S -b /root/queue-backup -l /var/log/kerio-queue-clean.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filter
|
||||||
|
|
||||||
|
| Option | prüft |
|
||||||
|
|---|---|
|
||||||
|
| `-s` Absender | Umschlag-Absender / Auth-User aus `.env`, `From:`, `Return-Path:`, `Sender:` |
|
||||||
|
| `-r` Empfänger | Umschlag-Empfänger aus `.env`, `To:`, `Cc:`, `Delivered-To:` |
|
||||||
|
| `-j` Betreff | `Subject:` |
|
||||||
|
| `-c` Inhalt | komplette `.eml` (Header + Body) |
|
||||||
|
|
||||||
|
Mehrere Muster des gleichen Typs sind ODER-verknüpft, verschiedene Typen UND-verknüpft.
|
||||||
|
Standard ist ein Teilstring-Vergleich, mit `-x` werden die Muster als Regex ausgewertet. Groß-/Kleinschreibung wird nie unterschieden.
|
||||||
|
|
||||||
|
Mit `--inspect` siehst du den Aufbau einiger `.env`-Dateien, um die Filter zu prüfen.
|
||||||
|
|
||||||
|
Alle Optionen zeigt `./kerio-queue-clean.sh --help`.
|
||||||
Executable
+462
@@ -0,0 +1,462 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# kerio-queue-clean.sh
|
||||||
|
#
|
||||||
|
# Entfernt Nachrichten aus der Kerio-Connect-Queue anhand von Filterregeln
|
||||||
|
# (Absender, Empfaenger, Betreff, Inhalt). Gedacht fuer den Fall, dass ein
|
||||||
|
# kompromittiertes Konto die Queue mit Spam flutet.
|
||||||
|
#
|
||||||
|
# Jede Nachricht besteht aus <name>.eml (Mail) und <name>.env (Umschlag).
|
||||||
|
# Die Queue darf Unterverzeichnisse haben; es wird rekursiv gesucht.
|
||||||
|
#
|
||||||
|
# Aufruf ohne Parameter (oder mit -i) startet die ncurses-Oberflaeche
|
||||||
|
# (dialog oder whiptail). Siehe --help.
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
VERSION="1.0"
|
||||||
|
QUEUE_DIR="/opt/kerio/mailserver/store/queue"
|
||||||
|
SERVICE_NAME="kerio-connect"
|
||||||
|
BACKUP_DIR=""
|
||||||
|
LOG_FILE=""
|
||||||
|
USE_REGEX=0
|
||||||
|
DRY_RUN=0
|
||||||
|
ASSUME_YES=0
|
||||||
|
STOP_SERVICE=0
|
||||||
|
TUI=0
|
||||||
|
INSPECT=0
|
||||||
|
|
||||||
|
SENDERS=()
|
||||||
|
RCPTS=()
|
||||||
|
SUBJECTS=()
|
||||||
|
CONTENTS=()
|
||||||
|
|
||||||
|
TMPDIR_KQ=$(mktemp -d "${TMPDIR:-/tmp}/kerio-queue-clean.XXXXXX") || exit 1
|
||||||
|
trap 'rm -rf "$TMPDIR_KQ"' EXIT
|
||||||
|
RESULT_FILE="$TMPDIR_KQ/result.tsv"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Matcher: liest <name>.env (optional) und <name>.eml und gibt bei Treffer
|
||||||
|
# eine Zeile "Umschlag-Absender <TAB> From-Header <TAB> Betreff" aus.
|
||||||
|
# Muster kommen ueber ENVIRON (keine Escape-Verarbeitung wie bei -v).
|
||||||
|
# - mehrere Muster desselben Typs -> ODER
|
||||||
|
# - verschiedene Typen kombiniert -> UND
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
read -r -d '' AWK_MATCH <<'AWK'
|
||||||
|
function hit(s, pats, n, a, i, p) {
|
||||||
|
n = split(pats, a, "\n")
|
||||||
|
s = tolower(s)
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
if (a[i] == "") continue
|
||||||
|
p = tolower(a[i])
|
||||||
|
if (rx) { if (s ~ p) return 1 }
|
||||||
|
else if (index(s, p)) return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
function hdr_done( name, val) {
|
||||||
|
if (cur == "") return
|
||||||
|
name = tolower(cur); sub(/:.*/, "", name)
|
||||||
|
val = cur; sub(/^[^:]*:[ \t]*/, "", val)
|
||||||
|
if (name == "from" || name == "return-path" || name == "sender") {
|
||||||
|
hsend = hsend "\n" val
|
||||||
|
if (name == "from" && hfrom == "") hfrom = val
|
||||||
|
} else if (name == "to" || name == "cc" || name == "delivered-to" || name == "x-original-to") {
|
||||||
|
hrcpt = hrcpt "\n" val
|
||||||
|
} else if (name == "subject" && subj == "") {
|
||||||
|
subj = val
|
||||||
|
}
|
||||||
|
cur = ""
|
||||||
|
}
|
||||||
|
function clean(s) { gsub(/[\t\r\n]+/, " ", s); sub(/^ +/, "", s); return s }
|
||||||
|
BEGIN {
|
||||||
|
sp = ENVIRON["KQ_SENDERS"]; rp = ENVIRON["KQ_RCPTS"]
|
||||||
|
jp = ENVIRON["KQ_SUBJECTS"]; cp = ENVIRON["KQ_CONTENTS"]
|
||||||
|
rx = ENVIRON["KQ_REGEX"] + 0; envf = ENVIRON["KQ_ENVF"]
|
||||||
|
}
|
||||||
|
FILENAME == envf {
|
||||||
|
sub(/\r$/, "")
|
||||||
|
if ($0 != "") envl[++en] = $0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{
|
||||||
|
sub(/\r$/, "")
|
||||||
|
if (!inbody) {
|
||||||
|
if ($0 == "") { hdr_done(); inbody = 1; if (cp == "" || chit) exit; next }
|
||||||
|
if ($0 ~ /^[ \t]/) { sub(/^[ \t]+/, " "); cur = cur $0 }
|
||||||
|
else { hdr_done(); cur = $0 }
|
||||||
|
if (cp != "" && !chit && hit($0, cp)) chit = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (cp != "" && hit($0, cp)) { chit = 1; exit }
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
hdr_done()
|
||||||
|
# Umschlag auswerten: Zeilen mit Schluesselwort vor der Adresse zuordnen,
|
||||||
|
# sonst gilt: erste Adresszeile = Absender, weitere = Empfaenger.
|
||||||
|
kw = 0
|
||||||
|
for (i = 1; i <= en; i++) {
|
||||||
|
pre = tolower(envl[i]); sub(/[<@].*/, "", pre)
|
||||||
|
if (pre ~ /from|sender|return|auth|user/) { esend = esend "\n" envl[i]; if (efirst == "") efirst = envl[i]; kw = 1 }
|
||||||
|
else if (pre ~ /rcpt|recip|to/) { ercpt = ercpt "\n" envl[i]; kw = 1 }
|
||||||
|
}
|
||||||
|
if (!kw) {
|
||||||
|
for (i = 1; i <= en; i++) {
|
||||||
|
if (envl[i] !~ /@|<>/) continue
|
||||||
|
if (efirst == "") { efirst = envl[i]; esend = envl[i] }
|
||||||
|
else ercpt = ercpt "\n" envl[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = 1
|
||||||
|
if (sp != "" && !hit(esend "\n" hsend, sp)) ok = 0
|
||||||
|
if (rp != "" && !hit(ercpt "\n" hrcpt, rp)) ok = 0
|
||||||
|
if (jp != "" && !hit(subj, jp)) ok = 0
|
||||||
|
if (cp != "" && !chit) ok = 0
|
||||||
|
if (ok) printf "%s\t%s\t%s\n", clean(efirst), clean(hfrom), clean(subj)
|
||||||
|
}
|
||||||
|
AWK
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Hilfsfunktionen
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
die() { echo "FEHLER: $*" >&2; exit 1; }
|
||||||
|
info() { echo "$*" >&2; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
kerio-queue-clean.sh $VERSION - Mails nach Filterregeln aus der Kerio-Connect-Queue entfernen
|
||||||
|
|
||||||
|
Aufruf:
|
||||||
|
$0 ncurses-Oberflaeche starten
|
||||||
|
$0 [Optionen] Kommandozeilenmodus
|
||||||
|
|
||||||
|
Filter (mehrfach angebbar; gleicher Typ = ODER, verschiedene Typen = UND):
|
||||||
|
-s, --sender MUSTER Absender (Umschlag/.env, From:, Return-Path:, Sender:)
|
||||||
|
-r, --recipient MUSTER Empfaenger (Umschlag/.env, To:, Cc:, Delivered-To:)
|
||||||
|
-j, --subject MUSTER Betreff
|
||||||
|
-c, --content MUSTER beliebiger Text in der .eml (Header + Body)
|
||||||
|
-x, --regex Muster als erweiterte Regex statt Teilstring
|
||||||
|
(Gross-/Kleinschreibung wird immer ignoriert)
|
||||||
|
|
||||||
|
Aktionen:
|
||||||
|
-n, --dry-run nur auflisten, nichts loeschen
|
||||||
|
-y, --yes ohne Rueckfrage loeschen
|
||||||
|
-b, --backup DIR Treffer nach DIR verschieben statt loeschen
|
||||||
|
-S, --stop-service Kerio-Dienst waehrend des Loeschens stoppen (empfohlen)
|
||||||
|
--service NAME Name des Dienstes (Standard: $SERVICE_NAME)
|
||||||
|
-l, --log DATEI geloeschte Mails protokollieren
|
||||||
|
|
||||||
|
Sonstiges:
|
||||||
|
-d, --queue DIR Queue-Verzeichnis (Standard: $QUEUE_DIR)
|
||||||
|
-i, --tui ncurses-Oberflaeche erzwingen
|
||||||
|
--inspect [N] N Beispiel-.env-Dateien + Header zeigen (Standard 3)
|
||||||
|
-h, --help diese Hilfe
|
||||||
|
|
||||||
|
Beispiele:
|
||||||
|
$0 -s hacked@firma.de -n
|
||||||
|
$0 -s hacked@firma.de -S -b /root/queue-backup -l /var/log/kerio-queue-clean.log
|
||||||
|
$0 -x -s '^.*@firma\.de' -j 'invoice|payment' -y
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
export_patterns() {
|
||||||
|
local IFS=$'\n'
|
||||||
|
export KQ_SENDERS="${SENDERS[*]}"
|
||||||
|
export KQ_RCPTS="${RCPTS[*]}"
|
||||||
|
export KQ_SUBJECTS="${SUBJECTS[*]}"
|
||||||
|
export KQ_CONTENTS="${CONTENTS[*]}"
|
||||||
|
export KQ_REGEX="$USE_REGEX"
|
||||||
|
}
|
||||||
|
|
||||||
|
have_filter() {
|
||||||
|
(( ${#SENDERS[@]} + ${#RCPTS[@]} + ${#SUBJECTS[@]} + ${#CONTENTS[@]} > 0 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
check_queue() {
|
||||||
|
[[ -d $QUEUE_DIR ]] || { echo "Queue-Verzeichnis '$QUEUE_DIR' existiert nicht."; return 1; }
|
||||||
|
[[ -r $QUEUE_DIR ]] || { echo "Keine Leserechte auf '$QUEUE_DIR' (als root starten?)."; return 1; }
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# scan_queue MODE (MODE: quiet | stderr | gauge)
|
||||||
|
# Schreibt Treffer nach $RESULT_FILE: basis<TAB>umschlag<TAB>from<TAB>betreff
|
||||||
|
scan_queue() {
|
||||||
|
local mode=$1 total i=0 eml base res last=-1 pct
|
||||||
|
: > "$RESULT_FILE"
|
||||||
|
export_patterns
|
||||||
|
total=$(find "$QUEUE_DIR" -type f -name '*.eml' 2>/dev/null | wc -l)
|
||||||
|
(( total == 0 )) && { [[ $mode == gauge ]] && echo 100; return 0; }
|
||||||
|
while IFS= read -r -d '' eml; do
|
||||||
|
i=$((i + 1))
|
||||||
|
base=${eml%.eml}
|
||||||
|
if [[ -f $base.env ]]; then
|
||||||
|
res=$(KQ_ENVF="$base.env" awk "$AWK_MATCH" "$base.env" "$eml" 2>/dev/null)
|
||||||
|
else
|
||||||
|
res=$(KQ_ENVF="" awk "$AWK_MATCH" "$eml" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
[[ -n $res ]] && printf '%s\t%s\n' "$base" "$res" >> "$RESULT_FILE"
|
||||||
|
pct=$(( i * 100 / total ))
|
||||||
|
if (( pct != last )); then
|
||||||
|
last=$pct
|
||||||
|
case $mode in
|
||||||
|
gauge) echo "$pct" ;;
|
||||||
|
stderr) printf '\rDurchsuche Queue: %3d%% (%d/%d)' "$pct" "$i" "$total" >&2 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done < <(find "$QUEUE_DIR" -type f -name '*.eml' -print0 2>/dev/null)
|
||||||
|
[[ $mode == stderr ]] && echo >&2
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
service_ctl() { # service_ctl stop|start
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl "$1" "$SERVICE_NAME"
|
||||||
|
elif [[ -x /etc/init.d/$SERVICE_NAME ]]; then
|
||||||
|
"/etc/init.d/$SERVICE_NAME" "$1"
|
||||||
|
else
|
||||||
|
echo "Dienst '$SERVICE_NAME' kann nicht gesteuert werden." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# delete_matches LISTE MODE (MODE: quiet | stderr | gauge)
|
||||||
|
# Loescht (oder verschiebt) alle Dateien <basis>.* der Eintraege in LISTE.
|
||||||
|
delete_matches() {
|
||||||
|
local list=$1 mode=$2 total i=0 base env from subj rel f done_cnt=0 err_cnt=0
|
||||||
|
total=$(wc -l < "$list")
|
||||||
|
(( total == 0 )) && return 0
|
||||||
|
while IFS=$'\t' read -r base env from subj; do
|
||||||
|
i=$((i + 1))
|
||||||
|
local files=()
|
||||||
|
for f in "$base".*; do [[ -e $f ]] && files+=("$f"); done
|
||||||
|
if (( ${#files[@]} > 0 )); then
|
||||||
|
if [[ -n $BACKUP_DIR ]]; then
|
||||||
|
rel=${base#"$QUEUE_DIR"/}
|
||||||
|
mkdir -p "$BACKUP_DIR/$(dirname "$rel")" &&
|
||||||
|
mv -f -- "${files[@]}" "$BACKUP_DIR/$(dirname "$rel")/"
|
||||||
|
else
|
||||||
|
rm -f -- "${files[@]}"
|
||||||
|
fi
|
||||||
|
if (( $? == 0 )); then
|
||||||
|
done_cnt=$((done_cnt + 1))
|
||||||
|
[[ -n $LOG_FILE ]] && printf '%s\t%s\t%s\t%s\t%s\n' \
|
||||||
|
"$(date '+%F %T')" "$base" "$env" "$from" "$subj" >> "$LOG_FILE"
|
||||||
|
else
|
||||||
|
err_cnt=$((err_cnt + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
case $mode in
|
||||||
|
gauge) echo $(( i * 100 / total )) ;;
|
||||||
|
stderr) (( i % 100 == 0 || i == total )) && printf '\rEntferne: %d/%d' "$i" "$total" >&2 ;;
|
||||||
|
esac
|
||||||
|
done < "$list"
|
||||||
|
[[ $mode == stderr ]] && echo >&2
|
||||||
|
echo "$done_cnt $err_cnt" > "$TMPDIR_KQ/delete.stat"
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_with_service() { # delete_with_service LISTE MODE
|
||||||
|
local stopped=0
|
||||||
|
if (( STOP_SERVICE )); then
|
||||||
|
if service_ctl stop >/dev/null 2>&1; then stopped=1
|
||||||
|
else echo "Warnung: Dienst konnte nicht gestoppt werden." >&2; fi
|
||||||
|
fi
|
||||||
|
delete_matches "$1" "$2"
|
||||||
|
(( stopped )) && service_ctl start >/dev/null 2>&1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
inspect_queue() {
|
||||||
|
local n=${1:-3} env eml
|
||||||
|
check_queue || exit 1
|
||||||
|
while IFS= read -r -d '' env; do
|
||||||
|
eml=${env%.env}.eml
|
||||||
|
echo "===== $env ====="
|
||||||
|
cat -- "$env"
|
||||||
|
if [[ -f $eml ]]; then
|
||||||
|
echo "----- Header aus $(basename "$eml") -----"
|
||||||
|
awk '/^\r?$/ {exit} /^(From|To|Cc|Subject|Return-Path|Sender):/' "$eml"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
n=$((n - 1)); (( n <= 0 )) && break
|
||||||
|
done < <(find "$QUEUE_DIR" -type f -name '*.env' -print0)
|
||||||
|
}
|
||||||
|
|
||||||
|
print_results() {
|
||||||
|
local base env from subj
|
||||||
|
printf '%-40s %-30s %s\n' "DATEI" "ABSENDER" "BETREFF"
|
||||||
|
while IFS=$'\t' read -r base env from subj; do
|
||||||
|
printf '%-40s %-30s %s\n' "${base#"$QUEUE_DIR"/}" "${env:-$from}" "$subj"
|
||||||
|
done < "$RESULT_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Kommandozeilenmodus
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
run_cli() {
|
||||||
|
check_queue >&2 || exit 1
|
||||||
|
have_filter || die "Mindestens ein Filter (-s/-r/-j/-c) ist noetig. Siehe --help."
|
||||||
|
|
||||||
|
scan_queue stderr
|
||||||
|
local count
|
||||||
|
count=$(wc -l < "$RESULT_FILE")
|
||||||
|
print_results
|
||||||
|
echo
|
||||||
|
echo "Treffer: $count"
|
||||||
|
(( count == 0 )) && exit 0
|
||||||
|
(( DRY_RUN )) && { echo "Trockenlauf - nichts geloescht."; exit 0; }
|
||||||
|
|
||||||
|
if (( ! ASSUME_YES )); then
|
||||||
|
local action="LOESCHEN"
|
||||||
|
[[ -n $BACKUP_DIR ]] && action="nach $BACKUP_DIR VERSCHIEBEN"
|
||||||
|
read -r -p "$count Nachrichten $action? Zum Bestaetigen 'ja' eingeben: " ans
|
||||||
|
[[ $ans == ja ]] || { echo "Abgebrochen."; exit 0; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
delete_with_service "$RESULT_FILE" stderr
|
||||||
|
local ok err
|
||||||
|
read -r ok err < "$TMPDIR_KQ/delete.stat"
|
||||||
|
echo "Erledigt: $ok entfernt, $err Fehler."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ncurses-Oberflaeche (dialog oder whiptail)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
DLG=""
|
||||||
|
ui() { "$DLG" --backtitle "Kerio Connect Queue Cleaner $VERSION" "$@" 3>&1 1>&2 2>&3; }
|
||||||
|
|
||||||
|
join_list() { local IFS=';'; echo "$*"; }
|
||||||
|
split_list() { # split_list ARRAYNAME "a;b;c"
|
||||||
|
local -n _arr=$1
|
||||||
|
local item
|
||||||
|
_arr=()
|
||||||
|
IFS=';' read -r -a _tmp <<< "$2"
|
||||||
|
for item in "${_tmp[@]}"; do
|
||||||
|
item=$(sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<< "$item")
|
||||||
|
[[ -n $item ]] && _arr+=("$item")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_list() { # edit_list ARRAYNAME TITEL
|
||||||
|
local -n _ref=$1
|
||||||
|
local val
|
||||||
|
val=$(ui --title "$2" --inputbox \
|
||||||
|
"Mehrere Muster mit ';' trennen (ODER-Verknuepfung).\nLeer lassen = Filter aus." \
|
||||||
|
10 70 "$(join_list "${_ref[@]}")") || return
|
||||||
|
split_list "$1" "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
yn() { (( $1 )) && echo "an" || echo "aus"; }
|
||||||
|
|
||||||
|
tui_search_and_delete() {
|
||||||
|
local msg rows cols count
|
||||||
|
if ! msg=$(check_queue); then ui --msgbox "$msg" 8 70; return; fi
|
||||||
|
if ! have_filter; then ui --msgbox "Bitte zuerst mindestens einen Filter setzen." 8 60; return; fi
|
||||||
|
|
||||||
|
scan_queue gauge | ui --title "Suche" --gauge "Durchsuche $QUEUE_DIR ..." 8 70 0
|
||||||
|
count=$(wc -l < "$RESULT_FILE")
|
||||||
|
if (( count == 0 )); then ui --msgbox "Keine passenden Nachrichten gefunden." 8 50; return; fi
|
||||||
|
|
||||||
|
rows=$(tput lines 2>/dev/null || echo 24); cols=$(tput cols 2>/dev/null || echo 80)
|
||||||
|
local selected="$TMPDIR_KQ/selected.tsv"
|
||||||
|
|
||||||
|
if (( count <= 2000 )); then
|
||||||
|
local args=() i=0 base env from subj w=$((cols - 20))
|
||||||
|
while IFS=$'\t' read -r base env from subj; do
|
||||||
|
i=$((i + 1))
|
||||||
|
local text="${env:-$from} | $subj"
|
||||||
|
args+=("$i" " ${text:0:$w}" on)
|
||||||
|
done < "$RESULT_FILE"
|
||||||
|
local choice
|
||||||
|
choice=$(ui --title "$count Treffer" --separate-output --checklist \
|
||||||
|
"Zu entfernende Nachrichten (Leertaste = abwaehlen):" \
|
||||||
|
$((rows - 4)) $((cols - 6)) $((rows - 12)) "${args[@]}") || return
|
||||||
|
[[ -z $choice ]] && return
|
||||||
|
awk 'NR==FNR {sel[$1]; next} (FNR in sel)' <(echo "$choice") "$RESULT_FILE" > "$selected"
|
||||||
|
else
|
||||||
|
print_results > "$TMPDIR_KQ/list.txt"
|
||||||
|
ui --title "$count Treffer" --scrolltext --textbox "$TMPDIR_KQ/list.txt" $((rows - 4)) $((cols - 6))
|
||||||
|
cp "$RESULT_FILE" "$selected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$(wc -l < "$selected")
|
||||||
|
local action="LOESCHEN"
|
||||||
|
[[ -n $BACKUP_DIR ]] && action="nach $BACKUP_DIR VERSCHIEBEN"
|
||||||
|
local svc=""
|
||||||
|
(( STOP_SERVICE )) && svc="\n\nDer Dienst '$SERVICE_NAME' wird dafuer kurz gestoppt."
|
||||||
|
ui --title "Bestaetigung" --defaultno --yesno "$count Nachrichten wirklich $action?$svc" 10 70 || return
|
||||||
|
|
||||||
|
delete_with_service "$selected" gauge | ui --title "Entferne" --gauge "Entferne Nachrichten ..." 8 70 0
|
||||||
|
local ok err
|
||||||
|
read -r ok err < "$TMPDIR_KQ/delete.stat"
|
||||||
|
ui --msgbox "Fertig.\n\nEntfernt: $ok\nFehler: $err" 10 50
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tui() {
|
||||||
|
if command -v dialog >/dev/null 2>&1; then DLG=dialog
|
||||||
|
elif command -v whiptail >/dev/null 2>&1; then DLG=whiptail
|
||||||
|
else die "Weder 'dialog' noch 'whiptail' installiert (apt install dialog). Alternativ Parameter nutzen, siehe --help."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local choice val
|
||||||
|
while true; do
|
||||||
|
choice=$(ui --title "Hauptmenue" --cancel-button "Beenden" --menu \
|
||||||
|
"Filter setzen und dann 'Suchen' waehlen.\nGleicher Typ = ODER, verschiedene Typen = UND." 22 78 12 \
|
||||||
|
1 "Queue-Pfad : $QUEUE_DIR" \
|
||||||
|
2 "Absender : $(join_list "${SENDERS[@]}")" \
|
||||||
|
3 "Empfaenger : $(join_list "${RCPTS[@]}")" \
|
||||||
|
4 "Betreff : $(join_list "${SUBJECTS[@]}")" \
|
||||||
|
5 "Inhalt (.eml) : $(join_list "${CONTENTS[@]}")" \
|
||||||
|
6 "Regex-Modus : $(yn $USE_REGEX)" \
|
||||||
|
7 "Backup statt rm : ${BACKUP_DIR:-aus}" \
|
||||||
|
8 "Dienst stoppen : $(yn $STOP_SERVICE) ($SERVICE_NAME)" \
|
||||||
|
9 "Protokoll : ${LOG_FILE:-aus}" \
|
||||||
|
S ">> Suchen und entfernen" \
|
||||||
|
I " Beispiel .env/Header ansehen" \
|
||||||
|
Q " Beenden") || break
|
||||||
|
case $choice in
|
||||||
|
1) val=$(ui --inputbox "Queue-Verzeichnis:" 8 70 "$QUEUE_DIR") && QUEUE_DIR=${val%/} ;;
|
||||||
|
2) edit_list SENDERS "Absender" ;;
|
||||||
|
3) edit_list RCPTS "Empfaenger" ;;
|
||||||
|
4) edit_list SUBJECTS "Betreff" ;;
|
||||||
|
5) edit_list CONTENTS "Inhalt" ;;
|
||||||
|
6) USE_REGEX=$(( !USE_REGEX )) ;;
|
||||||
|
7) val=$(ui --inputbox "Backup-Verzeichnis (leer = endgueltig loeschen):" 8 70 "$BACKUP_DIR") && BACKUP_DIR=${val%/} ;;
|
||||||
|
8) STOP_SERVICE=$(( !STOP_SERVICE )) ;;
|
||||||
|
9) val=$(ui --inputbox "Protokolldatei (leer = aus):" 8 70 "${LOG_FILE:-/var/log/kerio-queue-clean.log}") && LOG_FILE=$val ;;
|
||||||
|
S) tui_search_and_delete ;;
|
||||||
|
I) inspect_queue 3 > "$TMPDIR_KQ/inspect.txt" 2>&1
|
||||||
|
ui --title "Beispiele" --scrolltext --textbox "$TMPDIR_KQ/inspect.txt" 22 90 ;;
|
||||||
|
Q) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
clear
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Parameter
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[[ $# -eq 0 ]] && TUI=1
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-s|--sender) SENDERS+=("${2:?$1 braucht ein Argument}"); shift ;;
|
||||||
|
-r|--recipient) RCPTS+=("${2:?$1 braucht ein Argument}"); shift ;;
|
||||||
|
-j|--subject) SUBJECTS+=("${2:?$1 braucht ein Argument}"); shift ;;
|
||||||
|
-c|--content) CONTENTS+=("${2:?$1 braucht ein Argument}"); shift ;;
|
||||||
|
-x|--regex) USE_REGEX=1 ;;
|
||||||
|
-n|--dry-run) DRY_RUN=1 ;;
|
||||||
|
-y|--yes) ASSUME_YES=1 ;;
|
||||||
|
-b|--backup) BACKUP_DIR="${2:?$1 braucht ein Argument}"; BACKUP_DIR=${BACKUP_DIR%/}; shift ;;
|
||||||
|
-S|--stop-service) STOP_SERVICE=1 ;;
|
||||||
|
--service) SERVICE_NAME="${2:?$1 braucht ein Argument}"; shift ;;
|
||||||
|
-l|--log) LOG_FILE="${2:?$1 braucht ein Argument}"; shift ;;
|
||||||
|
-d|--queue) QUEUE_DIR="${2:?$1 braucht ein Argument}"; QUEUE_DIR=${QUEUE_DIR%/}; shift ;;
|
||||||
|
-i|--tui) TUI=1 ;;
|
||||||
|
--inspect) INSPECT=1; [[ ${2:-} =~ ^[0-9]+$ ]] && { INSPECT=$2; shift; } ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) usage >&2; die "Unbekannte Option: $1" ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( INSPECT )); then inspect_queue "$INSPECT"; exit 0; fi
|
||||||
|
if (( TUI )); then run_tui; else run_cli; fi
|
||||||
Reference in New Issue
Block a user