#!/bin/bash
#
# See (Routed/Local): https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md
# AND (Bridged): https://www.raspberrypi.org/documentation/configuration/wireless/access-point-bridged.md
#
# NOTES
#  Use sudo systemctl disable sdm-hostapd-iptables to disable routing if desired
#  hwmode G doesn't work well (at least with channel 7. Test more)
#
# Hotspot type:
#   local: Clients can only access the IP of the hotspot (web pages, etc)
#   routed: Clients can access the IP of the hotspot AND the network to which hotspot is connected
#   bridged: Clients appear to become part of the network to which the hotspot is connected
#
# To use without sdm:
# * Invoke with 'sudo sdm-hotspot config-file`
# * See https://github.com/gitbls/sdm/README.md for details on the --hotspot config-file
#
# NOTE: This file is obsolete, although it should work on Bullseye
#       For Bookworm, see the sdm plugin 'hotspot'
#

function saveorig() {
    #
    # $1 = filename to save
    # $2 = "" (move file to .orig) or 1 (keep original and copy to .orig)
    # Only renames it to $1.orig if $1.orig doesn't already exist
    # If $1.orig already exists, the file $1 is simply deleted
    #
    [ ! -f $1 ] && return
    [ -f $1.orig ] && ( [ "$2" != "1" ] && rm -f $1 ; return )
    [ "$2" == "1" ] && cp $1 $1.orig || mv $1 $1.orig
    return
}

function logtoboth() {
    #
    # Overridden if used in sdm
    #
    echo "$1"
    return
}

function doapt() {
    #
    # Overridden if used in sdm
    #
    apt $1
    return
}

# $SDMNSPAWN != "" if called from within sdm
if [ "$SDMNSPAWN" != "" ]
then
    source /etc/sdm/sdm-readparams
    # sdm copies the hotspot config file into the image in sdm-phase0
    fhotspot="/etc/sdm/assets/$(basename $hotspot)"
else
    fhotspot="$1"
fi

logtoboth "* Start Access Point (hotspot) installation and configuration"
#
# set defaults
#
hotconfig="local"   #local|routed|bridged
hotchannel="36"
hothwmode="a"
hotcountry=$wificountry
hotdev="wlan0"
hotwlanip="192.168.4.1"
hotdhcprange="192.168.4.2,192.168.4.32,255.255.255.0" #? Is netmask needed?
hotleasetime="24h"
hotssid="MyPiNet"
hotpassphrase="password"
hotdomain="wlan.net"
hotenable="true"
hotinclude=""

while IFS=":=" read rpifun value
do
    if [[ ! $rpifun =~ ^\ *# && -n $rpifun ]] # skip comment and malformed lines
    then
	value="${value%%\#*}"    # Del EOL comments
	value="${value%"${value##*[^[:blank:]]}"}"  # Del trailing spaces/tabs
	value="${value#\"}"     # Del opening double-quotes 
	value="${value%\"}"     # Del closing double-quotes 
	value="${value#\'}"     # Del opening single-quotes 
	value="${value%\'}"     # Del closing single-quotes 
	case "${rpifun,,}" in
	    # * do_resolution still needs to be sorted out
	    channel)
		hotchannel=$value
		;;
	    config)
		hotconfig=${value,,}
		;;
	    country)
		hotcountry=${value,,}
		;;
	    dev|device)
		hotdev=${value,,}
		;;
	    dhcprange|dhcp-range)
		hotdhcprange=$value
		;;
	    domain)
		hotdomain=${value,,}
		;;
	    enable)
		hotenable=${value,,}
		;;
	    hwmode|hw-mode)
		hothwmode=${value,,}
		;;
	    leasetime|lease-time)
		hotleasetime=$value
		;;
	    passphrase|password)
		hotpassphrase=$value
		;;
	    ssid)
		hotssid=$value
		;;
	    wlanip|wlan-ip)
		hotwlanip=$value
		;;
	    include)
		hotinclude=$value
		;;
	    *)
		logtoboth "% Unrecognized hotspot option '$rpifun' in '$fhotspot'"
		;;
	esac
    fi
done < $fhotspot

#
# Validity checks
#
if [[ ! "local|routed|bridged" =~ "$hotconfig" ]] || [ "$hotconfig" == "" ]
then
    logtoboth "? Unrecognized Access Point configuration type '$hotconfig'"
    exit
fi
if [ "$hotinclude" != "" ]
then
    [ ! -f $hotinclude ] && logtoboth "% hostapd.conf include file '$hotinclude' not found; ignoring"
fi

# install hostapd and dnsmasq
svcs="hostapd"
logtoboth "> Install and disable hostapd"
[ "$SDMNSPAWN" == "" ] && logtoboth "% Ignore any service start errors"
doapt "install --no-install-recommends --yes hostapd" "$showapt"
systemctl unmask hostapd
systemctl disable hostapd
if [ "$hotconfig" != "bridged" ]
then
    logtoboth "> Install and disable dnsmasq"
    svcs="hostapd and dnsmasq"
    doapt "install --no-install-recommends --yes dnsmasq" "$showapt"
    systemctl disable dnsmasq
fi
[ "$SDMNSPAWN" != "" ] && logtoboth "% $svcs will be re-enabled during FirstBoot"

# Create hostapd and dnsmasq config files
logtoboth "> hostapd will be configured for mode '$hotconfig'"
logtoboth "> Create hostapd configuration"
saveorig /etc/hostapd/hostapd.conf
echo "# sdm hostapd configuration $(date)" > /etc/hostapd/hostapd.conf
echo "interface=$hotdev" >> /etc/hostapd/hostapd.conf
[ "$hotconfig" == "bridged" ] && echo "bridge=br0" >> /etc/hostapd/hostapd.conf
cat >> /etc/hostapd/hostapd.conf <<EOF
country_code=${hotcountry^^}
# use ssid2 for double-quoted string, hexdump, or printf-escaped string
ssid=$hotssid
hw_mode=$hothwmode
# See https://en.wikipedia.org/wiki/List_of_WLAN_channels for list of WLAN channels
channel=$hotchannel
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=$hotpassphrase
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
#
# hostapd event logger configuration (from /usr/share/doc/hostapd/examples/hostapd.conf)z
#
# Two output method: syslog and stdout (only usable if not forking to
# background).
#
# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
# modules):
# bit 0 (1) = IEEE 802.11
# bit 1 (2) = IEEE 802.1X
# bit 2 (4) = RADIUS
# bit 3 (8) = WPA
# bit 4 (16) = driver interface
# bit 5 (32) = IAPP
# bit 6 (64) = MLME
#
# Levels (minimum value for logged events):
#  0 = verbose debugging
#  1 = debugging
#  2 = informational messages
#  3 = notification
#  4 = warning
#
logger_syslog=-1
logger_syslog_level=1
logger_stdout=-1
logger_stdout_level=1

EOF
# Cat the include file to the end of hostpad.conf if specified
[ "$hotinclude" != "" ] && cat $hotinclude >> /etc/hostapd/hostapd.conf

if [ "$hotconfig" != "bridged" ]
then
    logtoboth "> Create dnsmasq configuration"
    saveorig /etc/dnsmasq.conf 1
    cat >> /etc/dnsmasq.conf <<EOF
# sdm hotspot dnsmasq configuration $(date)
interface=$hotdev
dhcp-range=$hotdhcprange,$hotleasetime
domain=$hotdomain
address=/gw.$hotdomain/$hotwlanip
EOF

fi

# if routed enable sysctl routing
if [ "$hotconfig" == "routed" ]
then
    logtoboth "> Enable routing and masquerading for WiFi clients onto LAN"
    cat > /etc/sysctl.d/040-sdm-aprouted.conf <<EOF
# sdm hotspot routing configured on $(date)
# https://www.raspberrypi.org/documentation/configuration/wireless/access-point-routed.md"
# Enable IPv4 routing
net.ipv4.ip_forward=1
EOF
    logtoboth "> Configure service sdm-hostapd-iptables to enable iptables routing"
    cat > /etc/systemd/system/sdm-hostapd-iptables.service <<EOF
[Unit]
Description=sdm-hostapd-iptables
After=network.target

[Service]
#
# This service should be:
#  Enabled for a routed access point
#  Disabled for a local or bridged access point
#
ExecStart=/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
RemainAfterExit=true
Type=oneshot

[Install]
WantedBy=multi-user.target
EOF
    systemctl enable sdm-hostapd-iptables
fi

if [ "$hotconfig" == "bridged" ]
then
    logtoboth "> Configure network bridge br0 and enable systemd-networkd"
    cat > /etc/systemd/network/bridge-br0.netdev <<EOF
[NetDev]
Name=br0
Kind=bridge
EOF
    cat > /etc/systemd/network/br0-member-eth0.network <<EOF
[Match]
Name=eth0

[Network]
Bridge=br0
EOF
    systemctl enable systemd-networkd
    saveorig /etc/dhcpcd.conf 1
    sed -i '1 i\denyinterfaces wlan0 eth0' /etc/dhcpcd.conf
    echo -e "\n# sdm bridged network configuration $(date)" >> /etc/dhcpcd.conf
    echo 'interface br0' >> /etc/dhcpcd.conf
else
    # update dhcpcd config for local or routed hotspot
    logtoboth "> Disable wpa_supplicant configuration for $hotdev in dhcpcd"
    cat >> /etc/dhcpcd.conf <<EOF
# sdm $hotconfig network configuration $(date)"
interface $hotdev
    static ip_address=$hotwlanip/24
    nohook wpa_supplicant
EOF
fi

if [ "$hotenable" == "true" ]
then
    if [ "$SDMNSPAWN" != "" ]
    then
	logtoboth "> Set hostapd and dnsmasq to enable during FirstBoot"
	cat > /etc/sdm/0piboot/040-hostapd-enable.sh <<EOF
#!/bin/bash
#source /etc/sdm/sdm-readparams  #Not needed at the moment
logger "FirstBoot: Enable hostapd and dnsmasq"
systemctl enable hostapd
rfkill unblock wlan
EOF
	[ "$hotconfig" != "bridged" ] && echo "systemctl enable dnsmasq" >> /etc/sdm/0piboot/040-hostapd-enable.sh 
	chmod 755 /etc/sdm/0piboot/040-hostapd-enable.sh
    else  # SDMNSPAWN == "" (not in sdm)
	systemctl enable hostapd
	[ "$hotconfig" != "bridged" ] && systemctl enable dnsmasq
    fi
else
    if [ "$SDMNSPAWN" == "" ]
    then
	se="sudo systemctl enable hostapd"
	[ "$hotconfig" != "bridged" ] && se="$se ; sudo systemctl enable dnsmasq"
	logtoboth "> Use '$se' to enable the hotspot"
    fi
fi
logtoboth "* Hotspot Install Completed"
[ "$SDMNSPAWN" == "" ] && logtoboth "" && logtoboth "% Restart your system to ensure configuration is correct"
exit
