#!/bin/bash
#
# This is an sdm plugin for: network
#
# The plugin is called three times: for Phase 0, Phase 1, and post-install.
#

function loadparams() {
    source $SDMPT/etc/sdm/sdm-readparams
}

function getdefnm() {
    #
    # $1 has user-provided $netman
    #
    local dfn="$1"
    if [ "$dfn" != "" ]
    then
	[ "$dfn" == "nm" ] && dfn="network-manager"
	echo "$dfn"
    else
	[ ${raspiosver} -ge 12 ] && echo "network-manager" || echo "dhcpcd"
    fi
}

function cfgdhcpcdwifi() {
    #
    # call this after any wpa has been copied into $assetdir
    # Does checks required for dhcpcd (only)
    #
    if [ "$wpa" != "" ]
    then
	if [ -f $assetdir/wpa.conf ]
	then
            IFS="=" read a wificountry <<< $(grep 'country=' $assetdir/wpa.conf | head -n 1)
	else
	    logtobothex "? Plugin $pfx: Cannot find $assetdir/wpa.conf"
	fi
    else
	if [ "$wifissid" != "" -a "$wifipassword" != "" -a "$wificountry" != "" ]
	then
	    logtoboth "> Plugin $pfx: Create $assetdir/wpa.conf"
	    logtoboth "  with SSID: $wifissid Password: $wifipassword WiFi Country: $wificountry"
	    cat > $assetdir/wpa.conf <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
country=$wificountry
update_config=1
ap_scan=1

network={
    priority=10
    ssid="$wifissid"
    psk="$wifipassword"
}
EOF
	    logtoboth "> Plugin $pfx: Set WiFi Country '$wificountry' for sdm FirstBoot"
	    writeconfig    # Update config to save updated wificountry
	else
	    if [ "$nowifi" != "y" ]
	    then
		logtoboth "% Plugin $pfx: Some WiFi settings not configured or no wpa: wifissid wifipassword wificountry"
		logtoboth "  If you did not configure WiFi another way the WiFi network will not connect"
	    fi
	fi
    fi
}

function makenmconn() {
    cname="$1" makeif="$2" nmcmd="$3"

    if [ $newnm -eq 1 ]
    then
	if [ -f /etc/NetworkManager/system-connections/$cname.nmconnection ]
	then
	    logtoboth "% Plugin $pfx: NetworkManager connection $cname already exists; Deleting prior definition"
	    rm -f /etc/NetworkManager/system-connections/$cname.nmconnection
	fi
	logtoboth "> Plugin $pfx: Create connection '$cname' on interface '$makeif'"
	# Feed to bash so special character quoting doesn't blow up
	bash -c "nmcli --offline $nmcmd | tee /etc/NetworkManager/system-connections/$cname.nmconnection >/dev/null"
	sts=$?
	[ $sts -ne 0 ] && logtobothex "? Plugin $pfx: nmcli returned error '$sts' on create connection '$cname' with '$nmcmd'"
    else
	cat >> $nmdcfg <<EOF
logger "sdm FirstBoot: Create NetworkManager connection '$cname' on interface '$makeif'"
nmcli $nmcmd
EOF
    fi
}

function modnmconn() {
    cname="$1" cattr="$2" fname="$3"
    if [ $newnm -eq 1 ]
    then
	[ "$fname" != "" ] && fname="$(basename "$fname")" || fname="$cname"
	fname="${fname%.nmconnection}"
	bash -c "nmcli --offline c modify $cattr </etc/NetworkManager/system-connections/$fname.nmconnection | tee /tmp/$fname.nmconnection >/dev/null"
	sts=$?
	[ $sts -ne 0 ] && logtobothex "? Plugin $pfx: nmcli returned error '$sts' on modify '$cattr'"
	rm -f /etc/NetworkManager/system-connections/"$fname".nmconnection
	mv /tmp/"$fname".nmconnection /etc/NetworkManager/system-connections/"$fname".nmconnection
    else
	echo "nmcli c modify $cname $cattr" >> $nmdcfg
    fi
}

function getctype() {

    if [ "$ctype" != "" ]
    then
	[[ "wifi|ethernet" =~ "$ctype" ]] || logtobothex "? Plugin $pfx: '$ctype' is an invalid value for 'ctype'"
	thisctype=$ctype
    fi
    if [[ "$thisctype" == "" ]]
    then
	[[ "$ifname" =~ "wlan" ]] && thisctype=wifi || thisctype=ethernet
    fi
}

function converty() {
    [ -v noipv6 ] && noipv6=y
    [ -v nowifi ] && nowifi=y
    [ -v pskencrypt ] && pskencrypt=encrypt
    [ -v zeroconf ] && zeroconf=y
}

# $1 is the phase: "0", "1", or "post-install"
# $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ...
#
# Main code for the Plugin
#
phase=$1
pfx="$(basename $0)"     #For messages
args="$2"
loadparams
assetdir="$SDMPT/etc/sdm/assets/network"

vldargs="|cname|ctype|dhcpcdwait|dhcpcdappend|ifname|netman|noipv6|nmconf|nmconn|nowifi|powersave|pskencrypt|wificountry|wifissid|wifipassword|wpa|zeroconf"
vldargs="$vldargs|ipv4-static-ip|ipv4-static-gateway|ipv4-static-dns|ipv4-static-dns-search"
vldargs="$vldargs|ipv4-route-metric"
vldargs="$vldargs|autoconnect-priority|autoconnect"
vldargs="$vldargs|wifi-ssid|wifi-password|wifi-country"
vldargs="$vldargs|"
rqdargs=""

if [ "$phase" == "0" ]
then
    #
    # In Phase 0 all references to directories in the image must be preceded by $SDMPT
    #
    #logtoboth "* Plugin $pfx: Start Phase 0"

    plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit
    converty
    mkdir -p $assetdir
    netman=$(getdefnm "$netman")
    logtoboth "> Plugin $pfx: Configuring '$netman' for network configuration management"

    if [ "$wpa" != "" ]
    then
	if [ -f $wpa ]
	then
	    logtoboth "> Plugin $pfx: Copy wpa_supplicant config file '$wpa' to $assetdir/wpa.conf"
	    tr -d '\r' < $wpa > $assetdir/wpa.conf
	else
	    logtobothex "? Plugin $pfx: wpa file '$wpa' not found"
	fi
    fi

    if [ "$ipv4__static__ip" != "" ]
    then
	[ "$zeroconf" == "" ] || logtobothex "? Plugin $pfx: Arguments 'ipv4-static-ip' and 'zeroconf' conflict"
	[ "$netman" == "dhcpcd" ] && logtobothex "? Plugin $pfx: Argument 'ipv4-static-ip' conflicts with 'netman=dhcpcd'"
    fi
    [ "$wifissid" == "" ] && wifissid="$wifi__ssid"
    [ "$wifipassword" == "" ] && wifipassword="$wifi__password"
    owifissid="$wifissid"
    owifipassword="$wifipassword"
    if [ "$wifissid" != "" ]
    then
	[[ "$wifissid" != "" ]] && [[ "$wifipassword" == "" ]] && logtobothex "? Plugin $pfx: WiFi password required but not provided"
	[ "$wifissid" != "" ] && wifissid=$(adjust_wifinmpsk "$(stripquotes "$wifissid")")
	[ "$wifipassword" != "" ] && wifipassword=$(adjust_wifinmpsk "$(stripquotes "$wifipassword")")
	[[ "$wifissid" != "" ]] && [[ "$ifname" == "" ]] && logtobothex "? Plugin $pfx: WiFi device name must be provided with 'ifname' argument"
	[ "$pskencrypt" == "encrypt" ] && wifipassword=$(encrypt_wifipsk "$owifipassword" "$owifissid")
    fi

    thiscname="$cname"
    thisctype=""
    getctype
    if [ "$thisctype" == "wifi" ]
    then
	[ "$wifissid" == "" ] && logtobothex "? Plugin $pfx: WiFi SSID must be provided for WiFi device '$ifname'"
	[ "$thiscname" == "" ] && thiscname="${wifissid,,}" && thiscname="${thiscname// /}"
    else
	[ "$wifissid" != "" ] && logtobothex "? Plugin $pfx: WiFi SSID incompatible with non-WiFi network device '$ifname'"
    fi

    case "${netman}" in
	dhcpcd)
	    if [ "$dhcpcdappend" != "" ]
	    then
		if [ -f $dhcpcdappend ]
		then
		    logtoboth "> Plugin $pfx: Copy dhcpcdappend file '$dhcpcdappend' to $assetdir/dhcpcd-append.conf"
		    cp -a $dhcpcdappend $assetdir/dhcpcd-append.conf
		else
		    logtobothex "? Plugin $pfx: dhcpcdappend file '$dhcpcdappend' not found"
		fi
	    fi
	    cfgdhcpcdwifi
	    ;;
	nm|network-manager)
	    #
	    # Copy nmconf and nmconn files into IMG
	    #
	    for conf in nmconf nmconn
	    do
		if [ "${!conf}" != "" ]
		then
		    IFS="," read -a citems <<< "${!conf}"
		    for c in "${citems[@]}"
		    do
			if [ -f $c ]
			then
			    mkdir -p $assetdir/$conf
			    logtoboth "> Plugin $pfx: Copy $conf '$c' to $assetdir/$conf"
			    cp -a $c $assetdir/$conf
			else
			    logtobothex "? Plugin $pfx: $conf file '$c' not found"
			fi
		    done
		fi
	    done
	    ;;
    esac
    
    #logtoboth "* Plugin $pfx: Complete Phase 0"

elif [ "$phase" == "1" ]
then
    #
    # Phase 1 (in nspawn)
    #
    logtoboth "* Plugin $pfx: Start Phase 1"
    plugin_getargs $pfx "$args" "$vldargs" "$rqdargs"
    netman=$(getdefnm "$netman")
    #logfreespace "at start of Plugin $pfx Phase 1"

    #logfreespace "at end of $pfx Phase 1"
    logtoboth "* Plugin $pfx: Complete Phase 1"
elif [ "$phase" == "post-install" ]
then
    #
    # Plugin Post-install edits
    #
    logtoboth "* Plugin $pfx: Start Phase post-install"
    xwificountry="$wificountry"
    plugin_getargs $pfx "$args" "$vldargs" "$rqdargs"
    converty
    netman=$(getdefnm "$netman")
    plugin_printkeys
    # If no wificountry in plugin args, use previous setting
    [ "$wificountry" == "" ] && wificountry="$wifi__country"
    if [ "$wificountry" == "" ]
    then
	wificountry="$xwificountry"
    else
	logtoboth "> Plugin $pfx: Set WiFi Country '$wificountry' for sdm FirstBoot"
	writeconfig  # Update config to save updated wificountry
    fi
    #logfreespace "at start of Plugin $pfx Phase post-install"

    if [ -f $assetdir/wpa.conf ]
    then
	logtoboth "> Plugin $pfx: Copy $assetdir/wpa.conf to /etc/wpa_supplicant/wpa-supplicant.conf"
	cp -a $assetdir/wpa.conf /etc/wpa_supplicant/wpa_supplicant.conf
	setfileownmode /etc/wpa_supplicant/wpa_supplicant.conf 644
    fi

    case "${netman}" in
	dhcpcd)
	    [ "$raspiosver" -ge 12 ] && pkg="dhcpcd" || pkg="dhcpcd5"
	    logtoboth "> Plugin $pfx: Install $pkg"
	    installpkgsif "$pkg"
	    systemctl disable NetworkManager >/dev/null 2>&1
	    if [ -f $assetdir/dhcpcd-append.conf ]
	    then
		logtoboth "> Plugin $pfx: Append $assetdir/dhcpcd-append.conf to /etc/dhcpcd.conf"
		cat $assetdir/dhcpcd-append.conf >> /etc/dhcpcd.conf
		[ "$noipv6" == "y" ] && echo "noipv6" >> /etc/dhcpcd.conf
	    fi
	    #
	    # Set dhcpcd wait if requested
	    #
	    if [ -v dhcpcdwait ]
	    then
		mkdir -p /etc/systemd/system/dhcpcd.service.d/
		# use the same /path/to/dhcpcd that the dhcpcd service is
		dex=$(grep -E "ExecStart=.*/dhcpcd" /lib/systemd/system/dhcpcd.service| head -n 1 -)
		dhcf=${dex##ExecStart=}  #Strip leading ExecStart=
		dhcf=${dhcf%% *}         #Strip everything after the path (switches,etc)
		logtoboth "> Plugin $pfx: Enable dhcpcd [$dhcf] 'wait for network connection'"
		cat > /etc/systemd/system/dhcpcd.service.d/wait.conf << EOF
[Service]
ExecStart=
ExecStart=$dhcf -q -w
EOF
	    fi
	    #
	    # Fix dhcpcd hooks if needed
	    #
	    if [ ! -f /lib/dhcpcd/dhcpcd-hooks/10-wpa_supplicant ]
	    then
		if [ -f /usr/share/dhcpcd/hooks/10-wpa_supplicant ]
		then
		    logtoboth "> Plugin $pfx: Copy /usr/share/dhcpcd/hooks/10-wpa_supplicant to /lib/dhcpcd/dhcpcd-hooks (BUG)"
		    cp -a /usr/share/dhcpcd/hooks/10-wpa_supplicant /lib/dhcpcd/dhcpcd-hooks
		else
		    logtoboth "% Plugin $pfx: /lib/dhcpcd/dhcp-hooks/10-wpa_supplicant not found. WiFi will not work"
		fi
	    fi
	    ;;

	nm|network-manager)
	    thiscname="$cname"
	    thisctype=""
	    thisattr=""
	    [ "$wifissid" == "" ] && wifissid="$wifi__ssid"
	    [ "$wifipassword" == "" ] && wifipassword="$wifi__password"
	    owifissid="$wifissid"
	    owifipassword="$wifipassword"
	    [ "$wifissid" != "" ] && wifissid=$(adjust_wifinmpsk "$(stripquotes "$wifissid")")
	    [ "$wifipassword" != "" ] && wifipassword=$(adjust_wifinmpsk "$(stripquotes "$wifipassword")")
	    [ "$pskencrypt" == "encrypt" ] && wifipassword=$(encrypt_wifipsk "$owifipassword" "$owifissid")
	    if ! ispkginstalled network-manager     # Only print msg if it's really not installed. Less confusing
	    then
		logtoboth "> Plugin $pfx: Install Network Manager"
		installpkgsif network-manager
	    fi
	    systemctl enable NetworkManager >/dev/null 2>&1
	    systemctl disable dhcpcd >/dev/null 2>&1
	    for conf in nmconf nmconn
	    do
		if compgen -G "$assetdir/$conf/*" >/dev/null
		then
		    [ "$conf" == "nmconf" ] && tgt="conf.d" || tgt="system-connections"
		    for c in $assetdir/$conf/*
		    do
			cbn=$(basename $c)
			logtoboth "> Plugin $pfx: Copy $conf '$cbn' from $assetdir/$conf to /etc/NetworkManager/$tgt"
			cp -a $c /etc/NetworkManager/$tgt
			mv $c $assetdir/$conf/.${cbn}
		    done
		fi
	    done
	    # Configure powersave if requested
	    if [ "$powersave" != "" ]
	    then
		logtoboth "> Plugin $pfx: Configure WiFi powersave to '$powersave'"
		cat > /etc/NetworkManager/conf.d/010-sdm-powersave.conf <<EOF
[connection]
wifi.powersave = $powersave
EOF
	    fi
	    #
	    # Process other arguments if provided
	    #
	    if [ "${ifname}${wifissid}${ipv4__static__ip}${zeroconf}" != "" ]
	    then
		[ "$ifname" == "" ] && ifname="eth0"
		# Defer configuration to FirstBoot if running old NetworkManager (version older than bookworm)
		nmver=$(nmcli --version 2>/dev/null)
		nmver=${nmver##*version}
		[[ "$nmver" =~ ([[:digit:]]{,2}).([[:digit:]]{,2}).([[:digit:]]{,2}) ]]
		nmvermaj="${BASH_REMATCH[2]}"
		nmvermin="${BASH_REMATCH[3]}"
		[ "$nmvermaj" == "" ] && nmvermaj="1"
		[ "$nmvermin" == "" ] && nmvermin="41"
		[[ $nmvermaj -gt 1 ]] || [[ $nmvermin -ge 42 ]] && newnm=1 || newnm=0
		nmdcfg="/etc/sdm/xpiboot/005-nm-config.sh"  # Want it to run early in FirstBoot if newnm=0
		[ $newnm -eq 0 ] && echo "#!/bin/bash" > $nmdcfg

		getctype
		if [ "$thisctype" == "wifi" ] && [[ "$thiscname" == "" ]]
		then
		    thiscname="${wifissid,,}" && thiscname="${thiscname// /}"
		else
		    [ "$thiscname" == "" ] && thiscname="$ifname"
		fi

		[ "$wifissid" != "" ] && thisattr="ssid $wifissid wifi-sec.key-mgmt  wpa-psk wifi-sec.psk $wifipassword"

		if [ "$ipv4__static__ip" != "" ]
		then
		    logtoboth "> Plugin $pfx: Configure connection '$thiscname' for a Static IP"
		    logtoboth "  Static IP:                $ipv4__static__ip"
		    logtoboth "  Static Gateway:           $ipv4__static__gateway"
		    logtoboth "  Static DNS Server:        $ipv4__static__dns"
		    logtoboth "  Static DNS Domain Search: $ipv4__static__dns__search"
		    thisattr="$thisattr ipv4.addresses $ipv4__static__ip/24 ipv4.method manual"
		    [ "$ipv4__static__gateway" != "" ] && thisattr="$thisattr ipv4.gateway $ipv4__static__gateway"
		    [ "$ipv4__static__dns" != "" ] && thisattr="$thisattr ipv4.dns  $ipv4__static__dns"
		    [ "$ipv4__static__dns__search" != "" ] && thisattr="$thisattr ipv4.dns-search $ipv4__static__dns__search"
		fi

		[ "$autoconnect" != "" ] && logtoboth "> Plugin $pfx: Configure 'autoconnect=$autoconnect' on connection '$thiscname'" && thisattr="$thisattr connection.autoconnect $autoconnect"
		[ "$autoconnect__priority" != "" ] && \
		    logtoboth "> Plugin $pfx: Set autoconnect priority '$autoconnect__priority' on connection '$thiscname'" && \
		    thisattr="$thisattr connection.autoconnect-priority $autoconnect__priority"
		[ "$ipv4__route__metric" != "" ] && logtoboth "> Plugin $pfx: Set ipv4.route-metric '$ipv4__route__metric' on connection '$thiscname'" && \
		    thisattr="$thisattr ipv4.route-metric $ipv4__route__metric"
		[ "$noipv6" == "y" ] && logtoboth "> Plugin $pfx: Disable ipv6 on connection '$thiscname'" && thisattr="$thisattr ipv6.method disabled"
		thiscname="${thiscname// /}"
		makenmconn "$thiscname" $ifname "c add type $thisctype con-name $thiscname ifname $ifname $thisattr"

		if [ "$zeroconf" == "y" ]
		then
		   if [ "$thiscname" == "" ]
		   then
		       thiscname="$ifname-dhcp"
		       adddhcp="c add con-name $thiscname ifname $ifname type $thisctype"
		       [ "$noipv6" == "y" ] && logtoboth "> Plugin $pfx: Disable ipv6 on connection '$thiscname'" && adddhcp="$adddhcp ipv6.method disabled"
		       makenmconn $thiscname $ifname "$adddhcp"
		   fi
		   logtoboth "> Plugin $pfx: Configure autoconnect for zeroconf on connection '$thiscname'"
		   modnmconn $thiscname "connection.autoconnect true connection.autoconnect-priority 100 connection.autoconnect-retries 2"
		   addzeroconf="c add con-name $ifname-zeroconf type $thisctype ifname $ifname connection.autoconnect-priority 50 ipv4.method link-local ipv4.link-local enabled"
		   [ "$noipv6" == "y" ] && logtoboth "> Plugin $pfx: Disable ipv6 on connection '$ifname-zeroconf'" && addzeroconf="$addzeroconf ipv6.method disabled"
		   makenmconn $ifname-zeroconf $ifname "$addzeroconf"
		fi
	    fi
	    compgen -G "/etc/NetworkManager/conf.d/*" >/dev/null && setfileownmode "/etc/NetworkManager/conf.d/*" 644
	    compgen -G "/etc/NetworkManager/system-connections/*" >/dev/null && setfileownmode "/etc/NetworkManager/system-connections/*" 600
	    ;;
    esac
    logtoboth "* Plugin $pfx: Complete Phase post-install"
fi
exit 0
