occ_scan_bad_signature.sh was replaced by scan_bad_signature.sh

This commit is contained in:
2026-09-14 23:58:32 +02:00
parent f108a71a80
commit 54f16e0a20
-887
View File
@@ -1,887 +0,0 @@
#!/usr/bin/env bash
CUR_IFS=$IFS
script_name="$(basename $(realpath $0))"
script_dir="$(dirname $(realpath $0))"
conf_dir="${script_dir}/conf"
snippet_dir="${script_dir}/snippets"
report_dir="${script_dir}/reports"
declare -a unsorted_website_arr
declare -a website_arr
declare -a unsorted_account_arr
declare -a account_arr
declare -a unsorted_selected_user_arr
declare -a selected_user_arr
LOCK_DIR="/tmp/${script_name%%.*}.LOCK"
log_file="${LOCK_DIR}/${script_name%%.*}.log"
run_date=$(date +%Y-%m-%d-%H%M)
# =============
# --- Some functions
# =============
usage() {
[[ -n "$1" ]] && error "$1"
[[ $terminal ]] && echo -e "
\033[1mUsage:\033[m
$(basename $0) -s <website>
\033[1mDescription\033[m
Scans the encrypted files of one, several or all Nextcloud accounts
and reports every file that fails to decrypt (e.g. 'Bad Signature'
errors thrown by Server-Side Encryption). Every file is read once
and its content discarded - the script is READ-ONLY and changes,
moves or deletes nothing.
You will be asked interactively which account(s) to scan:
- a blank separated list of account names
- or 'all' to scan every existing account
\033[1mOptions\033[m
-s <website>
The site of the nextcloud instance.
\033[1mExample:\033[m
Runs this script on system 'cloud-01.oopen.de'
$(basename $0) -s cloud-01.oopen.de
"
clean_up 1
}
clean_up() {
# Perform program exit housekeeping
[[ -n "$scan_php_file" ]] && rm -f "$scan_php_file" 2> /dev/null
rm -rf "$LOCK_DIR"
blank_line
exit $1
}
is_number() {
return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);
# - also possible
# -
#[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1
#return $([[ ! -z "${1##*[!0-9]*}" ]])
}
echononl(){
if $terminal ; then
echo X\\c > /tmp/shprompt$$
if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then
echo -e -n "$*\\c" 1>&2
else
echo -e -n "$*" 1>&2
fi
rm /tmp/shprompt$$
fi
}
echo_done() {
if $terminal ; then
echo -e "\033[75G[ \033[32mdone\033[m ]"
fi
}
echo_ok() {
if $terminal ; then
echo -e "\033[75G[ \033[32mok\033[m ]"
fi
}
echo_warning() {
if $terminal ; then
echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]"
fi
}
echo_failed(){
if $terminal ; then
echo -e "\033[75G[ \033[1;31mfailed\033[m ]"
fi
}
echo_skipped() {
if $terminal ; then
echo -e "\033[75G[ \033[37mskipped\033[m ]"
fi
}
echo_funde() {
if $terminal ; then
echo -e "\033[75G[ \033[33m\033[1mFunde!\033[m ]"
fi
}
fatal (){
echo ""
echo ""
if $terminal ; then
echo -e " [ \033[31m\033[1mFatal\033[m ]: \033[37m\033[1m$*\033[m"
echo ""
echo -e " \033[31m\033[1mScript will be interrupted..\033[m!"
else
echo " [ Fatal ]: $*"
echo ""
echo " Script was terminated...."
fi
clean_up 1
}
error(){
echo ""
if $terminal ; then
echo -e " [ \033[31m\033[1mError\033[m ]: $*"
else
echo " [ Error ]: $*"
fi
echo ""
}
warn (){
if $terminal ; then
echo ""
echo -e " [ \033[33m\033[1mWarning\033[m ]: $*"
echo ""
fi
}
info (){
if $terminal ; then
echo ""
echo -e " [ \033[32m\033[1mInfo\033[m ]: $*"
echo ""
fi
}
# - Remove leading/trailling whitespaces
# -
trim() {
local var="$*"
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
## - Check if a given array (parameter 2) contains a given string (parameter 1)
## -
containsElement () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
## - Percentage (1 decimal) of parameter 1 (part) against parameter 2 (total)
## - Returns "0.0" if total is empty, zero or non-numeric.
## -
calc_percent() {
local _part="$1"
local _total="$2"
if [[ -z "$_total" ]] || ! [[ "$_total" =~ ^[0-9]+$ ]] || [[ "$_total" -eq 0 ]] ; then
echo "0.0"
else
awk -v b="$_part" -v t="$_total" 'BEGIN { printf "%.1f", (b/t)*100 }'
fi
}
blank_line() {
if $terminal ; then
echo ""
fi
}
# - Running in a terminal?
# -
if [[ -t 1 ]] ; then
terminal=true
else
terminal=false
fi
# ----------
# - Jobhandling
# ----------
if pgrep -f "$(basename $0)" | grep -q -v $$ ; then
msg="A previos instance of script \"`basename $0`\" seems already be running."
echo ""
if $terminal ; then
echo -e "[ \033[31m\033[1mFatal\033[m ]: $msg"
echo ""
echo -e " \033[31m\033[1mScript was interupted\033[m!"
else
echo " [ Fatal ]: $msg"
echo ""
echo " Script was interupted!"
fi
echo
exit 1
else
if [[ -d "$LOCK_DIR" ]] ; then
rm -rf "$LOCK_DIR" 2> /dev/null
fi
fi
# - If job already runs, stop execution..
# -
if mkdir "$LOCK_DIR" 2> /dev/null ; then
# - Remove lockdir when the script finishes, or when it receives a signal
# -
trap clean_up SIGHUP SIGINT SIGTERM
else
msg="A previos instance of script \"`basename $0`\" seems already be running."
echo ""
if $terminal ; then
echo -e "[ \033[31m\033[1mFatal\033[m ]: $msg"
echo ""
echo -e " \033[31m\033[1mScript was interupted\033[m!"
else
echo " [ Fatal ]: $msg"
echo ""
echo " Script was interupted!"
fi
echo
exit 1
fi
# -------------
# - Read in Commandline arguments
# -------------
while getopts hs: opt ; do
case $opt in
h) usage ;;
s) WEBSITE=$OPTARG ;;
\?) usage
esac
done
if [[ -z "$WEBSITE" ]] ; then
while IFS='' read -r -d '' _conf_file ; do
source $_conf_file
if [[ -n "$WEBSITE" ]] ; then
unsorted_website_arr+=("${WEBSITE}:$_conf_file")
fi
WEBSITE=""
done < <(find "${conf_dir}" -maxdepth 1 -type f -name "*.conf" -print0)
# - Sort array
# -
IFS=$'\n' website_arr=($(sort <<<"${unsorted_website_arr[*]}"))
# Which cloud instance (website) would you like to update
#
source ${snippet_dir}/get-cloud-instance-to-update.sh
else
while IFS='' read -r -d '' _conf_file ; do
if $(grep -E -q "WEBSITE=\"?${WEBSITE}\"?" ${_conf_file} 2> /dev/null) ; then
conf_file="${_conf_file}"
break
fi
done < <(find "${conf_dir}" -maxdepth 1 -type f -name "*.conf" -print0)
fi
# - Reset IFS
# -
IFS=$CUR_IFS
DEFAULT_SRC_BASE_DIR="/usr/local/src/nextcloud"
DEFAULT_HTTP_USER="www-data"
DEFAULT_HTTP_GROUP="www-data"
DEFAULT_PHP_ENGINE='FPM'
blank_line
echononl " Include Configuration file '$(basename "${conf_file}")'.."
if [[ ! -f $conf_file ]]; then
echo_skipped
fatal "Missing configuration file '$conf_file'."
else
source $conf_file
echo_ok
fi
DEFAULT_WEB_BASE_DIR="/var/www/${WEBSITE}"
[[ -n "$WEB_BASE_DIR" ]] || WEB_BASE_DIR=$DEFAULT_WEB_BASE_DIR
if [[ ! -d ${WEB_BASE_DIR} ]] ; then
fatal "Web base directory '$WEB_BASE_DIR' not found!"
fi
DATA_DIR="$(realpath ${WEB_BASE_DIR}/data)"
[[ -n "$PHP_ENGINE" ]] || PHP_ENGINE=$DEFAULT_PHP_ENGINE
INSTALL_DIR="$(realpath ${WEB_BASE_DIR}/nextcloud)"
CURRENT_VERSION="$(basename $INSTALL_DIR | cut -d"-" -f2)"
# =============
# --- Some
# =============
# - Support systemd ?
# -
SYSTEMD_EXISTS=false
systemd=$(which systemd)
systemctl=$(which systemctl)
if [[ -n "$systemd" ]] || [[ -n "$systemctl" ]] ; then
SYSTEMD_EXISTS=true
fi
#clear
if $terminal ; then
echo ""
echo -e "\033[32m-----\033[m"
echo -e "Scan encrypted files for \033[1mBad Signature\033[m errors on system \033[1m${WEB_BASE_DIR}\033[m"
echo -e "\033[1m
Read-only: every file is decrypted once and the content discarded,
reporting every file that fails Server-Side Encryption's signature
check. Nothing is changed, moved or deleted.\033[m"
echo -e "\033[32m-----\033[m"
fi
# =============
# --- Some checks
# =============
DEFAULT_HTTP_USER="www-data"
DEFAULT_HTTP_GROUP="www-data"
NGINX_IS_ENABLED=false
APACHE2_IS_ENABLED=false
# Get Webservice environment as IS_HTTPD_RUNNING, HTTP_USER, HTTP_GROUP..
#
source ${snippet_dir}/get-webservice-environment.sh
# Check PHP Version
#
source ${snippet_dir}/get-php-major-version.sh
# Get full qualified PHP command
#
source ${snippet_dir}/get-path-of-php-command.sh
if [[ ! -x "$PHP_BIN" ]]; then
fatal "No PHP binary found!"
fi
# =============
# --- Determine existing accounts
# =============
blank_line
echononl " Get list of current accounts.."
mapfile -t unsorted_account_arr < <(su -c "${PHP_BIN} ${INSTALL_DIR}/occ user:list" -s /bin/bash $HTTP_USER 2> ${log_file} | awk -F'[:[:space:]]+' '{print $3}' 2>> ${log_file} )
if [[ $? -gt 0 ]] || [[ -s "${log_file}" ]] ; then
echo_failed
fatal "$(cat $log_file)"
else
echo_ok
> "$log_file"
fi
# - Sort array
# -
IFS=$'\n' account_arr=($(sort <<<"${unsorted_account_arr[*]}"))
IFS=$CUR_IFS
# =============
# --- Ask which account(s) to scan
# =============
echo ""
echo ""
echo -e "\033[32m-----\033[m"
echo ""
echo -e " Existing accounts on \033[1m${WEBSITE}\033[m\n"
echo -en "\033[33m"
printf " %s\n" "${account_arr[@]}"
echo -en "\033[m"
blank_line
echo ""
echo ""
echo -e "\033[32m-----\033[m"
echo ""
echo -e " Which account(s) would you like to scan for 'Bad Signature' errors?"
echo ""
echo " - enter a list separated by spaces"
echo -e " - or type \033[33mall\033[m to scan every existing account"
echo ""
SELECTED_USERS=
while [[ -z "$(trim ${SELECTED_USERS})" ]] ; do
echononl " Account(s) to scan: "
read SELECTED_USERS
if [[ -z "$(trim ${SELECTED_USERS})" ]] ; then
echo -e "\n\t\033[33m\033[1mEntry must not be empty. Type \033[m\033[1mall\033[33m to scan every account.\033[m\n"
continue
fi
if [[ "${SELECTED_USERS,,}" = "all" ]] ; then
selected_user_arr=("${account_arr[@]}")
break
fi
IFS=' ' read -ra unsorted_selected_user_arr <<< "${SELECTED_USERS}"
_all_valid=true
for _user in "${unsorted_selected_user_arr[@]}" ; do
if ! containsElement "${_user}" "${account_arr[@]}" ; then
echo -e "\n\t\033[33m\033[1m No account \033[m\033[1m${_user}\033[33m present!\033[m - Try again..\n"
SELECTED_USERS=""
_all_valid=false
break
fi
done
$_all_valid || continue
# - Sort array
# -
IFS=$'\n' selected_user_arr=($(sort <<<"${unsorted_selected_user_arr[*]}"))
IFS=$CUR_IFS
done
# =============
# --- Write out the (embedded) PHP scan script into the Nextcloud
# --- install directory - it needs to sit next to 'occ' since it
# --- bootstraps Nextcloud the same way occ does.
# =============
scan_php_file="${INSTALL_DIR}/.occ_scan_bad_signature_$$.php"
cat > "$scan_php_file" <<'PHP_SCAN_EOF'
<?php
/**
* .occ_scan_bad_signature_*.php
*
* READ-ONLY Diagnose-Skript fuer Nextcloud Server-Side Encryption.
* Wird von occ_scan_bad_signature.sh automatisch erzeugt und nach
* Gebrauch wieder geloescht - nicht von Hand aufrufen/aendern.
*
* Was es tut:
* Geht durch alle Dateien EINES uebergebenen Benutzers und liest
* jede Datei einmal vollstaendig (im Stream, ohne den Inhalt zu
* speichern). Dadurch wird fuer jede verschluesselte Datei die
* vollstaendige Entschluesselung inkl. Signaturpruefung jedes
* Blocks erzwungen - genau der Schritt, der beim "Bad Signature"
* Fehler fehlschlaegt.
*
* Was es NICHT tut:
* Es schreibt, aendert oder loescht NICHTS. Reiner Lesezugriff.
*
* Aufruf:
* php .occ_scan_bad_signature_*.php <benutzername> [--before=YYYY-MM-DD] [--after=YYYY-MM-DD]
*
* Ausgabe (TSV, nach STDOUT):
* user \t path \t size_bytes \t mtime \t error
* Eine Zeile pro Datei, bei der das Lesen fehlgeschlagen ist.
* Fortschritts-/Debug-/Zusammenfassungsmeldungen gehen nach STDERR.
*/
error_reporting(E_ALL);
ini_set('display_errors', '1');
fwrite(STDERR, "DEBUG: Skript gestartet, PID=" . getmypid() . "\n");
// Faengt fatale, NICHT als Throwable auftretende Fehler ab (z.B. Memory
// Limit exhausted), die sonst komplett stumm bleiben wuerden.
register_shutdown_function(function () {
$err = error_get_last();
if ($err !== null && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
fwrite(STDERR, "DEBUG: FATAL bei Shutdown: [{$err['type']}] {$err['message']} in {$err['file']}:{$err['line']}\n");
} else {
fwrite(STDERR, "DEBUG: Shutdown ohne erkannten Fatal-Error.\n");
}
});
// WICHTIG: Muss VOR dem require von lib/base.php gesetzt werden.
// Ohne diese Konstante fuehrt lib/base.php am Ende automatisch
// OC::handleRequest() aus (als waere dies ein normaler HTTP-Request).
define('OC_CONSOLE', 1);
$oldWorkingDir = getcwd();
if ($oldWorkingDir === false) {
fwrite(STDERR, "Konnte aktuelles Arbeitsverzeichnis nicht ermitteln. Bitte mit absolutem Pfad aufrufen.\n");
exit(1);
}
chdir(__DIR__);
fwrite(STDERR, "DEBUG: vor require lib/base.php\n");
require_once __DIR__ . '/lib/base.php';
fwrite(STDERR, "DEBUG: nach require lib/base.php - Bootstrap abgeschlossen\n");
chdir($oldWorkingDir);
if (function_exists('posix_getuid') && posix_getuid() === 0) {
fwrite(STDERR, "Bitte NICHT als root ausfuehren, sondern als Webserver-User, z.B.:\n");
fwrite(STDERR, " sudo -u www-data php " . __FILE__ . "\n");
exit(1);
}
// Ab hier alles in einem Catch-All, damit ein Throwable garantiert HIER
// landet und nicht beim (auf STDOUT/STDERR ggf. still bleibenden)
// globalen Nextcloud-Handler.
try {
fwrite(STDERR, "DEBUG: hole UserManager/RootFolder ...\n");
// Argumente einlesen: erstes nicht-Options-Argument = Benutzername
// (optional, sonst alle Benutzer). --before=YYYY-MM-DD prueft nur
// Dateien, die VOR diesem Datum zuletzt geaendert wurden - spart bei
// grossen Accounts viel Zeit, sobald der Cutoff-Zeitpunkt bekannt ist.
$onlyUser = null;
$beforeTs = null;
$afterTs = null;
foreach (array_slice($argv, 1) as $arg) {
if (str_starts_with($arg, '--before=')) {
$beforeTs = strtotime(substr($arg, 9));
} elseif (str_starts_with($arg, '--after=')) {
$afterTs = strtotime(substr($arg, 8));
} elseif ($onlyUser === null) {
$onlyUser = $arg;
}
}
if ($beforeTs !== null) {
fwrite(STDERR, "DEBUG: Filter --before " . date('Y-m-d', $beforeTs) . "\n");
}
if ($afterTs !== null) {
fwrite(STDERR, "DEBUG: Filter --after " . date('Y-m-d', $afterTs) . "\n");
}
// Neuere Nextcloud-Versionen haben die alten Komfort-Methoden wie
// getUserManager()/getRootFolder() auf \OC::$server entfernt - der
// unterstuetzte Weg ist jetzt der PSR-11-Container ->get(...).
$userManager = \OC::$server->get(\OCP\IUserManager::class);
$rootFolder = \OC::$server->get(\OCP\Files\IRootFolder::class);
fwrite(STDERR, "DEBUG: UserManager/RootFolder OK\n");
$total = 0;
$skipped = 0;
$bad = 0;
$usersScanned = 0;
echo "user\tpath\tsize_bytes\tmtime\terror\n";
flush();
/**
* Rekursiv durch einen Ordner gehen und jede Datei vollstaendig lesen
* (ausser sie liegt ausserhalb von --before/--after).
*/
$scanFolder = function (\OCP\Files\Folder $folder, string $uid) use (&$scanFolder, &$total, &$skipped, &$bad, $beforeTs, $afterTs) {
foreach ($folder->getDirectoryListing() as $node) {
if ($node instanceof \OCP\Files\Folder) {
$scanFolder($node, $uid);
continue;
}
if (!($node instanceof \OCP\Files\File)) {
continue;
}
$mtimeRaw = $node->getMTime();
if (($beforeTs !== null && $mtimeRaw >= $beforeTs) || ($afterTs !== null && $mtimeRaw <= $afterTs)) {
$skipped++;
continue;
}
$total++;
$path = $node->getPath();
try {
$stream = $node->fopen('r');
if ($stream === false) {
throw new \RuntimeException('fopen lieferte false');
}
while (!feof($stream)) {
$chunk = fread($stream, 8 * 1024 * 1024);
if ($chunk === false) {
throw new \RuntimeException('fread schlug mitten im Stream fehl');
}
}
fclose($stream);
} catch (\Throwable $e) {
$bad++;
$mtime = date('c', $node->getMTime());
$msg = str_replace(["\t", "\n", "\r"], ' ', $e->getMessage());
echo "$uid\t$path\t{$node->getSize()}\t$mtime\t" . get_class($e) . ": $msg\n";
flush();
}
}
};
$scanUser = function (\OCP\IUser $user) use (&$scanFolder, $rootFolder, &$usersScanned) {
$uid = $user->getUID();
fwrite(STDERR, "Scanne Benutzer: $uid ...\n");
\OC_Util::setupFS($uid);
try {
$userFolder = $rootFolder->getUserFolder($uid);
} catch (\Throwable $e) {
fwrite(STDERR, " Ueberspringe $uid: kann User-Folder nicht laden (" . $e->getMessage() . ")\n");
return;
}
$scanFolder($userFolder, $uid);
$usersScanned++;
};
if ($onlyUser !== null) {
fwrite(STDERR, "DEBUG: suche Benutzer '$onlyUser' ...\n");
$user = $userManager->get($onlyUser);
if ($user === null) {
fwrite(STDERR, "Benutzer '$onlyUser' nicht gefunden.\n");
exit(1);
}
fwrite(STDERR, "DEBUG: Benutzer gefunden, starte Scan ...\n");
$scanUser($user);
} else {
$userManager->callForAllUsers($scanUser);
}
fwrite(STDERR, "\nFertig. Benutzer gescannt: $usersScanned, Dateien geprueft: $total, uebersprungen (Filter): $skipped, fehlgeschlagen: $bad\n");
} catch (\Throwable $e) {
fwrite(STDERR, "\n!!! UNBEHANDELTE AUSNAHME !!!\n");
fwrite(STDERR, get_class($e) . ": " . $e->getMessage() . "\n");
fwrite(STDERR, "in " . $e->getFile() . ":" . $e->getLine() . "\n");
fwrite(STDERR, $e->getTraceAsString() . "\n");
exit(2);
}
PHP_SCAN_EOF
chmod 644 "$scan_php_file" 2> /dev/null
mkdir -p "$report_dir" 2> /dev/null
report_file="${report_dir}/bad_signature_${WEBSITE}_${run_date}.tsv"
{
echo "=================================================================="
echo " Bad-Signature-Scan ${WEBSITE} ${run_date}"
echo "=================================================================="
echo ""
echo -e "user\tpath\tsize_bytes\tmtime\terror"
} > "$report_file"
if $terminal ; then
echo ""
echo -e "\033[1;32mStarting Script for \033[1;37m${WEBSITE}\033[m"
echo ""
echo -e " Cloud instance to be scanned.........: $WEBSITE"
echo ""
echo -e " Current version of nextcloud.........: $CURRENT_VERSION"
echo ""
echo -e " Web base directory...................: $WEB_BASE_DIR"
echo -e " Install directory....................: $INSTALL_DIR"
echo -e " Data directory.......................: $DATA_DIR"
echo ""
echo -e " Webserver user.......................: $HTTP_USER"
echo -e " Webserver group......................: $HTTP_GROUP"
echo ""
echo -e " PHP command..........................: $PHP_BIN"
echo ""
echo -e " Account(s) to scan...................: \033[33m${selected_user_arr[*]}\033[m"
echo ""
echo -e " Report file...........................: $report_file"
echo ""
echo ""
echo -n " Type upper case 'YES' to continue executing with this parameters: "
read OK
if [[ "$OK" = "YES" ]] ; then
echo ""
echo ""
echo -e "\033[1;32mGoing to scan encrypted files for each selected account on \033[1;37m$WEBSITE \033[m"
else
fatal "Abort by user request - Answer as not 'YES'"
fi
fi
# -----
# - Main part of the script
# -----
if $terminal ; then
echo ""
echo ""
echo -e "\033[37m\033[1mMain part of the script\033[m"
echo ""
fi
declare -i total_checked=0
declare -i total_bad=0
declare -i total_failed_users=0
for _user in "${selected_user_arr[@]}" ; do
user_tsv="${LOCK_DIR}/scan_${_user}.tsv"
# - Gut sichtbarer Account-Abschnitt im Reportfile - wird IMMER
# - angelegt, auch wenn der Scan fuer diesen Account fehlschlaegt.
# -
_sep_len=${#_user}
[[ $_sep_len -lt 9 ]] && _sep_len=9
_sep_line="$(printf '%*s' "$_sep_len" '' | tr ' ' '-')"
{
echo ""
echo ""
echo "$_sep_line"
echo "$_user"
echo "$_sep_line"
} >> "$report_file"
echononl " Scanning account \033[1;37m${_user}\033[m.."
su -c "$PHP_BIN $scan_php_file $_user" -s /bin/bash $HTTP_USER > "$user_tsv" 2> "$log_file"
_rc=$?
if [[ $_rc -ne 0 ]]; then
echo_failed
error "$(cat "$log_file")"
echo " [ FEHLER beim Scan - siehe Server-Log ]" >> "$report_file"
(( total_failed_users++ ))
continue
fi
# - Kopfzeile weglassen und im Account-Abschnitt anhaengen
# -
tail -n +2 "$user_tsv" >> "$report_file"
_user_bad=$(tail -n +2 "$user_tsv" | wc -l)
(( total_bad += _user_bad ))
# - Scan lief durch: bei Funden gelb hervorheben statt schlichtem 'ok'
# -
if [[ $_user_bad -gt 0 ]] ; then
echo_funde
else
echo_ok
fi
# - Leerzeile NACH der 'Scanning account ..'-Zeile, vor der
# - Account-Statistik.
# -
$terminal && echo ""
# - Kennzahlen aus der STDERR-Zusammenfassung herausziehen
# -
_summary="$(grep -E '^Fertig\.' "$log_file")"
_checked="$(echo "$_summary" | grep -oE 'Dateien geprueft: [0-9]+' | grep -oE '[0-9]+')"
_skipped="$(echo "$_summary" | grep -oE 'uebersprungen \(Filter\): [0-9]+' | grep -oE '[0-9]+')"
[[ -z "$_skipped" ]] && _skipped="0"
if [[ "$_checked" =~ ^[0-9]+$ ]] ; then
(( total_checked += _checked ))
_pct="$(calc_percent "$_user_bad" "$_checked")"
else
_checked="?"
_pct="?"
fi
# - Ergebnis-Block pro Account - gleiches Looking wie das
# - Gesamtergebnis am Ende (Konsole + Reportfile)
# -
if $terminal ; then
echo -e " \033[1;37mAccount ${_user}\033[m"
echo -e " Gescannte Dateien.......................: ${_checked}"
echo -e " Uebersprungene Dateien...................: ${_skipped}"
if [[ $_user_bad -gt 0 ]] ; then
echo -e " Dateien mit 'Bad Signature'..............: \033[1;33m${_user_bad} (${_pct} %)\033[m"
else
echo -e " Dateien mit 'Bad Signature'..............: ${_user_bad} (${_pct} %)"
fi
# - Leerzeile HINTER der Account-Statistik, damit sie nicht am
# - 'Scanning account ..' des naechsten Accounts klebt.
# -
echo ""
fi
{
echo " Account ${_user}"
echo " Gescannte Dateien.......................: ${_checked}"
echo " Uebersprungene Dateien...................: ${_skipped}"
echo " Dateien mit 'Bad Signature'..............: ${_user_bad} (${_pct} %)"
} >> "$report_file"
done
total_pct="$(calc_percent "$total_bad" "$total_checked")"
# - Gesamtergebnis ans Ende des Reportfiles - gleiches Looking wie
# - die Ergebnis-Bloecke pro Account
# -
{
echo ""
echo ""
echo "=================================================================="
echo " Gesamtergebnis"
echo "=================================================================="
echo ""
echo "Gescannte Accounts.........................: ${#selected_user_arr[@]}"
[[ $total_failed_users -gt 0 ]] && echo "Accounts mit Scan-Fehler...................: ${total_failed_users}"
echo "Gescannte Dateien insgesamt.................: ${total_checked}"
echo "Dateien mit 'Bad Signature' insgesamt.......: ${total_bad} (${total_pct} %)"
} >> "$report_file"
blank_line
if $terminal ; then
echo -e "\033[37m\033[1mErgebnis\033[m"
echo ""
echo -e " Gescannte Accounts.........................: ${#selected_user_arr[@]}"
[[ $total_failed_users -gt 0 ]] && echo -e " Accounts mit Scan-Fehler...................: \033[1;31m${total_failed_users}\033[m"
echo -e " Gescannte Dateien insgesamt.................: ${total_checked}"
echo -e " Dateien mit 'Bad Signature' insgesamt.......: \033[1;31m${total_bad} (${total_pct} %)\033[m"
echo -e " Report-Datei.................................: ${report_file}"
echo ""
fi
clean_up 0