#!/bin/bash

SYS_CONF="/etc/hostapd.conf"
BOOT_CONF="/boot/hostapd.conf"
USER_CONF="/data/etc/hostapd.conf"

CP_CONF="/etc/captive-portal.conf"

LOG="/var/log/hostapd.log"
PROG="/usr/sbin/hostapd"
DNSMASQ_LOG="/var/log/dnsmasq.log"

INPUT_VALUE_FILE=/tmp/.captive_portal_button_input

DNSMASQ_CONF=/var/run/dnsmasq.conf

test -x ${PROG} || exit 0
test -n "${OS_VERSION}" || source /etc/init.d/base
CONF=$(select_conf ${SYS_CONF} ${BOOT_CONF} ${USER_CONF})
test -s "${CONF}" || exit 0

source ${CP_CONF}


function running() {
    killall -0 $(basename ${PROG}) &> /dev/null
}

function get_button_value() {
    if [[ -n "${CAPTIVE_PORTAL_START_GPIO}" ]]; then
        gpio.sh ${CAPTIVE_PORTAL_START_GPIO}
    elif [[ -s "${INPUT_VALUE_FILE}" ]]; then
        cat ${INPUT_VALUE_FILE}
    else
        expr ${CAPTIVE_PORTAL_START_LEVEL} - 1
    fi
}

function read_input() {
    value=$(od -N 32 -v -t x2 /dev/input/${CAPTIVE_PORTAL_START_INPUT} | grep -E "^0000000 " | cut -d ' ' -f 8)
    printf "%d" "${value:-0}"
}

function watch_input() {
    expr ${CAPTIVE_PORTAL_START_LEVEL} - 1 > ${INPUT_VALUE_FILE}
    while true; do
        if value=$(read_input); then
            echo ${value} > ${INPUT_VALUE_FILE}
        else
            sleep 1
        fi
    done
}

function watch_button() {
    count=0
    while true; do
        sleep 1
        if [[ $(get_button_value) == ${CAPTIVE_PORTAL_START_LEVEL} ]]; then
            count=$((count + 1))
            logger -t hostapd "button pressed (count=${count})"
        else
            if [[ ${count} -gt 0 ]]; then
                logger -t hostapd "button released"
            fi
            count=0
        fi
        if [[ ${count} -ge ${CAPTIVE_PORTAL_START_HOLD_SECONDS} ]]; then
            count=0
            if ! running; then
                logger -t hostapd "button held, starting captive portal"
                start_hostapd
            elif running; then
                logger -t hostapd "button held, stopping captive portal"
                stop_hostapd
            fi
        fi
    done
}

function network_ok() {
    ip route | grep -q default
}

function configure_ifaces() {
    rfkill unblock wlan &>/dev/null

    # wait up to 5 seconds for interfaces
    count=0
    ifaces=${OS_WLAN}
    test -n "${OS_WLAN1}" && ifaces+=" ${OS_WLAN1}"
    while true; do
        ok=false
        for iface in ${ifaces}; do
            ifconfig ${iface} &>/dev/null && ok=true
        done
        test ${ok} == true && break
        sleep 1
        count=$((${count} + 1))
        if [[ ${count} -ge 5 ]]; then
            logger -t hostapd "no AP interfaces"
            return 1
        fi
    done
    
    wifi_ifaces=$(ip link | grep -oE 'wlan[[:digit:]]')
    ap_ifaces=""
    for iface in ${wifi_ifaces}; do
        ip link set ${iface} up
        ap_iface=ap${iface: -1}
        if iw dev ${iface} interface add ${ap_iface} type __ap &>/dev/null; then
            ap_ifaces+="${ap_iface} "
        fi
    done
    
    logger -t hostapd "configured interfaces: ${ap_ifaces[@]}"
}

function assign_iface_ip() {
    hostapd_conf=/var/run/hostapd.conf
    test -s ${DNSMASQ_CONF} || return
    dnsmasq_ip=$(cat ${DNSMASQ_CONF} | grep range | cut -d '=' -f 2 | cut -d '.' -f 1,2,3).1
    dnsmasq_iface=$(cat ${DNSMASQ_CONF} | grep interface | cut -d '=' -f 2)
    hostapd_iface=$(cat ${hostapd_conf} | grep interface | cut -d '=' -f 2)
    test "${dnsmasq_iface}" == "${hostapd_iface}" || test -z "${dnsmasq_iface}" || return
    ifconfig ${hostapd_iface} ${dnsmasq_ip}
}

function after_captive_portal_timeout() {
    if tail -n 9 ${DNSMASQ_LOG} | grep -q DHCPREQUEST; then
        logger -t hostapd "client connected within timeout, keeping captive portal on"
        return
    fi

    if [[ ${CAPTIVE_PORTAL_TIMEOUT_REBOOT} == true ]]; then
        logger -t hostapd "rebooting on timeout"
        reboot
    else
        logger -t hostapd "stopping on timeout"
        stop_hostapd
    fi
}

function start_hostapd() {
    # Stop wpa_supplicant if running in captive portal mode
    if [[ ${CAPTIVE_PORTAL_ENABLED} == true ]]; then
        /etc/init.d/S35wifi stop &>/dev/null
        panic inhibit
    fi

    run_conf=/var/run/hostapd.conf
    
    # Replace placeholders
    eval "echo \"$(cat ${CONF})\"" > ${run_conf}

    # Determine Wi-Fi driver
    iface=$(cat ${run_conf} | grep interface | cut -d '=' -f 2)
    module=$(basename $(readlink /sys/class/net/${iface}/device/driver/module 2>/dev/null) 2>/dev/null)

    logger -t hostapd "starting on ${iface} with driver ${module}"

    iwconfig ${iface} power off &> /dev/null
    iw ${iface} set power_save off &> /dev/null
    ${PROG} ${run_conf} &> ${LOG} &
    
    ( sleep 1 && assign_iface_ip ) &
}

function stop_hostapd() {
    killall $(basename ${PROG}) &>/dev/null
    logger -t hostapd "stopped"
}

function start() {
    configure_ifaces

    if [[ "${CAPTIVE_PORTAL_ENABLED}" == true ]]; then
        msg_begin "Starting captive-portal"
        if [[ -n "${CAPTIVE_PORTAL_START_INPUT}" ]]; then
            watch_input &
        fi
        if [[ -n "${CAPTIVE_PORTAL_START_GPIO}" ]] || [[ -n "${CAPTIVE_PORTAL_START_INPUT}" ]]; then
            watch_button &
        fi

        if [[ -n "${CAPTIVE_PORTAL_CONFIGURED_CMD}" ]]; then
            if eval ${CAPTIVE_PORTAL_CONFIGURED_CMD} &>/dev/null; then
                logger -t hostapd "unit is configured"
                msg_done "unit is configured"
            else
                logger -t hostapd "not configured, starting"
                start_hostapd
                if [[ ${CAPTIVE_PORTAL_TIMEOUT} -gt 0 ]]; then
                    logger -t hostapd "will stop after ${CAPTIVE_PORTAL_TIMEOUT}s"
                    msg_done "timeout mode"
                    sleep ${CAPTIVE_PORTAL_TIMEOUT} && after_captive_portal_timeout &>/dev/null &
                else
                    logger -t hostapd "will run indefinitely"
                    msg_done "always-on mode"
                fi
            fi
        else
            msg_done "button mode"
        fi
    else
        msg_begin "Starting hostapd"
        start_hostapd
        msg_done
    fi
}

function stop() {
    msg_begin "Stopping hostapd"
    stop_hostapd
    ps | grep hostapd | grep -v $$ | grep -v grep | tr -s ' ' | sed -e 's/^\s//' | cut -d ' ' -f 1 | xargs -r kill
    msg_done
}

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