#!/bin/bash
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2013 Michael Tremer                                           #
#                                                                             #
# 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/>.       #
#                                                                             #
###############################################################################

. /usr/lib/network/header-port

HOOK_SETTINGS="HOOK ADDRESS BSSID SSID CHANNEL MTU PHY"

ADDRESS=$(mac_generate)
BSSID=
CHANNEL=1
MTU=1500
PHY=
SSID=

hook_check_settings() {
	assert isset ADDRESS
	assert ismac ADDRESS
	assert isset CHANNEL
	assert isset BSSID
	assert ismac BSSID
	assert isset PHY
	assert ismac PHY
	assert isset SSID
}

hook_new() {
	while [ $# -gt 0 ]; do
		case "${1}" in
			--address=*)
				ADDRESS=$(cli_get_val ${1})
				;;
			--bssid=*)
				BSSID=$(cli_get_val ${1})
				;;
			--channel=*)
				CHANNEL=$(cli_get_val ${1})
				;;
			--mtu=*)
				MTU="$(cli_get_val "${1}")"
				;;
			--phy=*)
				PHY=$(cli_get_val ${1})
				;;
			--ssid=*)
				SSID=$(cli_get_val ${1})
				;;
			*)
				warning "Ignoring unknown argument '${1}'"
				;;
		esac
		shift
	done

	# Save address of phy do identify it again
	PHY=$(phy_get ${PHY})
	PHY=$(phy_get_address ${PHY})

	local port=$(port_find_free ${PORT_PATTERN_WIRELESS_ADHOC})
	assert isset port

	port_settings_write "${port}" ${HOOK_SETTINGS}

	exit ${EXIT_OK}
}

hook_edit() {
	local port=${1}
	assert isset port
	shift

	port_settings_read "${port}" ${HOOK_SETTINGS}

	while [ $# -gt 0 ]; do
		case "${1}" in
			--bssid=*)
				BSSID=$(cli_get_val ${1})
				;;
			--channel=*)
				CHANNEL=$(cli_get_val ${1})
				;;
			--mtu=*)
				MTU="$(cli_get_val "${1}")"
				;;
			--ssid=*)
				SSID=$(cli_get_val ${1})
				;;
			*)
				warning "Unknown argument '${1}'"
				;;
		esac
		shift
	done

	port_settings_write "${port}" ${HOOK_SETTINGS}

	exit ${EXIT_OK}
}

hook_create() {
	local port="${1}"
	assert isset port

	device_exists "${port}" && exit ${EXIT_OK}

	port_settings_read "${port}" ${HOOK_SETTINGS}

	# Check if the PHY is present.
	local phy="$(phy_get "${PHY}")"
	if ! isset phy; then
		log DEBUG "phy '${PHY}' is not present"
		exit ${EXIT_ERROR}
	fi

	# Create the wireless device, if it does not exist, yet.
	wireless_create "${port}" \
		--address="${ADDRESS}" \
		--phy="${phy}" \
		--type="ibss"

	exit ${EXIT_OK}
}

hook_remove() {
	local port="${1}"
	assert isset port

	if device_exists "${port}"; then
		wireless_remove "${port}"
	fi

	exit ${EXIT_OK}
}

hook_up() {
	local port=${1}
	assert isset port

	port_settings_read "${port}" ${HOOK_SETTINGS}

	# Check if the PHY is present.
	local phy=$(phy_get ${PHY})
	if ! isset phy; then
		log DEBUG "phy '${PHY}' is not present"
		exit ${EXIT_ERROR}
	fi

	# Create the wireless device, if it does not exist, yet.
	if ! device_exists ${port}; then
		wireless_create ${port} --address="${ADDRESS}" \
			--phy="${phy}" --type="ibss"
	fi

	exit ${EXIT_OK}
}

hook_down() {
	local port="${1}"
	assert isset port

	# Do nothing if the device already went away
	if ! device_exists "${port}"; then
		exit ${EXIT_OK}
	fi

	# Leave the ad-hoc network.
	wireless_ibss_leave "${port}"

	exit ${EXIT_OK}
}

hook_hotplug() {
	local port="${1}"
	assert isset port

	port_settings_read "${port}" ${HOOK_SETTINGS}

	case "$(hotplug_action)" in
		add)
			# Join the adhoc network after the device has
			# been created...
			if hotplug_event_port_is_interface "${port}"; then
				# Set the MTU.
				if isinteger MTU; then
					device_set_mtu "${port}" "${MTU}"
				fi

				wireless_ibss_join "${port}" --channel="${CHANNEL}" \
					--bssid="${BSSID}" --essid="${SSID}"

			# Bring up the port when the phy is plugged in
			elif hotplug_event_port_uses_phy "${port}"; then
				hook_up "${port}"
			fi
			;;

		*)
			exit ${EXIT_NOT_HANDLED}
			;;
	esac

	exit ${EXIT_OK}
}

hook_find_parent() {
	local port=${1}
	assert isset port

	local p child hook
	for p in $(ports_get); do
		hook=$(port_get_hook "${p}")

		if [ "${hook}" = "batman-adv" ]; then
			for child in $(port_get_children "${p}"); do
				[ "${child}" = "${port}" ] || continue

				print "${p}"
				return ${EXIT_OK}
			done
		fi
	done

	return ${EXIT_ERROR}
}
