#!/bin/bash
# -*- coding: utf-8 -*-

# antiX script for changing the hostname of a machine safely.
# 2023,2024 Robin.antiX, antiX community
# GPL v.3
# version 1.0
# dependencies: bind9-dnsutils (for nslookup, dig), grep, sed, yad, gettext, xauth, coreutils (for logname), sudo

# hint: sudo -u is used here to make sure the xauth magick cookie ~/.Xauthority
# files get the proper owner, since they fail working when written by root. This
# also allows to apply the user theme on the dialog windows rather than the
# GUI style set by root.

TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=antiX-hostname-changer
export TEXTDOMAINDIR TEXTDOMAIN
source gettext.sh

# check for root
if [ "$(whoami)" != "root" ]; then
    echo "$(eval_gettext "Error: This must be run by sudo or root.")"
    exit 1
fi

true_user="$(logname)"
old_hostname="$(hostname)"
pattern='^[a-z]+(([a-z0-9\-])*[a-z0-9]+)*$' # strict, not allow starting with a digit.
#pattern='^[a-z0-9]+(([a-z0-9\-])*[a-z0-9]+)*$' # relaxed, allow start with a digit.
pattern2='[-][-][-]*' # restrict consecutive dashes.
icon='/usr/share/icons/papirus-antix/48x48/apps/antiXhnc.png'

while :; do
    # ask user for new hostname
    new_hostname=$(sudo -u "$true_user" -- yad --title="$(eval_gettext "Change antiX hostname")" \
                  --center --borders=10 \
                  --window-icon="$icon" \
                  --entry \
                  --entry-label="$(eval_gettext "Enter new hostname:")" \
                  --entry-text="$old_hostname" \
                  --button="$(eval_gettext "Abort")":1 --button="$(eval_gettext "Change it!")":0)
    if [ $? -ne 0 ]; then exit 0; fi

    if [ "$new_hostname" == "$old_hostname" ]; then
        echo "$(eval_gettext "Nothing to do.")"
        exit 0
    fi

    # validate new hostname; we need to uses LANG=C for running the actual tests to avoid false negatives on ümlauts
    valid=true
    userlang=$(echo "$LANG")
    LANG=C
    # check max length (63 octets allowed)
    if [ ${#new_hostname} -ge 64 ]; then
        valid=false;
    # check whether name comprises of lower case ascii and hyphens only and does not start with a number, does not start or end with a hyphen.
    elif [[ ! "$new_hostname" =~ $pattern ]]; then
        valid=false;
    # check for consecutive hyphens
    elif [[ "$new_hostname" =~ $pattern2 ]]; then
        valid=false;
    fi
    LANG="$userlang"
    if ! $valid; then
	    echo "$(eval_gettext "Not a valid hostname.")"
        sudo -u "$true_user" -- yad --title="$(eval_gettext "Error on changing hostname")" \
        --center --borders=10 \
        --window-icon="$icon" \
        --fixed \
        --text="<b>""$(eval_gettext "Not a valid hostname.")""</b>\n\n ""$(eval_gettext "Please check RFC-952/RFC-1123 for details.")""\n\n <u>""$(eval_gettext "In short:")""</u>\n\n\t– ""$(eval_gettext "Up to 63 characters max.")""\n\n\t– ""$(eval_gettext "Only 7-bit ASCII.")""\n\n\t– ""$(eval_gettext "Lower-case only.")""\n\n\t– ""$(eval_gettext "Only alphanumeric characters or hyphens.")""\n\n\t– ""$(eval_gettext "No blanks, no underline, no dots, no ümlauts, no diacritics.")""\n\n\t– ""$(eval_gettext "No consecutive hyphens.")""\n\n\t– ""$(eval_gettext "Must not end or start with a hyphen.")""\n\n\t– ""$(eval_gettext "Must not start with a digit.")""\n" \
        --button="$(eval_gettext "Retry")":3 --button="$(eval_gettext "Abort")":1
	    if [ $? -eq 1 ]; then exit 1; fi
    else
        # check whether new hostname already exists in local network neighbourhood
        if nslookup "$new_hostname" >/dev/null 2>&1; then
            found="$(dig +short $new_hostname)"
            echo $"Error: hostname $new_hostname exists already, IP is $found in your LAN."
            sudo -u "$true_user" -- yad --title="$(eval_gettext "Error on changing hostname")" \
            --center --borders=10 \
            --window-icon="$icon" \
            --fixed \
            --text="<b>""$(eval_gettext "Duplicate hostname.")""</b>\n\n "$"The hostname <u>$new_hostname</u> already\n exists with IP <u>$found</u> in\n your local network neighbourhood.""\n\n ""$(eval_gettext "Make sure the hostname you pick is\n unique in your local network.")""\n" \
            --button="$(eval_gettext "Ignore")":5 --button="$(eval_gettext "Retry")":3 --button="$(eval_gettext "Abort")":1
            result=$?
	        if [ $result -eq 1 ]; then
	            exit 1
	        elif [ $result -eq 5 ]; then
                break
	        fi
        else
            break
        fi
    fi
done

line=$(grep -n "^127\(.[012]\{1\}[012345]\{0,2\}\)\{3\}[[:space:]]\+$(hostname)$" /etc/hosts)
rest="${line#*:}"
line_number="${line%:*}"
ip_localhost="${rest% *}"

# let's do it the safe way.
# add new hostname to /etc/hosts file
sed -i "$((line_number))i\\$ip_localhost $new_hostname" /etc/hosts

# put the new hostname into /etc/hostname
echo $new_hostname > /etc/hostname

# remove old hostname from /etc/hosts file
sed -i "$((line_number+1))d" /etc/hosts

# activate new hostname
hostname -F /etc/hostname

# handle X11 magick cookie
readarray -t files < <(sudo find /home -name .Xauthority -print)
for authfile in "$files"; do
    authuser="$(echo $authfile | cut -d/ -f3)"
    readarray -t Xauthorities < <(xauth -f $authfile list)
    for item in "$Xauthorities"; do
        cookie="$(echo "$item" | sed "s/^$old_hostname\(..*\)$/\1/")"
        newline="$new_hostname""$cookie"
        particle="$(echo $cookie | cut -d' ' -f1)"
        xauth -f "$authfile" add "$newline"
        xauth -f "$authfile" remove "$old_hostname""$particle"    
    done
done

# check whether hostname change was successful
test_hostname="$(hostname)"
if [ "$test_hostname" != "$new_hostname" ]; then
    echo "$(eval_gettext "Something went wrong. Please check the hostname, the files /etc/hostname and /etc/hosts and also the X11 xauth magick cookie manually")"
    sudo -u "$true_user" -- yad --title="$(eval_gettext "Error on changing hostname")" \
        --center --borders=10 \
        --window-icon="$icon" \
        --fixed \
        --text="<b>""$(eval_gettext "Hostname change has failed.")""</b>\n\n\t""$(eval_gettext "Something went wrong. Please check\n\tthe <i>hostname</i>, the files <i>/etc/hostname</i> and\n\t<i>/etc/hosts</i> and also the <i>X11 xauth magick\n\tcookie</i> manually.")""\n" \
        --button="$(eval_gettext "Will do so")":1
    exit 1
else
    echo $"antiX hostname was successfully changed from »$old_hostname« to »$test_hostname«."
    sudo -u "$true_user" -- yad --title="$(eval_gettext "Change antiX hostname")" \
        --center --borders=10 \
        --window-icon="$icon" \
        --fixed \
        --text="<b>""$(eval_gettext "Hostname has been changed.")""</b>\n\n ""$(eval_gettext "New <i>hostname</i> is:")"" <u>""$test_hostname""</u>\n" \
        --no-buttons \
        --timeout=7
    exit 0
fi
