#!/bin/bash
# Install and configure a Ubuntu-PC for the charity organization angestöpselt e.V. | angestoepselt.de
# created by matthias of angestöpselt e.V.
# created at 2022-02-17

version=0.2

set -o errexit
exec 100>/tmp/z31.lock || exit 1
flock 100 || exit 1

# Speed up script by not using unicode.
LC_ALL=C
LANG=C


# some static variables
DEBIAN_FRONTEND=noninteractive
current_user=$(id 1000 | awk -F '[()]' '{print $2}')
lockfile=/tmp/z31.lock
sysinfofile=$HOME/.config/systeminfo.json
random_tmpdir=$(mktemp)
SCRIPT_DIR="$(dirname "$0")"
TIME=$(date +%Y%m%d%H%M)
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
LOGFILE="$SCRIPT_DIR/$TIME-z31.log"

# Set variables for stdout print
COLOR_N='\e[0m'
COLOR_GREEN='\e[1;32m'
COLOR_RED='\e[1;31m'
COLOR_BLUE='\e[1;34m'
COLOR_WHITE='\e[1;97m'
OK="[${COLOR_GREEN}ok${COLOR_N}]"
FAIL="[${COLOR_RED}fail${COLOR_N}]"
INFO="[${COLOR_BLUE}info${COLOR_N}]"
SKIP="[${COLOR_BLUE}skip${COLOR_N}]"
PROGRESS="[${COLOR_BLUE}..${COLOR_N}]"

# log actual command and it's stderr to logfile in one go
runcmd_stdout() {
    echo "+ $1" >>"$LOGFILE"
    bash -c -o pipefail "$1" 2>>"$LOGFILE" | tee -a "$LOGFILE"
}

runcmd() {
    echo "+ $1" >>"$LOGFILE"
    bash -c -o pipefail "$1" >>"$LOGFILE" 2>&1
}

printprog() {
    echo -ne "${PROGRESS} $*\r"
}

printok() {
    echo -e "\r${OK} $*"
}

printfail() {
    echo -e "\r${FAIL} $*"
}

printinfo() {
    echo -e "${INFO} $*"
}

printskip() {
    echo -e "\r${SKIP} $*"
}

ErrorHandling() {
    set -eu
    printfail "Something went wrong, exiting. Check $LOGFILE for more details."
}

system_update() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "update cache"
    runcmd "sudo /usr/bin/apt-get update -qq"
    printok "update cache"

    printprog "upgrade system"
    runcmd "sudo /usr/bin/apt-get dist-upgrade -y -qq"
    printok "upgrade system"
}

system_install_base() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "add anydesk repo to source.list.d"
  
    if [ -f "/etc/apt/sources.list.d/anydesk-stable.list" ]; then
      printskip "add anydesk repo to source.list.d"
    else
      runcmd "sudo /usr/bin/wget -qO - https://keys.anydesk.com/repos/DEB-GPG-KEY | sudo apt-key add - && \
              echo "deb http://deb.anydesk.com/ all main" > /etc/apt/sources.list.d/anydesk-stable.list"
      printok "add anydesk repo to source.list.d"
    fi

    echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | sudo debconf-set-selections

    printprog "install apt packages (this can take some time)"
    runcmd "sudo /usr/bin/apt-get install --yes -qq -o Dpkg::Options::=--force-confdef --ignore-missing \
            ubuntu-restricted-extras \
            apt-transport-https \
            vlc \
            gdebi \
            gparted \
            tlp \
            language-pack-de-base \
            libreoffice \
            libreoffice-l10n-de \
            libreoffice-help-de \
            libreoffice-help-en-gb \
            libreoffice-l10n-en-gb \
            firefox \
            firefox-locale-de \
            firefox-locale-en \
            thunderbird \
            thunderbird-locale-de \
            thunderbird-locale-en \
            thunderbird-locale-en-gb \
            gimp \
            anydesk \
            libqt5qml5 \
            language-selector-common \
            openoffice.org-hyphenation \
            language-pack-kde-ar \
            language-pack-kde-uk \
            language-pack-kde-ru \
            language-pack-kde-fa"
    printok "install apt packages"
}

system_install_edubuntu() {
    set -o pipefail
    trap ErrorHandling ERR INT

    # Edubuntu packages (official support until 18.04) https://wiki.ubuntuusers.de/Edubuntu_Programme
    printprog "install Edubuntu packages (this can take some time)"
    runcmd "sudo /usr/bin/apt-get install --yes -qq -o Dpkg::Options::=--force-confdef --ignore-missing \
            blinken \
            calibre \
            cantor \
            chemtool \
            dia \
            einstein \
            fritzing \
            gamine \
            gcompris \
            goldendict \
            inkscape \
            kalgebra \
            kalzium \
            kanagram \
            kbruch \
            kgeography \
            khangman \
            kig \
            klettres \
            kmplot \
            kstars \
            ktouch \
            ktuberling \
            kturtle \
            kwordquiz \
            laby \
            lightspeed \
            lybniz \
            marble \
            melting \
            parley \
            pencil2d \
            ri-li \
            rocs \
            step \
            tuxmath \
            tuxpaint \
            tuxtype \
            yorick"
    printok "install Edubuntu packages"
}

system_clean() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "clean up apt cache and packages"
    runcmd "sudo /usr/bin/apt-get autoremove --yes"
    printok "clean up apt cache and packages"
}

system_configure() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "set timezone"
    runcmd "timedatectl set-timezone Europe/Berlin"
    printok "set timezone"
}

system_files_download() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog ""
    runcmd ""
    printok ""
}

system_modify_dm() {
    set -o pipefail
    trap ErrorHandling ERR INT
    # TODO: detect dm of device and choose modification
    # cat /etc/X11/default-display-manager > /usr/sbin/gdm3 (Gnome Desktop Manager)
    # goals to cover
    # - Wallpaper
    # - 
    printprog ""
    runcmd ""
    printok ""
}


system_checkinfo() {
    set -o pipefail
    trap ErrorHandling ERR INT

    # this part checks if the device is already registered in snipe-IT
    # is already registered it returns 1
    # this is a new device it returns 0

    if [ ! -f $sysinfofile ]; then
      printprog "write systeminfo to $sysinfofile"
      runcmd "mkdir /home/$current_user/.config/angestoepselt && /bin/bash /usr/local/bin/angestoepselt-info > $sysinfofile"
      printok "write systeminfo to $sysinfofile"
    fi

    printprog "read systeminfo"
    runcmd "jq -e . $sysinfofile >/dev/null 2>&1"
    printok "read systeminfo"

    HW_ADDRESS=$(cat $sysinfofile | jq -r .HW_ADDRESS)
    ASSET_TAG=$(cat $sysinfofile | jq -r .Asset_Tag)

    ## start localcheck
    if [[ $ASSET_TAG =~ ^[0-9]+$ ]]; then
      printinfo "already in Snipe $ASSET_TAG"
      return 1
    elif
      [ -f $SCRIPT_DIR/mac_white.list ] && grep -Fxq "$HW_ADDRESS" mac_white.list
    then
      unset HW_ADDRESS
      printinfo "Device is using a charity owned USB Ethernet-Adapter"
      return 1
    ## end localcheck
    else
    ## start snipe-it check
      printprog "check presence of snipe-IT API"
      runcmd "curl -o /dev/null --silent https://$SNIPEIT_DOMAIN"
      printok "check presence of snipe-IT API"

      printinfo "fetch info from Snipe-IT"
      result=$(curl --silent --request GET \
                    --url "https://$SNIPEIT_DOMAIN/api/v1/hardware?limit=1&search=${HW_ADDRESS//[:]/%3A}" \
                    --header 'accept: application/json' \
                    --header 'authorization: Bearer '$SNIPEIT_APIKEY'')

      [[ $(echo $result | jq '(has("error"))') == true ]] && { printinfo "$(echo $result | jq .error)"; return 1; }
      system_check_result=$(echo $result | jq .total) # doesn't work
      asset_tag=$(echo $result | jq -r .rows[].asset_tag)

      if [ -n "$asset_tag" ]; then
        printinfo "Device already present in Snipe-IT: RE$asset_tag"

        printprog "add ASSET_TAG to $sysinfofile"
        runcmd "jq '. |= . + {"Asset_Tag": \"'${asset_tag}'\"}' $sysinfofile > $random_tmpdir/systeminfo.json.tmp && mv $random_tmpdir/systeminfo.json.tmp $sysinfofile"
        printok "add ASSET_TAG to $sysinfofile"
      else
        printok "Device not in Snipe-IT"
        system_checkinfo_value="newdevice"
        return 0
      fi
    ## end snipe-it check
    fi
}

system_register() {
    set -o pipefail
    trap ErrorHandling ERR INT

    if [[ "$system_checkinfo_value" != "newdevice" ]]; then
      printskip "register Device at Snipe-IT"
    fi

    # is snipe-IT reachable?
    printprog "check presence of snipe-IT API"
    runcmd "curl -o /dev/null --silent https://$SNIPEIT_DOMAIN"
    printok "check presence of snipe-IT API"

    # convert json to variables, like key=value
    eval "$(jq -r '. | to_entries | .[] | .key + "=" + (.value | @sh)'< $sysinfofile)"

    printinfo "fetching variables for register $Platform at Snipe-IT"

    # get platform. at the moment this looks for a laptop in string. If ist not a laptop it is a desktop PC (model 2)
    [[ "$Platform" == *"laptop"* ]] && { model=1; display="$Displaysize @ $Resolution"; } || model=2
    # clean up anydesk variable. > room for improvement, only integer should be in the var. This can be handled in the angestoepselt-info script
    [[ "$Anydesk_ID" == *"SERVICE_NOT_RUNNING"* ]] && unset Anydesk_ID
    # cannont locate issue with true and false, simple workaround to fix it
    [[ "$Optical_Drive" == "FALSE" ]] && Optical_Drive=nein || Optical_Drive=ja


    # change this variables used in this post to your needs, read the README of this repo and read ahead in the Snipe-IT API docs for more Informations
    # https://snipe-it.readme.io/reference/updating-custom-fields
    post_data()
{
  cat <<EOF
{
  "status_id": "2",
  "name": "$Model",
  "model_id": "$model",
  "serial": "$serialno",
  "_snipeit_mac_address_1": "$HW_ADDRESS",
  "_snipeit_betriebssystem_2": "$Distro",
  "_snipeit_festplatte_4": "$HDD",
  "_snipeit_prozessor_5": "$CPU",
  "_snipeit_arbeitsspeicher_6": "$Memory",
  "_snipeit_optisches_laufwerk_7": "$Optical_Drive",
  "_snipeit_display_8": "$display",
  "_snipeit_akku_9": "$Battery_life",
  "_snipeit_anydeskid_10": "$Anydesk_ID"
}
EOF
}
    printprog "register $Platform at Snipe-IT"
    post_result=$(curl --silent --request POST \
        --url 'https://'$SNIPEIT_DOMAIN'/api/v1/hardware' \
        --header 'accept: application/json' \
        --header 'authorization: Bearer '$SNIPEIT_APIKEY'' \
        --header 'Content-Type: application/json' \
        --data "$(post_data)")

    post_status=$(echo $post_result |jq -r .status)
    post_message=$(echo $post_result |jq -r .message)
    asset_tag=$(echo $post_result |jq -r .payload.asset_tag)

    if [ "$post_status" = "success" ]; then
      printok "register $Platform at Snipe-IT"

      printprog "add PC to $sysinfofile"
      runcmd "jq '. |= . + {"Asset_Tag": \"'${asset_tag}'\"}' $sysinfofile > $random_tmpdir/systeminfo.json.tmp && mv $random_tmpdir/systeminfo.json.tmp $sysinfofile"
      printok "add PC to $sysinfofile"
    else
      printfail "$post_message"
    fi
}

script_check_root() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "check root privileges"
    runcmd "[ $(id -u) -eq 0 ]"
    printok "check root privileges"
}

script_check_snipesecrets() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printprog "check .secrets.env"
    runcmd "[ -f $SCRIPT_DIR/.secrets.env ] && export $(cat $SCRIPT_DIR/.secrets.env)"
    printok "check .secrets.env"
}

script_prerequisites() {
    set -o pipefail
    trap ErrorHandling ERR INT

    printinfo "install requirements"

    printprog "install required packages"
    runcmd "sudo /usr/bin/apt-get -qq --yes install jq moreutils dialog html2ps python3 python3-pip curl wkhtmltopdf"
    printok "install required packages"

    printprog "install angestoepselt-info"
    runcmd "sudo /usr/bin/wget –quiet $REPO/raw/branch/master/angestoepselt-info -O /usr/local/bin/angestoepselt-info && sudo chmod +x /usr/local/bin/angestoepselt-info"
    printok "install angestoepselt-info"

}


usage() {
  printf "%s" "\
Usage: $0 --option

    Options:
    INFO:
        -h|--help                   print this info
        -v|--version                show version of script

        -a|--all                    running all task below

        -u|--update                 perform an basic system update
        -i|--install                install all packages for this system
        -r|--register               Register PC in snipe-IT Asset Management
        -c|--configure              running basic system config settings
        -d|--files_download         download useful files to your HomeDirectory
        -m|--modify_dm              paint your Desktop Manager with angestoepselt design
        --checkinfo                 Check if PC is already in snipe-IT present
        --clean                     clean up trash, like files and from system tasks like packagemanger

"
  exit 1
}

POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
case $1 in
    -h|--help)
    usage
    ;;
    -u|--update)
    script_check_root
    system_update
    shift
    shift
    ;;
    -i|--install)
    script_check_root
    system_install_base
    system_install_edubuntu
    shift
    shift
    ;;
    --clean)
    script_check_root
    system_clean
    shift
    shift
    ;;
    --charity)
    charity=${OPTARG:=angestoepselt}
    shift
    shift
    ;;
    -c|--configure)
    script_check_root
    system_configure
    shift
    shift
    ;;
    -d|--files_download)
    system_files_download
    shift
    shift
    ;;
    -m|--modify_dm)
    script_check_root
    system_modify_dm
    shift
    shift
    ;;
    --checkinfo)
    script_check_snipesecrets
    system_checkinfo
    shift
    shift
    ;;
    -r|--register)
    script_check_snipesecrets
    system_register
    shift
    shift
    ;;
    -a|--all)
    script_check_root
    system_update
    system_install
    system_clean
    system_configure
    system_files_download
    system_modify_dm
    script_check_snipesecrets
    system_checkinfo
    system_register
    exit 0
    ;;
    -v|--version)
    printf '%s\n' "$0 v$version"
    exit 1
    ;;
    -*|--*)
    echo "Unknown option $1"
    usage
    exit 1
    ;;
    *)
    POSITIONAL_ARGS+=("$1")
    shift
    ;;
    esac
done

set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters

if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi

