#!/bin/bash
# Copyright (C) 2024 Evgeny Sinelnikov <sin@altlinux.org>
# Copyright (C) 2024 Andrey Limachko <liannnix@altlinux.org>
# Copyright (C) 2024 Elena Dyatlenko <lenka@altlinux.org>
# Copyright (C) 2024 Sergey Savelev <savelevsa@basealt.ru>
#
# The Domain Controller Status Diagnostic Utility
#
# This program is free software: you can redistribute it and/or modify
#
# SPDX-License-Identifier: GNU GPL v3.0
#
# shellcheck disable=SC1091
# shellcheck disable=SC2317
# shellcheck disable=SC2329

set -euo pipefail

. shell-getopt
. shell-temp

declare -r PROG_NAME="${0##*/}"
declare -r VERSION=0.5.0

declare -r PREDIAG_MODE="pre"
declare -r POSTDIAG_MODE="post"

declare -r PREDIAG_IGNORE="ignore"
declare -r PREDIAG_ONLY="only"
declare -r PREDIAG_REVERSE="reverse"
declare -r PREDIAG_NORMAL="normal"
declare -r PREDIAG_REVERSE_WITH_WARNINGS="warning"

global_retval=0
task_list="$*"
cmd="default"
current_date=$(date +"%Y-%m-%d_%H:%M")
temp_dir=
create_temporary temp_dir -d
journal="$temp_dir/report_$current_date.txt"
path_to_report_archive=
plain_mode=0

if [[ ! -v service_deploy_mode ]]; then
    service_deploy_mode="post"
fi

print_version() {
    cat <<EOF
$PROG_NAME version $VERSION
EOF
    exit 0
}

show_usage() {
    cat <<EOF
$PROG_NAME - The Domain Controller Status Diagnostic Utility

Usage: $PROG_NAME [options] [<diagnostic-task>]

Options:
    -l, --list			list of diagnostic tasks
    -V, --version		print program version and exit
    -v, --verbose               verbose output to terminal
    -r, --report=[file]         output to report file (optional file path)
    -a, --alterator             output for use in alterator apps
    -h, --help                  show this text and exit
EOF
    exit 0
}

text_title="        Domain Controller Diagnostic Tool
-------------------------------------------------
Version: $VERSION
Date: $current_date
-------------------------------------------------
System information:
Kernel: $(uname -r)"

create_title() {
    if test "$cmd" = "report"; then
        echo "$text_title" >>"$journal"
    else
        echo "$text_title"
    fi
}

task_func=
task_options=

for word in $task_list; do
    case "$word" in
    --* | -*)
        task_options="$task_options $word"
        ;;
    *)
        task_func="$task_func $word"
        ;;
    esac
done

if test -n "$task_func"; then
    task_options="$task_options -v"
    cmd="verbose"
fi

task_func="$(echo "$task_func" | tr ' ' '\n' | sort -u | tr '\n' ' ')"
task_func="$(echo "$task_func" | sed 's/[[:space:]]*//;s/[[:space:]]*$//')"
task_options="$(echo "$task_options" | tr ' ' '\n' | sort -u | tr '\n' ' ')"
task_options="$(echo "$task_options" | sed 's/[[:space:]]*//;s/[[:space:]]*$//')"

short_options="l,V,v,r::,h,p,a"
long_options="list,version,verbose,report::,help,plain,alterator"

TEMP=$(getopt -n "$PROG_NAME" -o "$short_options", -l "$long_options" -- "$@") || show_usage
eval set -- "$TEMP"

while :; do
    case "$1" in
    -l | --list)
        cmd="list"
        shift
        ;;
    -V | --version)
        print_version
        ;;
    -v | --verbose)
        cmd="verbose"
        create_title
        shift
        ;;
    -r | --report)
        cmd="report"
        create_title
        shift
        path_to_report_archive="${1#=}"
        shift
        ;;
    -p | --plain)
        plain_mode=1
        report_archive="$temp_dir/report_$current_date.tar.gz"
        shift
        ;;
    -a | --alterator)
        cmd="alterator"
        shift
        ;;
    -h | --help)
        show_usage
        ;;
    --)
        shift
        break
        ;;
    *)
        fatal "Unrecognized option: $1"
        ;;
    esac
done

task_show() {
    local func="$1"
    echo "$func"
}

# to make update_global_retval function simpler
__switch_1_and_2() {
    case "$1" in
    1)
        echo 2
        ;;
    2)
        echo 1
        ;;
    *)
        echo "$1"
        ;;
    esac
}

__update_global_retval() {
    local a="$1"
    local b="$2"
    a="$(__switch_1_and_2 "$a")"
    b="$(__switch_1_and_2 "$b")"
    local retval=$((a > b ? a : b))
    retval=$(__switch_1_and_2 $retval)
    if ((retval > 2)); then
        global_retval=1
    else
        global_retval="$retval"
    fi
}

task_run() {
    local retval=126
    local func="$1"

    if test -n "$task_func" && test "$cmd" != "report"; then
        echo "$task_func" | tr ' ' '\n' | grep -q "^$func\$" || return 0
    fi

    $func && retval=0 || retval="$?"

    return "$retval"
}

# Determining the test execution status
test_status() {
    local retval="$1"
    local task="$2"
    local action_on_prediag="$3"

    if [[ ("$action_on_prediag" == "$PREDIAG_REVERSE" || "$action_on_prediag" == "$PREDIAG_REVERSE_WITH_WARNINGS") && "$service_deploy_mode" == "$PREDIAG_MODE" ]]; then
        case "$retval" in
        0)
            retval=1
            __update_global_retval "$retval" "$global_retval"
            ;;
        1)
            retval=0
            __update_global_retval "$retval" "$global_retval"
            ;;
        2)
            if [ "$action_on_prediag" == "$PREDIAG_REVERSE_WITH_WARNINGS" ]; then
                retval=0
                __update_global_retval "$retval" "$global_retval"
            fi
            ;;
        *)
            __update_global_retval "$retval" "$global_retval"
            ;;
        esac
    else
        __update_global_retval "$retval" "$global_retval"
    fi

    case "$retval" in
    0)
        echo "[DONE]: $task"
        ;;
    1)
        echo "[FAIL]: $task"
        ;;
    2)
        echo "[WARN]: $task"
        ;;
    *)
        echo "[FAIL]: $task"
        ;;
    esac
}

delimiter_line() {
    echo
    echo "================================================="
}

# Depending on the parameter of the cmd variable, the function decides what to run
task() {
    local task="$1"
    local action_on_prediag="$2"

    if [[ ("$action_on_prediag" == "$PREDIAG_IGNORE" &&
        "$service_deploy_mode" == "$PREDIAG_MODE") ||
        ("$action_on_prediag" == "$PREDIAG_ONLY" &&
        "$service_deploy_mode" == "$POSTDIAG_MODE") ]]; then
        return 0
    fi

    case "$cmd" in
    list)
        task_show "$task"
        ;;
    report)
        local retval=0

        delimiter_line >>"$journal"
        task_run "$task" >>"$journal" 2>&1 || retval="$?"
        delimiter_line >>"$journal"
        test_status "$retval" "$task" "$action_on_prediag" >>"$journal"
        ;;
    default)
        local retval=0

        task_run "$task" &>/dev/null || retval="$?"
        test_status "$retval" "$task" "$action_on_prediag"
        ;;
    verbose)
        if test -z "$task_func" || grep -q "$task" <<<"$task_func"; then
            local retval=0
            delimiter_line
            task_run "$task" || retval="$?"
            delimiter_line
            test_status "$retval" "$task" "$action_on_prediag"
        fi
        ;;
    alterator)
        if test -z "$task_func" || grep -q "$task" <<<"$task_func"; then
            local retval=0
            task_run "$task" || retval="$?"
            test_status "$retval" "$task" "$action_on_prediag"
        fi
        ;;
    *)
        fatal "Unrecognized command: $cmd"
        ;;
    esac
}

# Getting a lowercase string
__get_upper_string() {
    tr '[:upper:]' '[:lower:]'
}

# Getting a realm record from smb.conf
__get_smb_realm() {
    local smb_realm=

    smb_realm="$(testparm -l -v -s 2>/dev/null | grep "^\s*realm\s*=" | sed -e 's/^\s*realm\s*=\s*//' -e 's/\s*$//')"
    smb_realm="$(echo "$smb_realm" | __get_upper_string)"

    echo "$smb_realm"
}

# Getting a realm record from krb5.conf
__get_krb5_default_realm() {
    local krb5_default_realm_line=
    local krb5_default_realm=

    krb5_default_realm_line="$(grep "^\s*default_realm\s\+" /etc/krb5.conf || true)"
    krb5_default_realm="$(echo "$krb5_default_realm_line" | sed -e 's/^\s*default_realm\s*=\s*//' -e 's/\s*$//')"
    krb5_default_realm="$(echo "$krb5_default_realm" | __get_upper_string)"

    echo "$krb5_default_realm"
}

# Getting a domain name from resolv.conf
__get_resolv_search_domains() {
    local search_line=
    local search_domains=

    search_line="$(grep "^search\s\+" /etc/resolv.conf || true)"
    search_domains="$(echo "$search_line" | sed -e 's/^search\s\+//' -e 's/\s\+$//')"

    echo "$search_domains"
}

# Checking if the package is installed in the system
__package_installed() {
    local retval=1
    local check_all=false
    local installed_list=
    local missing_list=
    local package=
    local name=
    local line=

    # The --check-all key is set as the first argument when calling this function.
    # The --check-all key in the "true" value allows you to check all packets in
    # the system that are passed as other arguments when calling the function.
    # The value "false" allows you to check whether one of several packages
    # is installed in the system.

    if test "$1" = "--check-all"; then
        check_all=true
        shift
    fi

    for package in "$@"; do
        if rpm -q --quiet "$package" >/dev/null; then
            name="$(rpm -q "$package" 2>/dev/null)"
            line=$'\t'"$name"

            if test -z "$installed_list"; then
                installed_list="$line"
            else
                installed_list="$installed_list"$'\n'"$line"
            fi
        else
            line=$'\t'"$package"

            if test -z "$missing_list"; then
                missing_list="$line"
            else
                missing_list="$missing_list"$'\n'"$line"
            fi
        fi
    done

    if test "$check_all" = true; then
        if test -n "$installed_list"; then
            echo "Found installed packages:"
            echo "$installed_list"
        fi

        if test -z "$missing_list"; then
            retval=0
        else
            echo "Missing packages:"
            echo "$missing_list"
            retval=1
        fi
        test -n "$installed_list" && test -n "$missing_list" && echo

        return "$retval"
    fi

    if test -n "$installed_list"; then
        echo "Found installed packages:"
        echo "$installed_list"
        retval=0
    else
        echo "None of the specified packages are installed."
        retval=1
    fi

    return "$retval"
}

# Checking the availability of writing general domain information
is_domain_info_available() {
    local retval=0
    local out=

    if which samba-tool &>/dev/null; then
        out="$(/usr/bin/samba-tool domain info 127.0.0.1 2>&1)" || retval=1
        if test "$retval" = 0; then
            echo -e "General information about the domain controller:\n$out"
        else
            echo -e "There are no superuser rights or errors were found.\n$out"
        fi
    else
        echo "Information could not be obtained, perhaps the samba package is missing from the system."
        retval=1
    fi

    return "$retval"
}

# Checking the correct spelling of the domain name of the host
is_hostname_correct() {
    local hostname=
    local validate_regex=
    local retval=0
    local smb_realm=

    smb_realm="$(__get_smb_realm)"
    hostname="$(hostname -s)"
    fqdn_hostname="$(hostname -f)"
    validate_regex="^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"

    if test "${#hostname}" -gt 15; then
        echo "Length must be less than 16 characters."
        retval=1
    else
        grep -E "$validate_regex" >/dev/null 2>&1 <<<"$hostname" || retval=1
        if test "$retval" -eq 1; then
            echo "Wrong hostname. It may contain prohibited characters."
        else
            grep -E "^[^.]+.$smb_realm$" >/dev/null 2>&1 <<<"$fqdn_hostname" || retval=2
            if test "$retval" -eq 2; then
                echo "Controller name is not FQDN or domain_realm does not match. This may cause some problems."
            fi
        fi
    fi

    if test "$retval" -eq 0; then
        echo "The domain name of the domain controller corresponds to the FQDN."
    fi

    return "$retval"
}

# Checking if the static and temporary hostname match
is_hostname_static_and_transient() {
    local retval=0
    local static_hostname=
    local transient_hostname=

    static_hostname="$(hostnamectl --static)" || retval=1
    transient_hostname="$(hostnamectl --transient)" || retval=1
    test "$static_hostname" = "$transient_hostname" || retval=2

    if test "$retval" = 0; then
        echo "The permanent hostname is the same as the transient one. The host name is as follows: $static_hostname"
    elif test "$retval" = 2; then
        echo "The permanent hostname does not match the transient one."
        echo "Static hostname: $static_hostname"
        echo "Transient hostname: $transient_hostname"
    else
        echo "The hostnamectl command could not be executed."
    fi

    return "$retval"
}

# Checking if the sysvol directory is empty
is_not_empty_sysvol() {
    local retval=0

    test -n "$(ls -A /var/lib/samba/sysvol 2>/dev/null)" || retval=2

    if test "$retval" = 0; then
        echo -e "The \"sysvol\" directory contains the following files (the first 10 files are presented):"
        ls -laA /var/lib/samba/sysvol

        # shellcheck disable=SC2012
        for dir in $(ls -d /var/lib/samba/sysvol/*/ | head -n 10); do
            dir_name="$(basename "$dir")"

            echo -e "\nListing for directory $dir_name (the first 10 files are presented):"
            ls -laA "$dir" | head -n 10
        done
    else
        echo -e "Error. There may be no rights to view the\ndirectory, the directory does not exist,\nor the directory is empty."
    fi

    return "$retval"
}

# Checking the location in sysvol of the folder with the domain name and "Policies" and "scripts" inside
does_sysvol_contain_necessary_files() {
    local retval=0
    local path_to_sysvol=
    local smb_realm=
    local files_list_in_sysvol=
    local required_files=

    path_to_sysvol="/var/lib/samba/sysvol"
    required_files="$(echo -e "Policies\nscripts")"

    if test -e /etc/samba/smb.conf; then
        smb_realm="$(__get_smb_realm)"

        if test -z "$smb_realm"; then
            echo -e "The realm record was not found in the \"smb.conf\" configuration file.\n"
            retval=1
        fi
    else
        echo "The smb.conf file does not exist, no further verification is possible."
        retval=1
    fi

    if test "$retval" = 0; then
        files_list_in_sysvol="$(ls -A "$path_to_sysvol/$smb_realm")"

        for folder in $required_files; do
            if echo "$files_list_in_sysvol" | grep -q "^$folder$"; then
                if test -z "$(ls -A "$path_to_sysvol/$smb_realm/$folder")"; then
                    echo -e "The \"$folder\" folder is empty."
                else
                    echo -e "The \"$folder\" folder is not empty."
                fi
            else
                echo -e "The \"$folder\" folder is not found."
            fi
        done
    fi

    return "$retval"
}

# Checking for the samba package in the system
is_samba_package_installed() {
    local retval=0
    local heimdal_name=
    local mit_name=
    local packages_info=

    heimdal_name="samba-dc"
    mit_name="samba-dc-mitkrb5"
    packages_info="$(__package_installed "$heimdal_name" "$mit_name")" || retval=1

    if test "$retval" = 0; then
        echo "$packages_info"
    else
        echo -e "Error. Perhaps the \"$heimdal_name\" package or \"$mit_name\" package is not installed"
    fi

    return "$retval"
}

# Checking a service status
__service_status() {
    local service="$1"
    local is_active=
    local is_enabled=
    local out=
    local values=
    out="$(systemctl show "$service" --property UnitFileState,ActiveState,SubState,Id 2>&1)"
    mapfile -t values <<<"$(echo "$out" | awk -F= '{if ($2) {print $2} else {print "None"}}')"
    [[ "${values[1]}" == "active" ]]
    is_active=$?
    [[ "${values[3]}" == "enabled" ]]
    is_enabled=$?
    echo "$out"
    return $(((is_active << 1) | is_enabled))
}

# Checking whether the Samba service is running
is_samba_service_running() {
    local retval=0
    local service="samba.service"
    local output

    output=$(__service_status "$service")
    status=$?
    case $status in
    0)
        echo -e "The \"$service\" service is enabled and running:\n$output"
        retval=0
        ;;
    1)
        echo -e "The \"$service\" service is active but not enabled:\n$output"
        retval=2
        ;;
    *)
        echo -e "The \"$service\" service is not active:\n$output"
        retval=1
        ;;
    esac

    return "$retval"
}

# Does the domain controller resolve DNS queries locally
is_resolve_local() {
    local retval=0
    local resolve_conf=
    local has_local=0
    local address=

    resolve_conf="/etc/resolv.conf"

    while IPS=' ' read -r line; do
        if echo "$line" | grep -qE '^[[:space:]]*#|^$'; then
            continue
        fi

        if echo "$line" | grep -qE '[[:space:]]*nameserver[[:space:]]+'; then
            address="$(echo "$line" | awk '{print $2}')"

            if test "$address" = "127.0.0.1"; then
                has_local=1
            fi
        fi
    done <"$resolve_conf"

    if test "$has_local" = 1; then
        echo "The resolv.conf configuration file contains the string nameserver 127.0.0.1."
    elif test "$has_local" = 0; then
        echo "Error: the resolv.conf file does not contain the nameserver 127.0.0.1 line."
        retval=1
    fi

    return "$retval"
}

# Checking for the presence of the krb5.conf configuration file and displaying the contents of the file
is_krb5_conf_file_exists() {
    local retval=0

    test -e /etc/krb5.conf || retval=1

    if test "$retval" = 0; then
        echo -e "The krb5.conf configuration file exists. Its contents are as follows:\n"
        ls -l /etc/krb5.conf
        echo
        cat /etc/krb5.conf
    else
        echo "The krb5.conf configuration file was not found."
    fi

    return "$retval"
}

# Checking for the presence of the smb.conf configuration file and displaying the contents of the file
is_smb_conf_file_exists() {
    local retval=0

    test -e /etc/samba/smb.conf || retval=1

    if test "$retval" = 0; then
        echo -e "The smb.conf configuration file exists. Its contents are as follows:\n"
        ls -l /etc/samba/smb.conf
        echo
        cat /etc/samba/smb.conf
    else
        echo "The smb.conf configuration file was not found."
    fi

    return "$retval"
}

# Checking the specified method of caching Kerberos tickets
is_there_way_to_cache_kerberos_tickets() {
    local retval=0
    local ccache_name=
    local ccache=

    ccache_name="default_ccache_name"
    ccache="$(/usr/sbin/control krb5-conf-ccache)"

    case "$ccache" in
    "default")
        echo "The Kerberos ticket caching policy is configured by default."
        echo "The $ccache_name parameter is not set in krb5.conf"
        ;;
    "tmpfile")
        echo "A temporary file is used to cache Kerberos tickets."
        ;;
    "keyring")
        echo "The type of caching of Kerberos tickets is set: keyring."
        echo "Possible problems with caching of Kerberos tickets."
        retval=2
        ;;

    "unknown")
        if grep -q "^[[:space:]]*default_ccache_name[[:space:]]*=" /etc/krb5.conf; then
            echo "The method of caching Kerberos tickets is not defined."
            echo "The default_ccache_name parameter may have been set incorrectly."
            retval=1
        else
            echo "The Kerberos ticket caching policy is configured by default."
        fi
        ;;
    *)
        echo "The Kerberos ticket caching method is used, which may cause problems."
        retval=1
        ;;
    esac

    return "$retval"
}

# Checking that the [realms] and [domain_realm] sections are not empty in krb5.conf
is_sections_with_domain_name_in_krb5_empty() {
    local retval=0
    local section_name=
    local krb5_conf=
    local in_section=0
    local check_out=0

    krb5_conf="/etc/krb5.conf"

    while IFS= read -r line; do
        if echo "$line" | grep -qE '^\[(realms|domain_realm)\]'; then
            if test "$in_section" = 1 && test "$check_out" = 0; then
                echo "Warning. The [$section_name] section is empty."
                retval=2
            fi

            section_name="${line//^\[\(.*\)\]/\1/}"
            in_section=1
            check_out=0
            continue
        fi

        if test "$in_section" = 1; then
            if echo "$line" | grep -qE '^\['; then
                if test "$check_out" = 0; then
                    echo "Warning. The [$section_name] section is empty."
                    retval=2
                fi

                in_section=0
                continue
            fi

            if ! echo "$line" | grep -qE '^[[:space:]]*($|#)' && test "$check_out" != 1; then
                echo "The [$section_name] section is not empty."
                check_out=1
            fi
        fi
    done <"$krb5_conf"

    if test "$in_section" = 1 && test "$check_out" = 0; then
        echo "Warning. The [$section_name] section is empty."
        retval=2
    fi

    return "$retval"
}

# Checking whether the search for the Kerberos settings for the domain via DNS is enabled
is_dns_lookup_kdc_enabled() {
    local retval=0

    if grep -q "^\s*dns_lookup_kdc\s*=\s*\([Tt][Rr][Uu][Ee]\|1\|[Yy][Ee][Ss]\)\s*$" /etc/krb5.conf; then
        echo "The dns_lookup_kdc parameter in file \"/etc/krb5.conf\" is enabled."
    else
        if grep -q "^\s*dns_lookup_kdc\s*=" /etc/krb5.conf; then
            echo "The dns_lookup_kdc parameter in file \"/etc/krb5.conf\" is disabled."
            retval=1
        else
            echo "The dns_lookup_kdc parameter in file \"/etc/krb5.conf\" is disabled by default."
            retval=2
        fi
    fi

    return "$retval"
}

# Checking whether the search for the Kerberos domain name via DNS is disabled
is_dns_lookup_realm_disabled() {
    local retval=0

    if grep -q "^\s*dns_lookup_realm\s*=\s*\([Ff][Aa][Ll][Ss][Ee]\|1\|[Nn][Oo]\)\s*$" /etc/krb5.conf; then
        echo "The dns_lookup_realm parameter in file \"/etc/krb5.conf\" is disabled."
    else
        echo "The dns_lookup_realm parameter in file \"/etc/krb5.conf\" is enabled."
        echo "This parameter should be disabled."
        retval=1
    fi

    return "$retval"
}

# Checking for the presence of the resolv.conf configuration file and displaying the contents of the file
is_resolv_conf_file_exists() {
    local retval=0

    test -e /etc/resolv.conf || retval=1

    if test "$retval" = 0; then
        echo -e "The resolv.conf configuration file exists. Its contents are as follows:\n"
        ls -l /etc/resolv.conf
        echo
        cat /etc/resolv.conf
    else
        echo "The resolv.conf configuration file was not found."
    fi

    return "$retval"
}

# Checking whether the realm record in the krb5.conf configuration file matches one of the domain names in the resolv.conf configuration file
does_resolv_conf_and_default_realm_it_match() {
    local retval=2
    local search_domains=
    local krb5_default_realm=

    if test -e /etc/resolv.conf; then
        search_domains="$(__get_resolv_search_domains)"

        echo "The following domain names are defined from the \"resolv.conf\" configuration file:"
        echo -e "$search_domains\n"
    else
        echo "The \"resolv.conf\" configuration file was not found."
        retval=1
    fi

    if test -e /etc/krb5.conf; then
        krb5_default_realm="$(__get_krb5_default_realm)"

        echo "The following realm entry has been defined from the \"krb5.conf\" configuration file:"
        echo -e "$krb5_default_realm\n"
    else
        echo -e "The \"krb5.conf\" configuration file was not found.\n"
        retval=1
    fi

    if test "$retval" != 1; then
        for domain in $search_domains; do
            test "$domain" = "$krb5_default_realm" && retval=0
        done
    fi

    if test "$retval" = 0; then
        echo "The realm entry from the \"krb5.conf\" file matches the domain name defined in the \"resolv.conf\" file."
    elif test "$retval" = 2; then
        echo "The realm entry from the \"krb5.conf\" file does not match any of the domain name entries in the \"resolv.conf\" file."
    fi

    return "$retval"
}

# Checking the correspondence of the domain entry in the krb5.conf configuration file to the realm entry in the smb.conf configuration file
does_smb_realm_and_krb5_default_realm_it_match() {
    local retval=0
    local smb_realm=
    local krb5_default_realm=

    if which testparm >/dev/null 2>&1; then
        smb_realm="$(__get_smb_realm)"

        echo "The compliance of the realm record in smb.conf is checked using the testparm utility."

        if test -z "$smb_realm"; then
            echo -e "The realm record was not found in the \"smb.conf\" configuration file.\n"
            retval=2
        else
            echo "The following realm entry has been defined from the \"smb.conf\" configuration file:"
            echo -e "$smb_realm\n"

        fi
    else
        echo -e "The \"testparm\" command could not be executed.\n"
        retval=1
    fi

    if test -e /etc/krb5.conf; then
        krb5_default_realm="$(__get_krb5_default_realm)"

        echo "The following realm entry has been defined from the \"krb5.conf\" configuration file:"
        echo -e "$krb5_default_realm\n"
    else
        echo -e "The \"krb5.conf\" configuration file was not found.\n"
        retval=1
    fi

    if test "$retval" != 1; then
        test "$smb_realm" = "$krb5_default_realm" || retval=2
    fi

    if test "$retval" = 0; then
        echo "The realm entry from the \"krb5.conf\" configuration file is the same as the realm entry from the \"smb.conf\" configuration file."
    elif test "$retval" = 2; then
        echo "the realm entry from the \"krb5.conf\" configuration file does not match the realm entry from the \"smb.conf\" configuration file."
    fi

    return "$retval"
}

# Samba databases are checked for errors
are_there_errors_in_samba_databases() {
    local retval=0
    local out=

    if which samba-tool &>/dev/null; then
        out="$(/usr/bin/samba-tool dbcheck 2>&1)" || retval=2
        if test "$retval" = 0; then
            echo -e "No errors were found in the Samba databases:\n$out"
        else
            echo -e "Warning. There are no superuser rights or errors were found.\n$out"
        fi
    else
        echo "Information could not be obtained, perhaps the samba package is missing from the system."
        retval=1
    fi

    return "$retval"
}

# Checking the activity of the ntp service
is_ntp_service_running() {
    local retval=0
    local time_info=

    time_info="$(timedatectl 2>/dev/null)" || retval=1

    if test "$retval" = 1; then
        echo "An error occurred while executing the timedatectl command. Perhaps there is no such command."
    elif test "$(timedatectl show -p NTP --value)" = "yes"; then
        echo -e "The NTP service is active:\n$time_info"
    else
        echo -e "Warning. The NTP service is inactive:\n$time_info"
        retval=2
    fi

    return "$retval"
}

# Checking if time synchronization is enabled
is_time_synchronization_enabled() {
    local retval=0
    local time_info=

    time_info="$(timedatectl 2>/dev/null)" || retval=1

    if test "$retval" = 1; then
        echo "An error occurred while executing the timedatectl command. Perhaps there is no such command."
    elif test "$(timedatectl show -p NTPSynchronized --value)" = "yes"; then
        echo -e "Syncing is enabled:\n$time_info"
    else
        echo -e "Warning. Time synchronization is disabled:\n$time_info"
        retval=2
    fi

    return "$retval"
}

# Defining rules for checking certain ports
__check_port_rules() {
    local retval=0
    local port="$1"
    local service_name="$2"

    case "$port" in
    53)
        if test "$service_name" != "bind.service" && test "$service_name" != "samba.service"; then
            echo "Error: An invalid service is listening on port $port: $service_name"
            retval=1
        fi
        ;;
    123)
        if test "$service_name" != "chronyd.service"; then
            echo "Warning: Port $port listens to a service other than chronyd: $service_name"
            retval=2
        fi
        ;;
    *)
        if test "$service_name" != "samba.service"; then
            echo "Error: An invalid service is listening on port $port: $service_name"
            retval=1
        fi
        ;;
    esac

    return "$retval"
}

# Checking that the port is being listened to
__check_port() {
    local retval=0
    local port="$1"
    local output="$2"
    local unique_entries=
    local rule_result=0

    if echo "$output" | grep -q ":$port "; then
        echo "Port: $port is in use:"

        while IFS= read -r line; do
            process_name="$(echo "$line" | awk -F'"' '{print $2}' | sed -E 's/\(.*//; s/\[.*//')"
            pid="$(echo "$line" | awk -F'pid=' '{print $2}' | awk -F',' '{print $1}')"
            service_name="$(systemctl status "$pid" 2>/dev/null | head -n1 | awk '{print $2}')"
            entry="$process_name (pid: $pid) - $service_name"

            if ! echo "$unique_entries" | grep -qw "$pid"; then
                unique_entries="$unique_entries $pid"
                echo "$entry"

                __check_port_rules "$port" "$service_name"
                rule_result="$?"
                if test "$rule_result" = 1; then
                    retval=1
                elif test "$rule_result" = 2 && test "$retval" != 1; then
                    retval=2
                fi
            fi
        done <<<"$(echo "$output" | grep ":$port ")"

    else
        echo "Port $port is not in use."
        retval=1
    fi

    return "$retval"
}

# Checking that certain services are listening on ports
is_ports_listening() {
    local retval=0
    local ports=
    local count=
    local current=1
    local output=

    ports="53 88 123 135 137 138 139 389 445 464 636 3268 3269"
    count="$(echo "$ports" | wc -w)"
    output="$(ss -tulpn)"

    for port in $ports; do
        if ! __check_port "$port" "$output"; then
            case "$port" in
            123)
                echo "Warning: Port $port is not listening."
                if test "$retval" != 1; then
                    retval=2
                fi
                ;;
            *)
                if ! echo "$output" | grep -q ":$port "; then
                    echo "Error: Port $port is not listening."
                fi
                retval=1
                ;;
            esac
        fi

        if test "$current" != "$count"; then
            echo "-------------------------------------------------"
        fi

        current="$((current + 1))"
    done

    return "$retval"
}

# Local validation for a list of trusted domains
is_list_trusts_validated() {
    local retval=0
    local trusts=

    if which samba-tool &>/dev/null; then
        trusts="$(samba-tool domain trust list | awk '{print $4}' | sed 's/Name\[//;s/\]//' | __get_upper_string)"

        if test -z "$trusts"; then
            echo "The list of trusts has not been found."
        else
            echo "List of trusts:"
            samba-tool domain trust list

            while IFS= read -r domain; do
                echo -e "\nLocal validation of trust relations for the domain \"$domain\":\n"

                if ! samba-tool domain trust validate "$domain" --validate-location=local; then
                    echo -e "\nAn error occurred during the local validation of trusts for the domain \"domain\"."
                    retval=1
                fi
            done <<<"$trusts"
        fi
    fi

    return "$retval"
}

# Checking if a service is disabled
__is_service_disabled() {
    local retval=0
    local service="$1"
    local output

    output=$(__service_status "$service")
    status=$?
    case $status in
    3)
        echo "The \"$service\" service is disabled:"
        echo "$output"
        retval=0
        ;;
    2)
        echo "The \"$service\" service is stopped but not disabled:"
        echo "$output"
        retval=2
        ;;
    *)
        echo "The \"$service\" service is not disabled."
        echo "This service should be turned off."
        echo "$output"
        retval=1
        ;;
    esac

    return "$retval"
}

# Checking whether the nscd service is disabled
is_nscd_service_disabled() {
    __is_service_disabled "nscd.service"
}

# Checking whether the nslcd service is disabled
is_nslcd_service_disabled() {
    __is_service_disabled "nslcd.service"
}

# Checking whether the smb service is disabled
is_smb_service_disabled() {
    __is_service_disabled "smb.service"
}

# Checking whether the nmb service is disabled
is_nmb_service_disabled() {
    __is_service_disabled "nmb.service"
}

task is_domain_info_available "$PREDIAG_REVERSE"
task is_hostname_correct "$PREDIAG_NORMAL"
task is_hostname_static_and_transient "$PREDIAG_NORMAL"
task is_not_empty_sysvol "$PREDIAG_REVERSE_WITH_WARNINGS"
task does_sysvol_contain_necessary_files "$PREDIAG_IGNORE"
task is_samba_package_installed "$PREDIAG_NORMAL"
task is_samba_service_running "$PREDIAG_REVERSE_WITH_WARNINGS"
task is_resolve_local "$PREDIAG_IGNORE"
task is_krb5_conf_file_exists "$PREDIAG_IGNORE"
task is_smb_conf_file_exists "$PREDIAG_IGNORE"
task is_there_way_to_cache_kerberos_tickets "$PREDIAG_IGNORE"
task is_sections_with_domain_name_in_krb5_empty "$PREDIAG_IGNORE"
task is_dns_lookup_kdc_enabled "$PREDIAG_IGNORE"
task is_dns_lookup_realm_disabled "$PREDIAG_IGNORE"
task is_resolv_conf_file_exists "$PREDIAG_NORMAL"
task does_resolv_conf_and_default_realm_it_match "$PREDIAG_IGNORE"
task does_smb_realm_and_krb5_default_realm_it_match "$PREDIAG_IGNORE"
task are_there_errors_in_samba_databases "$PREDIAG_IGNORE"
task is_ntp_service_running "$PREDIAG_NORMAL"
task is_time_synchronization_enabled "$PREDIAG_NORMAL"
task is_ports_listening "$PREDIAG_IGNORE"
task is_list_trusts_validated "$PREDIAG_IGNORE"
task is_nscd_service_disabled "$PREDIAG_NORMAL"
task is_nslcd_service_disabled "$PREDIAG_NORMAL"
task is_smb_service_disabled "$PREDIAG_NORMAL"
task is_nmb_service_disabled "$PREDIAG_NORMAL"

if test "$cmd" = "report"; then
    file_smb="/etc/samba/smb.conf"
    file_krb="/etc/krb5.conf"

    if test "$plain_mode" = 0; then
        # shellcheck disable=SC2153
        report_archive="$TMP/report_$current_date.tar.gz"
    fi

    tar -czf "${report_archive}" \
        -C "$(dirname "$journal")" "$(basename "$journal")" \
        -C "$(dirname "$file_smb")" "$(basename "$file_smb")" \
        -C "$(dirname "$file_krb")" "$(basename "$file_krb")"

    if test -n "$path_to_report_archive"; then
        cp "${report_archive}" "$path_to_report_archive"
        echo "The archive with the report is saved along the way: $path_to_report_archive"
    else
        if test "$plain_mode" = 0; then
            echo "The archive with the report is saved as: $report_archive"
        else
            cat "${report_archive}"
        fi
    fi
fi

exit "$global_retval"
