mirror of https://github.com/aredn/aredn.git
1593 lines
66 KiB
Lua
Executable File
1593 lines
66 KiB
Lua
Executable File
#!/usr/bin/lua
|
|
--[[
|
|
|
|
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
|
Copyright (C) 2021 Tim Wilkinson
|
|
Original Perl Copyright (C) 2019 Joe Ayers AE6XE
|
|
Original Perl Copyright (c) 2013 David Rivenburg et al. BroadBand-HamNet
|
|
See Contributors file for additional contributors
|
|
|
|
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 version 3 of the License.
|
|
|
|
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/>.
|
|
|
|
Additional Terms:
|
|
|
|
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
|
See AREDNLicense.txt for more info.
|
|
|
|
Attributions to the AREDN Project must be retained in the source code.
|
|
If importing this code into a new or existing project attribution
|
|
to the AREDN project must be added to the source code.
|
|
|
|
You must not misrepresent the origin of the material contained within.
|
|
|
|
Modified versions must be modified to attribute to the original source
|
|
and be marked in reasonable ways as differentiate it from the original
|
|
version
|
|
|
|
--]]
|
|
|
|
require("nixio")
|
|
require("aredn.http")
|
|
require("aredn.utils")
|
|
require("aredn.hardware")
|
|
require("uci")
|
|
require('luci.http')
|
|
local html = require("aredn.html")
|
|
require("aredn.info")
|
|
|
|
local errors = {}
|
|
local output = {}
|
|
local hidden = {}
|
|
|
|
-- uci cursor
|
|
local cursor = uci.cursor()
|
|
|
|
-- helpers start
|
|
|
|
function capture_and_match(cmd, pattern)
|
|
local f = io.popen(cmd)
|
|
if f then
|
|
for line in f:lines()
|
|
do
|
|
local r = line:match(pattern)
|
|
if r then
|
|
f:close()
|
|
return r
|
|
end
|
|
end
|
|
f:close()
|
|
end
|
|
end
|
|
|
|
function err(str)
|
|
errors[#errors + 1] = str
|
|
end
|
|
|
|
function out(str)
|
|
output[#output + 1] = str
|
|
end
|
|
|
|
function validate_hostname(raw_name, name_type)
|
|
local trimmed_name = raw_name:match("^%s*(.-)%s*$")
|
|
if trimmed_name == "" then
|
|
if name_type == "node" then
|
|
err("you must set the node name")
|
|
end
|
|
-- A missing tactical name is not an error
|
|
else
|
|
local hostname = trimmed_name:match("^%f[%w]([-%w]+)%f[%W]$") -- RFC 1123 + RFC 952
|
|
if not hostname then
|
|
err(string.format('"%s" is not a valid %s name; only alphanumerics and internal hyphens are allowed', trimmed_name, name_type))
|
|
elseif string.len(hostname) > 63 then
|
|
err(string.format('%s name "%s" exceeds 63 characters', name_type, hostname)) -- RFC 2181
|
|
else
|
|
return hostname
|
|
end
|
|
end
|
|
return ""
|
|
end
|
|
|
|
-- helper end
|
|
|
|
-- timezones
|
|
local tz_db_names = {}
|
|
for line in io.lines("/etc/zoneinfo")
|
|
do
|
|
local name, tz = line:match("^(.*)\t(.*)")
|
|
tz_db_names[#tz_db_names + 1] = { name = name, tz = tz }
|
|
end
|
|
|
|
-- online ping
|
|
local pingOK = false
|
|
if capture_and_match("ping -W1 -c1 8.8.8.8", "1 packets received") then
|
|
pingOK = true
|
|
end
|
|
|
|
-- must be global not local
|
|
dmz_lan_ip = ""
|
|
dmz_lan_mask = ""
|
|
lan_gw = ""
|
|
wan_ip = ""
|
|
wan_mask = ""
|
|
wan_gw = ""
|
|
passwd1 = ""
|
|
passwd2 = ""
|
|
wifi_intf = ""
|
|
|
|
local phycount = aredn.hardware.get_radio_count()
|
|
local radio_name = (aredn.hardware.get_radio() or {}).name or ""
|
|
local M9model = radio_name:match("M9")
|
|
local M3model = radio_name:match("M3")
|
|
local M39model = M9model or M3model
|
|
|
|
-- post_data
|
|
local parms = {}
|
|
local has_parms = false
|
|
if os.getenv("REQUEST_METHOD") == "POST" then
|
|
local request = luci.http.Request(nixio.getenv(),
|
|
function()
|
|
local v = io.read(1024)
|
|
if not v then
|
|
io.close()
|
|
end
|
|
return v
|
|
end
|
|
)
|
|
parms = request:formvalue()
|
|
for _,_ in pairs(parms)
|
|
do
|
|
has_parms = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if parms.button_uploaddata then
|
|
local si = capture("curl 'http://localnode:8080/cgi-bin/sysinfo.json?hosts=1' 2>/dev/null"):chomp()
|
|
-- strip closing }
|
|
si = si:sub(1, #si - 1)
|
|
|
|
-- get olsrd topo information
|
|
local topo = capture("curl 'http://localnode:9090/links' 2>/dev/null"):chomp()
|
|
|
|
-- add topo subdoc and close root doc
|
|
local newsi = string.format("%s,\"olsr\": %s}", si, topo)
|
|
|
|
-- PUT it to the server
|
|
local upcurl = os.execute("curl -f -H 'Accept: application/json' -X PUT -d '" .. newsi .. "' http://data.arednmesh.org/sysinfo >/dev/null 2>&1")
|
|
if upcurl == 0 then
|
|
out("AREDN online map updated")
|
|
else
|
|
err("ERROR: Cannot update online map.");
|
|
end
|
|
end
|
|
|
|
if parms.button_default then
|
|
local node = aredn.info.get_nvram("node")
|
|
local mac2 = aredn.info.get_nvram("mac2")
|
|
local dtdmac = aredn.info.get_nvram("dtdmac")
|
|
for line in io.lines("/etc/config.mesh/_setup.default")
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
line = line:gsub("<NODE>", node):gsub("<MAC2>", mac2):gsub("<DTDMAC>", dtdmac)
|
|
local k, v = line:match("^([^%s]*)%s*=%s*(.*)%s*$")
|
|
_G[k] = v
|
|
end
|
|
end
|
|
else
|
|
for k, v in pairs(parms)
|
|
do
|
|
if k:match("^%w+") then
|
|
v = v:gsub("^%s+", ""):gsub("%s+$", "")
|
|
_G[k] = v
|
|
end
|
|
end
|
|
if parms.button_reset or not has_parms then
|
|
local node = aredn.info.get_nvram("node")
|
|
local mac2 = aredn.info.get_nvram("mac2")
|
|
local dtdmac = aredn.info.get_nvram("dtdmac")
|
|
for line in io.lines("/etc/config.mesh/_setup")
|
|
do
|
|
if not (line:match("^%s*#") or line:match("^%s*$")) then
|
|
line = line:gsub("<NODE>", node):gsub("<MAC2>", mac2):gsub("<DTDMAC>", dtdmac)
|
|
local k, v = line:match("^([^%s]*)%s*=%s*(.*)%s*$")
|
|
_G[k] = v
|
|
end
|
|
end
|
|
local function h2s(hex)
|
|
local s = ""
|
|
if hex then
|
|
for i = 1,#hex,2
|
|
do
|
|
local p = hex:sub(i, i+1)
|
|
if p:match("[0-9a-f][0-9a-f]") then
|
|
s = s .. string.char(tonumber(p, 16))
|
|
else
|
|
s = s .. p
|
|
end
|
|
end
|
|
end
|
|
return s
|
|
end
|
|
wifi2_key = h2s(wifi2_key)
|
|
wifi2_ssid = h2s(wifi2_ssid)
|
|
wifi3_key = h2s(wifi3_key)
|
|
wifi3_ssid = h2s(wifi3_ssid)
|
|
end
|
|
end
|
|
|
|
if not wifi_intf or wifi_intf == "" then
|
|
wifi_intf = aredn.hardware.get_iface_name("wifi")
|
|
end
|
|
local phy = iwinfo.nl80211.phyname(wifi_intf)
|
|
|
|
-- make sure everything we need is initialized
|
|
local d0 = { "lan_dhcp", "olsrd_bridge", "wifi2_enable", "wifi_enable", "wifi3_enable", "dhcp_start", "dhcp_end" }
|
|
for _, k in ipairs(d0)
|
|
do
|
|
if not parms[k] then
|
|
parms[k] = "0"
|
|
end
|
|
if not _G[k] then
|
|
_G[k] = "0"
|
|
end
|
|
end
|
|
for _, k in ipairs({ "wifi_channel", "wifi2_channel", "wifi_txpower", "dhcp_start", "dhcp_end" })
|
|
do
|
|
if _G[k] then
|
|
_G[k] = tonumber(_G[k])
|
|
end
|
|
end
|
|
|
|
local nodetac
|
|
if parms.button_reset or parms.button_default or (not nodetac and not has_parms) then
|
|
nodetac = aredn.info.get_nvram("node")
|
|
tactical = aredn.info.get_nvram("tactical")
|
|
if tactical ~= "" then
|
|
nodetac = nodetac .. " / " .. tactical
|
|
end
|
|
else
|
|
nodetac = parms.nodetac
|
|
end
|
|
|
|
-- lan is always static
|
|
local lan_proto = "static"
|
|
|
|
-- enforce direct mode settings
|
|
-- (formerly known as dmz mode)
|
|
if not dmz_mode then
|
|
dmz_mode = cursor:get("aredn", "@dmz[0]", "mode")
|
|
end
|
|
dmz_mode = tonumber(dmz_mode)
|
|
if not dmz_mode or (dmz_mode ~= 0 and dmz_mode < 2) then
|
|
dmz_mode = 2
|
|
elseif dmz_mode > 5 then
|
|
dmz_mode = 5
|
|
end
|
|
|
|
if dmz_mode ~= 0 then
|
|
local ipshift = (ip_to_decimal(wifi_ip) * math.pow(2, dmz_mode)) % 0x1000000
|
|
local a, b = decimal_to_ip(ipshift):match("(%d+%.%d+%.%d+%.)(%d+)")
|
|
dmz_lan_ip = "1" .. a .. (tonumber(b) + 1)
|
|
dmz_lan_mask = decimal_to_ip((0xffffffff * math.pow(2, dmz_mode)) % 0x100000000)
|
|
local octet = dmz_lan_ip:match("%d+%.%d+%.%d+%.(%d+)")
|
|
dmz_dhcp_start = octet + 1
|
|
dmz_dhcp_end = dmz_dhcp_start + math.pow(2, dmz_mode) - 4;
|
|
parms.dmz_lan_ip = dmz_lan_ip
|
|
parms.dmz_lan_mask = dmz_lan_mask
|
|
parms.dmz_dhcp_start = dmz_dhcp_start
|
|
parms.dmz_dhcp_end = dmz_dhcp_end
|
|
else
|
|
dmz_dhcp_end = dmz_dhcp_start
|
|
parms.dmz_dhcp_end = dmz_dhcp_end
|
|
end
|
|
|
|
parms.dhcp_limit = dhcp_end - dhcp_start + 1
|
|
parms.dmz_dhcp_limit = dmz_dhcp_end - dmz_dhcp_start + 1
|
|
|
|
-- get the active wifi settings on a fresh page load
|
|
if not parms.reload and wifi_intf ~= "br-nomesh" then
|
|
wifi_txpower = capture_and_match("iwinfo " .. wifi_intf .. " info", "Tx%-Power: (%d+)")
|
|
local doesiwoffset = capture_and_match("iwinfo " .. wifi_intf .. " info", "TX power offset: (%d+)")
|
|
if wifi_txpower then
|
|
wifi_txpower = tonumber(wifi_txpower)
|
|
if doesiwoffset then
|
|
wifi_txpower = wifi_txpower - tonumber(doesiwoffset)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- lqm
|
|
local lqm_mode = false
|
|
if cursor:get("aredn", "@lqm[0]", "enable") == "1" then
|
|
lqm_mode = true
|
|
end
|
|
|
|
-- sanitize the active settings
|
|
if not wifi_txpower or wifi_txpower > aredn.hardware.wifi_maxpower(wifi_intf, wifi_channel) then
|
|
wifi_txpower = aredn.hardware.wifi_maxpower(wifi_intf, wifi_channel)
|
|
end
|
|
if not wifi_txpower or wifi_txpower < 1 then
|
|
wifi_txpower = 1
|
|
end
|
|
if not wifi_distance then
|
|
wifi_distance = 0
|
|
end
|
|
if tostring(wifi_distance):match("%D") then
|
|
wifi_distance = 0
|
|
end
|
|
|
|
-- stuff the sanitized data back into the parms tables
|
|
-- so they get saved correctly
|
|
parms.wifi_distance = wifi_distance
|
|
parms.wifi_txpower = wifi_txpower
|
|
|
|
-- apply the wifi settings
|
|
if (parms.button_apply or parms.button_save) and wifi_enable == "1" then
|
|
if not lqm_mode then
|
|
if phy then
|
|
if wifi_distance == 0 then
|
|
os.execute("iw phy " .. phy .. " set distance auto >/dev/null 2>&1")
|
|
else
|
|
os.execute("iw phy " .. phy .. " set distance " .. wifi_distance .. " >/dev/null 2>&1")
|
|
end
|
|
end
|
|
elseif parms.lqm_min_snr ~= nil then
|
|
-- validate values
|
|
local lqm_min_snr = tonumber(parms.lqm_min_snr)
|
|
local lqm_max_distance = tonumber(parms.lqm_max_distance)
|
|
local lqm_distance_unit = parms.lqm_distance_unit or "mile"
|
|
local lqm_min_quality = tonumber(parms.lqm_min_quality)
|
|
if not lqm_min_snr or lqm_min_snr < 0 or lqm_min_snr > 95 then
|
|
err("ERROR: Minimum SNR out of range (0-95)")
|
|
end
|
|
local distance_scale = 1000
|
|
if lqm_distance_unit == "mile" then
|
|
distance_scale = 1609.344
|
|
if not lqm_max_distance or lqm_max_distance < 0 or lqm_max_distance > 71 then
|
|
err("ERROR: Maximum Distance out of range (0-71)")
|
|
end
|
|
else
|
|
if not lqm_max_distance or lqm_max_distance < 0 or lqm_max_distance > 114 then
|
|
err("ERROR: Maximum Distance out of range (0-114)")
|
|
end
|
|
end
|
|
if not lqm_min_quality or lqm_min_quality < 0 or lqm_min_quality > 100 then
|
|
err("ERROR: Minimum Quality out of range (0-100)")
|
|
end
|
|
if #errors == 0 then
|
|
local cursorb = uci.cursor("/etc/config.mesh")
|
|
cursor:set("aredn", "@lqm[0]", "min_snr", parms.lqm_min_snr)
|
|
cursorb:set("aredn", "@lqm[0]", "min_snr", parms.lqm_min_snr)
|
|
cursor:set("aredn", "@lqm[0]", "max_distance", math.floor(parms.lqm_max_distance * distance_scale))
|
|
cursorb:set("aredn", "@lqm[0]", "max_distance", math.floor(parms.lqm_max_distance * distance_scale))
|
|
cursor:set("aredn", "@lqm[0]", "min_quality", parms.lqm_min_quality)
|
|
cursorb:set("aredn", "@lqm[0]", "min_quality", parms.lqm_min_quality)
|
|
cursor:commit("aredn")
|
|
cursorb:commit("aredn")
|
|
end
|
|
end
|
|
os.execute("iw dev " .. wifi_intf .. " set txpower fixed " .. wifi_txpower .. "00 >/dev/null 2>&1")
|
|
end
|
|
|
|
if (parms.button_updatelocation or parms.button_save) then
|
|
-- process gridsquare
|
|
local cursora = uci.cursor();
|
|
local cursorb = uci.cursor("/etc/config.mesh")
|
|
if (cursora:get("aredn", "@location[0]", "gridsquare") or "") ~= parms.gridsquare then
|
|
if parms.gridsquare ~= "" then
|
|
if parms.gridsquare:match("^[A-Z][A-Z]%d%d[a-z][a-z]$") then
|
|
cursora:set("aredn", "@location[0]", "gridsquare", parms.gridsquare)
|
|
cursorb:set("aredn", "@location[0]", "gridsquare", parms.gridsquare)
|
|
out("Gridsquare updated.")
|
|
else
|
|
err("ERROR: Gridsquare format is: 2-uppercase letters, 2-digits, 2-lowercase letters. (AB12cd)")
|
|
end
|
|
else
|
|
cursora:set("aredn", "@location[0]", "gridsquare", "")
|
|
cursorb:set("aredn", "@location[0]", "gridsquare", "")
|
|
out("Gridsquare purged.")
|
|
end
|
|
end
|
|
|
|
-- process lat/lng
|
|
if (cursora:get("aredn", "@location[0]", "lat") or "") ~= parms.latitude or (cursora:get("aredn", "@location[0]", "lon") or "") ~= parms.longitude then
|
|
if parms.latitude ~= "" and parms.longitude ~= "" then
|
|
if parms.latitude:match("^[-+]?%d%d?%.%d+$") and parms.longitude:match("^[-+]?%d%d?%d?%.%d+$") then
|
|
if tonumber(parms.latitude) >= -90 and tonumber(parms.latitude) <= 90 and tonumber(parms.longitude) >= -180 and tonumber(parms.longitude) <= 180 then
|
|
cursora:set("aredn", "@location[0]", "lat", parms.latitude)
|
|
cursorb:set("aredn", "@location[0]", "lat", parms.latitude)
|
|
cursora:set("aredn", "@location[0]", "lon", parms.longitude)
|
|
cursorb:set("aredn", "@location[0]", "lon", parms.longitude)
|
|
out("Lat/lon updated.")
|
|
else
|
|
err("ERROR: Lat/lon values must be between -90/90 and -180/180, respectively.")
|
|
end
|
|
else
|
|
err("ERROR: Lat/lon format is decimal: (ex. 30.121456 or -95.911154).")
|
|
end
|
|
else
|
|
cursora:set("aredn", "@location[0]", "lat", "")
|
|
cursorb:set("aredn", "@location[0]", "lat", "")
|
|
cursora:set("aredn", "@location[0]", "lon", "")
|
|
cursorb:set("aredn", "@location[0]", "lon", "")
|
|
out("Lat/lon purged.")
|
|
end
|
|
end
|
|
|
|
-- process antenna, azimuth and elevation
|
|
if wifi_enable == "1" then
|
|
local antenna = parms.antenna or ""
|
|
if (cursora:get("aredn", "@location[0]", "antenna") or "") ~= antenna then
|
|
cursora:set("aredn", "@location[0]", "antenna", antenna)
|
|
cursorb:set("aredn", "@location[0]", "antenna", antenna)
|
|
end
|
|
local antenna_aux = parms.antenna_aux or ""
|
|
if (cursora:get("aredn", "@location[0]", "antenna_aux") or "") ~= antenna_aux then
|
|
cursora:set("aredn", "@location[0]", "antenna_aux", antenna_aux)
|
|
cursorb:set("aredn", "@location[0]", "antenna_aux", antenna_aux)
|
|
end
|
|
if (cursora:get("aredn", "@location[0]", "azimuth") or "") ~= parms.azimuth then
|
|
local ant = aredn.hardware.get_current_antenna(wifi_intf)
|
|
if ant and ant.beamwidth == 360 then
|
|
parms.azimuth = ""
|
|
end
|
|
ant = aredn.hardware.get_current_antenna_aux(wifi_intf)
|
|
if ant and ant.beamwidth == 360 then
|
|
parms.azimuth = ""
|
|
end
|
|
local azimuth = tonumber(parms.azimuth)
|
|
if parms.azimuth == "" or (azimuth and azimuth >= 0 and azimuth < 360) then
|
|
cursora:set("aredn", "@location[0]", "azimuth", parms.azimuth)
|
|
cursorb:set("aredn", "@location[0]", "azimuth", parms.azimuth)
|
|
elseif parms.azimuth then
|
|
err("ERROR: Azimuth value must be blank or between 0 and 360.")
|
|
end
|
|
end
|
|
if (cursora:get("aredn", "@location[0]", "elevation") or "") ~= parms.elevation then
|
|
local elevation = tonumber(parms.elevation)
|
|
if parms.elevation == "" or (elevation and elevation >= -180 and elevation <= 180) then
|
|
cursora:set("aredn", "@location[0]", "elevation", parms.elevation)
|
|
cursorb:set("aredn", "@location[0]", "elevation", parms.elevation)
|
|
elseif parms.elevation then
|
|
err("ERROR: Elevation value must be blank or between -180 and 180.")
|
|
end
|
|
end
|
|
if (cursora:get("aredn", "@location[0]", "height") or "") ~= parms.height then
|
|
local height = tonumber(parms.height)
|
|
if parms.height == "" or (height and height >= 0 and height <= 500) then
|
|
cursora:set("aredn", "@location[0]", "height", parms.height)
|
|
cursorb:set("aredn", "@location[0]", "height", parms.height)
|
|
elseif parms.height then
|
|
err("ERROR: Height value must be blank or between 0 and 500.")
|
|
end
|
|
end
|
|
end
|
|
|
|
cursora:commit("aredn")
|
|
cursorb:commit("aredn")
|
|
cursor = cursora
|
|
end
|
|
|
|
-- retrieve location data
|
|
lat = cursor:get("aredn", "@location[0]", "lat")
|
|
if not lat then
|
|
lat = ""
|
|
end
|
|
lon = cursor:get("aredn", "@location[0]", "lon")
|
|
if not lon then
|
|
lon = ""
|
|
end
|
|
gridsquare = cursor:get("aredn", "@location[0]", "gridsquare")
|
|
if not gridsquare then
|
|
gridsquare = ""
|
|
end
|
|
local azimuth = cursor:get("aredn", "@location[0]", "azimuth") or ""
|
|
local elevation = cursor:get("aredn", "@location[0]", "elevation") or ""
|
|
local height = cursor:get("aredn", "@location[0]", "height") or ""
|
|
local antenna = cursor:get("aredn", "@location[0]", "antenna") or ""
|
|
local antenna_aux = cursor:get("aredn", "@location[0]", "antenna_aux") or ""
|
|
|
|
-- retrieve ntp_period
|
|
local cm = uci.cursor("/etc/config.mesh")
|
|
ntp_period = cm:get("aredn", "@ntp[0]", "period")
|
|
if not ntp_period then
|
|
ntp_period = "daily"
|
|
end
|
|
|
|
function is_vtun_client()
|
|
if nixio.fs.stat("/etc/config/vtun") then
|
|
for line in io.lines("/etc/config/vtun") do
|
|
if line:match("^config server") then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- validate and save configuration
|
|
if parms.button_save then
|
|
for _,zone in ipairs(tz_db_names)
|
|
do
|
|
if zone.name == time_zone_name then
|
|
parms.time_zone = zone.tz
|
|
break
|
|
end
|
|
end
|
|
|
|
if wifi_enable == "1" then
|
|
if not validate_netmask(wifi_mask) then
|
|
err("invalid Mesh netmask")
|
|
elseif not validate_ip_netmask(wifi_ip, wifi_mask) then
|
|
err("invalid Mesh IP address")
|
|
end
|
|
|
|
if #wifi_ssid > 32 then
|
|
err("invalid Mesh RF SSID")
|
|
end
|
|
|
|
if wifi_intf:match("^br") then
|
|
wifi_intf = aredn.hardware.get_board_network_ifname("wifi")
|
|
phy = iwinfo.nl80211.phyname(wifi_intf)
|
|
local defaultwifi = aredn.hardware.get_default_channel(wifi_intf)
|
|
wifi_channel = defaultwifi.channel
|
|
wifi_chanbw = tostring(defaultwifi.bandwidth)
|
|
end
|
|
local valid = false
|
|
for _, c in ipairs(aredn.hardware.get_rfchannels(wifi_intf))
|
|
do
|
|
if c.number == wifi_channel then
|
|
valid = true
|
|
break
|
|
end
|
|
end
|
|
if not valid then
|
|
err("invalid Mesh RF channel")
|
|
end
|
|
|
|
local wifi_country_validated = false
|
|
local countries = { "00","HX","AD","AE","AL","AM","AN","AR","AT","AU","AW","AZ","BA","BB","BD","BE","BG","BH","BL","BN","BO","BR","BY","BZ","CA","CH","CL","CN","CO","CR","CY","CZ","DE","DK","DO","DZ","EC","EE","EG","ES","FI","FR","GE","GB","GD","GR","GL","GT","GU","HN","HK","HR","HT","HU","ID","IE","IL","IN","IS","IR","IT","JM","JP","JO","KE","KH","KP","KR","KW","KZ","LB","LI","LK","LT","LU","LV","MC","MA","MO","MK","MT","MY","MX","NL","NO","NP","NZ","OM","PA","PE","PG","PH","PK","PL","PT","PR","QA","RO","RS","RU","RW","SA","SE","SG","SI","SK","SV","SY","TW","TH","TT","TN","TR","UA","US","UY","UZ","VE","VN","YE","ZA","ZW" }
|
|
for _,country in ipairs(countries)
|
|
do
|
|
if country == wifi_country then
|
|
wifi_country_validated = true
|
|
break
|
|
end
|
|
end
|
|
if not wifi_country_validated then
|
|
wifi_country = "00"
|
|
err("Invalid country")
|
|
end
|
|
end
|
|
|
|
if lan_proto == "static" then
|
|
if not validate_netmask(lan_mask) then
|
|
err("invalid LAN netmask")
|
|
elseif not lan_mask:match("^255%.255%.255%.") then
|
|
err("LAN netmask must begin with 255.255.255")
|
|
elseif not validate_ip_netmask(lan_ip, lan_mask) then
|
|
err("invalid LAN IP address")
|
|
else
|
|
if lan_dhcp ~= "" then
|
|
local start_addr = lan_ip:match("^(.*%.)%d+$") .. dhcp_start
|
|
local end_addr = lan_ip:match("^(.*%.)%d+$") .. dhcp_end
|
|
|
|
if not (validate_ip_netmask(start_addr, lan_mask) and validate_same_subnet(start_addr, lan_ip, lan_mask)) then
|
|
err("invalid DHCP start address")
|
|
end
|
|
if not (validate_ip_netmask(end_addr, lan_mask) and validate_same_subnet(end_addr, lan_ip, lan_mask)) then
|
|
err("invalid DHCP end address")
|
|
end
|
|
if dhcp_start > dhcp_end then
|
|
err("invalid DHCP start/end addresses")
|
|
end
|
|
end
|
|
if lan_gw ~= "" and not (validate_ip_netmask(lan_gw, lan_mask) and validate_same_subnet(lan_ip, lan_gw, lan_mask)) then
|
|
err("invalid LAN gateway")
|
|
end
|
|
end
|
|
end
|
|
|
|
if wan_proto == "static" then
|
|
if not validate_netmask(wan_mask) then
|
|
err("invalid WAN netmask")
|
|
elseif not validate_ip_netmask(wan_ip, wan_mask) then
|
|
err("invalid WAN IP address")
|
|
elseif not (validate_ip_netmask(wan_gw, wan_mask) and validate_same_subnet(wan_ip, wan_gw, wan_mask)) then
|
|
err("invalid WAN gateway")
|
|
end
|
|
end
|
|
|
|
if not validate_ip(wan_dns1) then
|
|
err("invalid WAN DNS 1")
|
|
end
|
|
if wan_dns2 ~= '' and not validate_ip(wan_dns2) then
|
|
err("invaliud WAN DNS 2")
|
|
end
|
|
|
|
if passwd1 ~= "" or passwd2 ~= "" then
|
|
if passwd1 ~= passwd2 then
|
|
err("passwords do not match")
|
|
end
|
|
if passwd1:match("#") then
|
|
err("passwords cannot contain '#'")
|
|
end
|
|
if passwd1 == "hsmm" then
|
|
err("password must be changed")
|
|
end
|
|
elseif nixio.fs.stat("/etc/config/unconfigured") then
|
|
err("password must be changed during initial configuration")
|
|
end
|
|
|
|
local raw_node, raw_tactical = nodetac:match("^([^/]*)(.*)$")
|
|
node = validate_hostname(raw_node, "node")
|
|
tactical = raw_tactical ~= "" and validate_hostname(string.sub(raw_tactical, 2), "tactical") or ""
|
|
|
|
if not validate_fqdn(ntp_server) then
|
|
err("invalid ntp server")
|
|
end
|
|
|
|
-- move update-clock to correct location if necessary
|
|
if parms.ntp_period == "daily" then
|
|
if not nixio.fs.stat("/etc/cron.daily/update-clock") then
|
|
if nixio.fs.stat("/etc/cron.hourly/update-clock") then
|
|
os.execute("mv /etc/cron.hourly/update-clock /etc/cron.daily/update-clock")
|
|
ntp_period = parms.ntp_period
|
|
else
|
|
err("update-clock script not found")
|
|
end
|
|
end
|
|
elseif parms.ntp_period == "hourly" then
|
|
if not nixio.fs.stat("/etc/cron.hourly/update-clock") then
|
|
if nixio.fs.stat("/etc/cron.daily/update-clock") then
|
|
os.execute("mv /etc/cron.daily/update-clock /etc/cron.hourly/update-clock")
|
|
ntp_period = parms.ntp_period
|
|
else
|
|
err("update-clock script not found")
|
|
end
|
|
end
|
|
else
|
|
err("ntp_period not set")
|
|
end
|
|
|
|
if wifi2_enable == "1" then
|
|
if #wifi2_ssid > 32 then
|
|
err("LAN Access Point SSID musr be 32 or less characters")
|
|
end
|
|
if #wifi2_key < 8 or #wifi2_key > 64 then
|
|
err("LAN Access Point Password must be at least 8 characters, up to 64")
|
|
end
|
|
if wifi2_key:match("'") or wifi2_ssid:match("'") then
|
|
err("The LAN Access Point password and ssid may not contain a single quote character")
|
|
end
|
|
if wifi2_channel < 30 and wifi2_hwmode == "11a" then
|
|
err("Changed to 5GHz Mesh LAN AP, please review channel selection")
|
|
end
|
|
if wifi2_channel > 30 and wifi2_hwmode == "11g" then
|
|
err("Changed to 2GHz Mesh LAN AP, please review channel slection")
|
|
end
|
|
end
|
|
if wifi3_enable == "1" then
|
|
if #wifi3_key > 64 then
|
|
err("WAN Wifi Client Password must be 64 characters or less")
|
|
end
|
|
if wifi3_key:match("'") or wifi3_ssid:match("'") then
|
|
err("The WAN Wifi Client password and ssid cannot contain a single quote character")
|
|
end
|
|
end
|
|
|
|
if phycount > 1 and wifi_enable == "1" and wifi2_enable == "1" and (wifi_channel < 36) == (wifi2_channel < 36) then
|
|
err("Mesh RF and LAN Access Point can not both use the same wireless card, review LAN AP settings")
|
|
end
|
|
if phycount > 1 and wifi_enable == "0" and wifi2_enable == "1" and wifi3_enable == "1" and wifi2_hwmode == wifi3_hwmode then
|
|
err("Some settings auto updated to avoid conflicts, please review and save one more time")
|
|
end
|
|
if wifi_enable == "1" and wifi2_enable == "1" and wifi3_enable == "1" then
|
|
err("Can not enable Mesh RF, LAN AP, and WAN Wifi Client with only 2 wireless cards, WAN Wifi Client turned off")
|
|
wifi2_enable = "0"
|
|
end
|
|
if phycount == 1 and wifi_enable == "1" and (wifi2_enable == "1" or wifi3_enable == "1") then
|
|
err("Can not enable Mesh RF along with LAN AP or WAN Wifi Client. Only Mesh RF enabled now, please review settings.")
|
|
wifi2_enable = "0"
|
|
wifi3_enable = "0"
|
|
end
|
|
|
|
if #errors == 0 then
|
|
parms.node = node
|
|
parms.tactical = tactical
|
|
|
|
if is_vtun_client() then
|
|
local vtun_name
|
|
cursor:foreach("vtun", "server",
|
|
function(section)
|
|
vtun_name = section.node:sub(1, #section.node - (#section.netip + 1) )
|
|
end
|
|
)
|
|
if vtun_name and parms.node and (vtun_name:upper() ~= parms.node:upper()) then
|
|
cursor:foreach("vtun", "server",
|
|
function(section)
|
|
for k,v in pairs(section) do
|
|
if k == ".name" then
|
|
local servnum = "@" .. v:gsub("%_","%[") .. "]"
|
|
local newname = parms.node .. "-" .. section.netip:gsub("%.", "%-")
|
|
cursor:set("vtun", servnum, "node", newname)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
)
|
|
cursor:commit("vtun")
|
|
write_all("/etc/config.mesh/vtun", read_all("/etc/config/vtun"))
|
|
end
|
|
end
|
|
|
|
if nixio.fs.stat("/etc/config/unconfigured") then
|
|
io.open("/tmp/unconfigured", "w"):close()
|
|
end
|
|
local function s2h(str)
|
|
local h = ""
|
|
for i = 1,#str
|
|
do
|
|
h = h .. string.format("%02x", string.byte(str, i))
|
|
end
|
|
return h
|
|
end
|
|
parms.wifi2_key = s2h(wifi2_key)
|
|
parms.wifi2_ssid = s2h(wifi2_ssid)
|
|
parms.wifi3_key = s2h(wifi3_key)
|
|
parms.wifi3_ssid = s2h(wifi3_ssid)
|
|
|
|
-- escape and limit description
|
|
parms.description_node = parms.description_node:sub(1,210):gsub('"',"""):gsub("'","'"):gsub("<","<"):gsub(">",">")
|
|
|
|
-- save the wifi interface
|
|
parms.wifi_intf = wifi_intf
|
|
|
|
-- save_setup
|
|
local f = io.open("/etc/config.mesh/_setup", "w")
|
|
if f then
|
|
for k, v in pairs(parms)
|
|
do
|
|
if k:match("^aprs_") or k:match("^dhcp_") or k:match("^dmz_") or k:match("^lan_") or k:match("^olsrd_") or k:match("^wan_") or k:match("^wifi_") or k:match("^wifi2_") or k:match("^wifi3_") or k:match("^dtdlink_") or k:match("^ntp_") or k:match("^time_") or k:match("^description_") or k:match("^compat_version") then
|
|
f:write(k .. " = " .. v .. "\n")
|
|
end
|
|
end
|
|
f:close()
|
|
end
|
|
|
|
-- commit new ntp_period value
|
|
local cm = uci.cursor("/etc/config.mesh")
|
|
if parms.ntp_period ~= cm:get("aredn", "@ntp[0]", "period") then
|
|
cm:set("aredn", "@ntp[0]", "period", parms.ntp_period)
|
|
cm:commit("aredn")
|
|
end
|
|
|
|
aredn.info.set_nvram("node", parms.node);
|
|
aredn.info.set_nvram("tactical", parms.tactical)
|
|
aredn.info.set_nvram("config", parms.config)
|
|
|
|
if not nixio.fs.stat("/tmp/web/save") then
|
|
nixio.fs.mkdir("/tmp/web/save")
|
|
end
|
|
os.execute("/usr/local/bin/node-setup > /tmp/web/save/node-setup.out 2>&1")
|
|
if nixio.fs.stat("/tmp/web/save/node-setup.out", "size") > 0 then
|
|
err(read_all("/tmp/web/save/node-setup.out"))
|
|
else
|
|
remove_all("/tmp/web/save")
|
|
-- set password
|
|
if passwd1 ~= "" then
|
|
local pw = passwd1:gsub("'", "\\'")
|
|
local f = io.popen("{ echo '" .. pw .. "'; sleep 1; echo '" .. pw .. "'; } | passwd")
|
|
f:read("*a")
|
|
f:close()
|
|
end
|
|
if nixio.fs.stat("/tmp/reboot-required") and not nixio.fs.stat("/tmp/reboot-required/reboot") and not nixio.fs.stat("/tmp/unconfigured") then
|
|
os.execute("/usr/local/bin/restart-services.sh > /dev/null 2>&1")
|
|
end
|
|
end
|
|
if nixio.fs.stat("/tmp/unconfigured") and #errors == 0 then
|
|
html.reboot()
|
|
end
|
|
end
|
|
end
|
|
|
|
remove_all("/tmp/web/save")
|
|
if parms.button_reboot then
|
|
html.reboot()
|
|
end
|
|
|
|
local desc = cursor:get("system", "@system[0]", "description")
|
|
if not desc then
|
|
desc = ""
|
|
end
|
|
local maptiles = cursor:get("aredn", "@map[0]", "maptiles")
|
|
if not maptiles then
|
|
maptiles = ""
|
|
end
|
|
local leafletcss = cursor:get("aredn", "@map[0]", "leafletcss")
|
|
if not leafletcss then
|
|
leafletcss = ""
|
|
end
|
|
local leafletjs = cursor:get("aredn", "@map[0]", "leafletjs")
|
|
if not leafletjs then
|
|
leafletjs = ""
|
|
end
|
|
|
|
-- generate page
|
|
|
|
http_header()
|
|
html.header(aredn.info.get_nvram("node") .. " setup", false)
|
|
|
|
html.print([[
|
|
<script>
|
|
|
|
function loadCSS(url, callback) {
|
|
var head = document.getElementsByTagName('head')[0];
|
|
var stylesheet = document.createElement('link');
|
|
stylesheet.rel = 'stylesheet';
|
|
stylesheet.type = 'text/css';
|
|
stylesheet.href = url;
|
|
stylesheet.onload = callback;
|
|
|
|
head.appendChild(stylesheet);
|
|
}
|
|
|
|
function loadScript(url, callback) {
|
|
var head = document.getElementsByTagName('head')[0];
|
|
var script = document.createElement('script');
|
|
script.type = 'text/javascript';
|
|
script.src = url;
|
|
script.onload = callback;
|
|
|
|
head.appendChild(script);
|
|
}
|
|
|
|
var map;
|
|
var marker;
|
|
var dotIcon;
|
|
|
|
var leafletLoad = function() {
|
|
map = L.map('map').setView([0.0, 0.0], 1);
|
|
dotIcon = L.icon({iconUrl: '/dot.png'});
|
|
]])
|
|
html.print("L.tileLayer('" .. maptiles .. "',")
|
|
html.print([[
|
|
{
|
|
maxZoom: 18,
|
|
attribution: 'Map data and images © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
|
|
'<a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>'
|
|
}).addTo(map);
|
|
]])
|
|
|
|
if tonumber(lat) and tonumber(lon) then
|
|
html.print("marker = new L.marker([" .. lat .. "," .. lon .. "],{draggable: true, icon: dotIcon});")
|
|
html.print("map.addLayer(marker);")
|
|
html.print("map.setView([" .. lat .. "," .. lon .. "],13);")
|
|
html.print("marker.on('drag', onMarkerDrag);")
|
|
else
|
|
html.print("map.on('click', onMapClick);")
|
|
end
|
|
|
|
html.print([[
|
|
}
|
|
|
|
function onMapClick(e) {
|
|
if (marker) {
|
|
marker.setLatLng(e.latlng.wrap());
|
|
}
|
|
else {
|
|
marker = new L.marker(e.latlng.wrap(),{draggable: true, icon: dotIcon});
|
|
map.addLayer(marker);
|
|
}
|
|
document.getElementsByName('latitude')[0].value=e.latlng.wrap().lat.toFixed(6).toString();
|
|
document.getElementsByName('longitude')[0].value=e.latlng.wrap().lng.toFixed(6).toString();
|
|
marker.on('drag', onMarkerDrag);
|
|
}
|
|
|
|
function onMarkerDrag(e) {
|
|
var m = e.target;
|
|
var p = m.getLatLng().wrap();
|
|
document.getElementsByName('latitude')[0].value=p.lat.toFixed(6).toString();
|
|
document.getElementsByName('longitude')[0].value=p.lng.toFixed(6).toString();
|
|
}
|
|
]])
|
|
|
|
if pingOK or (leafletcss:match("%.local%.mesh") and leafletjs:match("%.local%.mesh")) then
|
|
html.print("window.onload = function (event) { loadCSS('" .. leafletcss .. "',function () { loadScript('" .. leafletjs .."', leafletLoad); }); };")
|
|
end
|
|
|
|
html.print([[
|
|
function findLocation() {
|
|
navigator.geolocation.getCurrentPosition(foundLocation, noLocation);
|
|
}
|
|
|
|
function foundLocation(position) {
|
|
var jlat = position.coords.latitude;
|
|
var jlon = position.coords.longitude;
|
|
// update the fields
|
|
document.getElementsByName('latitude')[0].value=jlat.toFixed(6).toString();
|
|
document.getElementsByName('longitude')[0].value=jlon.toFixed(6).toString();
|
|
|
|
// try to update the map if Javascript libs have been loaded
|
|
if (typeof L != 'undefined') {
|
|
var latlng = L.latLng(jlat, jlon);
|
|
if (marker) {
|
|
marker.setLatLng(latlng);
|
|
}
|
|
else {
|
|
marker = new L.marker(latlng.wrap(),{draggable: true, icon: dotIcon});
|
|
map.addLayer(marker);
|
|
}
|
|
map.setView(latlng,13);
|
|
}
|
|
}
|
|
|
|
function noLocation() {
|
|
const req = new XMLHttpRequest();
|
|
req.addEventListener("load", function() {
|
|
try {
|
|
const json = JSON.parse(this.responseText);
|
|
foundLocation({ coords: { latitude: json.lat, longitude: json.lon }})
|
|
}
|
|
catch (_) {
|
|
}
|
|
});
|
|
req.open("GET", "http://ip-api.com/json");
|
|
req.send();
|
|
}
|
|
|
|
function updDist(x) {
|
|
var dvs= calcDistance(x);
|
|
var xcm=dvs['miles'];
|
|
var xc=dvs['meters'];
|
|
var xck=dvs['kilometers'];
|
|
|
|
var distBox = document.getElementById('dist');
|
|
var dist_meters=document.getElementsByName('wifi_distance')[0];
|
|
document.getElementsByName('wifi_distance_disp_miles')[0].value = xcm;
|
|
document.getElementsByName('wifi_distance_disp_km')[0].value = xck;
|
|
document.getElementsByName('wifi_distance_disp_meters')[0].value = xc;
|
|
dist_meters.value = xc;
|
|
|
|
// default of 0 means 'auto', so full range is always dist-norm
|
|
distBox.className = 'dist-norm';
|
|
}
|
|
|
|
function calcDistance(x) {
|
|
// x is in KILOMETERS
|
|
var dvs = new Object();
|
|
dvs['miles']=(x*0.621371192).toFixed(2);
|
|
dvs['meters']=Math.ceil(x*1000);
|
|
dvs['kilometers']=x;
|
|
return dvs;
|
|
}
|
|
|
|
function doSubmit() {
|
|
var desc_text = document.mainForm.description_node.value;
|
|
var singleLine = desc_text.replace(new RegExp( "\\n", "g" ), " ");
|
|
document.mainForm.description_node.value = singleLine;
|
|
return true;
|
|
}
|
|
|
|
function toggleMap(toggleButton) {
|
|
var mapdiv=document.getElementById('map');
|
|
if(toggleButton.value=='hide') {
|
|
// HIDE IT
|
|
mapdiv.style.display='none';
|
|
toggleButton.value='show';
|
|
toggleButton.innerHTML='Show Map';
|
|
} else {
|
|
// SHOW IT
|
|
mapdiv.style.display='block';
|
|
toggleButton.value='hide';
|
|
toggleButton.innerHTML='Hide Map';
|
|
}
|
|
// force the map to redraw
|
|
if(typeof map !== 'undefined') map.invalidateSize();
|
|
return false;
|
|
}
|
|
|
|
</script>
|
|
]])
|
|
|
|
html.print("</head>")
|
|
html.print("<body><center>")
|
|
|
|
html.alert_banner()
|
|
|
|
html.print("<form onSubmit='doSubmit();' name='mainForm' method=post action=/cgi-bin/setup enctype='multipart/form-data'>\n")
|
|
-- navbar
|
|
html.navbar_admin("setup")
|
|
html.print("<table width=790>")
|
|
-- control buttons
|
|
html.print([[<tr><td align=center>
|
|
<a href='/help.html#setup' target='_blank'>Help</a>
|
|
|
|
<input type=submit name=button_save value='Save Changes' title='Store these settings'>
|
|
<input type=submit name=button_reset value='Reset Values' title='Revert to the last saved settings'>
|
|
<input type=submit name=button_default value='Default Values' title='Set all values to their default'>
|
|
<input type=submit name=button_reboot value=Reboot style='font-weight:bold' title='Immediately reboot this node'>
|
|
</td></tr>
|
|
<tr><td> </td></tr>]])
|
|
|
|
if #output > 0 then
|
|
html.print("<tr><td align=center><table>")
|
|
html.print("<tr><td><ul style='padding-left:0'>")
|
|
for _,o in ipairs(output)
|
|
do
|
|
html.print("<li>" .. o .. "</li>")
|
|
end
|
|
html.print("</ul></td></tr></table>")
|
|
html.print("</td></tr>")
|
|
end
|
|
if #errors > 0 then
|
|
html.print("<tr><th>Configuration NOT saved!</th></tr>")
|
|
html.print("<tr><td align=center><table>")
|
|
html.print("<tr><td><ul style='padding-left:0'>")
|
|
for _,e in ipairs(errors)
|
|
do
|
|
html.print("<li>" .. e .. "</li>")
|
|
end
|
|
html.print("</ul></td></tr></table>")
|
|
html.print("</td></tr>")
|
|
elseif parms.button_save then
|
|
html.print("<tr><td align=center>")
|
|
html.print("<b>Configuration saved.</b><br><br>")
|
|
html.print("</td></tr>")
|
|
end
|
|
|
|
if #errors == 0 and nixio.fs.stat("/tmp/reboot-required") then
|
|
html.print("<tr><td align=center><h3>Reboot is required for changes to take effect</h3></td></tr>")
|
|
end
|
|
|
|
-- node name and type, password
|
|
|
|
html.print([[
|
|
<tr><td align=center>
|
|
<table cellpadding=5 border=0>
|
|
<tr>
|
|
<td>Node Name</td>
|
|
<td><input type=text name=nodetac value=']] .. nodetac .. [[' tabindex=1 size='50'></td>
|
|
<td align=right>Password</td>
|
|
<td><input class='password-input' type=password name=passwd1 value=']] .. passwd1 .. [[' size=8 tabindex=2><i class='password-toggle'></i></td>
|
|
]])
|
|
html.print([[
|
|
</tr>
|
|
<tr>
|
|
<td>Node Description (optional)</td>
|
|
<td><textarea rows='2' cols='60' wrap='soft' maxlength='210' id='node_description_entry' name='description_node' tabindex='4'>]] .. desc .. [[</textarea></td>
|
|
]])
|
|
hidden[#hidden + 1] = "<input type=hidden name=config value='mesh'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=compat_version value='" .. (compat_version or "") .. "'>"
|
|
html.print([[
|
|
<td>Verify Password</td>
|
|
<td><input class='password-input' type=password name=passwd2 value=']] .. passwd2 .. [[' size=8 tabindex=3><i class='password-toggle'></i></td>
|
|
</tr>
|
|
</table>
|
|
</td></tr>
|
|
<tr><td><br>
|
|
<table cellpadding=5 border=1 style='border-collapse:collapse;' width=100%><tr><td valign=top width=33%>
|
|
]])
|
|
|
|
-- reset wifi channel/bandwidth to default
|
|
if phycount > 0 and (nixio.fs.stat("/etc/config/unconfigured") or parms.button_reset) then
|
|
local defaultwifi = aredn.hardware.get_default_channel(wifi_intf)
|
|
wifi_channel = defaultwifi.channel
|
|
wifi_chanbw = tostring(defaultwifi.bandwidth)
|
|
end
|
|
|
|
-- mesh rf settings
|
|
html.print("<table width=100% style='border-collapse: collapse;'>")
|
|
if phycount == 1 then
|
|
html.print("<tr><th colspan=2>Mesh RF (" .. (M3model and "3GHz" or M9model and "900MHz" or wifi_channel < 36 and "2GHz" or "5GHz") .. ")</th></tr>")
|
|
else
|
|
html.print("<tr><th colspan=2>Mesh</th></tr>")
|
|
end
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_proto value='static'>"
|
|
|
|
-- add enable/disable
|
|
|
|
if phycount > 0 then
|
|
html.print("<tr><td>Enable</td><td><input type=checkbox name=wifi_enable value=1")
|
|
if wifi_enable == "1" then
|
|
html.print(" checked")
|
|
end
|
|
html.print("></td></tr>")
|
|
if phycount > 1 and wifi_enable == "1" then
|
|
local defaultwifi = aredn.hardware.get_default_channel(wifi_intf)
|
|
local alt_wifi = (not phy or phy == "phy0") and "wlan1" or "wlan0"
|
|
html.print("<tr><td>Band</td><td><select name=wifi_intf onChange='form.submit()'>")
|
|
if defaultwifi.channel == 149 then
|
|
html.print("<option value='" .. alt_wifi .. "'>2GHz</option>")
|
|
html.print("<option value='" .. wifi_intf .. "' selected>5GHz</option>")
|
|
else
|
|
html.print("<option value='" .. wifi_intf .. "' selected>2GHz</option>")
|
|
html.print("<option value='" .. alt_wifi .. "'>5GHz</option>")
|
|
end
|
|
html.print("</select></td></tr>")
|
|
end
|
|
end
|
|
html.print("<tr><td><nobr>IP Address</nobr></td><td><input type=text size=15 name=wifi_ip value='" .. wifi_ip .. "'></td></tr><tr><td>Netmask</td><td><input type=text size=15 name=wifi_mask value='" .. wifi_mask .. "'></td></tr>")
|
|
|
|
if wifi_enable == "1" then
|
|
html.print("<tr><td>SSID</td><td><input type=text size=15 name=wifi_ssid value='" .. wifi_ssid .. "'>-" .. wifi_chanbw .. "-v3</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_mode value='" .. wifi_mode .. "'>"
|
|
html.print("<tr><td>Channel</td><td><select name=wifi_channel>")
|
|
local rfchannels = aredn.hardware.get_rfchannels(wifi_intf)
|
|
table.sort(rfchannels, function(a, b) return a.number < b.number end)
|
|
for _, chan in ipairs(rfchannels)
|
|
do
|
|
html.print("<option value='" .. chan.number .. "' ".. (chan.number == tonumber(wifi_channel) and " selected" or "") .. ">" .. chan.label .. "</option>")
|
|
end
|
|
html.print("</select> <a href='/help.html#channel' target='_blank'><img src='/qmark.png'></a></td></tr>")
|
|
|
|
html.print("<tr><td>Channel Width</td><td><select name=wifi_chanbw>")
|
|
for _, width in ipairs(aredn.hardware.get_rfbandwidths(wifi_intf))
|
|
do
|
|
html.print("<option value='" .. width .. "'".. (wifi_chanbw == tostring(width) and " selected" or "") .. ">" .. width .. " MHz</option>")
|
|
end
|
|
html.print("</select></td></tr>")
|
|
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_country value='HX'>"
|
|
|
|
if lqm_mode then
|
|
html.print("<tr><th colspan=2 align=center><hr><small>Power & Link Quality</small></th></tr>")
|
|
else
|
|
html.print("<tr><th colspan=2 align=center><hr><small>Power & Distance</small></th></tr>")
|
|
end
|
|
html.print("<tr><td><nobr>Tx Power</nobr></td><td><select name=wifi_txpower>")
|
|
local txpoweroffset = aredn.hardware.wifi_poweroffset(wifi_intf)
|
|
for i = aredn.hardware.wifi_maxpower(wifi_intf, wifi_channel),1,-1
|
|
do
|
|
html.print("<option value='" .. i .. "'".. (i == tonumber(wifi_txpower) and " selected" or "") .. ">" .. (txpoweroffset + i) .. " dBm</option>")
|
|
end
|
|
html.print("</select> <a href='/help.html#power' target='_blank'><img src='/qmark.png'></a></td></tr>")
|
|
if lqm_mode then
|
|
local lqm_max_distance = tonumber(cursor:get("aredn", "@lqm[0]", "max_distance")) or 114750
|
|
local lqm_min_snr = cursor:get("aredn", "@lqm[0]", "min_snr") or "15"
|
|
local lqm_min_quality = cursor:get("aredn", "@lqm[0]", "min_quality") or "50"
|
|
html.print([[
|
|
<tr><td>Max Distance</td><td>
|
|
<script>
|
|
var dm = ]] .. lqm_max_distance .. [[;
|
|
switch (navigator.language.split("-")[1] || "unknown") {
|
|
case "US":
|
|
case "GB":
|
|
document.write("<input type=hidden name='lqm_distance_unit' value='mile'><input type=text size=4 name='lqm_max_distance' value='" + (dm / 1609.344).toFixed(1) + "' title='Maximum distance to a neighbor before it will be ignored'> miles")
|
|
break;
|
|
default:
|
|
document.write("<input type=hidden name='lqm_distance_unit' value='kilometer'><input type=text size=4 name='lqm_max_distance' value='" + (dm / 1000).toFixed(1) + "' title='Maximum distance to a neighbor before it will be blocked'> km")
|
|
break;
|
|
}
|
|
</script>
|
|
<a href='/help.html#linkqual' target='_blank'><img src='/qmark.png'></a>
|
|
</td></tr>
|
|
]])
|
|
html.print("<tr><td>Min SNR</td><td><input type=text size=4 name='lqm_min_snr' value='" .. lqm_min_snr .. "' title='Minimum SNR of neighbor before it will be accepted'> dB</td></tr>")
|
|
html.print("<tr><td>Min Quality</td><td><input type=text size=4 name='lqm_min_quality' value='" .. lqm_min_quality .. "' title='Minimum acceptable link quality'> %</td></tr>")
|
|
else
|
|
html.print("<tr id='dist' class='dist-norm'><td>Distance to<br/>FARTHEST Neighbor<br/><h3>'0' is auto</h3></td>")
|
|
|
|
local wifi_distance = math.floor(tonumber(wifi_distance))
|
|
local wifi_distance_disp_km = math.floor(wifi_distance / 1000)
|
|
local wifi_distance_disp_miles = string.format("%.2f", wifi_distance_disp_km * 0.621371192)
|
|
html.print("<td><input disabled size=6 type=text name='wifi_distance_disp_miles' value='" .. wifi_distance_disp_miles .. "' title='Distance to the farthest neighbor'> mi <a href='/help.html#distance' target='_blank'><img src='/qmark.png'></a><br />")
|
|
html.print("<input disabled size=6 type=text size=4 name='wifi_distance_disp_km' value='" .. wifi_distance_disp_km .. "' title='Distance to the farthest neighbor'> km<br />")
|
|
html.print("<input disabled size=6 type=text size=4 name='wifi_distance_disp_meters' value='" .. wifi_distance .."' title='Distance to the farthest neighbor'> m<br />")
|
|
html.print("<input id='distance_slider' type='range' min='0' max='150' step='1' value='" .. wifi_distance_disp_km .."' oninput='updDist(this.value)' onchange='updDist(this.value)' /><br />")
|
|
html.print("<input type='hidden' size='6' name='wifi_distance' value='" .. wifi_distance .. "' />")
|
|
html.print("</td></tr>")
|
|
end
|
|
html.print("<tr><td></td><td><input type=submit name=button_apply value=Apply title='Immediately use these active settings'></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_ssid value='" .. wifi_ssid .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_mode value='" .. wifi_mode .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_txpower value='" .. wifi_txpower .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_channel value='" .. wifi_channel .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_chanbw value='" .. wifi_chanbw .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_distance value='" .. wifi_distance .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi_country value='HX'>"
|
|
end
|
|
|
|
html.print("</table></td>")
|
|
|
|
-- lan settings
|
|
|
|
html.print([[
|
|
<td valign=top width=33%><table width=100%>
|
|
<tr><th colspan=2>LAN</th></tr>
|
|
<tr>
|
|
<td>LAN Mode</td>
|
|
<td><select name=dmz_mode onChange='form.submit()'">
|
|
]])
|
|
html.print("<option value='0'".. (dmz_mode == 0 and " selected" or "") .. ">NAT</option>")
|
|
html.print("<option value='2'".. (dmz_mode == 2 and " selected" or "") .. ">1 host Direct</option>")
|
|
html.print("<option value='3'".. (dmz_mode == 3 and " selected" or "") .. ">5 host Direct</option>")
|
|
html.print("<option value='4'".. (dmz_mode == 4 and " selected" or "") .. ">13 host Direct</option>")
|
|
html.print("<option value='5'".. (dmz_mode == 5 and " selected" or "") .. ">29 host Direct</option>")
|
|
html.print("</select> <a href='/help.html#lanmode' target='_blank'><img src='/qmark.png'></a></td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=lan_proto value='static'>"
|
|
|
|
if dmz_mode ~= 0 then
|
|
html.print("<tr><td><nobr>IP Address</nobr></td><td>" .. dmz_lan_ip .."</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_lan_ip value='" .. dmz_lan_ip .. "'>"
|
|
|
|
html.print("<tr><td>Netmask</td><td>" .. dmz_lan_mask .. "</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_lan_mask value='" ..dmz_lan_mask .. "'>"
|
|
|
|
html.print("<tr><td><nobr>DHCP Server</nobr></td><td><input type=checkbox name=lan_dhcp value=1" .. (lan_dhcp ~= "0" and " checked" or "") .. "></td></tr>")
|
|
html.print("<tr><td><nobr>DHCP Start</nobr></td><td>" .. dmz_dhcp_start .. "</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_dhcp_start value='" .. dmz_dhcp_start .. "'>"
|
|
|
|
html.print("<tr><td><nobr>DHCP End</nobr></td><td>" .. dmz_dhcp_end .. "</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_dhcp_end value='" .. dmz_dhcp_end .. "'>"
|
|
|
|
hidden[#hidden + 1] = "<input type=hidden name=lan_ip value='" .. lan_ip .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=lan_mask value='" .. lan_mask .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dhcp_start value='" .. dhcp_start .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dhcp_end value='" .. dhcp_end .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=lan_gw value='" .. lan_gw .."'>"
|
|
else
|
|
html.print("<tr><td><nobr>IP Address</nobr></td><td><input type=text size=15 name=lan_ip value='" .. lan_ip .. "'></td></tr>")
|
|
html.print("<tr><td>Netmask</td><td><input type=text size=15 name=lan_mask value='" .. lan_mask .."'></td></tr>")
|
|
if wan_proto == "disabled" then
|
|
html.print("<tr><td>Gateway</td><td><input type=text size=15 name=lan_gw value='" .. lan_gw .. "' title='leave blank if not needed'></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=lan_gw value='" .. lan_gw .. "'>"
|
|
end
|
|
html.print("<tr><td><nobr>DHCP Server</nobr></td><td><input type=checkbox name=lan_dhcp value=1" .. (lan_dhcp ~= "0" and " checked" or "") .. "></td></tr>")
|
|
html.print("<tr><td><nobr>DHCP Start</nobr></td><td><input type=text size=4 name=dhcp_start value='" .. dhcp_start .. "'></td></tr>")
|
|
html.print("<tr><td><nobr>DHCP End</nobr></td><td><input type=text size=4 name=dhcp_end value='" .. dhcp_end .. "'></td></tr>")
|
|
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_lan_ip value='" .. dmz_lan_ip .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_lan_mask value='" .. dmz_lan_mask .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_dhcp_start value='" .. dmz_dhcp_start .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dmz_dhcp_end value='" .. dmz_dhcp_end .. "'>"
|
|
end
|
|
|
|
local APokay = nixio.fs.stat("/usr/sbin/wpad")
|
|
if APokay and ((phycount > 1 and (wifi_enable ~= "1" or wifi3_enable ~= "1")) or (phycount == 1 and wifi_enable ~= "1" and wifi3_enable ~= "1") and not M39model) then
|
|
html.print("<tr><td colspan=2><hr></hr></td></tr>")
|
|
-- lan ap shows as an option
|
|
-- determine hardware options and set band ahd channels accordingly
|
|
if phycount == 1 then
|
|
chan = aredn.hardware.get_rfchannels("wlan0")
|
|
if chan[1].frequency < 3000 then
|
|
wifi2_hwmode = "11g"
|
|
if wifi2_channel > 14 then
|
|
wifi2_channel = 1
|
|
end
|
|
else
|
|
wifi2_hwmode = "11a"
|
|
if wifi2_channel < 36 then
|
|
wifi2_channel = 36
|
|
end
|
|
end
|
|
else
|
|
-- 2 band device
|
|
if wifi_enable == "1" then
|
|
local alt_wifi = (not phy or phy == "phy0") and "wlan1" or "wlan0"
|
|
chan = aredn.hardware.get_rfchannels(alt_wifi)
|
|
if #chan == 0 then
|
|
-- Hardware device can crash after switching - so at least make the page render until a reboot
|
|
chan[1] = { frequency = 2412, number = 1, label = nil }
|
|
end
|
|
if chan[1].frequency < 3000 then
|
|
wifi2_hwmode = "11g"
|
|
if wifi2_channel > 14 then
|
|
wifi2_channel = 1
|
|
end
|
|
else
|
|
wifi2_hwmode = "11a"
|
|
if wifi2_channel < 36 then
|
|
wifi2_channel = 36
|
|
end
|
|
end
|
|
else
|
|
if wifi2_enable ~= "1" and wifi3_enable == "1" and wifi3_hwmode == "11a" then
|
|
wifi2_hwmode = "11g"
|
|
end
|
|
if wifi2_enable ~= "1" and wifi3_enable == "1" and wifi3_hwmode == "11g" then
|
|
wifi2_hwmode = "11a"
|
|
end
|
|
local chan0 = aredn.hardware.get_rfchannels("wlan0")
|
|
local chan1 = aredn.hardware.get_rfchannels("wlan1")
|
|
if wifi2_hwmode == "11a" then
|
|
if #chan0 >= 1 and chan0[1].frequency > 3000 then
|
|
chan = chan0
|
|
else
|
|
chan = chan1
|
|
end
|
|
if wifi2_channel < 36 then
|
|
wifi2_channel = 36
|
|
end
|
|
else
|
|
if #chan0 >= 1 and chan0[1].frequency < 3000 then
|
|
chan = chan0
|
|
else
|
|
chan = chan1
|
|
end
|
|
if wifi2_channel > 14 then
|
|
wifi2_channel = 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
html.print("<tr><th colspan=2><small>LAN Access Point</small></th></tr><tr><td>Enable</td><td><input type=checkbox name=wifi2_enable value=1" .. (wifi2_enable == "1" and " checked" or "") .. "> <a href='/help.html#lanap' target='_blank'><img src='/qmark.png'></a></td></tr>")
|
|
if phycount > 1 then
|
|
if wifi_enable ~= "1" then
|
|
html.print("<tr><td>AP band</td><td><select name=wifi2_hwmode>")
|
|
html.print("<option value='11g'".. (wifi2_hwmode == "11g" and " selected" or "") .. ">2GHz</option>")
|
|
html.print("<option value='11a'".. (wifi2_hwmode == "11a" and " selected" or "") .. ">5GHz</option>")
|
|
html.print("</select></td></tr>")
|
|
else
|
|
html.print("<tr><td>AP band</td><td>" .. (wifi2_hwmode == "11g" and "2GHz" or "5GHz") .. "</td></tr>")
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_hwmode value='" .. wifi2_hwmode .."'>"
|
|
end
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_hwmode value='" .. wifi2_hwmode .."'>"
|
|
end
|
|
html.print("<tr><td>SSID</td><td><input type=text size=15 name=wifi2_ssid value='" .. wifi2_ssid .."'></td></tr>")
|
|
html.print("<tr><td>Channel</td><td><select name=wifi2_channel>")
|
|
if chan[1].frequency > 3000 then
|
|
html.print("<optgroup label='Standard Channels'>")
|
|
for _, ch in ipairs(chan)
|
|
do
|
|
if ch.number < 163 or ch.number == 165 then
|
|
html.print("<option value='" .. ch.number .. "'" .. (wifi2_channel == ch.number and " selected" or "") .. ">" .. ch.number .. "</option>")
|
|
end
|
|
end
|
|
html.print("</optgroup>")
|
|
html.print("<optgroup label='Extended Channels'>")
|
|
for _, ch in ipairs(chan)
|
|
do
|
|
if ch.number == 163 or ch.number > 165 then
|
|
html.print("<option value='" .. ch.number .. "'" .. (wifi2_channel == ch.number and " selected" or "") .. ">" .. ch.number .. "</option>")
|
|
end
|
|
end
|
|
html.print("</optgroup>")
|
|
else
|
|
for _, ch in ipairs(chan)
|
|
do
|
|
html.print("<option value='" ..ch.number .. "'" .. (wifi2_channel == ch.number and " selected" or "") .. ">" .. ch.number .. "</option>")
|
|
end
|
|
end
|
|
html.print("</select></td></tr>")
|
|
html.print("<tr><td>Encryption</td><td><select name=wifi2_encryption>")
|
|
html.print("<option value='psk2'".. (wifi2_encryption == "psk2" and " selected" or "") .. ">WPA2 PSK</option>")
|
|
html.print("<option value='psk'".. (wifi2_encryption == "psk" and " selected" or "") .. ">WPA PSK</option>")
|
|
html.print("</select></td></tr><tr><td>Password</td><td><input class='password-input' type=password size=15 name=wifi2_key value='" .. wifi2_key .. "'><i class='password-toggle'></i></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_enable value='" .. (wifi2_enable or "") .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_ssid value='" .. (wifi2_ssid or "") .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_key value='" .. (wifi2_key or "") .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_channel value='" .. (wifi2_channel or "") .."'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_encryption value='" .. (wifi2_encryption or "") .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi2_hwmode value='" .. (wifi2_hwmode or "") .."'>"
|
|
end
|
|
|
|
html.print("</table></td>")
|
|
|
|
-- wan settings
|
|
html.print("<td valign=top width=33%><table width=100%><tr><th colspan=2>WAN</th></tr><tr><td width=50%>Protocol</td><td><select name=wan_proto onChange='form.submit()'>")
|
|
html.print("<option value='static'".. (wan_proto == "static" and " selected" or "") .. ">Static</option>")
|
|
html.print("<option value='dhcp'".. (wan_proto == "dhcp" and " selected" or "") .. ">DHCP</option>")
|
|
html.print("<option value='disabled'".. (wan_proto == "disabled" and " selected" or "") .. ">disabled</option>")
|
|
html.print("</select></td></tr>")
|
|
|
|
if wan_proto == "static" then
|
|
html.print("<tr><td><nobr>IP Address</nobr></td>")
|
|
html.print("<td><input type=text size=15 name=wan_ip value='" .. wan_ip .."'></td></tr>")
|
|
html.print("<tr><td>Netmask</td>")
|
|
html.print("<td><input type=text size=15 name=wan_mask value='" .. wan_mask .. "'></td></tr>")
|
|
html.print("<tr><td>Gateway</td>")
|
|
html.print("<td><input type=text size=15 name=wan_gw value='" .. wan_gw .. "'></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wan_ip value='" .. wan_ip .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wan_mask value='" .. wan_mask .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wan_gw value='" .. wan_gw .."'>"
|
|
end
|
|
|
|
html.print("<tr><td><nobr>DNS 1</nobr></td><td><input type=text size=15 name=wan_dns1 value='" .. wan_dns1 .. "'></td></tr>")
|
|
html.print("<tr><td><nobr>DNS 2</nobr></td><td><input type=text size=15 name=wan_dns2 value='" .. wan_dns2 .. "'></td></tr>")
|
|
|
|
-- wan wifi client
|
|
if APokay and ((phycount > 1 and (wifi_enable ~= "1" or wifi2_enable ~= "1")) or (phycount == 1 and wifi_enable ~= "1" and wifi2_enable ~= "1")) and not M39model then
|
|
-- wifi client shows as an option
|
|
-- determine hardware options and set band accordingly
|
|
html.print("<tr><th colspan=2><hr></td></tr>")
|
|
|
|
if phycount == 1 then
|
|
local rc3 = os.execute("iw phy phy0 info | grep -q '5180 MHz' > /dev/null")
|
|
if rc3 ~= 0 then
|
|
wifi3_hwmode = "11g"
|
|
else
|
|
wifi3_hwmode = "11a"
|
|
end
|
|
else
|
|
-- 2 band
|
|
if wifi_enable == "1" then
|
|
local defaultwifi = aredn.hardware.get_default_channel(wifi_intf)
|
|
if defaultwifi.channel == 149 then
|
|
wifi3_hwmode = "11g"
|
|
else
|
|
wifi3_hwmode = "11a"
|
|
end
|
|
else
|
|
if wifi2_hwmode == "11g" and wifi2_enable == "1" then
|
|
wifi3_hwmode = "11a"
|
|
end
|
|
if wifi2_hwmode == "11a" and wifi2_enable == "1" then
|
|
wifi3_hwmode = "11g"
|
|
end
|
|
end
|
|
end
|
|
|
|
local connected = "/notdone.png"
|
|
local cmessage = "Not connected."
|
|
local wanintf = aredn.hardware.get_iface_name("wan")
|
|
local essid = capture_and_match("iwinfo " .. wanintf .. " info", 'ESSID: "(.+)"')
|
|
if essid == wifi3_ssid then
|
|
if pingOK then
|
|
connected = "/donedone.png"
|
|
cmessage = "Connected to Wifi and Internet."
|
|
else
|
|
connected = "/done.png"
|
|
cmessage = "Connected to Wifi. Not connected to Internet."
|
|
end
|
|
end
|
|
|
|
html.print("<tr><th colspan=2><small>WAN Wifi Client <img style='vertical-align:text-bottom' src='" .. connected .. "' title='" .. cmessage .. "'></small></th></tr>")
|
|
html.print("<tr><td>Enable</td><td><input type=checkbox name=wifi3_enable value=1" .. (wifi3_enable == "1" and " checked" or "") .. "> <a href='/help.html#wanclient' target='_blank'><img src='/qmark.png'></a></td></tr>")
|
|
|
|
if wifi_enable ~= "1" and wifi2_enable ~= "1" and phycount > 1 then
|
|
html.print("<tr><td>WAN Wifi Client band</td><td><select name=wifi3_hwmode>")
|
|
html.print("<option value='11g'".. (wifi3_hwmode == "11g" and " selected" or "") .. ">2GHz</option>")
|
|
html.print("<option value='11a'".. (wifi3_hwmode == "11a" and " selected" or "") .. ">5GHz</option>")
|
|
html.print("</select></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi3_hwmode value='" .. wifi3_hwmode .. "'>"
|
|
end
|
|
|
|
html.print("<tr><td>SSID</td><td><input type=text name=wifi3_ssid size=15 value='" .. wifi3_ssid .."'></select></td></tr>")
|
|
html.print("<tr><td>Password</td><td><input class='password-input' type=password size=15 name=wifi3_key value='" .. wifi3_key .. "'><i class='password-toggle'></i></td></tr>")
|
|
else
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi3_enable value='" .. wifi3_enable .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi3_ssid value='" .. wifi3_ssid .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi3_key value='" .. wifi3_key .. "'>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=wifi3_hwmode value='" .. wifi3_hwmode .. "'>"
|
|
end
|
|
-- end wan wifi client
|
|
|
|
html.print("</table></td></tr></table></td></tr></table><br></td></tr>")
|
|
|
|
-- Location and antenna settings
|
|
html.print("<tr><td align=center>")
|
|
html.print("<table border=0 style='min-width:790px'>")
|
|
html.print("<tr><th colspan=4>Location Settings</th></tr>")
|
|
html.print("<tr><td colspan=4><hr /></td></tr>")
|
|
html.print("<tr><td align=left>Latitude</td><td><input type=text name=latitude size=10 value='" .. lat .."' title='Latitude value (in decimal) (ie. 30.312354)' /></td>")
|
|
html.print("<td align='left' colspan='2'>")
|
|
local locdisabled = pingOK and "" or "disabled"
|
|
html.print("<button " .. locdisabled .. " type='button' id='findlocation' value='findloc' onClick='findLocation();'>Find Me!</button> ")
|
|
html.print("<input type=submit name='button_updatelocation' value='Apply Location Settings' title='Immediately use these location settings'>")
|
|
html.print(" <button " .. locdisabled .. " type='button' id='hideshowmap' value='show' onClick='toggleMap(this);'>Show Map</button> ")
|
|
html.print("<input " .. locdisabled .. " type='submit' name='button_uploaddata' value='Upload data to AREDN Servers' /> ")
|
|
|
|
html.print("</td><tr><td align=left>Longitude</td><td><input type=text name=longitude size=10 value='" .. lon .. "' title='Longitude value (in decimal) (ie. -95.334454)' /></td><td align=left>Grid Square <input type=text name=gridsquare maxlength=6 size=6 value='" .. gridsquare .. "' title='Gridsquare value (ie. AB12cd)' /></td></tr>")
|
|
html.print("<tr><td colspan=4><div id='map' style='height: 200px; display: none;'></div></td></tr>")
|
|
|
|
if wifi_enable == "1" then
|
|
html.print("<tr><th colspan=4 style='padding-top:20px'>Antenna Settings (optional)</th></tr>")
|
|
html.print("<tr><td colspan=4><hr /></td></tr>")
|
|
local ants = aredn.hardware.get_antennas(wifi_intf)
|
|
local antsx = aredn.hardware.get_antennas_aux(wifi_intf)
|
|
local changeh = true
|
|
local changea = true
|
|
if ants and #ants == 1 then
|
|
changea = false
|
|
antenna = ants[1].description
|
|
if ants[1].beamwidth == 360 then
|
|
changeh = false
|
|
end
|
|
end
|
|
if not changeh then
|
|
html.print("<tr><td align=left>Azimuth</td><td>-</td>")
|
|
else
|
|
html.print("<tr><td align=left>Azimuth</td><td><input type=text name=azimuth size=10 value='" .. azimuth .. "' title='azimuth (degrees)' /> °</td>")
|
|
end
|
|
if not changea then
|
|
html.print("<td align=left colspan=2>Antenna <input disabled value='" .. antenna .. "'></td>")
|
|
elseif ants then
|
|
html.print("<td align=left colspan=2>Antenna <select name=antenna>")
|
|
for _, ant in ipairs(ants)
|
|
do
|
|
html.print("<option value='" .. ant.model .. "'" .. (ant.model == antenna and " selected" or "") .. ">" .. ant.description .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
end
|
|
if not changeh then
|
|
html.print("</tr><tr><td align=left>Elevation</td><td>-</td>")
|
|
else
|
|
html.print("</tr><tr><td align=left>Elevation</td><td><input type=text name=elevation size=10 value='" .. elevation .. "' title='elevation above ground level (degrees)' /> °</td>")
|
|
end
|
|
if antsx then
|
|
html.print("<td align=left colspan=2>Aux Antenna <select name=antenna_aux>")
|
|
for _, ant in ipairs(antsx)
|
|
do
|
|
html.print("<option value='" .. ant.model .. "'" .. (ant.model == antenna_aux and " selected" or "") .. ">" .. ant.description .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
end
|
|
html.print("</tr><tr><td align=left>Height</td><td><input type=text name=height size=10 value='" .. height .. "' title='height above ground level (meters)' /> m</tr>")
|
|
end
|
|
html.print("<tr><td colspan=4><hr /></td></tr>")
|
|
html.print("<tr><td colspan=2>Timezone <select name=time_zone_name tabindex=10>")
|
|
for _,zone in ipairs(tz_db_names)
|
|
do
|
|
html.print("<option value='" .. zone.name .. "'".. (zone.name == time_zone_name and " selected" or "") .. ">" .. zone.name .. "</option>")
|
|
end
|
|
html.print("</select></td>")
|
|
html.print("<td align=center>NTP Server <input type=text name=ntp_server size=20 value='" .. ntp_server .. "'></td>")
|
|
if ntp_period == "daily" or not ntp_period then
|
|
html.print("<td>NTP Updates <select name=ntp_period><option value='daily' selected>daily</option><option value='hourly'>hourly</option></select></td></table></td></tr></table>")
|
|
elseif ntp_period == "hourly" then
|
|
html.print("<td>NTP Updates <select name=ntp_period><option value='daily'>daily</option><option value='hourly' selected>hourly</option></select></td></table></td></tr></table>")
|
|
end
|
|
|
|
hidden[#hidden + 1] = "<input type=hidden name=reload value=1>"
|
|
hidden[#hidden + 1] = "<input type=hidden name=dtdlink_ip value='" .. dtdlink_ip .. "'>"
|
|
if wan_intf then
|
|
hidden[#hidden + 1] = "<input type=hidden name=wan_intf value='" .. wan_intf .. "'>"
|
|
end
|
|
|
|
for _,hid in ipairs(hidden)
|
|
do
|
|
html.print(hid)
|
|
end
|
|
|
|
html.print("</form></center>")
|
|
html.print([[<style>
|
|
.password-input {
|
|
padding-right: 21px;
|
|
}
|
|
.password-toggle {
|
|
display: inline-block;
|
|
width: 21px;
|
|
height: 15px;
|
|
margin-left: -24px;
|
|
cursor: pointer;
|
|
background-image: url(/viz.png);
|
|
background-size: contain;
|
|
vertical-align: middle;
|
|
}
|
|
</style>
|
|
<script>
|
|
function togglePassword(e) {
|
|
var target = e.target.previousSibling;
|
|
if (target.type === "password") {
|
|
target.type = "text";
|
|
}
|
|
else {
|
|
target.type = "password";
|
|
}
|
|
}
|
|
var fields = document.querySelectorAll(".password-toggle");
|
|
for (var i = 0; i < fields.length; i++) {
|
|
fields[i].addEventListener("click", togglePassword);
|
|
}
|
|
</script>]])
|
|
html.footer()
|
|
html.print("</body></html>")
|
|
http_footer()
|