#!/usr/bin/php
<?php 
/*
 * Copyright (C) 2013-2014 RuneAudio Team
 * http://www.runeaudio.com
 *
 * RuneUI
 * copyright (C) 2013-2014 - Andrea Coiutti (aka ACX) & Simone De Gregori (aka Orion)
 *
 * RuneOS
 * copyright (C) 2013-2014 - Simone De Gregori (aka Orion) & Carmelo San Giovanni (aka Um3ggh1U)
 *
 * RuneAudio website and logo
 * copyright (C) 2013-2014 - ACX webdesign (Andrea Coiutti)
 *
 * 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, 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 RuneAudio; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.txt>.
 *
 *  file: command/refresh_nics
 *  version: 1.3
 *  coder: Simone De Gregori
 *
 */
// common include
ini_set('display_errors', '1');
// ini_set('error_reporting', -1);
ini_set('error_log', '/var/log/runeaudio/refresh_nics.log');
// Connect to Redis backend
$redis = new Redis();
$redis->connect('/tmp/redis.sock');
include('/var/www/app/libs/runeaudio.php');
include '/var/www/app/libs/vendor/ziegler/iwlist-parser.class.php';
// startup - lock the scan system
runelog('--------------------------- lock the scan system ---------------------------');
$redis->Set('lock_wifiscan', 1);
// random delay
$sleep = rand(1000000, 2000000);
usleep($sleep);
runelog('random delay: ', $sleep);
// startup - collect system data
runelog('--------------------------- collect system data ---------------------------');
$excluded_nics = array('ifb0', 'ifb1', 'p2p0', 'bridge');
$active_nics = sysCmd("ifconfig | grep UP | cut -d ':' -f 1");
$detected_nics = sysCmd("ip addr |grep \"BROADCAST,\" |cut -d':' -f1-2 |cut -d' ' -f2");
$detected_nics = array_diff($detected_nics, $excluded_nics);
$inactive_nics = array_diff($detected_nics, $active_nics);
$configured_nics = array_map("trim", sysCmd("netctl list | cut -d ' ' -f 2,3"));
$removed_nics = array_diff($configured_nics, $detected_nics);
// debug
runelog('########### active_nics ########### ', $active_nics);
// print_r($active_nics);
// print_r($excluded_nics);
runelog('########### excluded_nics ########### ', $excluded_nics);
// print_r($detected_nics);
runelog('########### detected_nics ########### ', $detected_nics);
// print_r(array_diff($detected_nics,$active_nics));
// print_r($configured_nics);
runelog('########### configured_nics ########### ', $configured_nics);
// print_r(array_diff($configured_nics,$detected_nics));
// print_r($removed_nics);
runelog('########### removed_nics ########### ', $removed_nics);
// handle inactive nics
runelog('--------------------------- handle inactive nics ---------------------------');
unset($nic);
runelog('--------------------------- handle active nics ---------------------------');
// check if there is a stored profile
// refesh visible wifi networks.
$iw = new iwlist_parser;
wrk_netconfig($redis, 'setnics');
$nics = wrk_netconfig($redis, 'getnics');
foreach ($nics as $nic => $nicdetail) {
    if ($nicdetail->wireless === 1) {
        if (empty(sysCmd('ifconfig | grep '.$nic.''))) {
            runelog('########### restart link for nic: '.$nic);
                sysCmd('ip link set '.$nic.' up');
        }
        runelog('  try nic: '.$nic);
        
        // test if service is enabled
        sysCmd('systemctl enable netctl-auto@'.$nic.'.service');
        
        // get stored profiles
        $wlans_profiles = sysCmd("netctl-auto list | cut -c3-");
        $redis->set('stored_profiles',json_encode($wlans_profiles));
        // scan for ap in range
        $scan = $iw->parseScanDev( $nic );
        $tmp_wlans = array();
        $redis->set('wlans',json_encode($scan));
        foreach ($scan as $nicname => $nicscan) {
            runelog('    scan for nic: '.$nic.' nicname = '.$nicname);
            foreach ($nicscan as $count => $wlan) {
                runelog('      scan for nic: '.$nic.' AP = '.$count);
                if ($wlan['ESSID'] === "" OR array_search ($wlan['ESSID'], $tmp_wlans) !== false) {
                    runelog('        SSID already found');
                } else {
                    foreach ($wlan as $key => $value) {
                        $wlans[$count]['nic'] = $nicname;
                        runelog('        scan for nic: '.$nic.' = '.$key.' = '.$value);
                        if ($key === 'ESSID') {
                            $wlans[$count][$key] = $value;
                            $tmp_wlans[] = $value;
                            if ($nicdetail->currentssid === $value) {
                                $wlans[$count]['connected'] = 1;
                            } else {
                                $wlans[$count]['connected'] = 0;
                            }
                            foreach($wlans_profiles as $item){
                                if($item === $value){
                                    $wlans[$count]['storedprofile'] = 1;
                                    break;   // found it!
                                } else {
                                    $wlans[$count]['storedprofile'] = 0;
                                }
                            }
                        }
                        if ($key === 'Encryption key') $wlans[$count]['encryption'] = $value;
                        $wlans[$count]['origin'] = 'scan';
                    }
                }
            }
        }

        // handle hidden but stored profiles
        foreach ($wlans_profiles as $item){
            if ((bool)strpos(serialize($wlans), $item) === false){
                $wlans[++$count]['nic'] = $nic;
                $wlans[$count]['ESSID'] = $item;
                if ($nicdetail->currentssid === $item) {
                    $wlans[$count]['connected'] = 1;
                } else {
                    $wlans[$count]['connected'] = 0;
                }
                $wlans[$count]['storedprofile'] = 1;
                $wlans[$count]['encryption'] = 'unknown';
                $wlans[$count]['origin'] = 'storage';
            }
        }

        //runelog('wlans response: ', $wlans);
        $wlans = json_encode($wlans);
        runelog('wlans response(encoded): ', $wlans);
        ui_render('wlans', $wlans);
    } else {
        sysCmd('systemctl enable netctl-ifplugd@'.$nic.'.service');
    }
}
// send nics status to RuneUI
ui_render('nics', json_encode($nics));
unset($nic);
// handle removed nics
runelog('--------------------------- handle removed nics ---------------------------');
if (!empty($removed_nics)) {
    foreach($removed_nics as $nic) {
        // check for orphan netctl startup profiles
        if (!empty(sysCmd('ls /etc/systemd/system/multi-user.target.wants/ | grep '.$nic))) {
            runelog('########### disable netctl profiles for nic: '.$nic);
            sysCmd('systemctl disable netctl-ifplugd@'.$nic);
            sysCmd('systemctl disable netctl-auto@'.$nic);
        }
    }
}

// handle removed nics

// start AP if configured and no IP is assigned
if (FALSE === $redis->hGet('AccessPoint', 'enabled')) {
    $redis->hSet('AccessPoint', 'enabled', 0);
}
if (FALSE === $redis->hGet('AccessPoint', 'ip-address')) {
    $redis->hSet('AccessPoint', 'ip-address', '192.168.1.1');
}
if (FALSE === $redis->hGet('AccessPoint', 'broadcast')) {
    $redis->hSet('AccessPoint', 'broadcast', '192.168.1.255');
}
if (FALSE === $redis->hGet('AccessPoint', 'ssid')) {
    $redis->hSet('AccessPoint', 'ssid', 'RuneAudioAP');
}
if (FALSE === $redis->hGet('AccessPoint', 'passphrase')) {
    $redis->hSet('AccessPoint', 'passphrase', 'RuneAudio');
}
if (FALSE === $redis->hGet('AccessPoint', 'dhcp-range')) {
    $redis->hSet('AccessPoint', 'dhcp-range', '192.168.1.2,192.168.1.254,24h');
}
if (FALSE === $redis->hGet('AccessPoint', 'dhcp-option')) {
    $redis->hSet('AccessPoint', 'dhcp-option', 'option:dns-server,192.168.1.1');
}
if (FALSE === $redis->hGet('AccessPoint', 'enable-NAT')) {
    $redis->hSet('AccessPoint', 'enable-NAT', 0);
}
if ($redis->hGet('AccessPoint', 'enabled') === '1') {
	runelog('------------------ try to start AP if no IP is assigned ---------------------');
	$got_one_IP = false;
	if (!empty($active_nics)) {
		foreach($active_nics as $nic) {
			if (($nic === "wlan0") && (sysCmd('ip address show dev '.$nic.' | grep inet | wc -l')[0] !== '0')) {
				runelog('########### got one IP for nic: '.$nic);
				$got_one_IP = true;
			}
		}
	}
	if ($got_one_IP === false) {
		runelog('########### no IP => start AP on wlan0');
		sysCmd('systemctl stop netctl-auto@wlan0');
		// change AP name
		$file = '/etc/hostapd/hostapd.conf';
        $newArray = wrk_replaceTextLine($file, '', 'ssid=', 'ssid='.$redis->hGet("AccessPoint", "ssid"));
        $fp = fopen($file, 'w');
        $return = fwrite($fp, implode("", $newArray));
        fclose($fp);
		// change passphrase
		$file = '/etc/hostapd/hostapd.conf';
        $newArray = wrk_replaceTextLine($file, '', 'wpa_passphrase=', 'wpa_passphrase='.$redis->hGet("AccessPoint", "passphrase"));
        $fp = fopen($file, 'w');
        $return = fwrite($fp, implode("", $newArray));
        fclose($fp);
		sysCmd('systemctl start hostapd');
		// change dhcp-range
		$file = '/etc/dnsmasq.conf';
        $newArray = wrk_replaceTextLine($file, '', 'dhcp-range=', 'dhcp-range='.$redis->hGet("AccessPoint", "dhcp-range"));
        $fp = fopen($file, 'w');
        $return = fwrite($fp, implode("", $newArray));
        fclose($fp);
		// change dhcp-option
		$file = '/etc/dnsmasq.conf';
        $newArray = wrk_replaceTextLine($file, '', 'dhcp-option=', 'dhcp-option='.$redis->hGet("AccessPoint", "dhcp-option"));
        $fp = fopen($file, 'w');
        $return = fwrite($fp, implode("", $newArray));
        fclose($fp);
		sysCmd('systemctl start dnsmasq');
		sysCmd('ip addr add '.$redis->hGet("AccessPoint", "ip-address").'/24 broadcast '.$redis->hGet("AccessPoint", "broadcast").' dev wlan0');
		if ($redis->hGet('AccessPoint', 'enable-NAT') === '1') {
			sysCmd('iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE');
			sysCmd('iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT');
			sysCmd('iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT');
			sysCmd('sysctl net.ipv4.ip_forward=1');
		}
	}
}
// start AP end

// end - unlock the scan system
runelog('--------------------------- unlock the scan system ---------------------------');
$redis->Set('lock_wifiscan', 0);
// colse Redis connection
$redis->close();
