luci-app-ddns: rollup to 2.3.0 to reflect changes on ddns-scripts 548/head
authorChristian Schoenebeck <christian.schoenebeck@gmail.com>
Sun, 15 Nov 2015 10:42:43 +0000 (11:42 +0100)
committerChristian Schoenebeck <christian.schoenebeck@gmail.com>
Sun, 15 Nov 2015 10:42:43 +0000 (11:42 +0100)
- support for new options "lookup_host", "param_enc" and "param_opt"
- rewritten ddns provider handling to only show/check needed options "domain", "username", etc.
- modified version check/handling incl. using new ipkg.compare_versions function
- modified map.title and map.description generation
- changed XHR.poll interval to 15 seconds on system status page
- using new value_parse function for testing and later implementation into cbi.lua
- some optimizations

Signed-off-by: Christian Schoenebeck <christian.schoenebeck@gmail.com>
applications/luci-app-ddns/luasrc/controller/ddns.lua
applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua
applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua
applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua
applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua
applications/luci-app-ddns/luasrc/tools/ddns.lua
applications/luci-app-ddns/luasrc/view/ddns/detail_logview.htm
applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm
applications/luci-app-ddns/luasrc/view/ddns/system_status.htm
applications/luci-app-ddns/po/de/ddns.po
applications/luci-app-ddns/po/templates/ddns.pot

index 4fbd3c3..29598ea 100644 (file)
@@ -10,28 +10,43 @@ local NX   = require "nixio"
 local NXFS = require "nixio.fs"
 local DISP = require "luci.dispatcher"
 local HTTP = require "luci.http"
 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 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 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
 
 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
 
        -- 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",
        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
 
        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
 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
                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]]
                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
                        [[ ]] .. 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,
                        section  = section,
                        enabled  = enabled,
                        iface    = iface,
-                       domain   = domain,
+                       lookup   = lookup_host,
                        reg_ip   = reg_ip,
                        pid      = pid,
                        datelast = datelast,
                        reg_ip   = reg_ip,
                        pid      = pid,
                        datelast = datelast,
@@ -235,3 +303,4 @@ function status()
        HTTP.prepare_content("application/json")
        HTTP.write_json(data)
 end
        HTTP.prepare_content("application/json")
        HTTP.write_json(data)
 end
+
index e655ce5..88429fd 100644 (file)
@@ -8,13 +8,15 @@ local NX   = require "nixio"
 local NXFS = require "nixio.fs"
 local SYS  = require "luci.sys"
 local UTIL = require "luci.util"
 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 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 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
 
 -- 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 -- #####################################################
 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")
                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
                        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 ..
                        font_red ..
                                translate("IPv6 not supported") ..
                        font_off ..
@@ -52,16 +54,46 @@ err_ipv6_other = bold_on ..
                        [[</a>]] ..
                 bold_off
 
                        [[</a>]] ..
                 bold_off
 
-function err_tab_basic(self)
+local function err_tab_basic(self)
        return translate("Basic Settings") .. " - " .. self.title .. ": "
 end
        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
        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
 
        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
 -- 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
 
        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 ?
 
 m.on_after_commit = function(self)
        if self.changed then    -- changes ?
@@ -126,19 +235,42 @@ m.on_after_commit = function(self)
        end
 end
 
        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
 -- 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 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 -- ###################################################
 
 -- cbi-section definition -- ###################################################
-ns = m:section( NamedSection, section, "service",
+local ns = m:section( NamedSection, section, "service",
        translate("Details for") .. ([[: <strong>%s</strong>]] % section),
        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 )
 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  #####################################################################################
 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"
 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
 
 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") )
 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
        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
 
 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
 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
 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, 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
                        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   = "-"
 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
 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
 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, 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
                        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 = 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)
 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
                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
                        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
 
                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
 
        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 = 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)
 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
                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
                        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
                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",
 dom = ns:taboption("basic", Value, "domain",
-               translate("Hostname/Domain"),
+               translate("Domain"),
                translate("Replaces [DOMAIN] in Update-URL") )
                translate("Replaces [DOMAIN] in Update-URL") )
-dom.rmempty    = false
-dom.placeholder        = "mypersonaldomain.dyndns.org"
+dom.placeholder = "myhost.example.com"
 function dom.validate(self, value)
 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
 
 end
 
--- IPv4/IPv6 - username -- #####################################################
+-- IPv4/IPv6 - username -- ####################################################
 user = ns:taboption("basic", Value, "username",
                translate("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)
 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
 
 end
 
--- IPv4/IPv6 - password -- #####################################################
+-- IPv4/IPv6 - password -- ####################################################
 pw = ns:taboption("basic", Value, "password",
                translate("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)
 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
        end
-       return value
 end
 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"
 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
        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
                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") .. " !"
        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
 
        end
 end
 
--- IPv4/IPv6 - cacert (NEW) -- #################################################
+-- IPv4/IPv6 - cacert -- ######################################################
 if has_ssl then
        cert = ns:taboption("basic", Value, "cacert",
                translate("Path to CA-Certificate"),
 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")
                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.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
        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
                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
 
 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") )
 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
        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") )
 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
        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") )
 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
                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")
 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
                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") )
 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
                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")
 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
                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") )
 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)
        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)
                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
                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
                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 = 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)
 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
 
        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) ..
                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)
        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
                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
 -- 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)
        return DDNS.read_value(self, section, "interface")
 end
 function eif4.validate(self, value)
+       local fsrc4 = src4:formvalue(section)
        if usev6:formvalue(section) == "1"
        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)
                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"
        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
                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",
 -- 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)
        return DDNS.read_value(self, section, "interface")
 end
 function eif6.validate(self, value)
+       local fsrc6 = src6:formvalue(section)
        if usev6:formvalue(section) == "0"
        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
                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)
        end
 end
 function eif6.write(self, section, value)
+       local fsrc6 = src6:formvalue(section)
        if usev6:formvalue(section) == "0"
        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
                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")
 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)
        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
                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
 
 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
 -- 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
                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
 
 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"),
 -- 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
 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")
                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
                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") )
 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
                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
 
 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",
 -- 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
        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"
                        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
                        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
 
 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.") )
 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"))
 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 = 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 = 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
 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)
        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)
        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
                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 = 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)
 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)
        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
                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 = 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
 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
                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 = 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"))
 --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
                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 = 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'")
 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
                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
 
 end
 
--- retry_interval -- ###########################################################
+-- retry_interval -- ##########################################################
 ri = ns:taboption("timer", Value, "retry_interval",
        translate("Error Retry Interval") )
 ri.template = "ddns/detail_value"
 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
 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
                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 = 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"))
 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
                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")
 lv = ns:taboption("logview", DummyValue, "_logview")
 lv.template = "ddns/detail_logview"
 lv.inputtitle = translate("Read / Reread log file")
index 25d09b7..23ce4f1 100644 (file)
@@ -5,18 +5,14 @@ local NX   = require "nixio"
 local NXFS = require "nixio.fs"
 local DISP = require "luci.dispatcher"
 local SYS  = require "luci.sys"
 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")
 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 ?
 
 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"
                .. "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"
 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")
 
 -- 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"
                .. 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)
 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
        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")
 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"
 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")
 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"
 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")
 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
 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
        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) 
 
 -- 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"
                .. [[<br />]]
                .. translate("To use cURL activate this option.")
        pc.orientation  = "horizontal"
-       pc.rmempty      = true
        pc.default      = "0"
        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
 end
 
 return m
index 2a65fd0..031bf51 100644 (file)
@@ -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.
 
 -- 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 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 -- ##################################################
 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
 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">]]
 
 -- html constants
 font_red = [[<font color="red">]]
@@ -22,15 +20,9 @@ bold_off = [[</strong>]]
 
 -- cbi-map definition -- #######################################################
 m = Map("ddns")
 
 -- 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
 
 -- 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
        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
        local dv = s:option(DummyValue, "_update_needed")
        dv.titleref = DISP.build_url("admin", "system", "packages")
        dv.rawhtml  = true
index 5b5925b..46fc0a4 100644 (file)
@@ -2,10 +2,10 @@
 -- Licensed to the public under the Apache License 2.0.
 
 local NXFS = require "nixio.fs"
 -- 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 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 ?
 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
                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">]]
 
 -- html constants
 font_red = [[<font color="red">]]
@@ -25,22 +25,8 @@ bold_off = [[</strong>]]
 
 -- cbi-map definition -- #######################################################
 m = Map("ddns")
 
 -- 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 ?
 
 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
 
        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)
 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)
        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)
        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
        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
                [[ ]] .. 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
        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",
 
 -- show PID and next update
 upd = ts:option( DummyValue, "_update",
index 4466063..ecc4131 100644 (file)
@@ -96,58 +96,6 @@ function get_pid(section)
        return pid
 end
 
        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
 -- 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
 
        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
                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
                self.section.changed = true
        end
 end
index 4dcb7bb..fd1d5be 100644 (file)
@@ -6,7 +6,7 @@
                var txt = document.getElementById("cbid.ddns." + section + "._logview.txt");    // TextArea
                if ( !txt ) { return; } // security check
 
                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%>";
                        function(x) {
                                if (x.responseText == "_nodata_")
                                        txt.value = "<%:File not found or empty%>";
index b6d4ebb..b409ed0 100644 (file)
@@ -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 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
                        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
                                        break;
                        }
 
                                        break;
                        }
 
-                       // domain
-                       // (data[i].domain ignored here
+                       // lookup
+                       // (data[i].lookup ignored here
 
                        // registered IP
                        // rip.innerHTML = "Registered IP";
 
                        // 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>';
                                rip.innerHTML = '';
                        else if (data[i].reg_ip == "_nodata_")
                                rip.innerHTML = '<em><%:No data%></em>';
 
                // do start/stop
                var btnXHR = new XHR();
 
                // 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 "&#38;" or "&amp;"
                        function(x, data) {
                                if (x.responseText == "_uncommitted_") {
                                        // we need a trick to display Ampersand "&" in stead of "&#38;" or "&amp;"
        }
 
        // force to immediate show status on page load (not waiting for XHR.poll)
        }
 
        // 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); }
                }
                function(x, data) {
                        if (data) { _data2elements(data); }
                }
        // 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
        // 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); }
                }
                function(x, data) {
                        if (data) { _data2elements(data); }
                }
index 06dca48..5bdcb03 100644 (file)
                                        break;
                        }
 
                                        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 = '<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) {
 
                        // registered IP
                        switch (data[j].reg_ip) {
-                               case "_nodomain_":
+                               case "_nolookup_":
                                        tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>';
                                        break;
                                case "_nodata_":
                                        tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>';
                                        break;
                                case "_nodata_":
        }
 
        // force to immediate show status (not waiting for XHR.poll)
        }
 
        // 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); }
                }
        );
 
                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); }
                }
                function(x, data) {
                        if (data) { _data2elements(x, data); }
                }
                <tr class="cbi-section-table-titles">
                        <th class="cbi-section-table-cell"><%:Configuration%></th>
                        <th class="cbi-section-table-cell"><%:Next Update%></th>
                <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>
                        <th class="cbi-section-table-cell"><%:Registered IP%></th>
                        <th class="cbi-section-table-cell"><%:Network%></th>
                </tr>
index 6ffde5d..e662adc 100644 (file)
@@ -1,15 +1,15 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: luci-app-ddns\n"
 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"
 "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"
 "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 "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"
 
 msgid "Check Interval"
 msgstr "Prüfinterval"
 
@@ -125,6 +128,12 @@ msgstr "Eigenes Update-Skript"
 msgid "DDNS Autostart disabled"
 msgstr "DDNS Autostart deaktiviert"
 
 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"
 
 msgid "DDNS Service provider"
 msgstr "DDNS-Dienstanbieter"
 
@@ -196,6 +205,9 @@ msgstr ""
 msgid "Disabled"
 msgstr "Deaktiviert"
 
 msgid "Disabled"
 msgstr "Deaktiviert"
 
+msgid "Domain"
+msgstr "Domäne"
+
 msgid "Dynamic DNS"
 msgstr "Dynamisches DNS"
 
 msgid "Dynamic DNS"
 msgstr "Dynamisches DNS"
 
@@ -284,8 +296,10 @@ msgstr "HTTPS nicht unterstützt"
 msgid "Hints"
 msgstr "Hinweise"
 
 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"
 
 msgid "IP address source"
 msgstr "IP-Adressquelle"
@@ -385,6 +399,12 @@ msgstr "Protokoll in Datei schreiben"
 msgid "Log to syslog"
 msgstr "Systemprotokoll verwenden"
 
 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."
 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 "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"
 
 msgid "Overview"
 msgstr "Übersicht"
 
@@ -484,17 +519,20 @@ msgstr "Prozess ID"
 msgid "Read / Reread log file"
 msgstr "Protokolldatei (neu) einlesen"
 
 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 "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"
 
 msgid "Run once"
 msgstr "Einmalig ausführen"
@@ -528,7 +566,7 @@ msgstr ""
 "Optionen."
 
 msgid "The default setting of '0' will retry infinite."
 "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"
 
 msgid "There is no service configured."
 msgstr "Kein Dienst konfiguriert"
@@ -661,8 +699,8 @@ msgstr "Stunden"
 msgid "installed"
 msgstr "installiert"
 
 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'"
 
 msgid "minimum value '0'"
 msgstr "Minimum Wert '0'"
index 3538680..7ab51dd 100644 (file)
@@ -50,6 +50,9 @@ msgstr ""
 msgid "Casual users should not change this setting"
 msgstr ""
 
 msgid "Casual users should not change this setting"
 msgstr ""
 
+msgid "Change provider"
+msgstr ""
+
 msgid "Check Interval"
 msgstr ""
 
 msgid "Check Interval"
 msgstr ""
 
@@ -96,6 +99,12 @@ msgstr ""
 msgid "DDNS Autostart disabled"
 msgstr ""
 
 msgid "DDNS Autostart disabled"
 msgstr ""
 
+msgid "DDNS Client Configuration"
+msgstr ""
+
+msgid "DDNS Client Documentation"
+msgstr ""
+
 msgid "DDNS Service provider"
 msgstr ""
 
 msgid "DDNS Service provider"
 msgstr ""
 
@@ -149,6 +158,9 @@ msgstr ""
 msgid "Disabled"
 msgstr ""
 
 msgid "Disabled"
 msgstr ""
 
+msgid "Domain"
+msgstr ""
+
 msgid "Dynamic DNS"
 msgstr ""
 
 msgid "Dynamic DNS"
 msgstr ""
 
@@ -230,7 +242,7 @@ msgstr ""
 msgid "Hints"
 msgstr ""
 
 msgid "Hints"
 msgstr ""
 
-msgid "Hostname/Domain"
+msgid "Hostname/FQDN to validate, if IP update happen or necessary"
 msgstr ""
 
 msgid "IP address source"
 msgstr ""
 
 msgid "IP address source"
@@ -315,6 +327,12 @@ msgstr ""
 msgid "Log to syslog"
 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."
 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 "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 ""
 
 msgid "Overview"
 msgstr ""
 
@@ -403,16 +436,19 @@ msgstr ""
 msgid "Read / Reread log file"
 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 "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 ""
 
 msgstr ""
 
-msgid "Replaces [USERNAME] in Update-URL"
+msgid "Replaces [USERNAME] in Update-URL (URL-encoded)"
 msgstr ""
 
 msgid "Run once"
 msgstr ""
 
 msgid "Run once"
@@ -563,7 +599,7 @@ msgstr ""
 msgid "installed"
 msgstr ""
 
 msgid "installed"
 msgstr ""
 
-msgid "invalid - Sample"
+msgid "invalid FQDN / required - Sample"
 msgstr ""
 
 msgid "minimum value '0'"
 msgstr ""
 
 msgid "minimum value '0'"