diff options
31 files changed, 1050 insertions, 622 deletions
diff --git a/applications/luci-app-ddns/luasrc/controller/ddns.lua b/applications/luci-app-ddns/luasrc/controller/ddns.lua index 4fbd3c3fcf..29598ea8ad 100644 --- a/applications/luci-app-ddns/luasrc/controller/ddns.lua +++ b/applications/luci-app-ddns/luasrc/controller/ddns.lua @@ -10,28 +10,43 @@ local NX = require "nixio" local NXFS = require "nixio.fs" local DISP = require "luci.dispatcher" local HTTP = require "luci.http" -local UCI = require "luci.model.uci" +local I18N = require "luci.i18n" -- not globally avalible here +local IPKG = require "luci.model.ipkg" local SYS = require "luci.sys" -local DDNS = require "luci.tools.ddns" -- ddns multiused functions +local UCI = require "luci.model.uci" local UTIL = require "luci.util" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions -DDNS_MIN = "2.4.2-1" -- minimum version of service required +local srv_name = "ddns-scripts" +local srv_ver_min = "2.5.0" -- minimum version of service required +local srv_ver_cmd = [[/usr/lib/ddns/dynamic_dns_updater.sh --version | awk {'print $2'}]] +local app_name = "luci-app-ddns" +local app_title = "Dynamic DNS" +local app_version = "2.3.0-1" function index() local nxfs = require "nixio.fs" -- global definitions not available local sys = require "luci.sys" -- in function index() local ddns = require "luci.tools.ddns" -- ddns multiused functions - local verinst = ddns.ipkg_ver_installed("ddns-scripts") - local verok = ddns.ipkg_ver_compare(verinst, ">=", "2.0.0-0") - -- do NOT start it not ddns-scripts version 2.x - if not verok then - return - end + local muci = require "luci.model.uci" + -- no config create an empty one if not nxfs.access("/etc/config/ddns") then nxfs.writefile("/etc/config/ddns", "") end + -- preset new option "lookup_host" if not already defined + local uci = muci.cursor() + local commit = false + uci:foreach("ddns", "service", function (s) + if not s["lookup_host"] and s["domain"] then + uci:set("ddns", s[".name"], "lookup_host", s["domain"]) + commit = true + end + end) + if commit then uci:commit("ddns") end + uci:unload("ddns") + entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59) entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints", @@ -42,7 +57,60 @@ function index() entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true end --- function to read all sections status and return data array +-- Application specific information functions +function app_description() + return I18N.translate("Dynamic DNS allows that your router can be reached with " .. + "a fixed hostname while having a dynamically changing IP address.") + .. [[<br />]] + .. I18N.translate("OpenWrt Wiki") .. ": " + .. [[<a href="http://wiki.openwrt.org/doc/howto/ddns.client" target="_blank">]] + .. I18N.translate("DDNS Client Documentation") .. [[</a>]] + .. " --- " + .. [[<a href="http://wiki.openwrt.org/doc/uci/ddns" target="_blank">]] + .. I18N.translate("DDNS Client Configuration") .. [[</a>]] +end +function app_title_back() + return [[<a href="]] + .. DISP.build_url("admin", "services", "ddns") + .. [[">]] + .. I18N.translate(app_title) + .. [[</a>]] +end + +-- Standardized application/service functions +function app_title_main() + return [[<a href="javascript:alert(']] + .. I18N.translate("Version Information") + .. [[\n\n]] .. app_name + .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] .. app_version + .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("required") .. [[:]] + .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] + .. srv_ver_min .. [[ ]] .. I18N.translate("or higher") + .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("installed") .. [[:]] + .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] + .. (service_version() or I18N.translate("NOT installed")) + .. [[\n\n]] + .. [[')">]] + .. I18N.translate(app_title) + .. [[</a>]] +end +function service_version() + local ver = nil + IPKG.list_installed(srv_name, function(n, ver, d) + -- nothing to do + end + ) + if not ver then + ver = UTIL.exec(srv_ver_cmd) + if #ver == 0 then ver = nil end + end + return ver +end +function service_ok() + return IPKG.compare_versions((service_version() or "0"), ">=", srv_ver_min) +end + +-- internal function to read all sections status and return data array local function _get_status() local uci = UCI.cursor() local service = SYS.init.enabled("ddns") and 1 or 0 @@ -118,12 +186,12 @@ local function _get_status() end -- try to get registered IP - local domain = s["domain"] or "_nodomain_" + local lookup_host = s["lookup_host"] or "_nolookup_" local dnsserver = s["dns_server"] or "" local force_ipversion = tonumber(s["force_ipversion"] or 0) local force_dnstcp = tonumber(s["force_dnstcp"] or 0) local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]] - command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 .. + command = command .. [[ get_registered_ip ]] .. lookup_host .. [[ ]] .. use_ipv6 .. [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver local reg_ip = SYS.exec(command) if reg_ip == "" then @@ -135,7 +203,7 @@ local function _get_status() section = section, enabled = enabled, iface = iface, - domain = domain, + lookup = lookup_host, reg_ip = reg_ip, pid = pid, datelast = datelast, @@ -235,3 +303,4 @@ function status() HTTP.prepare_content("application/json") HTTP.write_json(data) end + diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua index e655ce5219..88429fd12c 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua @@ -8,13 +8,15 @@ local NX = require "nixio" local NXFS = require "nixio.fs" local SYS = require "luci.sys" local UTIL = require "luci.util" +local HTTP = require "luci.http" local DISP = require "luci.dispatcher" local WADM = require "luci.tools.webadmin" local DTYP = require "luci.cbi.datatypes" +local CTRL = require "luci.controller.ddns" -- this application's controller local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- takeover arguments -- ####################################################### -local section = arg[1] +local section = arg[1] -- check supported options -- ################################################## -- saved to local vars here because doing multiple os calls slow down the system @@ -31,15 +33,15 @@ local bold_on = "<strong>" local bold_off = "</strong>" -- error text constants -- ##################################################### -err_ipv6_plain = translate("IPv6 not supported") .. " - " .. +local err_ipv6_plain = translate("IPv6 not supported") .. " - " .. translate("please select 'IPv4' address version") -err_ipv6_basic = bold_on .. +local err_ipv6_basic = bold_on .. font_red .. translate("IPv6 not supported") .. font_off .. "<br />" .. translate("please select 'IPv4' address version") .. bold_off -err_ipv6_other = bold_on .. +local err_ipv6_other = bold_on .. font_red .. translate("IPv6 not supported") .. font_off .. @@ -52,16 +54,46 @@ err_ipv6_other = bold_on .. [[</a>]] .. bold_off -function err_tab_basic(self) +local function err_tab_basic(self) return translate("Basic Settings") .. " - " .. self.title .. ": " end -function err_tab_adv(self) +local function err_tab_adv(self) return translate("Advanced Settings") .. " - " .. self.title .. ": " end -function err_tab_timer(self) +local function err_tab_timer(self) return translate("Timer Settings") .. " - " .. self.title .. ": " end +-- read services/services_ipv6 files -- ######################################## +local services4 = { } -- IPv4 -- +local fd4 = io.open("/usr/lib/ddns/services", "r") +if fd4 then + local ln, s, t + repeat + ln = fd4:read("*l") + s = ln and ln:match('^%s*".*') -- only handle lines beginning with " + s = s and s:gsub('"','') -- remove " + t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces + if t then services4[t[1]]=t[2] end + until not ln + fd4:close() +end + +local services6 = { } -- IPv6 -- +local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r") +if fd6 then + local ln, s, t + repeat + ln = fd6:read("*l") + s = ln and ln:match('^%s*".*') -- only handle lines beginning with " + s = s and s:gsub('"','') -- remove " + t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces + if t then services6[t[1]]=t[2] end + until not ln + fd6:close() +end + +-- multi-used functions -- #################################################### -- function to verify settings around ip_source -- will use dynamic_dns_lucihelper to check if -- local IP can be read @@ -105,17 +137,94 @@ local function _verify_ip_source() end end --- cbi-map definition -- ####################################################### -m = Map("ddns") +-- function to check if option is used inside url or script +-- return -1 on error, 0 NOT required, 1 required +local function _option_used(option, urlscript) + local surl -- search string for url + local ssh -- search string for script + local required -- option used inside url or script -m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] .. - translate("Dynamic DNS") .. [[</a>]] + if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain' + elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username' + elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password' + elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc' + elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt' + else + error("undefined option") + return -1 -- return on error + end -m.description = translate("Dynamic DNS allows that your router can be reached with " .. - "a fixed hostname while having a dynamically changing " .. - "IP address.") + local required = false + -- handle url + if urlscript:find('http') then + required = ( urlscript:find(surl) ) + -- handle script + else + if not urlscript:find("/") then + -- might be inside ddns-scripts directory + urlscript = "/usr/lib/ddns/" .. urlscript + end + -- problem with script exit here + if not NXFS.access(urlscript) then return -1 end + + local f = io.input(urlscript) + -- still problem with script exit here + if not f then return -1 end + for l in f:lines() do + repeat + if l:find('^#') then break end -- continue on comment lines + required = ( l:find(surl) or l:find(ssh) ) + until true + if required then break end + end + f:close() + end + return (required and 1 or 0) +end -m.redirect = DISP.build_url("admin", "services", "ddns") +-- function to verify if option is valid +local function _option_validate(self, value) + -- section is globally defined here be calling agrument (see above) + local fusev6 = usev6:formvalue(section) + local fsvc4 = svc4:formvalue(section) + local fsvc6 = svc6:formvalue(section) + local urlsh, used + + -- IP-Version dependent custom service selected + if (fusev6 == "0" and fsvc4 == "-") or + (fusev6 == "1" and fsvc6 == "-") then + -- read custom url + urlsh = uurl:formvalue(section) + -- no url then read custom script + if not urlsh or (#urlsh == 0) then + urlsh = ush:formvalue(section) + end + -- IPv4 read from services4 table + elseif (fusev6 == "0") then + urlsh = services4[fsvc4] + -- IPv6 read from services6 table + else + urlsh = services6[fsvc6] + end + -- problem with url or script exit here + -- error handled somewhere else + if not urlsh or (#urlsh == 0) then return "" end + + used = _option_used(self.option, urlsh) + -- on error or not used return empty sting + if used < 1 then return "" end + -- needed but no data then return error + if not value or (#value == 0) then + return nil, err_tab_basic(self) .. translate("missing / required") + end + return value +end + +-- cbi-map definition -- ####################################################### +local m = Map("ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") m.on_after_commit = function(self) if self.changed then -- changes ? @@ -126,19 +235,42 @@ m.on_after_commit = function(self) end end +-- provider switch was requested, save and reload page +if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1] + local fsvc + local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section) + if fusev6 == "1" then + fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section) + else + fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section) + end + + if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed + m:set(section, "use_ipv6", fusev6) -- save it + end + + if fsvc ~= "-" then -- NOT "custom" + m:set(section, "service_name", fsvc) -- save it + else -- else + m:del(section, "service_name") -- delete it + end + m.uci:save(m.config) + + -- reload page + HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) ) + return +end + -- read application settings -- ################################################ -- date format; if not set use ISO format -date_format = m.uci:get(m.config, "global", "date_format") or "%F %R" +local date_format = m.uci:get(m.config, "global", "date_format") or "%F %R" -- log directory -log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns" +local log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns" -- cbi-section definition -- ################################################### -ns = m:section( NamedSection, section, "service", +local ns = m:section( NamedSection, section, "service", translate("Details for") .. ([[: <strong>%s</strong>]] % section), - translate("Configure here the details for selected Dynamic DNS service.") - .. [[<br /><a href="http://wiki.openwrt.org/doc/uci/ddns#version_1x" target="_blank">]] - .. translate("For detailed information about parameter settings look here.") - .. [[</a>]] ) + translate("Configure here the details for selected Dynamic DNS service.") ) ns.instance = section -- arg [1] ns:tab("basic", translate("Basic Settings"), nil ) ns:tab("advanced", translate("Advanced Settings"), nil ) @@ -146,17 +278,33 @@ ns:tab("timer", translate("Timer Settings"), nil ) ns:tab("logview", translate("Log File Viewer"), nil ) -- TAB: Basic ##################################################################################### --- enabled -- ################################################################# +-- enabled -- ################################################################# en = ns:taboption("basic", Flag, "enabled", translate("Enabled"), translate("If this service section is disabled it could not be started." .. "<br />" .. "Neither from LuCI interface nor from console") ) en.orientation = "horizontal" -function en.parse(self, section) - DDNS.flag_parse(self, section) + +-- IPv4/IPv6 - lookup_host -- ################################################# +luh = ns:taboption("basic", Value, "lookup_host", + translate("Lookup Hostname"), + translate("Hostname/FQDN to validate, if IP update happen or necessary") ) +luh.rmempty = false +luh.placeholder = "myhost.example.com" +function luh.validate(self, value) + if not value + or not (#value > 0) + or not DTYP.hostname(value) then + return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'" + else + return UTIL.trim(value) + end +end +function luh.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end --- use_ipv6 (NEW) -- ########################################################## +-- use_ipv6 -- ################################################################ usev6 = ns:taboption("basic", ListValue, "use_ipv6", translate("IP address version"), translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") ) @@ -179,36 +327,15 @@ function usev6.validate(self, value) end return nil, err_tab_basic(self) .. err_ipv6_plain end -function usev6.write(self, section, value) - if value == "0" then -- force rmempty - return self.map:del(section, self.option) - else - return self.map:set(section, self.option, value) - end +function usev6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end --- IPv4 - service_name -- ###################################################### +-- IPv4 - service_name -- ##################################################### svc4 = ns:taboption("basic", ListValue, "ipv4_service_name", translate("DDNS Service provider") .. " [IPv4]" ) svc4.default = "-" svc4:depends("use_ipv6", "0") -- only show on IPv4 - -local services4 = { } -local fd4 = io.open("/usr/lib/ddns/services", "r") - -if fd4 then - local ln - repeat - ln = fd4:read("*l") - local s = ln and ln:match('^%s*"([^"]+)"') - if s then services4[#services4+1] = s end - until not ln - fd4:close() -end - -for _, v in UTIL.vspairs(services4) do svc4:value(v) end -svc4:value("-", translate("-- custom --") ) - function svc4.cfgvalue(self, section) local v = DDNS.read_value(self, section, "service_name") if not v or #v == 0 then @@ -229,14 +356,18 @@ function svc4.write(self, section, value) self.map:del(section, self.option) -- to be shure if value ~= "-" then -- and write "service_name self.map:del(section, "update_url") -- delete update_url + self.map:del(section, "update_script") -- delete update_script return self.map:set(section, "service_name", value) else return self.map:del(section, "service_name") end end end +function svc4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv6 - service_name -- ###################################################### +-- IPv6 - service_name -- ##################################################### svc6 = ns:taboption("basic", ListValue, "ipv6_service_name", translate("DDNS Service provider") .. " [IPv6]" ) svc6.default = "-" @@ -244,23 +375,6 @@ svc6:depends("use_ipv6", "1") -- only show on IPv6 if not has_ipv6 then svc6.description = err_ipv6_basic end - -local services6 = { } -local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r") - -if fd6 then - local ln - repeat - ln = fd6:read("*l") - local s = ln and ln:match('^%s*"([^"]+)"') - if s then services6[#services6+1] = s end - until not ln - fd6:close() -end - -for _, v in UTIL.vspairs(services6) do svc6:value(v) end -svc6:value("-", translate("-- custom --") ) - function svc6.cfgvalue(self, section) local v = DDNS.read_value(self, section, "service_name") if not v or #v == 0 then @@ -282,33 +396,42 @@ function svc6.write(self, section, value) self.map:del(section, self.option) -- delete "ipv6_service_name" helper if value ~= "-" then -- and write "service_name self.map:del(section, "update_url") -- delete update_url + self.map:del(section, "update_script") -- delete update_script return self.map:set(section, "service_name", value) else return self.map:del(section, "service_name") end end end +function svc6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - change Provider -- ############################################# +svs = ns:taboption("basic", Button, "_switch") +svs.title = translate("Really change DDNS provider?") +svs.inputtitle = translate("Change provider") +svs.inputstyle = "apply" --- IPv4/IPv6 - update_url -- ################################################### +-- IPv4/IPv6 - update_url -- ################################################## uurl = ns:taboption("basic", Value, "update_url", translate("Custom update-URL"), translate("Update URL to be used for updating your DDNS Provider." .. "<br />" .. "Follow instructions you will find on their WEB page.") ) -uurl:depends("ipv4_service_name", "-") -uurl:depends("ipv6_service_name", "-") function uurl.validate(self, value) - local script = ush:formvalue(section) + local fush = ush:formvalue(section) + local fusev6 = usev6:formvalue(section) - if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or - (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then + if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or + (fusev6 == "1" and svc6:formvalue(section) ~= "-") then return "" -- suppress validate error elseif not value then - if not script or not (#script > 0) then + if not fush or (#fush == 0) then return nil, err_tab_basic(self) .. translate("missing / required") else return "" -- suppress validate error / update_script is given end - elseif (#script > 0) then + elseif (#fush > 0) then return nil, err_tab_basic(self) .. translate("either url or script could be set") end @@ -325,80 +448,159 @@ function uurl.validate(self, value) return value end +function uurl.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4/IPv6 - update_script -- ################################################ +-- IPv4/IPv6 - update_script -- ############################################### ush = ns:taboption("basic", Value, "update_script", translate("Custom update-script"), translate("Custom update script to be used for updating your DDNS Provider.") ) -ush:depends("ipv4_service_name", "-") -ush:depends("ipv6_service_name", "-") function ush.validate(self, value) - local url = uurl:formvalue(section) + local fuurl = uurl:formvalue(section) + local fusev6 = usev6:formvalue(section) - if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or - (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then + if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or + (fusev6 == "1" and svc6:formvalue(section) ~= "-") then return "" -- suppress validate error elseif not value then - if not url or not (#url > 0) then + if not fuurl or (#fuurl == 0) then return nil, err_tab_basic(self) .. translate("missing / required") else return "" -- suppress validate error / update_url is given end - elseif (#url > 0) then + elseif (#fuurl > 0) then return nil, err_tab_basic(self) .. translate("either url or script could be set") elseif not NXFS.access(value) then return nil, err_tab_basic(self) .. translate("File not found") end return value end +function ush.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4/IPv6 - domain -- ####################################################### +-- IPv4/IPv6 - domain -- ###################################################### dom = ns:taboption("basic", Value, "domain", - translate("Hostname/Domain"), + translate("Domain"), translate("Replaces [DOMAIN] in Update-URL") ) -dom.rmempty = false -dom.placeholder = "mypersonaldomain.dyndns.org" +dom.placeholder = "myhost.example.com" function dom.validate(self, value) - if not value - or not (#value > 0) - or not DTYP.hostname(value) then - return nil, err_tab_basic(self) .. translate("invalid - Sample") .. ": 'mypersonaldomain.dyndns.org'" - else - return value - end + return _option_validate(self, value) +end +function dom.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end --- IPv4/IPv6 - username -- ##################################################### +-- IPv4/IPv6 - username -- #################################################### user = ns:taboption("basic", Value, "username", translate("Username"), - translate("Replaces [USERNAME] in Update-URL") ) -user.rmempty = false + translate("Replaces [USERNAME] in Update-URL (URL-encoded)") ) function user.validate(self, value) - if not value then - return nil, err_tab_basic(self) .. translate("missing / required") - end - return value + return _option_validate(self, value) +end +function user.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end --- IPv4/IPv6 - password -- ##################################################### +-- IPv4/IPv6 - password -- #################################################### pw = ns:taboption("basic", Value, "password", translate("Password"), - translate("Replaces [PASSWORD] in Update-URL") ) -pw.rmempty = false + translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") ) pw.password = true function pw.validate(self, value) - if not value then - return nil, err_tab_basic(self) .. translate("missing / required") + return _option_validate(self, value) +end +function pw.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - param_enc -- ################################################### +pe = ns:taboption("basic", Value, "param_enc", + translate("Optional Encoded Parameter"), + translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") ) +function pe.validate(self, value) + return _option_validate(self, value) +end +function pe.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - param_enc -- ################################################### +po = ns:taboption("basic", Value, "param_opt", + translate("Optional Parameter"), + translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") ) +function po.validate(self, value) + return _option_validate(self, value) +end +function po.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- handled service dependent show/display -- ################################## +-- IPv4 -- +local cv4 = svc4:cfgvalue(section) +if cv4 ~= "-" then + svs:depends ("ipv4_service_name", "-" ) -- show only if "-" + ush:depends ("ipv4_service_name", "?") + uurl:depends("ipv4_service_name", "?") +else + uurl:depends("ipv4_service_name", "-") + ush:depends ("ipv4_service_name", "-") + dom:depends("ipv4_service_name", "-" ) + user:depends("ipv4_service_name", "-" ) + pw:depends("ipv4_service_name", "-" ) + pe:depends("ipv4_service_name", "-" ) + po:depends("ipv4_service_name", "-" ) +end +for s, u in UTIL.kspairs(services4) do + svc4:value(s) -- fill DropDown-List + if cv4 ~= s then + svs:depends("ipv4_service_name", s ) + else + dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") ) + user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") ) + pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") ) + pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") ) + po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") ) end - return value end +svc4:value("-", translate("-- custom --") ) + +-- IPv6 -- +local cv6 = svc6:cfgvalue(section) +if cv6 ~= "-" then + svs:depends ("ipv6_service_name", "-" ) + uurl:depends("ipv6_service_name", "?") + ush:depends ("ipv6_service_name", "?") +else + uurl:depends("ipv6_service_name", "-") + ush:depends ("ipv6_service_name", "-") + dom:depends("ipv6_service_name", "-" ) + user:depends("ipv6_service_name", "-" ) + pw:depends("ipv6_service_name", "-" ) + pe:depends("ipv6_service_name", "-" ) + po:depends("ipv6_service_name", "-" ) +end +for s, u in UTIL.kspairs(services6) do + svc6:value(s) -- fill DropDown-List + if cv6 ~= s then + svs:depends("ipv6_service_name", s ) + else + dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") ) + user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") ) + pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") ) + pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") ) + po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") ) + end +end +svc6:value("-", translate("-- custom --") ) --- IPv4/IPv6 - use_https (NEW) -- ############################################## +-- IPv4/IPv6 - use_https -- ################################################### if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then https = ns:taboption("basic", Flag, "use_https", translate("Use HTTP Secure") ) https.orientation = "horizontal" - https.rmempty = false -- force validate function function https.cfgvalue(self, section) local value = AbstractValue.cfgvalue(self, section) if not has_ssl and value == "1" then @@ -410,9 +612,6 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then end return value end - function https.parse(self, section) - DDNS.flag_parse(self, section) - end function https.validate(self, value) if (value == "1" and has_ssl ) or value == "0" then return value end return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !" @@ -427,7 +626,7 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then end end --- IPv4/IPv6 - cacert (NEW) -- ################################################# +-- IPv4/IPv6 - cacert -- ###################################################### if has_ssl then cert = ns:taboption("basic", Value, "cacert", translate("Path to CA-Certificate"), @@ -435,8 +634,8 @@ if has_ssl then translate("or") .. bold_on .. " IGNORE " .. bold_off .. translate("to run HTTPS without verification of server certificates (insecure)") ) cert:depends("use_https", "1") - cert.rmempty = false -- force validate function cert.default = "/etc/ssl/certs" + cert.forcewrite = true function cert.validate(self, value) if https:formvalue(section) == "0" then return "" -- supress validate error if NOT https @@ -451,10 +650,13 @@ if has_ssl then return nil, err_tab_basic(self) .. translate("file or directory not found or not 'IGNORE'") .. " !" end + function cert.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end end --- TAB: Advanced ################################################################################## --- IPv4 - ip_source -- ######################################################### +-- TAB: Advanced ################################################################################# +-- IPv4 - ip_source -- ######################################################## src4 = ns:taboption("advanced", ListValue, "ipv4_source", translate("IP address source") .. " [IPv4]", translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") ) @@ -500,8 +702,11 @@ function src4.write(self, section, value) self.map:del(section, self.option) -- delete "ipv4_source" helper return self.map:set(section, "ip_source", value) -- and write "ip_source end +function src4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv6 - ip_source -- ######################################################### +-- IPv6 - ip_source -- ######################################################## src6 = ns:taboption("advanced", ListValue, "ipv6_source", translate("IP address source") .. " [IPv6]", translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") ) @@ -552,8 +757,11 @@ function src6.write(self, section, value) self.map:del(section, self.option) -- delete "ipv4_source" helper return self.map:set(section, "ip_source", value) -- and write "ip_source end +function src6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 - ip_network (default "wan") -- ######################################## +-- IPv4 - ip_network (default "wan") -- ####################################### ipn4 = ns:taboption("advanced", ListValue, "ipv4_network", translate("Network") .. " [IPv4]", translate("Defines the network to read systems IPv4-Address from") ) @@ -586,8 +794,11 @@ function ipn4.write(self, section, value) return self.map:set(section, "ip_network", value) -- and write "ip_network" end end +function ipn4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv6 - ip_network (default "wan6") -- ####################################### +-- IPv6 - ip_network (default "wan6") -- ###################################### ipn6 = ns:taboption("advanced", ListValue, "ipv6_network", translate("Network") .. " [IPv6]" ) ipn6:depends("ipv6_source", "network") @@ -626,8 +837,11 @@ function ipn6.write(self, section, value) return self.map:set(section, "ip_network", value) -- and write "ip_network" end end +function ipn6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################# +-- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################ iurl4 = ns:taboption("advanced", Value, "ipv4_url", translate("URL to detect") .. " [IPv4]", translate("Defines the Web page to read systems IPv4-Address from") ) @@ -668,8 +882,11 @@ function iurl4.write(self, section, value) return self.map:set(section, "ip_url", value) -- and write "ip_url" end end +function iurl4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ########################### +-- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ########################## iurl6 = ns:taboption("advanced", Value, "ipv6_url", translate("URL to detect") .. " [IPv6]" ) iurl6:depends("ipv6_source", "web") @@ -716,8 +933,11 @@ function iurl6.write(self, section, value) return self.map:set(section, "ip_url", value) -- and write "ip_url" end end +function iurl6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 + IPv6 - ip_interface -- ############################################### +-- IPv4 + IPv6 - ip_interface -- ############################################## ipi = ns:taboption("advanced", ListValue, "ip_interface", translate("Interface"), translate("Defines the interface to read systems IP-Address from") ) @@ -732,16 +952,18 @@ for _, v in pairs(SYS.net.devices()) do end end function ipi.validate(self, value) - if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface") - or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then + local fusev6 = usev6:formvalue(section) + if (fusev6 == "0" and src4:formvalue(section) ~= "interface") + or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then return "" else return value end end function ipi.write(self, section, value) - if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface") - or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then + local fusev6 = usev6:formvalue(section) + if (fusev6 == "0" and src4:formvalue(section) ~= "interface") + or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then return true else -- get network from device to @@ -751,21 +973,24 @@ function ipi.write(self, section, value) return self.map:set(section, self.option, value) end end +function ipi.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 + IPv6 - ip_script (NEW) -- ############################################ +-- IPv4 + IPv6 - ip_script -- ################################################# ips = ns:taboption("advanced", Value, "ip_script", translate("Script"), translate("User defined script to read systems IP-Address") ) ips:depends("ipv4_source", "script") -- IPv4 ips:depends("ipv6_source", "script") -- or IPv6 -ips.rmempty = false ips.placeholder = "/path/to/script.sh" function ips.validate(self, value) + local fusev6 = usev6:formvalue(section) local split if value then split = UTIL.split(value, " ") end - if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script") - or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then + if (fusev6 == "0" and src4:formvalue(section) ~= "script") + or (fusev6 == "1" and src6:formvalue(section) ~= "script") then return "" elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then return nil, err_tab_adv(self) .. @@ -775,15 +1000,19 @@ function ips.validate(self, value) end end function ips.write(self, section, value) - if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script") - or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then + local fusev6 = usev6:formvalue(section) + if (fusev6 == "0" and src4:formvalue(section) ~= "script") + or (fusev6 == "1" and src6:formvalue(section) ~= "script") then return true else return self.map:set(section, self.option, value) end end +function ips.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 - interface - default "wan" -- ######################################### +-- IPv4 - interface - default "wan" -- ######################################## -- event network to monitor changes/hotplug/dynamic_dns_updater.sh -- only needs to be set if "ip_source"="web" or "script" -- if "ip_source"="network" or "interface" we use their network @@ -798,27 +1027,32 @@ function eif4.cfgvalue(self, section) return DDNS.read_value(self, section, "interface") end function eif4.validate(self, value) + local fsrc4 = src4:formvalue(section) if usev6:formvalue(section) == "1" - or src4:formvalue(section) == "network" - or src4:formvalue(section) == "interface" then + or fsrc4 == "network" + or fsrc4 == "interface" then return "" -- ignore IPv6, network, interface else return value end end function eif4.write(self, section, value) + local fsrc4 = src4:formvalue(section) if usev6:formvalue(section) == "1" - or src4:formvalue(section) == "network" - or src4:formvalue(section) == "interface" then + or fsrc4 == "network" + or fsrc4 == "interface" then return true -- ignore IPv6, network, interface else self.map:del(section, self.option) -- delete "ipv4_interface" helper return self.map:set(section, "interface", value) -- and write "interface" end end +function eif4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv6 - interface (NEW) - default "wan6" -- ################################## --- event network to monitor changes/hotplug (NEW) +-- IPv6 - interface - default "wan6" -- ####################################### +-- event network to monitor changes/hotplug -- only needs to be set if "ip_source"="web" or "script" -- if "ip_source"="network" or "interface" we use their network eif6 = ns:taboption("advanced", ListValue, "ipv6_interface", @@ -836,9 +1070,10 @@ function eif6.cfgvalue(self, section) return DDNS.read_value(self, section, "interface") end function eif6.validate(self, value) + local fsrc6 = src6:formvalue(section) if usev6:formvalue(section) == "0" - or src4:formvalue(section) == "network" - or src4:formvalue(section) == "interface" then + or fsrc6 == "network" + or fsrc6 == "interface" then return "" -- ignore IPv4, network, interface elseif not has_ipv6 then return nil, err_tab_adv(self) .. err_ipv6_plain @@ -847,23 +1082,26 @@ function eif6.validate(self, value) end end function eif6.write(self, section, value) + local fsrc6 = src6:formvalue(section) if usev6:formvalue(section) == "0" - or src4:formvalue(section) == "network" - or src4:formvalue(section) == "interface" then + or fsrc6 == "network" + or fsrc6 == "interface" then return true -- ignore IPv4, network, interface else self.map:del(section, self.option) -- delete "ipv6_interface" helper return self.map:set(section, "interface", value) -- and write "interface" end end +function eif6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4/IPv6 - bind_network -- ################################################# +-- IPv4/IPv6 - bind_network -- ################################################ if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then bnet = ns:taboption("advanced", ListValue, "bind_network", translate("Bind Network") ) bnet:depends("ipv4_source", "web") bnet:depends("ipv6_source", "web") - bnet.rmempty = true bnet.default = "" bnet:value("", translate("-- default --")) WADM.cbi_add_networks(bnet) @@ -883,9 +1121,12 @@ if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then if (value ~= "" and has_ssl ) or value == "" then return value end return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !" end + function bnet.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end end --- IPv4 + IPv6 - force_ipversion (NEW) -- ###################################### +-- IPv4 + IPv6 - force_ipversion -- ########################################### -- optional to force wget/curl and host to use only selected IP version -- command parameter "-4" or "-6" if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then @@ -907,19 +1148,9 @@ if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then if (value == "1" and has_force) or value == "0" then return value end return nil, err_tab_adv(self) .. translate("Force IP Version not supported") end - function fipv.parse(self, section) - DDNS.flag_parse(self, section) - end - function fipv.write(self, section, value) - if value == "1" then - return self.map:set(section, self.option, value) - else - return self.map:del(section, self.option) - end - end end --- IPv4 + IPv6 - dns_server (NEW) -- ########################################### +-- IPv4 + IPv6 - dns_server -- ################################################ -- optional DNS Server to use resolving my IP if "ip_source"="web" dns = ns:taboption("advanced", Value, "dns_server", translate("DNS-Server"), @@ -928,7 +1159,7 @@ dns = ns:taboption("advanced", Value, "dns_server", dns.placeholder = "mydns.lan" function dns.validate(self, value) -- if .datatype is set, then it is checked before calling this function - if not value then + if not value or (#value == 0) then return "" -- ignore on empty elseif not DTYP.host(value) then return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address") @@ -946,8 +1177,11 @@ function dns.validate(self, value) end end end +function dns.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- IPv4 + IPv6 - force_dnstcp (NEW) -- ######################################### +-- IPv4 + IPv6 - force_dnstcp -- ############################################## if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then tcp = ns:taboption("advanced", Flag, "force_dnstcp", translate("Force TCP on DNS") ) @@ -969,12 +1203,9 @@ if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then end return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported") end - function tcp.parse(self, section) - DDNS.flag_parse(self, section) - end end --- IPv4 + IPv6 - proxy (NEW) -- ################################################ +-- IPv4 + IPv6 - proxy -- ##################################################### -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port] if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then pxy = ns:taboption("advanced", Value, "proxy", @@ -996,7 +1227,7 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then end function pxy.validate(self, value) -- if .datatype is set, then it is checked before calling this function - if not value then + if not value or (#value == 0) then return "" -- ignore on empty elseif has_proxy then local ipv6 = usev6:formvalue(section) or "0" @@ -1015,9 +1246,12 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then return nil, err_tab_adv(self) .. translate("PROXY-Server not supported") end end + function pxy.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end end --- use_syslog -- ############################################################### +-- use_syslog -- ############################################################## slog = ns:taboption("advanced", ListValue, "use_syslog", translate("Log to syslog"), translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") ) @@ -1027,26 +1261,24 @@ slog:value("1", translate("Info")) slog:value("2", translate("Notice")) slog:value("3", translate("Warning")) slog:value("4", translate("Error")) +function slog.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- use_logfile (NEW) -- ######################################################## +-- use_logfile -- ############################################################# logf = ns:taboption("advanced", Flag, "use_logfile", translate("Log to file"), translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" .. translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] ) logf.orientation = "horizontal" -logf.rmempty = false -- we want to save in /etc/config/ddns file on "0" because -logf.default = "1" -- if not defined write to log by default -function logf.parse(self, section) - DDNS.flag_parse(self, section) -end +logf.default = "1" -- if not defined write to log by default --- TAB: Timer ##################################################################################### --- check_interval -- ########################################################### +-- TAB: Timer #################################################################################### +-- check_interval -- ########################################################## ci = ns:taboption("timer", Value, "check_interval", translate("Check Interval") ) ci.template = "ddns/detail_value" -ci.default = 10 -ci.rmempty = false -- validate ourselves for translatable error messages +ci.default = "10" function ci.validate(self, value) if not DTYP.uinteger(value) or tonumber(value) < 1 then @@ -1061,7 +1293,7 @@ function ci.validate(self, value) end end function ci.write(self, section, value) - -- simulate rmempty=true remove default + -- remove when default local secs = DDNS.calc_seconds(value, cu:formvalue(section)) if secs ~= 600 then --default 10 minutes return self.map:set(section, self.option, value) @@ -1070,20 +1302,22 @@ function ci.write(self, section, value) return self.map:del(section, self.option) end end +function ci.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- check_unit -- ############################################################### +-- check_unit -- ############################################################## cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error", translate("Interval to check for changed IP" .. "<br />" .. "Values below 5 minutes == 300 seconds are not supported") ) cu.template = "ddns/detail_lvalue" cu.default = "minutes" -cu.rmempty = false -- want to control write process cu:value("seconds", translate("seconds")) cu:value("minutes", translate("minutes")) cu:value("hours", translate("hours")) --cu:value("days", translate("days")) function cu.write(self, section, value) - -- simulate rmempty=true remove default + -- remove when default local secs = DDNS.calc_seconds(ci:formvalue(section), value) if secs ~= 600 then --default 10 minutes return self.map:set(section, self.option, value) @@ -1091,13 +1325,16 @@ function cu.write(self, section, value) return true end end +function cu.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- force_interval (modified) -- ################################################ +-- force_interval (modified) -- ############################################### fi = ns:taboption("timer", Value, "force_interval", translate("Force Interval") ) fi.template = "ddns/detail_value" -fi.default = 72 -- see dynamic_dns_updater.sh script -fi.rmempty = false -- validate ourselves for translatable error messages +fi.default = "72" -- see dynamic_dns_updater.sh script +--fi.rmempty = false -- validate ourselves for translatable error messages function fi.validate(self, value) if not DTYP.uinteger(value) or tonumber(value) < 0 then @@ -1131,15 +1368,18 @@ function fi.write(self, section, value) return self.map:del(section, self.option) end end +function fi.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- force_unit -- ############################################################### +-- force_unit -- ############################################################## fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error", translate("Interval to force updates send to DDNS Provider" .. "<br />" .. "Setting this parameter to 0 will force the script to only run once" .. "<br />" .. "Values lower 'Check Interval' except '0' are not supported") ) fu.template = "ddns/detail_lvalue" fu.default = "hours" -fu.rmempty = false -- want to control write process +--fu.rmempty = false -- want to control write process --fu:value("seconds", translate("seconds")) fu:value("minutes", translate("minutes")) fu:value("hours", translate("hours")) @@ -1153,15 +1393,17 @@ function fu.write(self, section, value) return true end end +function fu.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- retry_count (NEW) -- ######################################################## +-- retry_count -- ############################################################# rc = ns:taboption("timer", Value, "retry_count") rc.title = translate("Error Retry Counter") rc.description = translate("On Error the script will stop execution after given number of retrys") .. "<br />" .. translate("The default setting of '0' will retry infinite.") -rc.default = 0 -rc.rmempty = false -- validate ourselves for translatable error messages +rc.default = "0" function rc.validate(self, value) if not DTYP.uinteger(value) then return nil, err_tab_timer(self) .. translate("minimum value '0'") @@ -1169,21 +1411,15 @@ function rc.validate(self, value) return value end end -function rc.write(self, section, value) - -- simulate rmempty=true remove default - if tonumber(value) ~= self.default then - return self.map:set(section, self.option, value) - else - return self.map:del(section, self.option) - end +function rc.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end --- retry_interval -- ########################################################### +-- retry_interval -- ########################################################## ri = ns:taboption("timer", Value, "retry_interval", translate("Error Retry Interval") ) ri.template = "ddns/detail_value" -ri.default = 60 -ri.rmempty = false -- validate ourselves for translatable error messages +ri.default = "60" function ri.validate(self, value) if not DTYP.uinteger(value) or tonumber(value) < 1 then @@ -1202,13 +1438,16 @@ function ri.write(self, section, value) return self.map:del(section, self.option) end end +function ri.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- retry_unit -- ############################################################### +-- retry_unit -- ############################################################## ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error", translate("On Error the script will retry the failed action after given time") ) ru.template = "ddns/detail_lvalue" ru.default = "seconds" -ru.rmempty = false -- want to control write process +--ru.rmempty = false -- want to control write process ru:value("seconds", translate("seconds")) ru:value("minutes", translate("minutes")) --ru:value("hours", translate("hours")) @@ -1222,8 +1461,11 @@ function ru.write(self, section, value) return true -- will be deleted by retry_interval end end +function ru.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end --- TAB: LogView (NEW) ############################################################################# +-- TAB: LogView ################################################################################## lv = ns:taboption("logview", DummyValue, "_logview") lv.template = "ddns/detail_logview" lv.inputtitle = translate("Read / Reread log file") diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua index 25d09b73bb..23ce4f13f7 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua @@ -5,18 +5,14 @@ local NX = require "nixio" local NXFS = require "nixio.fs" local DISP = require "luci.dispatcher" local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- cbi-map definition -- ####################################################### local m = Map("ddns") - -m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] - .. translate("Dynamic DNS") .. [[</a>]] - -m.description = translate("Dynamic DNS allows that your router can be reached with " .. - "a fixed hostname while having a dynamically changing IP address.") - -m.redirect = DISP.build_url("admin", "services", "ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") function m.commit_handler(self) if self.changed then -- changes ? @@ -52,17 +48,7 @@ ali.description = translate("Non-public and by default blocked IP's") .. ":" .. "0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16" .. [[<br /><strong>IPv6: </strong>]] .. "::/32, f000::/4" -ali.reempty = true ali.default = "0" -function ali.parse(self, section) - DDNS.flag_parse(self, section) -end -function ali.validate(self, value) - if value == self.default then - return "" -- default = empty - end - return value -end -- date_format -- ############################################################# local df = ns:option(Value, "date_format") @@ -71,7 +57,6 @@ df.description = [[<a href="http://www.cplusplus.com/reference/ctime/strftime/" .. translate("For supported codes look here") .. [[</a>]] df.template = "ddns/global_value" -df.rmempty = true df.default = "%F %R" df.date_string = "" function df.cfgvalue(self, section) @@ -80,55 +65,45 @@ function df.cfgvalue(self, section) self.date_string = DDNS.epoch2date(epoch, value) return value end -function df.validate(self, value) - if value == self.default then - return "" -- default = empty - end - return value +function df.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end -- run_dir -- ################################################################# local rd = ns:option(Value, "run_dir") rd.title = translate("Status directory") rd.description = translate("Directory contains PID and other status information for each running section") -rd.rmempty = true rd.default = "/var/run/ddns" -function rd.validate(self, value) - if value == self.default then - return "" -- default = empty - end - return value +-- no need to validate. if empty default is used everything else created by dns-scripts +function rd.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end -- log_dir -- ################################################################# local ld = ns:option(Value, "log_dir") ld.title = translate("Log directory") ld.description = translate("Directory contains Log files for each running section") -ld.rmempty = true ld.default = "/var/log/ddns" -function ld.validate(self, value) - if value == self.default then - return "" -- default = empty - end - return value +-- no need to validate. if empty default is used everything else created by dns-scripts +function ld.parse(self, section, novld) + DDNS.value_parse(self, section, novld) end -- log_lines -- ############################################################### local ll = ns:option(Value, "log_lines") ll.title = translate("Log length") ll.description = translate("Number of last lines stored in log files") -ll.rmempty = true ll.default = "250" function ll.validate(self, value) local n = tonumber(value) if not n or math.floor(n) ~= n or n < 1 then return nil, self.title .. ": " .. translate("minimum value '1'") end - if value == self.default then - return "" -- default = empty - end return value end +function ll.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end -- use_curl -- ################################################################ if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) @@ -139,17 +114,7 @@ and NXFS.access("/usr/bin/curl") then .. [[<br />]] .. translate("To use cURL activate this option.") pc.orientation = "horizontal" - pc.rmempty = true pc.default = "0" - function pc.parse(self, section) - DDNS.flag_parse(self, section) - end - function pc.validate(self, value) - if value == self.default then - return "" -- default = empty - end - return value - end end return m diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua index 2a65fd04fa..031bf513cf 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua @@ -1,9 +1,9 @@ -- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> -- Licensed to the public under the Apache License 2.0. -local CTRL = require "luci.controller.ddns" -- this application's controller local DISP = require "luci.dispatcher" local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- check supported options -- ################################################## @@ -11,8 +11,6 @@ local DDNS = require "luci.tools.ddns" -- ddns multiused functions has_ssl = DDNS.check_ssl() -- HTTPS support and --bind-network / --interface has_proxy = DDNS.check_proxy() -- Proxy support has_dnstcp = DDNS.check_bind_host() -- DNS TCP support --- correct ddns-scripts version -need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN) -- html constants font_red = [[<font color="red">]] @@ -22,15 +20,9 @@ bold_off = [[</strong>]] -- cbi-map definition -- ####################################################### m = Map("ddns") - -m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] .. - translate("Dynamic DNS") .. [[</a>]] - -m.description = translate("Dynamic DNS allows that your router can be reached with " .. - "a fixed hostname while having a dynamically changing " .. - "IP address.") - -m.redirect = DISP.build_url("admin", "services", "ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") -- SimpleSection definition -- ################################################# -- show Hints to optimize installation and script usage @@ -39,7 +31,7 @@ s = m:section( SimpleSection, translate("Below a list of configuration tips for your system to run Dynamic DNS updates without limitations") ) -- ddns_scripts needs to be updated for full functionality -if need_update then +if not CTRL.service_ok() then local dv = s:option(DummyValue, "_update_needed") dv.titleref = DISP.build_url("admin", "system", "packages") dv.rawhtml = true diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua index 5b5925b25e..46fc0a4a29 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua @@ -2,10 +2,10 @@ -- Licensed to the public under the Apache License 2.0. local NXFS = require "nixio.fs" -local CTRL = require "luci.controller.ddns" -- this application's controller local DISP = require "luci.dispatcher" local HTTP = require "luci.http" local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- show hints ? @@ -15,7 +15,7 @@ show_hints = not (DDNS.check_ipv6() -- IPv6 support and DDNS.check_bind_host() -- DNS TCP support ) -- correct ddns-scripts version -need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN) +need_update = not CTRL.service_ok() -- html constants font_red = [[<font color="red">]] @@ -25,22 +25,8 @@ bold_off = [[</strong>]] -- cbi-map definition -- ####################################################### m = Map("ddns") - -m.title = [[<a href="javascript:alert(']] - .. translate("Version Information") - .. [[\n\nluci-app-ddns]] - .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("luci-app-ddns") - .. [[\n\nddns-scripts ]] .. translate("required") .. [[:]] - .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.DDNS_MIN .. [[ ]] .. translate("or higher") - .. [[\n\nddns-scripts ]] .. translate("installed") .. [[:]] - .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("ddns-scripts") - .. [[\n\n]] - .. [[')">]] - .. translate("Dynamic DNS") .. [[</a>]] - -m.description = translate("Dynamic DNS allows that your router can be reached with " .. - "a fixed hostname while having a dynamically changing " .. - "IP address.") +m.title = CTRL.app_title_main() +m.description = CTRL.app_description() m.on_after_commit = function(self) if self.changed then -- changes ? @@ -122,21 +108,21 @@ function ts.create(self, name) HTTP.redirect( self.extedit:format(name) ) end --- Domain and registered IP -- ################################################# -dom = ts:option(DummyValue, "_domainIP", - translate("Hostname/Domain") .. "<br />" .. translate("Registered IP") ) +-- Lookup_Host and registered IP -- ################################################# +dom = ts:option(DummyValue, "_lookupIP", + translate("Lookup Hostname") .. "<br />" .. translate("Registered IP") ) dom.template = "ddns/overview_doubleline" function dom.set_one(self, section) - local domain = self.map:get(section, "domain") or "" - if domain ~= "" then - return domain + local lookup = self.map:get(section, "lookup_host") or "" + if lookup ~= "" then + return lookup else return [[<em>]] .. translate("config error") .. [[</em>]] end end function dom.set_two(self, section) - local domain = self.map:get(section, "domain") or "" - if domain == "" then return "" end + local lookup = self.map:get(section, "lookup_host") or "" + if lookup == "" then return "" end local dnsserver = self.map:get(section, "dnsserver") or "" local use_ipv6 = tonumber(self.map:get(section, "use_ipv6") or 0) local force_ipversion = tonumber(self.map:get(section, "force_ipversion") or 0) @@ -145,7 +131,7 @@ function dom.set_two(self, section) if not NXFS.access(command, "rwx", "rx", "rx") then NXFS.chmod(command, 755) end - command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 .. + command = command .. [[ get_registered_ip ]] .. lookup .. [[ ]] .. use_ipv6 .. [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver local ip = SYS.exec(command) if ip == "" then ip = translate("no data") end @@ -157,9 +143,6 @@ ena = ts:option( Flag, "enabled", translate("Enabled")) ena.template = "ddns/overview_enabled" ena.rmempty = false -function ena.parse(self, section) - DDNS.flag_parse(self, section) -end -- show PID and next update upd = ts:option( DummyValue, "_update", diff --git a/applications/luci-app-ddns/luasrc/tools/ddns.lua b/applications/luci-app-ddns/luasrc/tools/ddns.lua index 4466063cb3..ecc4131364 100644 --- a/applications/luci-app-ddns/luasrc/tools/ddns.lua +++ b/applications/luci-app-ddns/luasrc/tools/ddns.lua @@ -96,58 +96,6 @@ function get_pid(section) return pid end --- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>" -function ipkg_ver_compare(ver1, comp, ver2) - if not ver1 or not ver2 - or not comp or not (#comp > 0) then return nil end - -- correct compare string - if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~=" - elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<=" - elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">=" - elseif comp == "=" or comp == "==" then comp = "==" - elseif comp == "<<" then comp = "<" - elseif comp == ">>" then comp = ">" - else return nil end - - local av1 = UTIL.split(ver1, "[%.%-]", nil, true) - local av2 = UTIL.split(ver2, "[%.%-]", nil, true) - - for i = 1, math.max(table.getn(av1),table.getn(av2)), 1 do - local s1 = av1[i] or "" - local s2 = av2[i] or "" - - -- first "not equal" found return true - if comp == "~=" and (s1 ~= s2) then return true end - -- first "lower" found return true - if (comp == "<" or comp == "<=") and (s1 < s2) then return true end - -- first "greater" found return true - if (comp == ">" or comp == ">=") and (s1 > s2) then return true end - -- not equal then return false - if (s1 ~= s2) then return false end - end - - -- all equal and not compare greater or lower then true - return not (comp == "<" or comp == ">") -end - --- read version information for given package if installed -function ipkg_ver_installed(pkg) - local version = nil - local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r") - if control then - local ln - repeat - ln = control:read("*l") - if ln and ln:match("^Version: ") then - version = ln:gsub("^Version: ", "") - break - end - until not ln - control:close() - end - return version -end - -- replacement of build-in read of UCI option -- modified AbstractValue.cfgvalue(self, section) from cbi.lua -- needed to read from other option then current value definition @@ -172,24 +120,77 @@ function read_value(self, section, option) end end --- replacement of build-in Flag.parse of cbi.lua --- modified to mark section as changed if value changes --- current parse did not do this, but it is done AbstaractValue.parse() -function flag_parse(self, section) - local fexists = self.map:formvalue( - luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option) +-- replacement of build-in parse of "Value" +-- modified AbstractValue.parse(self, section, novld) from cbi.lua +-- validate is called if rmempty/optional true or false +-- before write check if forcewrite, value eq default, and more +function value_parse(self, section, novld) + local fvalue = self:formvalue(section) + local fexist = ( fvalue and (#fvalue > 0) ) -- not "nil" and "not empty" + local cvalue = self:cfgvalue(section) + local rm_opt = ( self.rmempty or self.optional ) + local eq_cfg -- flag: equal cfgvalue - if fexists then - local fvalue = self:formvalue(section) and self.enabled or self.disabled - local cvalue = self:cfgvalue(section) - if fvalue ~= self.default or (not self.optional and not self.rmempty) then - self:write(section, fvalue) - else - self:remove(section) + -- If favlue and cvalue are both tables and have the same content + -- make them identical + if type(fvalue) == "table" and type(cvalue) == "table" then + eq_cfg = (#fvalue == #cvalue) + if eq_cfg then + for i=1, #fvalue do + if cvalue[i] ~= fvalue[i] then + eq_cfg = false + end + end end - if (fvalue ~= cvalue) then self.section.changed = true end - else - self:remove(section) + if eq_cfg then + fvalue = cvalue + end + end + + -- removed parameter "section" from function call because used/accepted nowhere + -- also removed call to function "transfer" + local vvalue, errtxt = self:validate(fvalue) + + -- error handling; validate return "nil" + if not vvalue then + if novld then -- and "novld" set + return -- then exit without raising an error + end + + if fexist then -- and there is a formvalue + self:add_error(section, "invalid", errtxt) + return -- so data are invalid + + elseif not rm_opt then -- and empty formvalue but NOT (rmempty or optional) set + self:add_error(section, "missing", errtxt) + return -- so data is missing + end + end + -- for whatever reason errtxt set and not handled above + assert( not (errtxt and (#errtxt > 0)), "unhandled validate error" ) + + -- lets continue with value returned from validate + eq_cfg = ( vvalue == cvalue ) -- update equal_config flag + local vexist = ( vvalue and (#vvalue > 0) ) -- not "nil" and "not empty" + local eq_def = ( vvalue == self.default ) -- equal_default flag + + -- not forcewrite and (rmempty or optional) + -- and (no data or equal_default) + if not self.forcewrite and rm_opt + and (not vexist or eq_def) then + if self:remove(section) then -- remove data from UCI + self.section.changed = true -- and push events + end + return + end + + -- not forcewrite and no changes, so nothing to write + if not self.forcewrite and eq_cfg then + return + end + + -- write data to UCI; raise event only on changes + if self:write(section, vvalue) and not eq_cfg then self.section.changed = true end end diff --git a/applications/luci-app-ddns/luasrc/view/ddns/detail_logview.htm b/applications/luci-app-ddns/luasrc/view/ddns/detail_logview.htm index 4dcb7bb6fe..fd1d5be268 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/detail_logview.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/detail_logview.htm @@ -6,7 +6,7 @@ var txt = document.getElementById("cbid.ddns." + section + "._logview.txt"); // TextArea if ( !txt ) { return; } // security check - XHR.get('<%=url('admin/services/ddns/logview')%>/' + section, null, + XHR.get('<%=url([[admin]], [[services]], [[ddns]], [[logview]])%>/' + section, null, function(x) { if (x.responseText == "_nodata_") txt.value = "<%:File not found or empty%>"; diff --git a/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm b/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm index b6d4ebb9fd..b409ed0728 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm @@ -19,7 +19,7 @@ var section = data[i].section // Section to handle var cbx = document.getElementById("cbid.ddns." + section + ".enabled"); // Enabled var btn = document.getElementById("cbid.ddns." + section + "._startstop"); // Start/Stop button - var rip = document.getElementById("cbid.ddns." + section + "._domainIP.two"); // Registered IP + var rip = document.getElementById("cbid.ddns." + section + "._lookupIP.two"); // Registered IP var lup = document.getElementById("cbid.ddns." + section + "._update.one"); // Last Update var nup = document.getElementById("cbid.ddns." + section + "._update.two"); // Next Update if ( !(cbx && btn && rip && lup && nup) ) { return; } // security check @@ -76,12 +76,12 @@ break; } - // domain - // (data[i].domain ignored here + // lookup + // (data[i].lookup ignored here // registered IP // rip.innerHTML = "Registered IP"; - if (data[i].domain == "_nodomain_") + if (data[i].lookup == "_nolookup_") rip.innerHTML = ''; else if (data[i].reg_ip == "_nodata_") rip.innerHTML = '<em><%:No data%></em>'; @@ -136,7 +136,7 @@ // do start/stop var btnXHR = new XHR(); - btnXHR.post('<%=url('admin/services/ddns/startstop')%>/' + section + '/' + cbx.checked, { token: '<%=token%>' }, + btnXHR.post('<%=url([[admin]], [[services]], [[ddns]], [[startstop]])%>/' + section + '/' + cbx.checked, { token: '<%=token%>' }, function(x, data) { if (x.responseText == "_uncommitted_") { // we need a trick to display Ampersand "&" in stead of "&" or "&" @@ -155,7 +155,7 @@ } // force to immediate show status on page load (not waiting for XHR.poll) - XHR.get('<%=url('admin/services/ddns/status')%>', null, + XHR.get('<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null, function(x, data) { if (data) { _data2elements(data); } } @@ -164,7 +164,7 @@ // define only ONE XHR.poll in a page because if one is running it blocks the other one // optimum is to define on Map or Section Level from here you can reach all elements // we need update every 15 seconds only - XHR.poll(15, '<%=url('admin/services/ddns/status')%>', null, + XHR.poll(5, '<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null, function(x, data) { if (data) { _data2elements(data); } } diff --git a/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm b/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm index 06dca48a9c..5bdcb03e73 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm @@ -69,15 +69,15 @@ break; } - // domain - if (data[j].domain == "_nodomain_") + // lookup + if (data[j].lookup == "_nolookup_") tr.insertCell(-1).innerHTML = '<em><%:config error%></em>'; else - tr.insertCell(-1).innerHTML = data[j].domain; + tr.insertCell(-1).innerHTML = data[j].lookup; // registered IP switch (data[j].reg_ip) { - case "_nodomain_": + case "_nolookup_": tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; break; case "_nodata_": @@ -111,13 +111,13 @@ } // force to immediate show status (not waiting for XHR.poll) - XHR.get('<%=url('admin/services/ddns/status')%>', null, + XHR.get('<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null, function(x, data) { if (data) { _data2elements(x, data); } } ); - XHR.poll(5, '<%=url('admin/services/ddns/status')%>', null, + XHR.poll(15, '<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null, function(x, data) { if (data) { _data2elements(x, data); } } @@ -132,7 +132,7 @@ <tr class="cbi-section-table-titles"> <th class="cbi-section-table-cell"><%:Configuration%></th> <th class="cbi-section-table-cell"><%:Next Update%></th> - <th class="cbi-section-table-cell"><%:Hostname/Domain%></th> + <th class="cbi-section-table-cell"><%:Lookup Hostname%></th> <th class="cbi-section-table-cell"><%:Registered IP%></th> <th class="cbi-section-table-cell"><%:Network%></th> </tr> diff --git a/applications/luci-app-ddns/po/de/ddns.po b/applications/luci-app-ddns/po/de/ddns.po index 6ffde5d547..e662adc780 100644 --- a/applications/luci-app-ddns/po/de/ddns.po +++ b/applications/luci-app-ddns/po/de/ddns.po @@ -1,15 +1,15 @@ msgid "" msgstr "" "Project-Id-Version: luci-app-ddns\n" -"POT-Creation-Date: 2015-05-08 21:29+0100\n" -"PO-Revision-Date: 2015-05-08 21:47+0100\n" +"POT-Creation-Date: 2015-11-04 19:10-0100\n" +"PO-Revision-Date: 2015-11-14 18:31+0100\n" "Last-Translator: Christian Schoenebeck <christian.schoenebeck@gmail.com>\n" "Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.7.5\n" +"X-Generator: Poedit 1.8.6\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-Basepath: .\n" @@ -70,6 +70,9 @@ msgstr "" msgid "Casual users should not change this setting" msgstr "Standard Benutzer sollten diese Einstellung nicht ändern." +msgid "Change provider" +msgstr "Anbieter wechseln" + msgid "Check Interval" msgstr "Prüfinterval" @@ -125,6 +128,12 @@ msgstr "Eigenes Update-Skript" msgid "DDNS Autostart disabled" msgstr "DDNS Autostart deaktiviert" +msgid "DDNS Client Configuration" +msgstr "DDNS Client Konfiguration" + +msgid "DDNS Client Documentation" +msgstr "DDNS Client Dokumentation" + msgid "DDNS Service provider" msgstr "DDNS-Dienstanbieter" @@ -196,6 +205,9 @@ msgstr "" msgid "Disabled" msgstr "Deaktiviert" +msgid "Domain" +msgstr "Domäne" + msgid "Dynamic DNS" msgstr "Dynamisches DNS" @@ -284,8 +296,10 @@ msgstr "HTTPS nicht unterstützt" msgid "Hints" msgstr "Hinweise" -msgid "Hostname/Domain" -msgstr "Rechnername/Domäne" +msgid "Hostname/FQDN to validate, if IP update happen or necessary" +msgstr "" +"Hostname/FQDN um zu überprüfen, ob eine Aktualisierung stattgefunden hat " +"oder notwendig ist" msgid "IP address source" msgstr "IP-Adressquelle" @@ -385,6 +399,12 @@ msgstr "Protokoll in Datei schreiben" msgid "Log to syslog" msgstr "Systemprotokoll verwenden" +msgid "Lookup Hostname" +msgstr "Nachschlage-Hostname" + +msgid "NOT installed" +msgstr "NICHT installiert" + msgid "" "Neither GNU Wget with SSL nor cURL installed to select a network to use for " "communication." @@ -454,6 +474,21 @@ msgstr "" msgid "On Error the script will stop execution after given number of retrys" msgstr "Das Skript wird nach der gegebenen Anzahl von Fehlversuchen beendet." +msgid "OpenWrt Wiki" +msgstr "OpenWrt Wiki" + +msgid "Optional Encoded Parameter" +msgstr "Optionaler codierten Parameter" + +msgid "Optional Parameter" +msgstr "Optionaler Parameter" + +msgid "Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)" +msgstr "Optional: Ersetzt [PARAMENC] in der Update-URL (URL-codiert)" + +msgid "Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)" +msgstr "Optional: Ersetzt [PARAMENC] in der Update-URL (NICHT URL-codiert)" + msgid "Overview" msgstr "Übersicht" @@ -484,17 +519,20 @@ msgstr "Prozess ID" msgid "Read / Reread log file" msgstr "Protokolldatei (neu) einlesen" +msgid "Really change DDNS provider?" +msgstr "Wirklich DDNS-Anbieter wechseln?" + msgid "Registered IP" msgstr "Registrierte IP" msgid "Replaces [DOMAIN] in Update-URL" msgstr "Ersetzt [DOMAIN] in der Update-URL" -msgid "Replaces [PASSWORD] in Update-URL" -msgstr "Ersetzt [PASSWORD] in der Update-URL" +msgid "Replaces [PASSWORD] in Update-URL (URL-encoded)" +msgstr "Ersetzt [PASSWORD] in der Update-URL (URL-codiert)" -msgid "Replaces [USERNAME] in Update-URL" -msgstr "Ersetzt [USERNAME] in der Update-URL" +msgid "Replaces [USERNAME] in Update-URL (URL-encoded)" +msgstr "Ersetzt [USERNAME] in der Update-URL (URL-codiert)" msgid "Run once" msgstr "Einmalig ausführen" @@ -528,7 +566,7 @@ msgstr "" "Optionen." msgid "The default setting of '0' will retry infinite." -msgstr "Der Standard-Wert von '0' wird es endlosen erneut versuchen." +msgstr "Beim Standard-Wert von '0' wird es endlos erneut versucht." msgid "There is no service configured." msgstr "Kein Dienst konfiguriert" @@ -661,8 +699,8 @@ msgstr "Stunden" msgid "installed" msgstr "installiert" -msgid "invalid - Sample" -msgstr "ungültig - Beispiel" +msgid "invalid FQDN / required - Sample" +msgstr "ungültige FQDN / Pflichtfeld - Beispiel" msgid "minimum value '0'" msgstr "Minimum Wert '0'" diff --git a/applications/luci-app-ddns/po/templates/ddns.pot b/applications/luci-app-ddns/po/templates/ddns.pot index 35386802bf..7ab51dd93a 100644 --- a/applications/luci-app-ddns/po/templates/ddns.pot +++ b/applications/luci-app-ddns/po/templates/ddns.pot @@ -50,6 +50,9 @@ msgstr "" msgid "Casual users should not change this setting" msgstr "" +msgid "Change provider" +msgstr "" + msgid "Check Interval" msgstr "" @@ -96,6 +99,12 @@ msgstr "" msgid "DDNS Autostart disabled" msgstr "" +msgid "DDNS Client Configuration" +msgstr "" + +msgid "DDNS Client Documentation" +msgstr "" + msgid "DDNS Service provider" msgstr "" @@ -149,6 +158,9 @@ msgstr "" msgid "Disabled" msgstr "" +msgid "Domain" +msgstr "" + msgid "Dynamic DNS" msgstr "" @@ -230,7 +242,7 @@ msgstr "" msgid "Hints" msgstr "" -msgid "Hostname/Domain" +msgid "Hostname/FQDN to validate, if IP update happen or necessary" msgstr "" msgid "IP address source" @@ -315,6 +327,12 @@ msgstr "" msgid "Log to syslog" msgstr "" +msgid "Lookup Hostname" +msgstr "" + +msgid "NOT installed" +msgstr "" + msgid "" "Neither GNU Wget with SSL nor cURL installed to select a network to use for " "communication." @@ -373,6 +391,21 @@ msgstr "" msgid "On Error the script will stop execution after given number of retrys" msgstr "" +msgid "OpenWrt Wiki" +msgstr "" + +msgid "Optional Encoded Parameter" +msgstr "" + +msgid "Optional Parameter" +msgstr "" + +msgid "Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)" +msgstr "" + +msgid "Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)" +msgstr "" + msgid "Overview" msgstr "" @@ -403,16 +436,19 @@ msgstr "" msgid "Read / Reread log file" msgstr "" +msgid "Really change DDNS provider?" +msgstr "" + msgid "Registered IP" msgstr "" msgid "Replaces [DOMAIN] in Update-URL" msgstr "" -msgid "Replaces [PASSWORD] in Update-URL" +msgid "Replaces [PASSWORD] in Update-URL (URL-encoded)" msgstr "" -msgid "Replaces [USERNAME] in Update-URL" +msgid "Replaces [USERNAME] in Update-URL (URL-encoded)" msgstr "" msgid "Run once" @@ -563,7 +599,7 @@ msgstr "" msgid "installed" msgstr "" -msgid "invalid - Sample" +msgid "invalid FQDN / required - Sample" msgstr "" msgid "minimum value '0'" diff --git a/applications/luci-app-privoxy/Makefile b/applications/luci-app-privoxy/Makefile index e16eb4176e..260f840c63 100644 --- a/applications/luci-app-privoxy/Makefile +++ b/applications/luci-app-privoxy/Makefile @@ -10,11 +10,11 @@ PKG_NAME:=luci-app-privoxy # Version == major.minor.patch # increase "minor" on new functionality and "patch" on patches/optimization -PKG_VERSION:=1.0.4 +PKG_VERSION:=1.0.5 # Release == build # increase on changes of translation files -PKG_RELEASE:=2 +PKG_RELEASE:=1 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> diff --git a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua index c415f8e064..8ea2496173 100644 --- a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua @@ -14,7 +14,7 @@ local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" targe local VERINST = CTRL.ipkg_ver_installed("privoxy") local VEROK = CTRL.ipkg_ver_compare(VERINST,">=",CTRL.PRIVOXY_MIN) -local TITLE = [[</a><a href="javascript:alert(']] +local TITLE = [[<a href="javascript:alert(']] .. translate("Version Information") .. [[\n\nluci-app-privoxy]] .. [[\n\t]] .. translate("Version") .. [[:\t]] @@ -26,6 +26,7 @@ local TITLE = [[</a><a href="javascript:alert(']] .. [[\n\n]] .. [[')">]] .. translate("Privoxy WEB proxy") + .. [[</a>]] local DESC = translate("Privoxy is a non-caching web proxy with advanced filtering " .. "capabilities for enhancing privacy, modifying web page data and HTTP headers, " diff --git a/applications/luci-app-qos/luasrc/model/cbi/qos/qos.lua b/applications/luci-app-qos/luasrc/model/cbi/qos/qos.lua index a8ea57e311..177b17b07e 100644 --- a/applications/luci-app-qos/luasrc/model/cbi/qos/qos.lua +++ b/applications/luci-app-qos/luasrc/model/cbi/qos/qos.lua @@ -41,6 +41,16 @@ t:value("Priority", translate("priority")) t:value("Express", translate("express")) t:value("Normal", translate("normal")) t:value("Bulk", translate("low")) + +local uci = require "luci.model.uci" +uci.cursor():foreach("qos", "class", + function (section) + local n = section[".name"] + if string.sub(n,-string.len("_down"))~="_down" then + t:value(n) + end + end) + t.default = "Normal" srch = s:option(Value, "srchost", translate("Source host")) diff --git a/applications/luci-app-radicale/Makefile b/applications/luci-app-radicale/Makefile index 960618dbac..1501d4f0e8 100644 --- a/applications/luci-app-radicale/Makefile +++ b/applications/luci-app-radicale/Makefile @@ -10,7 +10,7 @@ PKG_NAME:=luci-app-radicale # Version == major.minor.patch # increase "minor" on new functionality and "patch" on patches/optimization -PKG_VERSION:=1.0.1 +PKG_VERSION:=1.0.2 # Release == build # increase on changes of translation files diff --git a/applications/luci-app-radicale/luasrc/controller/radicale.lua b/applications/luci-app-radicale/luasrc/controller/radicale.lua index 35f5a83a0c..10ec1fe545 100644 --- a/applications/luci-app-radicale/luasrc/controller/radicale.lua +++ b/applications/luci-app-radicale/luasrc/controller/radicale.lua @@ -82,7 +82,7 @@ function service_ok() end function app_title_main() - return [[</a><a href="javascript:alert(']] + return [[<a href="javascript:alert(']] .. I18N.translate("Version Information") .. [[\n\n]] .. luci_app_name() .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] @@ -100,12 +100,14 @@ function app_title_main() .. [[\n\n]] .. [[')">]] .. I18N.translate("Radicale CalDAV/CardDAV Server") + .. [[</a>]] end function app_title_back() - return [[</a><a href="]] + return [[<a href="]] .. DISP.build_url("admin", "services", "radicale") .. [[">]] .. I18N.translate("Radicale CalDAV/CardDAV Server") + .. [[</a>]] end function app_description() return I18N.translate("The Radicale Project is a complete CalDAV (calendar) and CardDAV (contact) server solution.") .. [[<br />]] diff --git a/contrib/package/freifunk-common/files/usr/bin/neigh.sh b/contrib/package/freifunk-common/files/usr/bin/neigh.sh index cf4342450c..b1dc01a77e 100755 --- a/contrib/package/freifunk-common/files/usr/bin/neigh.sh +++ b/contrib/package/freifunk-common/files/usr/bin/neigh.sh @@ -2,9 +2,69 @@ . /usr/share/libubox/jshn.sh -VARS="localIP:Local remoteIP:Remote validityTime:vTime linkQuality:LQ neighborLinkQuality:NLQ linkCost:Cost" +hostsfile_getname() +{ + local config="$1" + local i=0 + local value file -for HOST in 127.0.0.1 ::1;do + while value="$( uci -q get $config.@LoadPlugin[$i].library )"; do { + case "$value" in + 'olsrd_nameservice.so.'*) + file="$( uci -q get $config.@LoadPlugin[$i].hosts_file )" + break + ;; + esac + + i=$(( i + 1 )) + } done + + echo "${file:-/var/run/hosts_olsr}" +} + +read_hostnames() +{ + local file_list=" $( hostsfile_getname 'olsrd' ) $(hostsfile_getname 'olsrd6' ) " + local line ip hostname file file_list_uniq + + for file in $file_list; do { + case " $file_list_uniq " in + *" $file "*) + ;; + *) + file_list_uniq="$file_list_uniq $file" + ;; + esac + } done + + for file in $file_list_uniq; do { + [ -e "$file" ] || continue + + while read -r line; do { + case "$line" in + [0-9]*) + # 2001:bf7:820:901::1 stuttgarter-core.olsr # myself + # 10.63.160.161 AlexLaterne # 10.63.160.161 + set -f + set +f -- $line + ip="$1" + hostname="$2" + + # global vars, e.g. + # IP_1_2_3_4='foo' or IP_2001_bf7_820_901__1='bar' + eval IP_${ip//[.:]/_}="$hostname" + ;; + esac + } done <"$file" + } done +} + +read_hostnames + +VARS='localIP:Local remoteIP:Remote validityTime:vTime linkQuality:LQ' +VARS="$VARS neighborLinkQuality:NLQ linkCost:Cost remoteHostname:Host" + +for HOST in '127.0.0.1' '::1';do json_init json_load "$(echo /links|nc ${HOST} 9090)" if json_is_a links array;then @@ -29,6 +89,7 @@ for HOST in 127.0.0.1 ::1;do ;;*) for v in ${VARS};do eval printf \"%-\${_${v%:*}}s \" \$${v%:*} + eval remoteHostname="\$IP_${remoteIP//[.:]/_}" done echo ;;esac diff --git a/contrib/package/freifunk-policyrouting/files/etc/rc.d/S15-freifunk-policyrouting b/contrib/package/freifunk-policyrouting/files/etc/rc.d/S15-freifunk-policyrouting deleted file mode 120000 index 3c3dad87d1..0000000000 --- a/contrib/package/freifunk-policyrouting/files/etc/rc.d/S15-freifunk-policyrouting +++ /dev/null @@ -1 +0,0 @@ -../init.d/freifunk-policyrouting
\ No newline at end of file diff --git a/documentation/api/modules/luci.model.uci.html b/documentation/api/modules/luci.model.uci.html index 9e50d9900d..cf75aebccb 100644 --- a/documentation/api/modules/luci.model.uci.html +++ b/documentation/api/modules/luci.model.uci.html @@ -1119,6 +1119,12 @@ Name of created section Set a value or create a named section. + +When invoked with three arguments <code>config</code>, <code>sectionname</code>, <code>sectiontype</code>, +then a named section of the given type is created. + +When invoked with four arguments <code>config</code>, <code>sectionname</code>, <code>optionname</code> and +<code>optionvalue</code> then the value of the specified option is set to the given value. @@ -1138,7 +1144,7 @@ Set a value or create a named section. </li> <li> - value: UCI value or nil if you want to create a section + value: UCI value or nothing if you want to create a section </li> </ul> diff --git a/modules/luci-base/Makefile b/modules/luci-base/Makefile index 54506b023a..26cbe0820e 100644 --- a/modules/luci-base/Makefile +++ b/modules/luci-base/Makefile @@ -12,7 +12,7 @@ LUCI_TYPE:=mod LUCI_BASENAME:=base LUCI_TITLE:=LuCI core libraries -LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2 PKG_SOURCE_URL:=https://luasrcdiet.googlecode.com/files diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua index a92d8affb6..4b35731727 100644 --- a/modules/luci-base/luasrc/http.lua +++ b/modules/luci-base/luasrc/http.lua @@ -208,6 +208,7 @@ function splice(fd, size) end function redirect(url) + if url == "" then url = "/" end status(302, "Found") header("Location", url) close() diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua index 20e1032760..81fc416fed 100644 --- a/modules/luci-base/luasrc/model/network.lua +++ b/modules/luci-base/luasrc/model/network.lua @@ -1,8 +1,8 @@ -- Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. -local type, next, pairs, ipairs, loadfile, table - = type, next, pairs, ipairs, loadfile, table +local type, next, pairs, ipairs, loadfile, table, select + = type, next, pairs, ipairs, loadfile, table, select local tonumber, tostring, math = tonumber, tostring, math @@ -16,6 +16,7 @@ local utl = require "luci.util" local dsp = require "luci.dispatcher" local uci = require "luci.model.uci" local lng = require "luci.i18n" +local jsc = require "luci.jsonc" module "luci.model.network" @@ -31,10 +32,10 @@ local _protocols = { } local _interfaces, _bridge, _switch, _tunnel local _ubusnetcache, _ubusdevcache, _ubuswificache -local _uci_real, _uci_state +local _uci function _filter(c, s, o, r) - local val = _uci_real:get(c, s, o) + local val = _uci:get(c, s, o) if val then local l = { } if type(val) == "string" then @@ -44,9 +45,9 @@ function _filter(c, s, o, r) end end if #l > 0 then - _uci_real:set(c, s, o, table.concat(l, " ")) + _uci:set(c, s, o, table.concat(l, " ")) else - _uci_real:delete(c, s, o) + _uci:delete(c, s, o) end elseif type(val) == "table" then for _, val in ipairs(val) do @@ -55,16 +56,16 @@ function _filter(c, s, o, r) end end if #l > 0 then - _uci_real:set(c, s, o, l) + _uci:set(c, s, o, l) else - _uci_real:delete(c, s, o) + _uci:delete(c, s, o) end end end end function _append(c, s, o, a) - local val = _uci_real:get(c, s, o) or "" + local val = _uci:get(c, s, o) or "" if type(val) == "string" then local l = { } for val in val:gmatch("%S+") do @@ -73,7 +74,7 @@ function _append(c, s, o, a) end end l[#l+1] = a - _uci_real:set(c, s, o, table.concat(l, " ")) + _uci:set(c, s, o, table.concat(l, " ")) elseif type(val) == "table" then local l = { } for _, val in ipairs(val) do @@ -82,7 +83,7 @@ function _append(c, s, o, a) end end l[#l+1] = a - _uci_real:set(c, s, o, l) + _uci:set(c, s, o, l) end end @@ -95,15 +96,15 @@ function _stror(s1, s2) end function _get(c, s, o) - return _uci_real:get(c, s, o) + return _uci:get(c, s, o) end function _set(c, s, o, v) if v ~= nil then if type(v) == "boolean" then v = v and "1" or "0" end - return _uci_real:set(c, s, o, v) + return _uci:set(c, s, o, v) else - return _uci_real:delete(c, s, o) + return _uci:delete(c, s, o) end end @@ -127,7 +128,7 @@ function _wifi_state(key, val, field) for radio, radiostate in pairs(_ubuswificache) do for ifc, ifcstate in pairs(radiostate.interfaces) do if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then - local s = _uci_real:get_all('wireless.%s' % ifcstate.section) + local s = _uci:get_all('wireless.%s' % ifcstate.section) if s then ifcstate.section = s['.name'] end @@ -153,7 +154,7 @@ function _wifi_lookup(ifn) local num = 0 ifnidx = tonumber(ifnidx) - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device == radio then num = num + 1 @@ -166,20 +167,9 @@ function _wifi_lookup(ifn) return sid - -- looks like wifi, try to locate the section via state vars + -- looks like wifi, try to locate the section via ubus state elseif _wifi_iface(ifn) then - local sid = _wifi_state("ifname", ifn, "section") - if not sid then - _uci_state:foreach("wireless", "wifi-iface", - function(s) - if s.ifname == ifn then - sid = s['.name'] - return false - end - end) - end - - return sid + return _wifi_state("ifname", ifn, "section") end end @@ -205,8 +195,7 @@ end function init(cursor) - _uci_real = cursor or _uci_real or uci.cursor() - _uci_state = _uci_real:substate() + _uci = cursor or _uci or uci.cursor() _interfaces = { } _bridge = { } @@ -281,13 +270,13 @@ function init(cursor) end function save(self, ...) - _uci_real:save(...) - _uci_real:load(...) + _uci:save(...) + _uci:load(...) end function commit(self, ...) - _uci_real:commit(...) - _uci_real:load(...) + _uci:commit(...) + _uci:load(...) end function ifnameof(self, x) @@ -345,7 +334,7 @@ end function add_network(self, n, options) local oldnet = self:get_network(n) if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then - if _uci_real:section("network", "interface", n, options) then + if _uci:section("network", "interface", n, options) then return network(n) end elseif oldnet and oldnet:is_empty() then @@ -360,7 +349,7 @@ function add_network(self, n, options) end function get_network(self, n) - if n and _uci_real:get("network", n) == "interface" then + if n and _uci:get("network", n) == "interface" then return network(n) end end @@ -369,7 +358,7 @@ function get_networks(self) local nets = { } local nls = { } - _uci_real:foreach("network", "interface", + _uci:foreach("network", "interface", function(s) nls[s['.name']] = network(s['.name']) end) @@ -383,18 +372,18 @@ function get_networks(self) end function del_network(self, n) - local r = _uci_real:delete("network", n) + local r = _uci:delete("network", n) if r then - _uci_real:delete_all("network", "alias", + _uci:delete_all("network", "alias", function(s) return (s.interface == n) end) - _uci_real:delete_all("network", "route", + _uci:delete_all("network", "route", function(s) return (s.interface == n) end) - _uci_real:delete_all("network", "route6", + _uci:delete_all("network", "route6", function(s) return (s.interface == n) end) - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) local net local rest = { } @@ -404,10 +393,10 @@ function del_network(self, n) end end if #rest > 0 then - _uci_real:set("wireless", s['.name'], "network", + _uci:set("wireless", s['.name'], "network", table.concat(rest, " ")) else - _uci_real:delete("wireless", s['.name'], "network") + _uci:delete("wireless", s['.name'], "network") end end) end @@ -417,31 +406,31 @@ end function rename_network(self, old, new) local r if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then - r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old)) + r = _uci:section("network", "interface", new, _uci:get_all("network", old)) if r then - _uci_real:foreach("network", "alias", + _uci:foreach("network", "alias", function(s) if s.interface == old then - _uci_real:set("network", s['.name'], "interface", new) + _uci:set("network", s['.name'], "interface", new) end end) - _uci_real:foreach("network", "route", + _uci:foreach("network", "route", function(s) if s.interface == old then - _uci_real:set("network", s['.name'], "interface", new) + _uci:set("network", s['.name'], "interface", new) end end) - _uci_real:foreach("network", "route6", + _uci:foreach("network", "route6", function(s) if s.interface == old then - _uci_real:set("network", s['.name'], "interface", new) + _uci:set("network", s['.name'], "interface", new) end end) - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) local net local list = { } @@ -453,12 +442,12 @@ function rename_network(self, old, new) end end if #list > 0 then - _uci_real:set("wireless", s['.name'], "network", + _uci:set("wireless", s['.name'], "network", table.concat(list, " ")) end end) - _uci_real:delete("network", old) + _uci:delete("network", old) end end return r or false @@ -470,7 +459,7 @@ function get_interface(self, i) else local ifc local num = { } - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 @@ -485,6 +474,21 @@ function get_interface(self, i) end end +local function swdev_from_board_json() + local boardinfo = jsc.parse(nfs.readfile("/etc/board.json") or "") + if type(boardinfo) == "table" and type(boardinfo.network) == "table" then + local net, val + for net, val in pairs(boardinfo.network) do + if type(val) == "table" and type(val.ifname) == "string" and + val.create_vlan == true + then + return val.ifname + end + end + end + return nil +end + function get_interfaces(self) local iface local ifaces = { } @@ -493,7 +497,7 @@ function get_interfaces(self) local baseof = { } -- find normal interfaces - _uci_real:foreach("network", "interface", + _uci:foreach("network", "interface", function(s) for iface in utl.imatch(s.ifname) do if not _iface_ignore(iface) and not _wifi_iface(iface) then @@ -510,7 +514,7 @@ function get_interfaces(self) end -- find vlan interfaces - _uci_real:foreach("network", "switch_vlan", + _uci:foreach("network", "switch_vlan", function(s) if not s.device then return @@ -526,7 +530,7 @@ function get_interfaces(self) end end if not base or not base:match("^eth%d") then - base = "eth0" + base = swdev_from_board_json() or "eth0" end else base = s.device @@ -551,7 +555,7 @@ function get_interfaces(self) -- find wifi interfaces local num = { } local wfs = { } - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 @@ -572,7 +576,7 @@ function ignore_interface(self, x) end function get_wifidev(self, dev) - if _uci_real:get("wireless", dev) == "wifi-device" then + if _uci:get("wireless", dev) == "wifi-device" then return wifidev(dev) end end @@ -581,7 +585,7 @@ function get_wifidevs(self) local devs = { } local wfd = { } - _uci_real:foreach("wireless", "wifi-device", + _uci:foreach("wireless", "wifi-device", function(s) wfd[#wfd+1] = s['.name'] end) local dev @@ -601,9 +605,9 @@ end function add_wifinet(self, net, options) if type(options) == "table" and options.device and - _uci_real:get("wireless", options.device) == "wifi-device" + _uci:get("wireless", options.device) == "wifi-device" then - local wnet = _uci_real:section("wireless", "wifi-iface", nil, options) + local wnet = _uci:section("wireless", "wifi-iface", nil, options) return wifinet(wnet) end end @@ -611,7 +615,7 @@ end function del_wifinet(self, net) local wnet = _wifi_lookup(net) if wnet then - _uci_real:delete("wireless", wnet) + _uci:delete("wireless", wnet) return true end return false @@ -684,7 +688,7 @@ end function network(name, proto) if name then - local p = proto or _uci_real:get("network", name, "proto") + local p = proto or _uci:get("network", name, "proto") local c = p and _protocols[p] or protocol return c(name) end @@ -695,7 +699,7 @@ function protocol.__init__(self, name) end function protocol._get(self, opt) - local v = _uci_real:get("network", self.sid, opt) + local v = _uci:get("network", self.sid, opt) if type(v) == "table" then return table.concat(v, " ") end @@ -730,7 +734,7 @@ function protocol.ifname(self) end if not ifname then local num = { } - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device then num[s.device] = num[s.device] @@ -779,17 +783,21 @@ function protocol.uptime(self) end function protocol.expires(self) - local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired")) - local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime")) - if a and l then - l = l - (nxo.sysinfo().uptime - a) - return l > 0 and l or 0 + local u = self:_ubus("uptime") + local d = self:_ubus("data") + + if type(u) == "number" and type(d) == "table" and + type(d.leasetime) == "number" + then + local r = (d.leasetime - (u % d.leasetime)) + return r > 0 and r or 0 end + return -1 end function protocol.metric(self) - return tonumber(_uci_state:get("network", self.sid, "metric")) or 0 + return self:_ubus("metric") or 0 end function protocol.ipaddr(self) @@ -797,6 +805,20 @@ function protocol.ipaddr(self) return addrs and #addrs > 0 and addrs[1].address end +function protocol.ipaddrs(self) + local addrs = self:_ubus("ipv4-address") + local rv = { } + + if type(addrs) == "table" then + local n, addr + for n, addr in ipairs(addrs) do + rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask } + end + end + + return rv +end + function protocol.netmask(self) local addrs = self:_ubus("ipv4-address") return addrs and #addrs > 0 and @@ -835,6 +857,28 @@ function protocol.ip6addr(self) end end +function protocol.ip6addrs(self) + local addrs = self:_ubus("ipv6-address") + local rv = { } + local n, addr + + if type(addrs) == "table" then + for n, addr in ipairs(addrs) do + rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask } + end + end + + addrs = self:_ubus("ipv6-prefix-assignment") + + if type(addrs) == "table" then + for n, addr in ipairs(addrs) do + rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask } + end + end + + return rv +end + function protocol.gw6addr(self) local _, route for _, route in ipairs(self:_ubus("route") or { }) do @@ -885,7 +929,7 @@ function protocol.is_empty(self) rv = false end - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) local n for n in utl.imatch(s.network) do @@ -937,12 +981,12 @@ function protocol.get_interface(self) else local ifn = nil local num = { } - for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do + for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do ifn = ifn:match("^[^:/]+") return ifn and interface(ifn, self) end ifn = nil - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 @@ -977,7 +1021,7 @@ function protocol.get_interfaces(self) local num = { } local wfs = { } - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 @@ -1020,7 +1064,7 @@ function protocol.contains_interface(self, ifname) local wif = _wifi_lookup(ifname) if wif then local n - for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do + for n in utl.imatch(_uci:get("wireless", wif, "network")) do if n == self.sid then return true end @@ -1066,7 +1110,8 @@ function interface.name(self) end function interface.mac(self) - return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper() + local mac = self:_ubus("macaddr") + return mac and mac:upper() end function interface.ipaddrs(self) @@ -1278,22 +1323,11 @@ function wifidev.is_up(self) return (_ubuswificache[self.sid].up == true) end - local up = false - _uci_state:foreach("wireless", "wifi-iface", - function(s) - if s.device == self.sid then - if s.up == "1" then - up = true - return false - end - end - end) - - return up + return false end function wifidev.get_wifinet(self, net) - if _uci_real:get("wireless", net) == "wifi-iface" then + if _uci:get("wireless", net) == "wifi-iface" then return wifinet(net) else local wnet = _wifi_lookup(net) @@ -1306,7 +1340,7 @@ end function wifidev.get_wifinets(self) local nets = { } - _uci_real:foreach("wireless", "wifi-iface", + _uci:foreach("wireless", "wifi-iface", function(s) if s.device == self.sid then nets[#nets+1] = wifinet(s['.name']) @@ -1320,7 +1354,7 @@ function wifidev.add_wifinet(self, options) options = options or { } options.device = self.sid - local wnet = _uci_real:section("wireless", "wifi-iface", nil, options) + local wnet = _uci:section("wireless", "wifi-iface", nil, options) if wnet then return wifinet(wnet, options) end @@ -1329,12 +1363,12 @@ end function wifidev.del_wifinet(self, net) if utl.instanceof(net, wifinet) then net = net.sid - elseif _uci_real:get("wireless", net) ~= "wifi-iface" then + elseif _uci:get("wireless", net) ~= "wifi-iface" then net = _wifi_lookup(net) end - if net and _uci_real:get("wireless", net, "device") == self.sid then - _uci_real:delete("wireless", net) + if net and _uci:get("wireless", net, "device") == self.sid then + _uci:delete("wireless", net) return true end @@ -1347,26 +1381,58 @@ wifinet = utl.class() function wifinet.__init__(self, net, data) self.sid = net + local n = 0 local num = { } - local netid - _uci_real:foreach("wireless", "wifi-iface", + local netid, sid + _uci:foreach("wireless", "wifi-iface", function(s) + n = n + 1 if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 if s['.name'] == self.sid then + sid = "@wifi-iface[%d]" % n netid = "%s.network%d" %{ s.device, num[s.device] } return false end end end) + if sid then + local _, k, r, i + for k, r in pairs(_ubuswificache) do + if type(r) == "table" and + type(r.interfaces) == "table" + then + for _, i in ipairs(r.interfaces) do + if type(i) == "table" and i.section == sid then + self._ubusdata = { + radio = k, + dev = r, + net = i + } + end + end + end + end + end + local dev = _wifi_state("section", self.sid, "ifname") or netid self.netid = netid self.wdev = dev self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } - self.iwdata = data or _uci_state:get_all("wireless", self.sid) or - _uci_real:get_all("wireless", self.sid) or { } +end + +function wifinet.ubus(self, ...) + local n, v = self._ubusdata + for n = 1, select('#', ...) do + if type(v) == "table" then + v = v[select(n, ...)] + else + return nil + end + end + return v end function wifinet.get(self, opt) @@ -1378,19 +1444,23 @@ function wifinet.set(self, opt, val) end function wifinet.mode(self) - return _uci_state:get("wireless", self.sid, "mode") or "ap" + return self:ubus("net", "config", "mode") or self:get("mode") or "ap" end function wifinet.ssid(self) - return _uci_state:get("wireless", self.sid, "ssid") + return self:ubus("net", "config", "ssid") or self:get("ssid") end function wifinet.bssid(self) - return _uci_state:get("wireless", self.sid, "bssid") + return self:ubus("net", "config", "bssid") or self:get("bssid") end function wifinet.network(self) - return _uci_state:get("wifinet", self.sid, "network") + local net, networks = nil, { } + for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do + networks[#networks+1] = net + end + return networks end function wifinet.id(self) @@ -1402,7 +1472,7 @@ function wifinet.name(self) end function wifinet.ifname(self) - local ifname = self.iwinfo.ifname + local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then ifname = self.wdev end @@ -1410,9 +1480,8 @@ function wifinet.ifname(self) end function wifinet.get_device(self) - if self.iwdata.device then - return wifidev(self.iwdata.device) - end + local dev = self:ubus("radio") or self:get("device") + return dev and wifidev(dev) or nil end function wifinet.is_up(self) @@ -1421,7 +1490,7 @@ function wifinet.is_up(self) end function wifinet.active_mode(self) - local m = _stror(self.iwdata.mode, self.iwinfo.mode) or "ap" + local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap" if m == "ap" then m = "Master" elseif m == "sta" then m = "Client" @@ -1438,11 +1507,11 @@ function wifinet.active_mode_i18n(self) end function wifinet.active_ssid(self) - return _stror(self.iwdata.ssid, self.iwinfo.ssid) + return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid") end function wifinet.active_bssid(self) - return _stror(self.iwdata.bssid, self.iwinfo.bssid) or "00:00:00:00:00:00" + return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid") end function wifinet.active_encryption(self) @@ -1469,8 +1538,8 @@ function wifinet.bitrate(self) end function wifinet.channel(self) - return self.iwinfo.channel or - tonumber(_uci_state:get("wireless", self.iwdata.device, "channel")) + return self.iwinfo.channel or self:ubus("dev", "config", "channel") or + tonumber(self:get("channel")) end function wifinet.signal(self) @@ -1482,7 +1551,7 @@ function wifinet.noise(self) end function wifinet.country(self) - return self.iwinfo.country or "00" + return self.iwinfo.country or self:ubus("dev", "config", "country") or "00" end function wifinet.txpower(self) @@ -1548,8 +1617,8 @@ end function wifinet.get_networks(self) local nets = { } local net - for net in utl.imatch(tostring(self.iwdata.network)) do - if _uci_real:get("network", net) == "interface" then + for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do + if _uci:get("network", net) == "interface" then nets[#nets+1] = network(net) end end diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc index 80464f7cec..7591b68b02 100644 --- a/modules/luci-base/luasrc/model/uci.luadoc +++ b/modules/luci-base/luasrc/model/uci.luadoc @@ -235,12 +235,18 @@ Saves changes made to a config to make them committable. ---[[ Set a value or create a named section. +When invoked with three arguments `config`, `sectionname`, `sectiontype`, +then a named section of the given type is created. + +When invoked with four arguments `config`, `sectionname`, `optionname` and +`optionvalue` then the value of the specified option is set to the given value. + @class function @name Cursor.set @param config UCI config @param section UCI section name @param option UCI option or UCI section type -@param value UCI value or nil if you want to create a section +@param value UCI value or nothing if you want to create a section @return Boolean whether operation succeeded ]] diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua index dcf8230b3e..787bc66f90 100644 --- a/modules/luci-base/luasrc/util.lua +++ b/modules/luci-base/luasrc/util.lua @@ -9,6 +9,7 @@ local ldebug = require "luci.debug" local string = require "string" local coroutine = require "coroutine" local tparser = require "luci.template.parser" +local json = require "luci.jsonc" local _ubus = require "ubus" local _ubus_connection = nil @@ -600,55 +601,11 @@ function ubus(object, method, data) end function serialize_json(x, cb) - local rv, push = nil, cb - if not push then - rv = { } - push = function(tok) rv[#rv+1] = tok end - end - - if x == nil then - push("null") - elseif type(x) == "table" then - -- test if table is array like - local k, v - local n1, n2 = 0, 0 - for k in pairs(x) do n1 = n1 + 1 end - for k in ipairs(x) do n2 = n2 + 1 end - - if n1 == n2 and n1 > 0 then - push("[") - for k = 1, n2 do - if k > 1 then - push(",") - end - serialize_json(x[k], push) - end - push("]") - else - push("{") - for k, v in pairs(x) do - push("%q:" % tostring(k)) - serialize_json(v, push) - if next(x, k) then - push(",") - end - end - push("}") - end - elseif type(x) == "number" or type(x) == "boolean" then - if (x ~= x) then - -- NaN is the only value that doesn't equal to itself. - push("Number.NaN") - else - push(tostring(x)) - end + local js = json.stringify(x) + if type(cb) == "function" then + cb(js) else - push('"%s"' % tostring(x):gsub('["%z\1-\31\\]', - function(c) return '\\u%04x' % c:byte(1) end)) - end - - if not cb then - return table.concat(rv, "") + return js end end diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/network.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/network.lua index 879e54b249..aa533cb70b 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/network.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/network.lua @@ -235,6 +235,8 @@ function iface_status(ifaces) proto = net:proto(), uptime = net:uptime(), gwaddr = net:gwaddr(), + ipaddrs = net:ipaddrs(), + ip6addrs = net:ip6addrs(), dnsaddrs = net:dnsaddrs(), name = device:shortname(), type = device:type(), @@ -246,29 +248,9 @@ function iface_status(ifaces) rx_packets = device:rx_packets(), tx_packets = device:tx_packets(), - ipaddrs = { }, - ip6addrs = { }, subdevices = { } } - local _, a - for _, a in ipairs(device:ipaddrs()) do - data.ipaddrs[#data.ipaddrs+1] = { - addr = a:host():string(), - netmask = a:mask():string(), - prefix = a:prefix() - } - end - for _, a in ipairs(device:ip6addrs()) do - if not a:is6linklocal() then - data.ip6addrs[#data.ip6addrs+1] = { - addr = a:host():string(), - netmask = a:mask():string(), - prefix = a:prefix() - } - end - end - for _, device in ipairs(net:get_interfaces() or {}) do data.subdevices[#data.subdevices+1] = { name = device:shortname(), diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/iface_add.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/iface_add.lua index da1e140de0..e48e3b4bdf 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/iface_add.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/iface_add.lua @@ -17,7 +17,12 @@ newnet = m:field(Value, "_netname", translate("Name of the new interface"), newnet:depends("_attach", "") newnet.default = arg[1] and "net_" .. arg[1]:gsub("[^%w_]+", "_") -newnet.datatype = "uciname" +newnet.datatype = "and(uciname,maxlength(15))" + +advice = m:field(DummyValue, "d1", translate("Note: interface name length"), + translate("Maximum length of the name is 15 characters including " .. + "the automatic protocol/bridge prefix (br-, 6in4-, pppoe- etc.)" + )) newproto = m:field(ListValue, "_netproto", translate("Protocol of the new interface")) diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/vlan.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/vlan.lua index 40f8014f1a..ce3c3ef325 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/vlan.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/vlan.lua @@ -107,6 +107,12 @@ m.uci:foreach("network", "switch", local sp = s:option(ListValue, "mirror_source_port", translate("Mirror source port")) local mp = s:option(ListValue, "mirror_monitor_port", translate("Mirror monitor port")) + sp:depends("enable_mirror_tx", "1") + sp:depends("enable_mirror_rx", "1") + + mp:depends("enable_mirror_tx", "1") + mp:depends("enable_mirror_rx", "1") + local pt for pt = 0, num_ports - 1 do local name diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua index 9a2bd5d201..b91c29b088 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua @@ -361,7 +361,8 @@ s:tab("encryption", translate("Wireless Security")) s:tab("macfilter", translate("MAC-Filter")) s:tab("advanced", translate("Advanced Settings")) -s:taboption("general", Value, "ssid", translate("<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>")) +ssid = s:taboption("general", Value, "ssid", translate("<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>")) +ssid.datatype = "maxlength(32)" mode = s:taboption("general", ListValue, "mode", translate("Mode")) mode.override_values = true diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/fstab/mount.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/fstab/mount.lua index 2652e00f25..f5751673fd 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/fstab/mount.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_system/fstab/mount.lua @@ -112,7 +112,7 @@ o.default = [[ mkdir -p /tmp/extroot mount --bind / /tmp/introot mount /dev/sda1 /tmp/extroot -tar -C /tmp/intproot -cvf - . | tar -C /tmp/extroot -xf - +tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf - umount /tmp/introot umount /tmp/extroot</pre> ]] %{ diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_overview.htm b/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_overview.htm index 9a77f89106..b222cbca18 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_overview.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_overview.htm @@ -135,7 +135,7 @@ html += String.format('<strong><%:Uptime%>:</strong> %t<br />', ifc.uptime); } - if (ifc.type != 'tunnel') + if (ifc.macaddr) { html += String.format('<strong><%:MAC-Address%>:</strong> %s<br />', ifc.macaddr); } @@ -153,10 +153,9 @@ for (var i = 0; i < ifc.ipaddrs.length; i++) html += String.format( - '%s%s/%d', + '%s%s', i ? ', ' : '', - ifc.ipaddrs[i].addr, - ifc.ipaddrs[i].prefix + ifc.ipaddrs[i] ); html += '<br />'; @@ -168,10 +167,9 @@ for (var i = 0; i < ifc.ip6addrs.length; i++) html += String.format( - '%s%s/%d', + '%s%s', i ? ', ' : '', - ifc.ip6addrs[i].addr.toUpperCase(), - ifc.ip6addrs[i].prefix + ifc.ip6addrs[i].toUpperCase() ); html += '<br />'; @@ -250,7 +248,7 @@ <input type="button" class="cbi-button cbi-button-reload" style="width:100px" onclick="iface_shutdown('<%=net[1]%>', true)" title="<%:Reconnect this interface%>" value="<%:Connect%>" /> <input type="button" class="cbi-button cbi-button-reset" style="width:100px" onclick="iface_shutdown('<%=net[1]%>', false)" title="<%:Shutdown this interface%>" value="<%:Stop%>" /> <input type="button" class="cbi-button cbi-button-edit" style="width:100px" onclick="location.href='<%=url("admin/network/network", net[1])%>'" title="<%:Edit this interface%>" value="<%:Edit%>" id="<%=net[1]%>-ifc-edit" /> - <input type="submit" class="cbi-button cbi-button-remove" style="width:100px" onclick="iface_delete('<%=net[1]%>')" value="<%:Delete%>" /> + <input type="button" class="cbi-button cbi-button-remove" style="width:100px" onclick="iface_delete('<%=net[1]%>')" value="<%:Delete%>" /> </td> </tr> <% end %> diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_status.htm b/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_status.htm index 1ebdbfcfb4..94d9e88117 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_status.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_network/iface_status.htm @@ -25,7 +25,7 @@ html += String.format('<strong><%:Uptime%>:</strong> %t<br />', ifc.uptime); } - if (ifc.type != 'tunnel') + if (ifc.macaddr) { html += String.format('<strong><%:MAC-Address%>:</strong> %s<br />', ifc.macaddr); } @@ -43,10 +43,9 @@ for (var i = 0; i < ifc.ipaddrs.length; i++) html += String.format( - '%s%s/%d', + '%s%s', i ? ', ' : '', - ifc.ipaddrs[i].addr, - ifc.ipaddrs[i].prefix + ifc.ipaddrs[i] ); html += '<br />'; @@ -58,10 +57,9 @@ for (var i = 0; i < ifc.ip6addrs.length; i++) html += String.format( - '%s%s/%d', + '%s%s', i ? ', ' : '', - ifc.ip6addrs[i].addr.toUpperCase(), - ifc.ip6addrs[i].prefix + ifc.ip6addrs[i].toUpperCase() ); html += '<br />'; |