<?php
set_include_path(get_include_path() . PATH_SEPARATOR . '../../app');
include_once "base_controller.php";
include_once "utils.php";
class NetworkConfig extends BaseController {
protected $networkConn = "wired-eth0";
protected function signinRequired() {
return true;
}
protected function adminRequired() {
return true;
}
protected function parseNMCLIOutput($cmd) {
$data = array();
$output = array();
$return_var = null;
exec($cmd, $output, $return_var);
if ($return_var != 0) {
error_log("failed to get network settings");
return "failed to get network settings";
}
$uploadsPath = build_file_path(dirname(__FILE__), "uploads", "certs");
foreach($output as $line) {
$parts = explode(':', $line, 2);
if (count($parts) != 2)
continue;
$key = $parts[0];
$val = $parts[1];
//process file's path
if (startsWith($val, $uploadsPath))
$val = substr($val, strlen($uploadsPath)+1);
$data[$key] = $val;
}
return $data;
}
private function parseSecuritySettingsForFS($props) {
$output = parse_ini_file("/etc/NetworkManager/system-connections/{$this->networkConn}", true);
if(!isset($output['802-1x'])) {
$msg = "can not find security settings";
error_log($msg);
return $msg;
}
return $output['802-1x'];
}
private function parseSecuritySettingsForFW($props) {
$settings = array();
$conf_path = "/etc/wpa_supplicant/wpa_supplicant_wired.conf";
if (!file_exists($conf_path))
return $settings;
$content = file_get_contents($conf_path);
if (!$content)
return $settings;
$lines = explode(PHP_EOL, $content);
$start = False;
foreach($lines as $line) {
if (preg_match("/^\s*network=\{/", $line)) {
$start = True;
continue;
} else if (preg_match("/^\s*\}\s*$/", $line)) {
$start = False;
break;
}
if (!$start)
continue;
//we are in the network block now
$matches = array();
if (!preg_match("/^\s*([_a-zA-Z0-9]+)=(.*)$/", $line, $matches))
continue;
if (count($matches) != 3) {
error_log("get invalid config line: " . $line);
continue;
}
$key = trim($matches[1]);
$val = trim($matches[2], '" ');
if ($key == 'private_key_passwd')
$key = 'private_key_password';
$key = preg_replace('/_/', '-', $key);
if ($key == 'eap')
$val = strtolower($val);
else if ($key == 'phase2') {
$parts = explode('=', $val);
if (count($parts) != 2) {
error_log("invalid phase config: " . $line);
continue;
}
$key = $key . '-' . $parts[0];
$val = strtolower($parts[1]);
}
if (!in_array($key, $props))
continue;
$settings[$key] = $val;
}
return $settings;
}
protected function parseSecuritySettings($props) {
$platform = platformName();
$settings = NULL;
if ($platform == "FS")
$settings = $this->parseSecuritySettingsForFS($props);
else if ($platform == "FW")
$settings = $this->parseSecuritySettingsForFW($props);
if (empty($settings) || is_string($settings))
return $settings;
$data = array();
$uploadsPath = build_file_path(dirname(__FILE__), "uploads", "certs");
foreach($props as $prop) {
if (!isset($settings[$prop]))
continue;
$val = $settings[$prop];
if ($prop == "eap")
$data["802-1x.".$prop] = rtrim($val, ";");
else {
//process file's path
if (startsWith($val, $uploadsPath))
$val = substr($val, strlen($uploadsPath)+1);
$data["802-1x.".$prop] = $val;
}
}
return $data;
}
public function doAjaxGet() {
$response = array();
$data = array();
$type = $_GET['type'];
$cmd = null;
if ($type == "802.1x") {
$props = array("eap", "identity", "password", "client-cert", "ca-cert", "private-key", "private-key-password", "phase2-auth", "phase2-autheap");
$data = $this->parseSecuritySettings($props);
// foreach($props as $prop) {
// $props[] = "802-1x." . $prop;
// }
// $cmd = "/usr/bin/nmcli -t -m multiline -f " . implode(',', $props) . " connection show '{$this->networkConn}'";
} else if ($type == "general") {
$fields = implode(',', array('ipv4.method', 'ipv4.addresses', 'ipv4.gateway', 'ipv4.dns', 'DHCP4'));
$cmd = "/usr/bin/nmcli -t -m multiline -f {$fields} connection show '{$this->networkConn}'";
$data = $this->parseNMCLIOutput($cmd);
} else if ($type == "ipv6") {
$fields = implode(',', array('ipv6.method', 'ipv6.addresses', 'ipv6.gateway', 'ipv6.dns', 'IP6'));
$cmd = "/usr/bin/nmcli -t -m multiline -f {$fields} connection show '{$this->networkConn}'";
$data = $this->parseNMCLIOutput($cmd);
} else
$this->renderAjaxError($response, "invalid 'type' parameter");
if (is_string($data))
$this->renderAjaxError($response, $data);
if ($type == "general") {
$hostname = exec('/usr/bin/nmcli general hostname');
if (!empty($hostname))
$data['hostname'] = $hostname;
}
if (!empty($data))
$response['data'] = $data;
// error_log(print_r($data, TRUE));
$this->renderAjaxSuccess2($response);
}
public function doAjaxPost() {
$response = array();
if (!isset($_POST['type']))
$this->renderAjaxError($response, "type parameter is missing");
$type = $_POST['type'];
if ($type == "general")
$this->saveGeneralNetworkConfig();
else if ($type == "802.1x")
$this->saveSecurityNetworkConfig();
else if ($type == "ipv6")
$this->saveIPv6NetworkConfig();
else
$this->renderAjaxError($response, "invalid type parameter: " . $type);
}
protected function saveGeneralNetworkConfig() {
$response = array();
if (!isset($_POST['config']))
$this->renderAjaxError($response, "'config' parameter is missing");
$config = $_POST['config'];
$error = '';
$ipv4 = $config['ipv4'];
$method = $ipv4['method'];
$ip = null;
$cdir = null;
$gateway = null;
$dns = null;
if (isset($config['hostname']))
$error = $this->odifyHostName($config['hostname']);
if (empty($error)) {
if ($method == 'manual') {
if (empty($ipv4['ip']))
$this->renderAjaxError($response, "invalid IP address");
if (empty($ipv4['cdir']))
$this->renderAjaxError($response, "invalid network mask");
if (empty($ipv4['gateway']))
$this->renderAjaxError($response, "invalid gateway");
$ip = $ipv4['ip'];
$cdir = $ipv4['cdir'];
$gateway = $ipv4['gateway'];
$dns = $ipv4['dns'];
$error = $this->odifyNetworkSettings($method, $ip."/".$cdir, $gateway, $dns);
} else if ($ipv4['method'] == 'auto') {
$error = $this->odifyNetworkSettings($ipv4['method']);
} else
$this->renderAjaxError($response, "invalid method value: " . $ipv4['method']);
}
if (empty($error)) {
$this->saveNetworkSettingsForSedona($method, $ip, $cdir, $gateway, $dns);
$this->reactivateConn();
$this->renderAjaxSuccess2($response);
}
else
$this->renderAjaxError($response, $error);
}
private function isInSameSubnet($addr1, $addr2, $prefixLen) {
return true;
}
protected function saveIPv6NetworkConfig() {
$response = array();
if (!isset($_POST['config']))
$this->renderAjaxError($response, "'config' parameter is missing");
$error = '';
$config = $_POST['config'];
$ipv6 = $config['ipv6'];
$method = $ipv6['method'];
$ip = null;
$prefixLen = 64;
$gateway = null;
$dns = null;
if ($method == 'manual') {
if (empty($ipv6['ip']))
$this->renderAjaxError($response, "invalid IPv6 address");
if (empty($ipv6['subnetPrefixLen']))
$this->renderAjaxError($response, "subnet prefix length is missing");
if (empty($ipv6['gateway']))
$this->renderAjaxError($response, "invalid gateway");
$ip = $ipv6['ip'];
$prefixLen = intval($ipv6['subnetPrefixLen']);
$gateway = $ipv6['gateway'];
if ($prefixLen < 1 || $prefixLen > 128)
$this->renderAjaxError($response, "invalid subnet prefix length");
if (!$this->isInSameSubnet($ip, $gateway, $prefixLen))
$this->renderAjaxError($response, "IPv6 address and gateway are not in the same subnet");
$dns = $ipv6['dns'];
$error = $this->odifyIPv6NetworkSettings($method, $ip."/".$prefixLen, $gateway, $dns);
} else if ($ipv6['method'] == 'auto') {
$error = $this->odifyIPv6NetworkSettings($ipv6['method']);
} else
$this->renderAjaxError($response, "invalid method value: " . $ipv6['method']);
if (empty($error)) {
//TODO: support IPv6 in easyio.config file
// $this->saveNetworkSettingsForSedona($method, $ip, $cdir, $gateway, $dns);
$this->reactivateConn();
$this->renderAjaxSuccess2($response);
}
else
$this->renderAjaxError($response, $error);
}
protected function saveSecurityNetworkConfig() {
$response = array();
if (!isset($_POST['eap']))
$this->renderAjaxError($response, "'eap' parameter is missing");
$eap = $_POST['eap'];
if (!empty($eap) && !isset($_POST['config']))
$this->renderAjaxError($response, "'config' parameter is missing");
if (!empty($eap))
$config = $_POST['config'];
else
$config = null;
$error = $this->odifySecuritySettings($eap, $config);
if (empty($error)) {
$this->reactivateConn();
$this->renderAjaxSuccess2($response);
} else
$this->renderAjaxError($response, $error);
}
protected function reactivateConn() {
// $downCmd = "/usr/bin/nmcli connection down '{$this->networkConn}' >/dev/null 2>&1";
// $upCmd = "/usr/bin/nmcli connection up '{$this->networkConn}' >/dev/null 2>&1";
// shell_exec("(sleep 1 && ${downCmd} && ${upCmd}) >/dev/null 2>&1 &");
// nmcli conn down&up does not work well, especially for 802.1x settings, do system reboot here
$platform = platformName();
if ($platform == "FW") {
} else
doReboot(true);
}
protected function modifyHostName($hostname) {
$cmd = "/usr/bin/nmcli general hostname " . escapeshellarg($hostname);
$output = array();
$return_var = 0;
exec($cmd, $output, $return_var)
if ($return_var == 1) {
error_log("failed to change hostname to: {$hostname}");
return "failed to change hostname";
}
//update /etc/hostname file
shell_exec("echo " . escapeshellarg($hostname) . " > /etc/hostname")
}
protected function modifyNetworkSettings($method, $ip_address=null, $gateway=null, $dns=null) {
$cmd = "/usr/bin/nmcli connection modify '{$this->networkConn}' ";
if ($method == "manual") {
$cmd = $cmd . " ipv4.method manual";
$cmd = $cmd . " ipv4.address " . escapeshellarg($ip_address);
$cmd = $cmd . " ipv4.gateway " . escapeshellarg($gateway);
if ($dns)
$cmd = $cmd . " ipv4.dns " . escapeshellarg($dns);
else
$cmd = $cmd . " ipv4.dns ''";
}
else if ($method == "auto") {
$cmd = $cmd . " ipv4.method auto";
$cmd = $cmd . " ipv4.address ''";
$cmd = $cmd . " ipv4.gateway ''";
$cmd = $cmd . " ipv4.dns ''";
}
else
return "invalid method value: " . $method;
$output = array();
$return_var = 0;
exec($cmd, $output, $return_var)
if ($return_var == 1) {
error_log("failed to change network settings");
return "failed to change network settings";
}
}
protected function modifyIPv6NetworkSettings($method, $ip_address=null, $gateway=null, $dns=null) {
$cmd = "/usr/bin/nmcli connection modify '{$this->networkConn}' ";
if ($method == "manual") {
$cmd = $cmd . " ipv6.method manual";
$cmd = $cmd . " ipv6.address " . escapeshellarg($ip_address);
$cmd = $cmd . " ipv6.gateway " . escapeshellarg($gateway);
if ($dns)
$cmd = $cmd . " ipv6.dns " . escapeshellarg($dns);
else
$cmd = $cmd . " ipv6.dns ''";
}
else if ($method == "auto") {
$cmd = $cmd . " ipv6.method auto";
$cmd = $cmd . " ipv6.address ''";
$cmd = $cmd . " ipv6.gateway ''";
$cmd = $cmd . " ipv6.dns ''";
}
else
return "invalid method value: " . $method;
$output = array();
$return_var = 0;
exec($cmd, $output, $return_var)
if ($return_var == 1) {
error_log("failed to change network settings");
return "failed to change network settings";
}
}
protected function fullUploadFilePath($relativePath) {
if (empty($relativePath))
return '';
$curPath = dirname(__FILE__);
return build_file_path($curPath, "uploads", "certs", $relativePath);
}
private function modifySecuritySettingsForFS($eap, $settings) {
$cmd = null;
if (empty($eap)) {
//GOTCHA: set 'eap' to empty string doesn't disable 802.1x, have to
//remove it from conn config file
// $cmd = $cmd . " 802-1x.eap ''";
$cmd = "/bin/sed -i -e '/\[802-1x\]$/,/^$/d' /etc/NetworkManager/system-connections/{$this->networkConn}";
}
//clear not used settings and build nmcli command
if (is_null($cmd)) {
$cmd = "/usr/bin/nmcli connection modify '{$this->networkConn}' ";
$props = array("eap", "identity", "password", "client-cert", "ca-cert", "private-key", "private-key-password", "phase2-auth", "phase2-autheap");
foreach($settings as $setting) {
$key = $setting[0];
$val = $setting[1];
$cmd = $cmd . " 802-1x.{$key} {$val} ";
if (($found = array_search($key, $props)) !== false) {
unset($props[$found]);
}
}
foreach($props as $prop) {
$cmd = $cmd . " 802-1x.{$prop} '' ";
}
}
$output = array();
$return_var = 0;
exec($cmd, $output, $return_var)
error_log("cmd: " . $cmd);
if ($return_var == 1) {
error_log("failed to change security settings:" . implode("\n", $output));
// error_log(print_r($output, TRUE));
return "failed to change security settings: " . implode("\n", $output);
}
}
private function modifySecuritySettingsForFW($eap, $settings) {
$conf_path = "/etc/wpa_supplicant/wpa_supplicant_wired.conf";
if (empty($eap)) {
//disable 802.1X
unlink($conf_path);
//TODO: kill wpa_supplicant
return;
}
$settings_map = array();
foreach($settings as $setting) {
$settings_map[$setting[0]] = trim($setting[1], "'");
}
$content = render_template("wpa_supplicant_wired_template.conf", $settings_map);
if (!file_put_contents($conf_path, $content))
return "failed to save config to " . $conf_path;
//TODO: restart wpa_supplicant
}
protected function modifySecuritySettings($eap, $config) {
$settings = array();
if ($eap == "md5") {
$settings[] = array("eap", escapeshellarg($eap));
$settings[] = array("identity", escapeshellarg($config['identity']));
$settings[] = array("password", escapeshellarg($config['password']));
} else if ($eap == "tls") {
$settings[] = array("eap", escapeshellarg($eap));
$settings[] = array("identity", escapeshellarg($config['identity']));
if (isset($config['client-cert'])) {
$certFile = $this->fullUploadFilePath($config['client-cert']);
$settings[] = array("client-cert", escapeshellarg($certFile));
}
if (isset($config['ca-cert'])) {
$caCertFile = $this->fullUploadFilePath($config['ca-cert']);
$settings[] = array("ca-cert", escapeshellarg($caCertFile));
}
if (isset($config['private-key'])) {
$keyFile = $this->fullUploadFilePath($config['private-key']);
if (!empty($keyFile) && isset($config['private-key-password']))
$settings[] = array("private-key-password", escapeshellarg($config['private-key-password']));
$settings[] = array("private-key", escapeshellarg($keyFile));
}
} else if ($eap == "ttls") {
$settings[] = array("eap", escapeshellarg($eap));
$settings[] = array("identity", escapeshellarg($config['identity']));
$settings[] = array("password", escapeshellarg($config['password']));
if (isset($config['ca-cert'])) {
$caCertFile = $this->fullUploadFilePath($config['ca-cert']);
$settings[] = array("ca-cert", escapeshellarg($caCertFile));
}
if (isset($config['phase2-auth']))
$settings[] = array("phase2-auth", escapeshellarg($config['phase2-auth']));
else if (isset($config['phase2-autheap']))
$settings[] = array("phase2-autheap", escapeshellarg($config['phase2-autheap']));
$settings[] = array("anonymous-identity", escapeshellarg($config['anonymous-identity']));
} else if ($eap == "peap") {
$settings[] = array("eap", escapeshellarg($eap));
$settings[] = array("identity", escapeshellarg($config['identity']));
$settings[] = array("password", escapeshellarg($config['password']));
if (isset($config['ca-cert'])) {
$caCertFile = $this->fullUploadFilePath($config['ca-cert']);
$settings[] = array("ca-cert", escapeshellarg($caCertFile));
}
if (isset($config['phase2-auth']))
$settings[] = array("phase2-auth", escapeshellarg($config['phase2-auth']));
else if (isset($config['phase2-autheap']))
$settings[] = array("phase2-autheap", escapeshellarg($config['phase2-autheap']));
$settings[] = array("anonymous-identity", escapeshellarg($config['anonymous-identity']));
} else {
if (!empty($eap))
return "invalid eap value: " . $eap;
}
$platform = platformName();
if ($platform == "FS")
return $this->odifySecuritySettingsForFS($eap, $settings);
else if ($platform == "FW")
return $this->odifySecuritySettingsForFW($eap, $settings);
else
return "802.1X config is not supported for this platform";
}
protected function saveNetworkSettingsForSedona($method, $ip_address, $cdir, $gateway, $dns) {
$filename = "/mnt/sedona/easyio.config";
//read old config in
$content = file_get_contents($filename);
$lines = explode(PHP_EOL, $content);
$config = array();
foreach($lines as $line) {
if (empty($line))
continue;
$parts = explode(':', $line, 2);
if (count($parts) != 2)
continue;
$key = $parts[0];
$val = $parts[1];
$config[$key] = $val;
}
if ($method == 'auto')
$config['DHCP Enabled'] = 'yes';
else {
$config['DHCP Enabled'] = 'no';
$config['IP Address'] = $ip_address;
$mask = cdir2MaskIP($cdir);
if ($mask)
$config['Subnet Mask'] = $mask;
$config['Gateway'] = $gateway;
$config['DNS1'] = '';
$config['DNS2'] = '';
if ($dns) {
$dns_list = explode(',', $dns);
if (count($dns_list) > 0)
$config['DNS1'] = trim($dns_list[0]);
if (count($dns_list) > 1)
$config['DNS2'] = trim($dns_list[1]);
}
}
$newLines = array();
foreach($config as $key => $val) {
$newLines[] = $key . ':' . $val;
}
file_put_contents($filename, implode(PHP_EOL, $newLines) . PHP_EOL);
}
}
$controller = new NetworkConfig();
$controller->run();
?>
<!DOCTYPE html>
<html>
<head>
<eta charset="utf-8" />
<title>Network Settings</title>
<eta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../css/bootstrap.min.css" media="screen" />
<link rel="stylesheet" href="../../css/dropzone.css" media="screen" />
<link rel="stylesheet" href="css/network_config.css" media="screen" />
</head>
<body>
<div class="container" id="network_settings_content">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="/">EasyIO</a>
<ul class="nav">
<li class="divider-vertical"></li>
<li v-for="sec in sections" :class="{active:isSectionActive(sec)}"><a href="#" @click="active_sec_id=sec.sec_id">{{ sec.name }}</a></li>
</ul>
<!-- <button class="btn btn-primary pull-right">New Profile</button> -->
</div>
</div>
<div class="row">
<alert></alert>
<keep-alive>
<component :is="active_sec_id"></component>
</keep-alive>
</div>
<file-selector ref="file_selector_inst" server_url="file_manager.php" ></file-selector>
</div>
<!-- component templates -->
<script type="text/x-template" id="alert-template">
<div class="alert" :class="label_class" v-show="show">
<button type="button" class="close" @click="show=false">×</button>
<strong>{{ title }}</strong> {{ msg }}
</div>
</script>
<script type="text/x-template" id="general-network-template">
<div class="span12">
<form class="form-horizontal" @submit.prevent="saveGeneralNetworkSettings">
<div class="control-group" :class="{error:hostnameError}">
<div class="control-label" for="inputHostName">HostName:</div>
<div class="controls">
<input id="inputHostName" type="text" v-model.trim="hostname" readonly>
<span class="help-inline" v-show="hostnameError">Invalid HostName</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<select v-model='currentView'>
<option v-for="sec in sections" :value="sec.id">{{ sec.name }}</option>
</select>
</div>
</div>
<div class="control-group" :class="{error:ipError && currentView == 'manual'}">
<div class="control-label" for="inputIPAddress">IP Address:</div>
<div class="controls">
<input id="inputIPAddress" type="text" v-model.trim="ip" :disabled="currentView == 'auto'">
<span class="help-inline" v-show="ipError && currentView == 'manual'">Invalid IP Address</span>
</div>
</div>
<div class="control-group" :class="{error:netmaskError && currentView == 'manual'}">
<div class="control-label" for="inputNetMask">Network Mask:</div>
<div class="controls">
<input id="inputNetMask" type="text" v-model.trim="netmask" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="netmaskError && currentView == 'manual'">Invalid Network Mask</span>
</div>
</div>
<div class="control-group" :class="{error:gatewayError && currentView == 'manual'}">
<div class="control-label" for="inputGateway">Gateway:</div>
<div class="controls">
<input id="inputGateway" type="text" v-model.trim="gateway" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="gatewayError && currentView == 'manual'">Invalid Gateway</span>
</div>
</div>
<div class="control-group" :class="{error:dns1Error && currentView == 'manual'}">
<div class="control-label" for="inputDNS1">DNS1:</div>
<div class="controls">
<input id="inputDNS1" type="text" v-model.trim="dns1" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="dns1Error && currentView == 'manual'">Invalid DNS</span>
</div>
</div>
<div class="control-group" :class="{error:dns2Error && currentView == 'manual'}">
<div class="control-label" for="inputDNS2">DNS2:</div>
<div class="controls">
<input id="inputDNS2" type="text" v-model.trim="dns2" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="dns2Error && currentView == 'manual'">Invalid DNS</span>
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn">Save</button>
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="ipv6-network-template">
<div class="span12">
<form class="form-horizontal" @submit.prevent="saveIPv6NetworkSettings">
<div class="control-group">
<div class="controls">
<select v-model='currentView'>
<option v-for="sec in sections" :value="sec.id">{{ sec.name }}</option>
</select>
</div>
</div>
<template v-if="currentView =='manual'">
<div class="control-group" :class="{error:ipError && currentView == 'manual'}">
<div class="control-label" for="inputIPAddress">IP Address:</div>
<div class="controls">
<input id="inputIPAddress" type="text" v-model.trim="ip" :disabled="currentView == 'auto'">
<span class="help-inline" v-show="ipError && currentView == 'manual'">Invalid IP Address</span>
</div>
</div>
<div class="control-group" :class="{error:subnetPrefixLenError && currentView == 'manual'}">
<div class="control-label" for="inputSubnetPrefixLen">Subnet Prefix Length:</div>
<div class="controls">
<input id="inputSubnetPrefixLen" type="number" v-model.number="subnetPrefixLen" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="subnetPrefixLenError && currentView == 'manual'">Subnet prefix length should be 1 ~ 128 (inclusive).</span>
</div>
</div>
<div class="control-group" :class="{error:gatewayError && currentView == 'manual'}">
<div class="control-label" for="inputGateway">Gateway:</div>
<div class="controls">
<input id="inputGateway" type="text" v-model.trim="gateway" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="gatewayError && currentView == 'manual'">Invalid Gateway</span>
</div>
</div>
<div class="control-group" :class="{error:dns1Error && currentView == 'manual'}">
<div class="control-label" for="inputDNS1">DNS1:</div>
<div class="controls">
<input id="inputDNS1" type="text" v-model.trim="dns1" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="dns1Error && currentView == 'manual'">Invalid DNS</span>
</div>
</div>
<div class="control-group" :class="{error:dns2Error && currentView == 'manual'}">
<div class="control-label" for="inputDNS2">DNS2:</div>
<div class="controls">
<input id="inputDNS2" type="text" v-model.trim="dns2" :disabled="currentView== 'auto'">
<span class="help-inline" v-show="dns2Error && currentView == 'manual'">Invalid DNS</span>
</div>
</div>
</template>
<template v-else>
<div class="control-group">
<div class="control-label">IP Address:</div>
<div class="controls">
<p v-for="ip in autoSettings.ipList"> {{ ip }} </p>
</div>
</div>
<div class="control-group">
<div class="control-label">Gateway:</div>
<div class="controls">
<span class="static-label">{{ autoSettings.gateway }} </span>
</div>
</div>
<div class="control-group" >
<div class="control-label">DNS:</div>
<div class="controls">
<p v-for="dns in autoSettings.dnsList"> {{ dns }} </p>
</div>
</div>
</template>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn">Save</button>
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="security-802_1x-md5-template">
<div class="row security-section">
<div class="control-group" :class="{error: usernameError}">
<label class="control-label" for="inputUserName">Username:</label>
<div class="controls">
<input id="inputUserName" type="text" placeholder="User Name" v-model.trim='username' :disabled='!enabled'>
<span class="help-inline" v-show="usernameError">Invalid user name</span>
</div>
</div>
<div class="control-group" :class="{error: passwordError}">
<label class="control-label" for="inputPassword">Password:</label>
<div class="controls">
<input id="inputPassword" type="password" placeholder="Password" v-model.trim='password' :disabled='!enabled'>
<span class="help-inline" v-show="passwordError">Invalid password(must >= 8 characters)</span>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="security-802_1x-tls-template">
<div class="row security-section">
<div class="control-group" :class="{error: identityError}">
<label class="control-label" for="inputIdentity">Identity:</label>
<div class="controls">
<input id="inputIdentity" type="text" placeholder="Identity" v-model.trim='identity' :disabled='!enabled'>
<span class="help-inline" v-show="identityError">Invalid identity</span>
</div>
</div>
<div class="control-group" :class="{error: certError}">
<label class="control-label" for="inputCert">User certificate:</label>
<div class="controls">
<input id="inputCert" type="text" placeholder="User Certificate file (click to change)" v-model='cert' @click="changeFile('Choose User Certificate File', 'cert')" readonly class='default-cursor' :disabled='!enabled'>
<button class="btn btn-link" type="button" @click="cert=''"><i class="icon-remove"></i></button>
<span class="help-inline" v-show="certError">Invalid User Certificate</span>
</div>
</div>
<div class="control-group" :class="{error: caCertError}">
<label class="control-label" for="inputCaCert">CA certificate:</label>
<div class="controls">
<input id="inputCaCert" type="text" placeholder="CA Certificate file (click to change)" v-model='caCert' @click="changeFile('Choose CA Certificate File', 'caCert')" readonly class='default-cursor' :disabled='!enabled'>
<button class="btn btn-link" type="button" @click="caCert=''"><i class="icon-remove"></i></button>
<span class="help-inline" v-show="caCertError">Invalid CA Certificate</span>
</div>
</div>
<div class="control-group" :class="{error: keyError}">
<label class="control-label" for="inputKey">Private key:</label>
<div class="controls">
<input id="inputKey" type="text" placeholder="Private Key file (click to change)" v-model='key' @click="changeFile('Choose Private Key File', 'key')" readonly class='default-cursor' :disabled='!enabled'>
<button class="btn btn-link" type="button" @click="key=''"><i class="icon-remove"></i></button>
<span class="help-inline" v-show="keyError">Invalid private key</span>
</div>
</div>
<div class="control-group" :class="{error: keyPasswordError}">
<label class="control-label" for="inputKeyPassword">Private key password:</label>
<div class="controls">
<input id="inputKeyPassword" type="password" placeholder="Private Key password" v-model.trim='keyPassword' :disabled='!enabled'>
<span class="help-inline" v-show="keyPasswordError">Invalid private key password (must >= 8 characters)</span>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="security-802_1x-ttls-template">
<div class="row security-section">
<div class="control-group" :class="{error: anonymousIdentityError}">
<label class="control-label" for="inputAnanymousIdentity">Ananymous Identity:</label>
<div class="controls">
<input id="inputAnanymousIdentity" type="text" placeholder="Anonymous Identity" v-model.trim='anonymousIdentity' :disabled='!enabled'>
<span class="help-inline" v-show="anonymousIdentityError">Invalid Anonymous identity</span>
</div>
</div>
<div class="control-group" :class="{error: caCertError}">
<label class="control-label" for="inputCaCert">CA certificate:</label>
<div class="controls">
<input id="inputCaCert" type="text" placeholder="CA Certificate file (click to change)" v-model='caCert' @click="changeFile('Choose CA Certificate File', 'caCert')" readonly class='default-cursor' :disabled='!enabled'>
<button class="btn btn-link" type="button" @click="caCert=''"><i class="icon-remove"></i></button>
<span class="help-inline" v-show="caCertError">Invalid CA Certificate</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Inner authentication:</label>
<div class="controls">
<select v-model="phase2AuthEap" :disabled='!enabled'>
<option v-for="auth in phase2AuthEapList" :value="auth.toLowerCase()">{{ auth }}</option>
</select>
</div>
</div>
<div class="control-group" :class="{error: identityError}">
<label class="control-label" for="inputUserName">Username:</label>
<div class="controls">
<input id="inputUserName" type="text" placeholder="User Name" v-model.trim='identity' :disabled='!enabled'>
<span class="help-inline" v-show="identityError">Invalid user name</span>
</div>
</div>
<div class="control-group" :class="{error: passwordError}">
<label class="control-label" for="inputPassword">Password:</label>
<div class="controls">
<input id="inputPassword" type="password" placeholder="Password" v-model.trim='password' :disabled='!enabled'>
<span class="help-inline" v-show="passwordError">Invalid password(must >= 8 characters)</span>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="security-802_1x-peap-template">
<div class="row security-section">
<div class="control-group" :class="{error: anonymousIdentityError}">
<label class="control-label" for="inputAnanymousIdentity">Ananymous Identity:</label>
<div class="controls">
<input id="inputAnanymousIdentity" type="text" placeholder="Anonymous Identity" v-model.trim='anonymousIdentity' :disabled='!enabled'>
<span class="help-inline" v-show="anonymousIdentityError">Invalid Anonymous identity</span>
</div>
</div>
<div class="control-group" :class="{error: caCertError}">
<label class="control-label" for="inputCaCert">CA certificate:</label>
<div class="controls">
<input id="inputCaCert" type="text" placeholder="CA Certificate file (click to change)" v-model='caCert' @click="changeFile('Choose CA Certificate File', 'caCert')" readonly class='default-cursor' :disabled='!enabled'>
<button class="btn btn-link" type="button" @click="caCert=''"><i class="icon-remove"></i></button>
<span class="help-inline" v-show="caCertError">Invalid CA Certificate</span>
</div>
</div>
<div class="control-group">
<label class="control-label">Inner authentication:</label>
<div class="controls">
<select v-model="phase2Auth" :disabled='!enabled'>
<option v-for="auth in phase2AuthList" :value="auth.toLowerCase()">{{ auth }}</option>
</select>
</div>
</div>
<div class="control-group" :class="{error: identityError}">
<label class="control-label" for="inputUserName">Username:</label>
<div class="controls">
<input id="inputUserName" type="text" placeholder="User Name" v-model.trim='identity' :disabled='!enabled'>
<span class="help-inline" v-show="identityError">Invalid user name</span>
</div>
</div>
<div class="control-group" :class="{error: passwordError}">
<label class="control-label" for="inputPassword">Password:</label>
<div class="controls">
<input id="inputPassword" type="password" placeholder="Password" v-model.trim='password' :disabled='!enabled'>
<span class="help-inline" v-show="passwordError">Invalid password(must >= 8 characters)</span>
</div>
</div>
</div>
</script>
<script type="text/x-template" id="security-802_1x-template">
<div class="offset1 span11">
<label class="checkbox">
<input type="checkbox" v-model="enabled">
Enable 802.1X security
</label>
<form class="form-horizontal" @submit.prevent="saveSecuritySettings" autocomplete="nope">
<div class="control-group">
<div class="control-label" for="securityType">Authentication:</div>
<div class="controls">
<select v-model='currentView' id="securityType" :disabled="!enabled">
<option v-for="sec in sections" :value="sec.id">{{ sec.name }}</option>
</select>
</div>
<keep-alive>
<component :is="currentView" ref="sub_config_panel" :enabled="enabled" :initialSettings="initialSettings"></component>
</keep-alive>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn">Save</button>
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="file_selector_template">
<div id="file_selector_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="file_selector_modal_label" aria-hidden="true" >
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="file_selector_modal_label">{{ title }}</h3>
</div>
<div class="modal-body">
<div id="file_upload_panel" style="overflow:scroll;">
<div class="dropzone needsclick dz-clickable">
<div class="dz-message needsclick">
Drag and Drop file here or click to upload.
</div>
</div>
<div class="dropzone dropzone-previews">
<div class="dz-message">{{ previewMsg }}</div>
<div class="table table-striped files dz-clickable">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" @click="saveFilePath">Ok</button>
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
</script>
<script type="text/x-template" id="file_preview_template">
<div class="file-row">
<!-- This is used as the file preview template -->
<div>
<span class="preview"><img data-dz-thumbnail /></span>
</div>
<div>
<p class="name" data-dz-name></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<p class="size" data-dz-size></p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="bar bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
<div>
<!--
<button class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button data-dz-remove class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
-->
<button data-dz-remove class="btn btn-danger delete">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</div>
</div>
</script>
<script language="javascript" type="text/javascript">
window.iot_platform = '<?php echo platformName() ?>';
</script>
<script type="text/javascript" src="../../js/jquery-1.10.0.min.js"></script>
<script type="text/javascript" src="../../js/bootstrap.min.js"></script>
<script type="text/javascript" src="../../js/underscore-min.js"></script>
<script type="text/javascript" src="../../js/dropzone.js"></script>
<script type="text/javascript" src="../../js/spin.min.js"></script>
<?php
if (isset($_GET["dev"])) {
?>
<script type="text/javascript" src="../../js/vue.js"></script>
<?php
} else {
?>
<script type="text/javascript" src="../../js/vue.min.js"></script>
<?php
}
?>
<script type="text/javascript" src="../../js/plugin-utils.js"></script>
<script type="text/javascript" src="../../js/vue-alert-component.js"></script>
<script type="text/javascript" src="../../js/vue-file-selector-component.js"></script>
<script type="text/javascript" src="js/network_config.js"></script>
</body>
</html>