#!/bin/sh
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2007-2022  IPFire Team  <info@ipfire.org>                     #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

. /etc/sysconfig/rc
. ${rc_functions}

eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)

WIRELESS_CONFIG="/var/ipfire/ethernet/wireless"

function device_is_wireless() {
	local device=${1}

	if [ -d "/sys/class/net/${device}/wireless" ]; then
		return 0
	fi

	return 1
}

function wpa_supplicant_make_config() {
	local device=${1}
	local config=${2}
	shift 2

	# Check if device is wireless.
	local wireless="false"
	if device_is_wireless ${device}; then
		wireless="true"
	fi

	# Write a configuration file header.
	(
		echo "#"
		echo "# THIS FILE IS AUTOMATICALLY GENERATED AND"
		echo "# ANY CUSTOM CHANGES WILL BE OVERWRITTEN!"
		echo "#"
		echo
		echo "ctrl_interface=/var/run/wpa_supplicant"
		echo
	) > ${config}

	local items=0

	local line
	while IFS="," read -ra line; do
		# Skip commented lines.
		[ "${line:0:1}" = "#" ] && continue

		# Skip disabled entries.
		[ "${line[2]}" = "on" ] || continue

		wpa_supplicant_config_line \
			${device} ${config} \
			--wireless="${wireless}" \
			--mode="${line[3]}" \
			--wpa-mode="${line[4]}" \
			--ssid="${line[5]}" \
			--psk="${line[6]}" \
			--priority="${line[7]}" \
			--auth-mode="${line[8]}" \
			--anonymous-identity="${line[9]}" \
			--identity="${line[10]}" \
			--password="${line[11]}"

		items=$(( ${items} + 1 ))

	done < ${WIRELESS_CONFIG}

	# Return exit code 2, when there are no entries in the
	# configuration file.
	if [ "${items}" = "0" ]; then
		return 2
	fi

	return 0
}

function wpa_supplicant_config_line() {
	local device=${1}
	local config=${2}
	shift 2

	local ieee80211w
	local anonymous_identity
	local auth_alg
	local auth_mode
	local identity
	local proto
	local key_mgmt
	local pairwise
	local group
	local mode
	local password
	local priority
	local psk
	local ssid
	local wep_tx_keyidx
	local wep_key0
	local wireless="true"
	local wpa_mode

	while [ $# -gt 0 ]; do
		case "${1}" in
			--anonymous-identity=*)
				anonymous_identity=${1#--anonymous-identity=}
				;;
			--auth-mode=*)
				auth_mode=${1#--auth-mode=}
				;;
			--identity=*)
				identity=${1#--identity=}
				;;
			--mode=*)
				mode=${1#--mode=}
				;;
			--password=*)
				password=${1#--password=}
				;;
			--priority=*)
				priority=${1#--priority=}
				;;
			--psk=*)
				psk=${1#--psk=}
				;;
			--ssid=*)
				ssid=${1#--ssid=}
				;;
			--wireless=*)
				wireless=${1#--wireless=}
				;;
			--wpa-mode=*)
				wpa_mode=${1#--wpa-mode=}
				;;
		esac
		shift
	done

	case "${mode}" in
		EAP)
			key_mgmt="WPA-EAP-SHA256 WPA-EAP"
			;;
		WPA3)
			key_mgmt="SAE"

			ieee80211w="2"
			;;
		WPA2)
			auth_alg="OPEN"
			proto="RSN"
			key_mgmt="WPA-PSK-SHA256 WPA-PSK"
			;;
		WPA)
			auth_alg="OPEN"
			proto="WPA"
			key_mgmt="WPA-PSK-SHA256 WPA-PSK"
			;;
		WEP)
			auth_alg="SHARED"
			key_mgmt="NONE"

			wep_tx_keyidx=0
			wep_key0=${psk}
			psk=""
			;;
		NONE)
			auth_alg="OPEN"
			key_mgmt="NONE"
			;;
		*)
			# Unsupported mode.
			return 1
			;;
	esac

	if [ "${mode}" = "EAP" -o "${mode}" = "WPA" -o "${mode}" = "WPA2" ]; then
		case "${wpa_mode}" in
			CCMP-CCMP)
				pairwise="CCMP"
				group="CCMP"
				;;
			CCMP-TKIP)
				pairwise="CCMP"
				group="TKIP"
				;;
			TKIP-TKIP)
				pairwise="TKIP"
				group="TKIP"
				;;
			*)
				pairwise="CCMP TKIP"
				group="CCMP TKIP"
				;;
		esac
	fi

	(
		echo "network={"

		if [ -n "${ssid}" ]; then
			echo "	ssid=\"${ssid}\""
		fi
		if [ "${wireless}" = "true" ]; then
			echo "	scan_ssid=1"
		fi
		if [ -n "${auth_alg}" ]; then
			echo "	auth_alg=${auth_alg}"
		fi
		if [ -n "${key_mgmt}" ]; then
			echo "	key_mgmt=${key_mgmt}"
		fi
		if [ -n "${psk}" ]; then
			if [ "${key_mgmt}" = "SAE" ]; then
				echo "	sae_password=\"${psk}\""
			else
				echo "	psk=\"${psk}\""
			fi
		fi
		if [ -n "${wep_tx_keyidx}" ]; then
			echo "	wep_tx_keyidx=${wep_tx_keyidx}"
		fi
		if [ -n "${wep_key0}" ]; then
			echo "	wep_key0=\"${wep_key0}\""
		fi
		if [ -n "${proto}" ]; then
			echo "	proto=${proto}"
		fi
		if [ -n "${pairwise}" -a -n "${group}" ]; then
			echo "	pairwise=${pairwise}"
			echo "	group=${group}"
		fi
		if [ -n "${priority}" ]; then
			echo "	priority=${priority}"
		fi
		if [ -n "${ieee80211w}" ]; then
			echo "	ieee80211w=${ieee80211w}"
		fi

		# EAP
		if [ "${mode}" = "EAP" ]; then
			if [ -n "${auth_mode}" ]; then
				echo "	eap=${auth_mode}"
			else
				echo "	eap=PEAP TTLS"
			fi

			if [ "${auth_mode}" = "TTLS" -a -n "${anonymous_identity}" ]; then
				echo "	anonymous_identity=\"${anonymous_identity}\""
			fi

			if [ -n "${identity}" -a -n "${password}" ]; then
				echo "	identity=\"${identity}\""
				echo "	password=\"${password}\""
			fi
		fi

		echo "}"
		echo
	) >> ${config}
}

function wpa_supplicant_start() {
	local device=${1}
	local config="/etc/wpa_supplicant.conf"

	# Write configuration file.
	wpa_supplicant_make_config ${device} ${config}
	[ $? -eq 0 ] || return 0

	# Build wpa_supplicant command line.
	local wpa_suppl_cmd="wpa_supplicant -B -qqq -i${device} -c${config}"

	if ! device_is_wireless ${device}; then
		wpa_suppl_cmd="${wpa_suppl_cmd} -Dwired"
	fi

	# Run the shiz.
	boot_mesg "Starting wireless client on ${RED_DEV}..."
	loadproc ${wpa_suppl_cmd}

	# Run wpa_cli to handle reconnection events.
	boot_mesg "Starting wireless event handler on ${RED_DEV}..."
	wpa_cli -B -a /etc/rc.d/init.d/networking/wpa_supplicant.exe
}

function wpa_supplicant_stop() {
	boot_mesg "Stopping wireless event handler on ${RED_DEV}..."
	killproc wpa_cli

	# wpa_cli does not send a disconnect event when get stopped.
	# So we manually have to send it to the wpa_supplicant.exe.
	/etc/rc.d/init.d/networking/wpa_supplicant.exe "${RED_DEV}" DISCONNECTED

	boot_mesg "Stopping wireless client on ${RED_DEV}..."
	killproc wpa_supplicant

	# Tidy up /tmp directory.
	rm -f /tmp/wpa_ctrl_*
}

case "${1}" in
	start)
		if [ -n "${RED_DEV}" ] && device_is_wireless ${RED_DEV}; then
			wpa_supplicant_start ${RED_DEV}
		fi
		;;

	stop)
		if [ -n "${RED_DEV}" ] && device_is_wireless ${RED_DEV}; then
			wpa_supplicant_stop
		fi
		;;

	restart)
		${0} stop
		sleep 1
		${0} start
		;;

	status)
		statusproc wpa_supplicant
		;;

	*)
		echo "Usage: ${0} {start|stop|restart|status}"
		exit 1
		;;
esac
