#!/bin/bash
# Copyright (c) 2015 SUSE LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

set -e

TEXTDOMAIN='jeos-firstboot'

. /etc/os-release
. "$0-functions"

stty_size() {
    set -- `stty size`; LINES=$1; COLUMNS=$2
    # stty size can return zero when not ready or
    # its a serial console
    if [ "$COLUMNS" = "0" -o "$LINES" = "0" ]; then
        LINES=24
        COLUMNS=80
    fi
}; stty_size

# for testing we may run as non root
if [ -w /run ]; then
    export TMPDIR=/run
    # debugging
    if [ -n "$FIRSTBOOT_DEBUG" ]; then
	set -x
	exec 2>/var/log/firstboot-debug
    fi
else
    dry=1
fi

if [ -n "$dry" ]; then
    run() {
	echo "$@"
    }
else
    run() {
	"$@"
    }
fi

dialog_out=`mktemp -qt 'firstboot-XXXXXX'`
cleanup() {
	echo .oOo.oOo.oOo. > $dialog_out
	rm -f "$dialog_out"
	# reenable systemd and kernel logs
	run kill -s SIGRTMAX-10 1
	run setterm -msg on
}
trap cleanup EXIT

# avoid kernel messages spamming our console
run setterm -msg off
# Avoid systemd messages spamming our console
run kill -s SIGRTMAX-9 1
# sleep to avoid systemd bug, bsc#1119382
sleep 1

systemd_firstboot_args=('--setup-machine-id')

result=
list=
keytable=''
locale=''
password=''

let dh_menu=LINES-15
let dh_text=LINES-5

d(){
    retval=
    while true
    do
        dialog --backtitle "$PRETTY_NAME" "$@" 2>"$dialog_out"
        retval=$?
        case $retval in
          0)
            # need || true as dialog doesn't write newlines
            read result < $dialog_out || true
            return 0
            ;;
          1)
            dialog --yesno $"Do you really want to quit?" 0 0 && exit 1
            continue
            ;;
          255)
            # xargs to remove whitespaces
            result_error="$(xargs -a "$dialog_out")"
            if [ -z "$result_error" ]; then
                dialog --yesno $"Do you really want to quit?" 0 0 && exit 1
                continue
            fi
            logger -p err -t jeos-firstboot "$result_error"
            dialog --msgbox $"Exiting due to error, please check the system log" 0 0
            exit 2
            ;;
        esac
    done
}

warn(){
    d --title $"Warning" --msgbox "$1" 6 40
}

menulist()
{
    list=()
    local line
    while read line; do
	    list+=("$line" '')
    done < <("$@"||true)
    [ -n "$list" ]
}

# for some reason localectl doesn't work here
#menulist localectl --no-pager list-keymaps
findkeymaps()
{
    list=()
    local line
    while read line; do
	    list+=("${line%.map.gz}" '')
    done < <(find /usr/share/kbd/keymaps -name '*.map.gz' -printf "%f\n" | sort -u)
    [ -n "$list" ]
}

findlocales()
{
    list=()
    local l locale
    # List only locales which are both in live-langset-data and glibc-locale(-base)
    for l in /usr/share/langset/*; do
        locale="${l#/usr/share/langset/}"
        [ -d "/usr/lib/locale/${locale}.utf8" ] || continue
        list+=("${locale}" '')
    done
    [ -n "$list" ]
}

if [ -z "$LOCALE_PRESET" ]; then
    default="en_US"
    [ -f /etc/locale.conf ] && locale_lang="$(awk -F= '$1 == "LANG" { split($2,fs,"."); print fs[1]; exit }' /etc/locale.conf)"
    [ -n "$locale_lang" ] && default="$locale_lang"

    list=() # Set by findlocales
    newlocale="$default"
    if ! findlocales; then
        d --msgbox $"No locales found" 0 0
    elif [ "${#list[@]}" -eq 2 ]; then
        newlocale="${list[0]}"
        d --msgbox $"Locale set to $newlocale.\nTo change to a different one, install glibc-locale and use\n'localectl set-locale LANG=ex_AMPLE.UTF-8'." 8 50
    else
        d --default-item "$default" --menu $"Select System Locale" 0 0 $dh_menu "${list[@]}"
        newlocale="${result}"
    fi

    if [ -n "$newlocale" ]; then
        locale="${newlocale}.UTF-8"
        systemd_firstboot_args+=("--locale=$locale")

        # Run langset to get consolefont and keymap set up
        run langset.sh $locale || warn $"Setting the locale failed"
    fi
else
    locale="$LOCALE_PRESET"
    systemd_firstboot_args+=("--locale=$locale")
fi

if [ -z "$KEYTABLE_PRESET" ]; then
    default="us"
    [ -f /etc/vconsole.conf ] && vconsole_keymap="$(awk -F= '$1 == "KEYMAP" { split($2,fs,"."); print fs[1]; exit }' /etc/vconsole.conf)"
    [ -n "$vconsole_keymap" ] && default="$vconsole_keymap"

    if findkeymaps \
        && d --default-item "$default" --menu  $"Select Keyboard Layout" 0 0 $dh_menu "${list[@]}"; then
        if [ -n "$result" ]; then
            keytable="$result"

                # Activate the selected keyboard layout
                run langset.sh "$locale" "$keytable" || warn $"Setting the keyboard layout failed"
        fi
    else
        d --msgbox $"Error setting keyboard" 0 0
    fi
else
    keytable="$result"
fi

[ -n "${locale}" ] && language="${locale%%_*}" || language="en"
force_english_license=0
export LANG="$locale"

if [[ "$(ps h -o tty -p $$)" = tty[0-9]* ]]; then
    # Those languages can't be displayed in the console
    declare -A start_kmscon
    start_kmscon["cs"]=1
    start_kmscon["ja"]=1
    start_kmscon["zh"]=1
    start_kmscon["ko"]=1

    if [ -n "$locale" -a -n "${start_kmscon[${language}]+_}" ]; then
        export LOCALE_PRESET="$locale"
        export KEYTABLE_PRESET="$keytable"

        if kmscon_available; then
            ret_file="$(mktemp)"
            kmscon --silent --font-size 10 --palette vga --no-reset-env -l -- /bin/sh -c "$0; echo \$? > $ret_file; kill \$PPID"
            exit $(cat "$ret_file"; rm -f "$ret_file")
        elif fbiterm_available; then
            exec fbiterm -- "$0"
        else
            # No kmscon or fbiterm, fall back to english
            export LANG="en_US.UTF-8"
            force_english_license=1
        fi
    fi
fi

# Find the location of the EULA
# An EULA in /etc takes precedence
EULA_FILE=/etc/YaST2/licenses/base/license.txt
[ -e "${EULA_FILE}" ] || EULA_FILE=/usr/share/licenses/product/base/license.txt

# Failsafe: If neither a license nor the no-acceptance-needed flag are found, quit.
if ! [ -e "$EULA_FILE" -o -e "${EULA_FILE%/*}/no-acceptance-needed" ]; then
	d --msgbox $"No license found - cannot continue" 6 40
	exit 1
fi

if [ -e "$EULA_FILE" -a ! -e "${EULA_FILE%/*}/no-acceptance-needed" ]; then
    if [ "$force_english_license" = "0" ]; then
        for i in "${EULA_FILE%.txt}.$locale.txt" \
                "${EULA_FILE%.txt}.${locale%%.UTF-8}.txt" \
                "${EULA_FILE%.txt}.${language}.txt"; do
            if [ -e "$i" ]; then
            EULA_FILE="$i"
            break
            fi
        done
    fi

    while ! dialog --backtitle "$PRETTY_NAME" --textbox "$EULA_FILE" $dh_text 85 --and-widget --yesno $"Do you agree with the terms of the license?" 0 0; do
	d --msgbox $"Well, we cannot continue then ..." 6 40
    done
fi

default="$(readlink -f /etc/localtime)"
default="${default##/usr/share/zoneinfo/}"

# timedatectl doesn't work as dbus is not up yet
# menulist timedatectl --no-pager list-timezones
if menulist awk \
    'BEGIN{print "UTC"; sort="sort"}/^#/{next;}{print $3|sort}END{close(sort)}' \
    /usr/share/zoneinfo/zone.tab \
    && d --default-item "$default" --menu $"Select Time Zone" 0 0 $dh_menu "${list[@]}"; then
	if [ -n "$result" ]; then
	    systemd_firstboot_args+=("--timezone=$result")
	fi
else
	d --msgbox $"error setting timezone" 0 0
fi

# systemd-firstboot does not set the timezone if it exists, langset.sh created it
run rm -f /etc/localtime
run systemd-firstboot "${systemd_firstboot_args[@]}"

# NOTE: must be last as dialog file is used to set the password
while true; do
    password=
    if d --insecure --passwordbox  $"Enter root Password" 0 0; then
	password="$result"
	if d --insecure --passwordbox  $"Confirm root Password" 0 0; then
	    if [ "$password" != "$result" ]; then
		d --msgbox $"Entered passwords don't match" 5 40 || true
		continue
	    fi
	    # don't use that one as we need to switch locale
	    #systemd_firstboot_args+=("--root-password-file=$dialog_out")
	fi
    fi
    if [ -z "$password" ]; then
	warn $"Warning: No root password set.

You cannot log in that way. A debug shell will be started on tty9 just this time. Use it to e.g. import your ssh key." 0 0 || true
	run systemctl start debug-shell.service
    fi
    break
done

if [ -x /usr/bin/SUSEConnect ]; then
    d --msgbox $"Please register this image using your existing SUSE entitlement.

As \"root\" use the following command:

 SUSEConnect -e company@example.com -r YOUR_CODE

to register the instance with SCC

Without registration this instance does not have access to updates and
security fixes." 0 0 || true
fi

d --infobox $"Collecting network info ..." 3 33
## Configure initial network settings
#
shopt -s nullglob

candidates=()
for p in /sys/class/net/* ; do
        test -f "$p" && continue # skip bonding_masters file

        # only devices having ID_NET_NAME.* attrs
        udevadm info -q all -p "$p" | grep -qs ID_NET_NAME || continue
        d=${p##*/}

        unset IPADDR GATEWAYS
        eval `wicked test dhcp4 "$d" 2>/dev/null | grep -E "^IPADDR=|^GATEWAYS="`
        ip link set down "$d" # set link down after probe once done

        test -n "$IPADDR"   && candidates+=("$d")  || continue # ok, track it
        test -n "$GATEWAYS" && candidates=("$d")  || continue # just use it
        break
done

for d in ${candidates[@]} ; do
        rm -f "/etc/sysconfig/network/ifcfg-$d" || exit 1
        printf "STARTMODE=auto\nBOOTPROTO=dhcp\n" \
                > "/etc/sysconfig/network/ifcfg-$d"
        break # one is enough
done

config_wireless=false
if is_raspberry && ls -d /sys/class/net/*/wireless &>/dev/null; then
    if dialog --yesno $"Configure wireless network?" 0 0; then
	config_wireless=true
    fi
fi
if [ "$config_wireless" = "true" ]; then
    # Wi-fi
    #
    get_wlan_devices()
    {
        list=()
        local line
        while read line; do
                list+=("${line}" '') 
        done < <(ls -d /sys/class/net/*/wireless | awk -F'/' '{ print $5 }')
        [ -n "$list" ]
    }

    get_wlan_networks()
    {
        list=()
        local line
        while read line; do
                list+=("${line}" '') 
        done < <(ip link set $wlan_device up && iwlist $wlan_device scan|grep ESSID|cut -d':' -f2|cut -d'"' -f2|sort -u)
        [ -n "${list[*]}" ]
    }

    wlan_error(){
        d --title $"Error" --msgbox "$1" 0 0
        dialog --yesno $"Do you want to retry?" 0 0
    }
    while true
    do
        if get_wlan_devices && d --menu  $"Select Wireless card to configure" 0 0 $dh_menu "${list[@]}"; then
            wlan_device="$result"
        else
            if wlan_error $"Error listing wlan devices"; then
                continue
            else
                break
            fi
        fi

        if get_wlan_networks && d --menu  $"Select Wireless network to connect" 0 0 $dh_menu "${list[@]}"; then
            wlan_network="$result"
        else
            if wlan_error $"Error listing wireless networks"; then
                continue
            else
                break
            fi
        fi

        if d --menu $"Select authentication mode" 0 0 $dh_menu $"WPA-PSK" '' $"WPA-EAP" '' $"Open" ''; then
            wlan_auth_mode="$result"
        fi
        wlan_auth_mode_conf=
        if [ "$wlan_auth_mode" = "WPA-EAP" ]; then
            wlan_auth_mode_conf=eap
            if d --inputbox $"Username" 0 0; then
                wlan_username="$result"
            fi
        fi
        if [ "$wlan_auth_mode" = "WPA-PSK" ]; then
                wlan_auth_mode_conf=psk
        fi
        if [ "$wlan_auth_mode" = "Open" ]; then
                wlan_auth_mode_conf=open
        fi
        if [ "$wlan_auth_mode" != "Open" ]; then	
            wlan_password=
            if d --insecure --passwordbox  $"Password" 0 0; then
                wlan_password="$result"
            fi
        fi
        break
    done
 
    config_file=`mktemp -qt 'firstboot-XXXXXX'`

    cat << EOF > $config_file
BOOTPROTO='dhcp'
STARTMODE='auto'
WIRELESS_AP_SCANMODE='1'
WIRELESS_AUTH_MODE='$wlan_auth_mode_conf'
WIRELESS_ESSID='$wlan_network'
WIRELESS_MODE='Managed'
EOF

    if [ $wlan_auth_mode = "WPA-PSK" ]; then
        echo "WIRELESS_WPA_PSK='$wlan_password'" >> $config_file
    fi

    if [ $wlan_auth_mode = "WPA-EAP" ]; then
        echo "WIRELESS_WPA_IDENTITY='$wlan_username'" >> $config_file
        echo "WIRELESS_WPA_PASSWORD='$wlan_password'" >> $config_file
        echo "WIRELESS_EAP_AUTH='mschapv2'" >> $config_file
    fi

    run mv -f $config_file /etc/sysconfig/network/ifcfg-$wlan_device
	
fi

d --infobox $"Applying firstboot settings ..." 3 40 || true
# FIXME: systemd-firstboot doesn't set password if shadow present
if [ -n "$password" ]; then
    run  echo "root:$password" | run /usr/sbin/chpasswd
fi

EFI_SYSTAB="/sys/firmware/efi/systab"
# Look for EFI dir to see if the machine is booted in UEFI mode
run modprobe efivars
if ! [ -f "$EFI_SYSTAB" ]; then
    run sed -i -e "s/LOADER_TYPE=.*/LOADER_TYPE=grub2/g" /etc/sysconfig/bootloader 
fi

# Test if snapper is available
if [ -x /usr/bin/snapper ]; then
    if ! btrfs qgroup show / &>/dev/null; then
        # Run snapper to setup quota for btrfs
        run /usr/bin/snapper --no-dbus setup-quota || warn $"Could not setup quota for btrfs"
    fi

    if [ ! -e /.snapshots/2 ]; then
        run create_snapshot 2 "Initial Status" "yes" || true
    fi
    if [ -x /usr/lib/snapper/plugins/grub ]; then
        run /usr/lib/snapper/plugins/grub --refresh
    fi
fi

if [ "$config_wireless" = "true" ]; then
    run ifdown $wlan_device 2>/dev/null || true
    run ifup $wlan_device || true
fi
# vim: sw=4
