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 UCI  = require "luci.model.uci"
+local I18N = require "luci.i18n"               -- not globally avalible here
+local IPKG = require "luci.model.ipkg"
 local SYS  = require "luci.sys"
-local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
+local UCI  = require "luci.model.uci"
 local UTIL = require "luci.util"
+local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
 
-DDNS_MIN = "2.4.2-1"   -- minimum version of service required
+local srv_name    = "ddns-scripts"
+local srv_ver_min = "2.5.0"                    -- minimum version of service required
+local srv_ver_cmd = [[/usr/lib/ddns/dynamic_dns_updater.sh --version | awk {'print $2'}]]
+local app_name    = "luci-app-ddns"
+local app_title   = "Dynamic DNS"
+local app_version = "2.3.0-1"
 
 function index()
        local nxfs      = require "nixio.fs"            -- global definitions not available
        local sys       = require "luci.sys"            -- in function index()
        local ddns      = require "luci.tools.ddns"     -- ddns multiused functions
-       local verinst   = ddns.ipkg_ver_installed("ddns-scripts")
-       local verok     = ddns.ipkg_ver_compare(verinst, ">=", "2.0.0-0")
-       -- do NOT start it not ddns-scripts version 2.x
-       if not verok then
-               return
-       end
+       local muci      = require "luci.model.uci"
+
        -- no config create an empty one
        if not nxfs.access("/etc/config/ddns") then
                nxfs.writefile("/etc/config/ddns", "")
        end
 
+       -- preset new option "lookup_host" if not already defined
+       local uci = muci.cursor()
+       local commit = false
+       uci:foreach("ddns", "service", function (s)
+               if not s["lookup_host"] and s["domain"] then
+                       uci:set("ddns", s[".name"], "lookup_host", s["domain"])
+                       commit = true
+               end
+       end)
+       if commit then uci:commit("ddns") end
+       uci:unload("ddns")
+
        entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59)
        entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true
        entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints",
@@ -42,7 +57,60 @@ function index()
        entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true
 end
 
--- function to read all sections status and return data array
+-- Application specific information functions
+function app_description()
+       return  I18N.translate("Dynamic DNS allows that your router can be reached with " ..
+                       "a fixed hostname while having a dynamically changing IP address.")
+               .. [[<br />]]
+               .. I18N.translate("OpenWrt Wiki") .. ": "
+               .. [[<a href="http://wiki.openwrt.org/doc/howto/ddns.client" target="_blank">]]
+               .. I18N.translate("DDNS Client Documentation") .. [[</a>]]
+               .. " --- "
+               .. [[<a href="http://wiki.openwrt.org/doc/uci/ddns" target="_blank">]]
+               .. I18N.translate("DDNS Client Configuration") .. [[</a>]]
+end
+function app_title_back()
+       return  [[<a href="]]
+               .. DISP.build_url("admin", "services", "ddns")
+               .. [[">]]
+               .. I18N.translate(app_title)
+               .. [[</a>]]
+end
+
+-- Standardized application/service functions
+function app_title_main()
+       return  [[<a href="javascript:alert(']]
+                       .. I18N.translate("Version Information")
+                       .. [[\n\n]] .. app_name
+                       .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] .. app_version
+                       .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("required") .. [[:]]
+                       .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
+                               .. srv_ver_min .. [[ ]] .. I18N.translate("or higher")
+                       .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("installed") .. [[:]]
+                       .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
+                               .. (service_version() or I18N.translate("NOT installed"))
+                       .. [[\n\n]]
+               .. [[')">]]
+               .. I18N.translate(app_title)
+               .. [[</a>]]
+end
+function service_version()
+       local ver = nil
+       IPKG.list_installed(srv_name, function(n, ver, d)
+                       -- nothing to do
+               end
+       )
+       if not ver then
+               ver = UTIL.exec(srv_ver_cmd)
+               if #ver == 0 then ver = nil end
+       end
+       return  ver
+end
+function service_ok()
+       return  IPKG.compare_versions((service_version() or "0"), ">=", srv_ver_min)
+end
+
+-- internal function to read all sections status and return data array
 local function _get_status()
        local uci        = UCI.cursor()
        local service    = SYS.init.enabled("ddns") and 1 or 0
@@ -118,12 +186,12 @@ local function _get_status()
                end
 
                -- try to get registered IP
-               local domain    = s["domain"] or "_nodomain_"
+               local lookup_host = s["lookup_host"] or "_nolookup_"
                local dnsserver = s["dns_server"] or ""
                local force_ipversion = tonumber(s["force_ipversion"] or 0)
                local force_dnstcp = tonumber(s["force_dnstcp"] or 0)
                local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]]
-               command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 ..
+               command = command .. [[ get_registered_ip ]] .. lookup_host .. [[ ]] .. use_ipv6 ..
                        [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver
                local reg_ip = SYS.exec(command)
                if reg_ip == "" then
@@ -135,7 +203,7 @@ local function _get_status()
                        section  = section,
                        enabled  = enabled,
                        iface    = iface,
-                       domain   = domain,
+                       lookup   = lookup_host,
                        reg_ip   = reg_ip,
                        pid      = pid,
                        datelast = datelast,
@@ -235,3 +303,4 @@ function status()
        HTTP.prepare_content("application/json")
        HTTP.write_json(data)
 end
+
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 HTTP = require "luci.http"
 local DISP = require "luci.dispatcher"
 local WADM = require "luci.tools.webadmin"
 local DTYP = require "luci.cbi.datatypes"
+local CTRL = require "luci.controller.ddns"    -- this application's controller
 local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
 
 -- takeover arguments -- #######################################################
-local section  = arg[1]
+local section = arg[1]
 
 -- check supported options -- ##################################################
 -- saved to local vars here because doing multiple os calls slow down the system
@@ -31,15 +33,15 @@ local bold_on       = "<strong>"
 local bold_off = "</strong>"
 
 -- error text constants -- #####################################################
-err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
+local err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
                translate("please select 'IPv4' address version")
-err_ipv6_basic = bold_on ..
+local err_ipv6_basic = bold_on ..
                        font_red ..
                                translate("IPv6 not supported") ..
                        font_off ..
                        "<br />" .. translate("please select 'IPv4' address version") ..
                 bold_off
-err_ipv6_other = bold_on ..
+local err_ipv6_other = bold_on ..
                        font_red ..
                                translate("IPv6 not supported") ..
                        font_off ..
@@ -52,16 +54,46 @@ err_ipv6_other = bold_on ..
                        [[</a>]] ..
                 bold_off
 
-function err_tab_basic(self)
+local function err_tab_basic(self)
        return translate("Basic Settings") .. " - " .. self.title .. ": "
 end
-function err_tab_adv(self)
+local function err_tab_adv(self)
        return translate("Advanced Settings") .. " - " .. self.title .. ": "
 end
-function err_tab_timer(self)
+local function err_tab_timer(self)
        return translate("Timer Settings") .. " - " .. self.title .. ": "
 end
 
+-- read services/services_ipv6 files -- ########################################
+local services4 = { }          -- IPv4 --
+local fd4 = io.open("/usr/lib/ddns/services", "r")
+if fd4 then
+       local ln, s, t
+       repeat
+               ln = fd4:read("*l")
+               s  = ln and ln:match('^%s*".*') -- only handle lines beginning with "
+               s  = s  and  s:gsub('"','')     -- remove "
+               t  = s  and UTIL.split(s,"(%s+)",nil,true)      -- split on whitespaces
+               if t then services4[t[1]]=t[2] end
+       until not ln
+       fd4:close()
+end
+
+local services6 = { }          -- IPv6 --
+local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r")
+if fd6 then
+       local ln, s, t
+       repeat
+               ln = fd6:read("*l")
+               s  = ln and ln:match('^%s*".*') -- only handle lines beginning with "
+               s  = s  and  s:gsub('"','')     -- remove "
+               t  = s  and UTIL.split(s,"(%s+)",nil,true)      -- split on whitespaces
+               if t then services6[t[1]]=t[2] end
+       until not ln
+       fd6:close()
+end
+
+-- multi-used functions -- ####################################################
 -- function to verify settings around ip_source
 -- will use dynamic_dns_lucihelper to check if
 -- local IP can be read
@@ -105,17 +137,94 @@ local function _verify_ip_source()
        end
 end
 
--- cbi-map definition -- #######################################################
-m = Map("ddns")
+-- function to check if option is used inside url or script
+-- return -1 on error, 0 NOT required, 1 required
+local function _option_used(option, urlscript)
+       local surl      -- search string for url
+       local ssh       -- search string for script
+       local required  -- option used inside url or script
 
-m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] ..
-               translate("Dynamic DNS") .. [[</a>]]
+       if     option == "domain"    then surl, ssh = '%[DOMAIN%]', '%$domain'
+       elseif option == "username"  then surl, ssh = '%[USERNAME%]', '%$username'
+       elseif option == "password"  then surl, ssh = '%[PASSWORD%]', '%$password'
+       elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc'
+       elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt'
+       else
+               error("undefined option")
+               return -1       -- return on error
+       end
 
-m.description = translate("Dynamic DNS allows that your router can be reached with " ..
-                       "a fixed hostname while having a dynamically changing " ..
-                       "IP address.")
+       local required = false
+       -- handle url
+       if urlscript:find('http') then
+               required = ( urlscript:find(surl) )
+       -- handle script
+       else
+               if not urlscript:find("/") then
+                       -- might be inside ddns-scripts directory
+                       urlscript = "/usr/lib/ddns/" .. urlscript
+               end
+               -- problem with script exit here
+               if not NXFS.access(urlscript) then return -1 end
+
+               local f = io.input(urlscript)
+               -- still problem with script exit here
+               if not f then return -1 end
+               for l in f:lines() do
+                       repeat
+                               if l:find('^#') then break end  -- continue on comment lines
+                               required = ( l:find(surl) or l:find(ssh) )
+                       until true
+                       if required then break end
+               end
+               f:close()
+       end
+       return (required and 1 or 0)
+end
 
-m.redirect = DISP.build_url("admin", "services", "ddns")
+-- function to verify if option is valid
+local function _option_validate(self, value)
+       -- section is globally defined here be calling agrument (see above)
+       local fusev6 = usev6:formvalue(section)
+       local fsvc4  = svc4:formvalue(section)
+       local fsvc6  = svc6:formvalue(section)
+       local urlsh, used
+
+       -- IP-Version dependent custom service selected
+       if (fusev6 == "0" and fsvc4 == "-") or
+          (fusev6 == "1" and fsvc6 == "-") then
+               -- read custom url
+               urlsh = uurl:formvalue(section)
+               -- no url then read custom script
+               if not urlsh or (#urlsh == 0) then
+                       urlsh = ush:formvalue(section)
+               end
+       -- IPv4 read from services4 table
+       elseif (fusev6 == "0") then
+               urlsh = services4[fsvc4]
+       -- IPv6 read from services6 table
+       else
+               urlsh = services6[fsvc6]
+       end
+       -- problem with url or script exit here
+       -- error handled somewhere else
+       if not urlsh or (#urlsh == 0) then return "" end
+
+       used = _option_used(self.option, urlsh)
+       -- on error or not used return empty sting
+       if used < 1 then return "" end
+       -- needed but no data then return error
+       if not value or (#value == 0) then
+               return nil, err_tab_basic(self) .. translate("missing / required")
+       end
+       return value
+end
+
+-- cbi-map definition -- #######################################################
+local m        = Map("ddns")
+m.title                = CTRL.app_title_back()
+m.description  = CTRL.app_description()
+m.redirect     = DISP.build_url("admin", "services", "ddns")
 
 m.on_after_commit = function(self)
        if self.changed then    -- changes ?
@@ -126,19 +235,42 @@ m.on_after_commit = function(self)
        end
 end
 
+-- provider switch was requested, save and reload page
+if m:formvalue("cbid.ddns.%s._switch" % section) then  -- section == arg[1]
+       local fsvc
+       local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section)
+       if fusev6 == "1" then
+               fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section)
+       else
+               fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section)
+       end
+
+       if fusev6 ~= (m:get(section, "use_ipv6") or "0") then   -- IPv6 was changed
+               m:set(section, "use_ipv6", fusev6)              -- save it
+       end
+
+       if fsvc ~= "-" then                                     -- NOT "custom"
+               m:set(section, "service_name", fsvc)            -- save it
+       else                                                    -- else
+               m:del(section, "service_name")                  -- delete it
+       end
+       m.uci:save(m.config)
+
+       -- reload page
+       HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
+       return
+end
+
 -- read application settings -- ################################################
 -- date format; if not set use ISO format
-date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
+local date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
 -- log directory
-log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
+local log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
 
 -- cbi-section definition -- ###################################################
-ns = m:section( NamedSection, section, "service",
+local ns = m:section( NamedSection, section, "service",
        translate("Details for") .. ([[: <strong>%s</strong>]] % section),
-       translate("Configure here the details for selected Dynamic DNS service.")
-       .. [[<br /><a href="http://wiki.openwrt.org/doc/uci/ddns#version_1x" target="_blank">]]
-       .. translate("For detailed information about parameter settings look here.")
-       .. [[</a>]] )
+       translate("Configure here the details for selected Dynamic DNS service.") )
 ns.instance = section  -- arg [1]
 ns:tab("basic", translate("Basic Settings"), nil )
 ns:tab("advanced", translate("Advanced Settings"), nil )
@@ -146,17 +278,33 @@ ns:tab("timer", translate("Timer Settings"), nil )
 ns:tab("logview", translate("Log File Viewer"), nil )
 
 -- TAB: Basic  #####################################################################################
--- enabled  -- #################################################################
+-- enabled -- #################################################################
 en = ns:taboption("basic", Flag, "enabled",
        translate("Enabled"),
        translate("If this service section is disabled it could not be started." .. "<br />" ..
                "Neither from LuCI interface nor from console") )
 en.orientation = "horizontal"
-function en.parse(self, section)
-       DDNS.flag_parse(self, section)
+
+-- IPv4/IPv6 - lookup_host -- #################################################
+luh = ns:taboption("basic", Value, "lookup_host",
+               translate("Lookup Hostname"),
+               translate("Hostname/FQDN to validate, if IP update happen or necessary") )
+luh.rmempty    = false
+luh.placeholder = "myhost.example.com"
+function luh.validate(self, value)
+       if not value
+       or not (#value > 0)
+       or not DTYP.hostname(value) then
+               return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
+       else
+               return UTIL.trim(value)
+       end
+end
+function luh.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
--- use_ipv6 (NEW)  -- ##########################################################
+-- use_ipv6 -- ################################################################
 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
        translate("IP address version"),
        translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
@@ -179,36 +327,15 @@ function usev6.validate(self, value)
        end
        return nil, err_tab_basic(self) .. err_ipv6_plain
 end
-function usev6.write(self, section, value)
-       if value == "0" then    -- force rmempty
-               return self.map:del(section, self.option)
-       else
-               return self.map:set(section, self.option, value)
-       end
+function usev6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
--- IPv4 - service_name -- ######################################################
+-- IPv4 - service_name -- #####################################################
 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
        translate("DDNS Service provider") .. " [IPv4]" )
 svc4.default   = "-"
 svc4:depends("use_ipv6", "0")  -- only show on IPv4
-
-local services4 = { }
-local fd4 = io.open("/usr/lib/ddns/services", "r")
-
-if fd4 then
-       local ln
-       repeat
-               ln = fd4:read("*l")
-               local s = ln and ln:match('^%s*"([^"]+)"')
-               if s then services4[#services4+1] = s end
-       until not ln
-       fd4:close()
-end
-
-for _, v in UTIL.vspairs(services4) do svc4:value(v) end
-svc4:value("-", translate("-- custom --") )
-
 function svc4.cfgvalue(self, section)
        local v =  DDNS.read_value(self, section, "service_name")
        if not v or #v == 0 then
@@ -229,14 +356,18 @@ function svc4.write(self, section, value)
                self.map:del(section, self.option)      -- to be shure
                if value ~= "-" then                    -- and write "service_name
                        self.map:del(section, "update_url")     -- delete update_url
+                       self.map:del(section, "update_script")  -- delete update_script
                        return self.map:set(section, "service_name", value)
                else
                        return self.map:del(section, "service_name")
                end
        end
 end
+function svc4.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv6 - service_name -- ######################################################
+-- IPv6 - service_name -- #####################################################
 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
        translate("DDNS Service provider") .. " [IPv6]" )
 svc6.default   = "-"
@@ -244,23 +375,6 @@ svc6:depends("use_ipv6", "1")      -- only show on IPv6
 if not has_ipv6 then
        svc6.description = err_ipv6_basic
 end
-
-local services6 = { }
-local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r")
-
-if fd6 then
-       local ln
-       repeat
-               ln = fd6:read("*l")
-               local s = ln and ln:match('^%s*"([^"]+)"')
-               if s then services6[#services6+1] = s end
-       until not ln
-       fd6:close()
-end
-
-for _, v in UTIL.vspairs(services6) do svc6:value(v) end
-svc6:value("-", translate("-- custom --") )
-
 function svc6.cfgvalue(self, section)
        local v =  DDNS.read_value(self, section, "service_name")
        if not v or #v == 0 then
@@ -282,33 +396,42 @@ function svc6.write(self, section, value)
                self.map:del(section, self.option)      -- delete "ipv6_service_name" helper
                if value ~= "-" then                    -- and write "service_name
                        self.map:del(section, "update_url")     -- delete update_url
+                       self.map:del(section, "update_script")  -- delete update_script
                        return self.map:set(section, "service_name", value)
                else
                        return self.map:del(section, "service_name")
                end
        end
 end
+function svc6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
+
+-- IPv4/IPv6 - change Provider -- #############################################
+svs            = ns:taboption("basic", Button, "_switch")
+svs.title      = translate("Really change DDNS provider?")
+svs.inputtitle = translate("Change provider")
+svs.inputstyle = "apply"
 
--- IPv4/IPv6 - update_url -- ###################################################
+-- IPv4/IPv6 - update_url -- ##################################################
 uurl = ns:taboption("basic", Value, "update_url",
        translate("Custom update-URL"),
        translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
                "Follow instructions you will find on their WEB page.") )
-uurl:depends("ipv4_service_name", "-")
-uurl:depends("ipv6_service_name", "-")
 function uurl.validate(self, value)
-       local script = ush:formvalue(section)
+       local fush   = ush:formvalue(section)
+       local fusev6 = usev6:formvalue(section)
 
-       if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or
-          (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then
+       if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
+          (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
                return ""       -- suppress validate error
        elseif not value then
-               if not script or not (#script > 0) then
+               if not fush or (#fush == 0) then
                        return nil, err_tab_basic(self) .. translate("missing / required")
                else
                        return ""       -- suppress validate error / update_script is given
                end
-       elseif (#script > 0) then
+       elseif (#fush > 0) then
                return nil, err_tab_basic(self) .. translate("either url or script could be set")
        end
 
@@ -325,80 +448,159 @@ function uurl.validate(self, value)
 
        return value
 end
+function uurl.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4/IPv6 - update_script -- ################################################
+-- IPv4/IPv6 - update_script -- ###############################################
 ush = ns:taboption("basic", Value, "update_script",
        translate("Custom update-script"),
        translate("Custom update script to be used for updating your DDNS Provider.") )
-ush:depends("ipv4_service_name", "-")
-ush:depends("ipv6_service_name", "-")
 function ush.validate(self, value)
-       local url = uurl:formvalue(section)
+       local fuurl  = uurl:formvalue(section)
+       local fusev6 = usev6:formvalue(section)
 
-       if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or
-          (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then
+       if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
+          (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
                return ""       -- suppress validate error
        elseif not value then
-               if not url or not (#url > 0) then
+               if not fuurl or (#fuurl == 0) then
                        return nil, err_tab_basic(self) .. translate("missing / required")
                else
                        return ""       -- suppress validate error / update_url is given
                end
-       elseif (#url > 0) then
+       elseif (#fuurl > 0) then
                return nil, err_tab_basic(self) .. translate("either url or script could be set")
        elseif not NXFS.access(value) then
                return nil, err_tab_basic(self) .. translate("File not found")
        end
        return value
 end
+function ush.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4/IPv6 - domain -- #######################################################
+-- IPv4/IPv6 - domain -- ######################################################
 dom = ns:taboption("basic", Value, "domain",
-               translate("Hostname/Domain"),
+               translate("Domain"),
                translate("Replaces [DOMAIN] in Update-URL") )
-dom.rmempty    = false
-dom.placeholder        = "mypersonaldomain.dyndns.org"
+dom.placeholder = "myhost.example.com"
 function dom.validate(self, value)
-       if not value
-       or not (#value > 0)
-       or not DTYP.hostname(value) then
-               return nil, err_tab_basic(self) ..      translate("invalid - Sample") .. ": 'mypersonaldomain.dyndns.org'"
-       else
-               return value
-       end
+       return _option_validate(self, value)
+end
+function dom.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
--- IPv4/IPv6 - username -- #####################################################
+-- IPv4/IPv6 - username -- ####################################################
 user = ns:taboption("basic", Value, "username",
                translate("Username"),
-               translate("Replaces [USERNAME] in Update-URL") )
-user.rmempty = false
+               translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
 function user.validate(self, value)
-       if not value then
-               return nil, err_tab_basic(self) .. translate("missing / required")
-       end
-       return value
+       return _option_validate(self, value)
+end
+function user.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
--- IPv4/IPv6 - password -- #####################################################
+-- IPv4/IPv6 - password -- ####################################################
 pw = ns:taboption("basic", Value, "password",
                translate("Password"),
-               translate("Replaces [PASSWORD] in Update-URL") )
-pw.rmempty  = false
+               translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
 pw.password = true
 function pw.validate(self, value)
-       if not value then
-               return nil, err_tab_basic(self) .. translate("missing / required")
+       return _option_validate(self, value)
+end
+function pw.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
+
+-- IPv4/IPv6 - param_enc -- ###################################################
+pe = ns:taboption("basic", Value, "param_enc",
+               translate("Optional Encoded Parameter"),
+               translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
+function pe.validate(self, value)
+       return _option_validate(self, value)
+end
+function pe.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
+
+-- IPv4/IPv6 - param_enc -- ###################################################
+po = ns:taboption("basic", Value, "param_opt",
+               translate("Optional Parameter"),
+               translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
+function po.validate(self, value)
+       return _option_validate(self, value)
+end
+function po.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
+
+-- handled service dependent show/display -- ##################################
+-- IPv4 --
+local cv4 = svc4:cfgvalue(section)
+if cv4 ~= "-" then
+       svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
+       ush:depends ("ipv4_service_name", "?")
+       uurl:depends("ipv4_service_name", "?")
+else
+       uurl:depends("ipv4_service_name", "-")
+       ush:depends ("ipv4_service_name", "-")
+       dom:depends("ipv4_service_name", "-" )
+       user:depends("ipv4_service_name", "-" )
+       pw:depends("ipv4_service_name", "-" )
+       pe:depends("ipv4_service_name", "-" )
+       po:depends("ipv4_service_name", "-" )
+end
+for s, u in UTIL.kspairs(services4) do
+       svc4:value(s)   -- fill DropDown-List
+       if cv4 ~= s then
+               svs:depends("ipv4_service_name", s )
+       else
+               dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
+               user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
+               pw:depends  ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
+               pe:depends  ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
+               po:depends  ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
        end
-       return value
 end
+svc4:value("-", translate("-- custom --") )
+
+-- IPv6 --
+local cv6 = svc6:cfgvalue(section)
+if cv6 ~= "-" then
+       svs:depends ("ipv6_service_name", "-" )
+       uurl:depends("ipv6_service_name", "?")
+       ush:depends ("ipv6_service_name", "?")
+else
+       uurl:depends("ipv6_service_name", "-")
+       ush:depends ("ipv6_service_name", "-")
+       dom:depends("ipv6_service_name", "-" )
+       user:depends("ipv6_service_name", "-" )
+       pw:depends("ipv6_service_name", "-" )
+       pe:depends("ipv6_service_name", "-" )
+       po:depends("ipv6_service_name", "-" )
+end
+for s, u in UTIL.kspairs(services6) do
+       svc6:value(s)   -- fill DropDown-List
+       if cv6 ~= s then
+               svs:depends("ipv6_service_name", s )
+       else
+               dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
+               user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
+               pw:depends  ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
+               pe:depends  ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
+               po:depends  ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
+       end
+end
+svc6:value("-", translate("-- custom --") )
 
--- IPv4/IPv6 - use_https (NEW) -- ##############################################
+-- IPv4/IPv6 - use_https -- ###################################################
 if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
        https = ns:taboption("basic", Flag, "use_https",
                translate("Use HTTP Secure") )
        https.orientation = "horizontal"
-       https.rmempty = false -- force validate function
        function https.cfgvalue(self, section)
                local value = AbstractValue.cfgvalue(self, section)
                if not has_ssl and value == "1" then
@@ -410,9 +612,6 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
                end
                return value
        end
-       function https.parse(self, section)
-               DDNS.flag_parse(self, section)
-       end
        function https.validate(self, value)
                if (value == "1" and has_ssl ) or value == "0" then return value end
                return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
@@ -427,7 +626,7 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
        end
 end
 
--- IPv4/IPv6 - cacert (NEW) -- #################################################
+-- IPv4/IPv6 - cacert -- ######################################################
 if has_ssl then
        cert = ns:taboption("basic", Value, "cacert",
                translate("Path to CA-Certificate"),
@@ -435,8 +634,8 @@ if has_ssl then
                translate("or") .. bold_on .. " IGNORE " .. bold_off ..
                translate("to run HTTPS without verification of server certificates (insecure)") )
        cert:depends("use_https", "1")
-       cert.rmempty = false -- force validate function
        cert.default = "/etc/ssl/certs"
+       cert.forcewrite = true
        function cert.validate(self, value)
                if https:formvalue(section) == "0" then
                        return ""       -- supress validate error if NOT https
@@ -451,10 +650,13 @@ if has_ssl then
                return nil, err_tab_basic(self) ..
                        translate("file or directory not found or not 'IGNORE'") .. " !"
        end
+       function cert.parse(self, section, novld)
+               DDNS.value_parse(self, section, novld)
+       end
 end
 
--- TAB: Advanced  ##################################################################################
--- IPv4 - ip_source -- #########################################################
+-- TAB: Advanced  #################################################################################
+-- IPv4 - ip_source -- ########################################################
 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
        translate("IP address source") .. " [IPv4]",
        translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
@@ -500,8 +702,11 @@ function src4.write(self, section, value)
        self.map:del(section, self.option)               -- delete "ipv4_source" helper
        return self.map:set(section, "ip_source", value) -- and write "ip_source
 end
+function src4.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv6 - ip_source -- #########################################################
+-- IPv6 - ip_source -- ########################################################
 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
        translate("IP address source") .. " [IPv6]",
        translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
@@ -552,8 +757,11 @@ function src6.write(self, section, value)
        self.map:del(section, self.option)               -- delete "ipv4_source" helper
        return self.map:set(section, "ip_source", value) -- and write "ip_source
 end
+function src6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 - ip_network (default "wan") -- ########################################
+-- IPv4 - ip_network (default "wan") -- #######################################
 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
        translate("Network") .. " [IPv4]",
        translate("Defines the network to read systems IPv4-Address from") )
@@ -586,8 +794,11 @@ function ipn4.write(self, section, value)
                return self.map:set(section, "ip_network", value) -- and write "ip_network"
        end
 end
+function ipn4.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv6 - ip_network (default "wan6") -- #######################################
+-- IPv6 - ip_network (default "wan6") -- ######################################
 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
        translate("Network") .. " [IPv6]" )
 ipn6:depends("ipv6_source", "network")
@@ -626,8 +837,11 @@ function ipn6.write(self, section, value)
                return self.map:set(section, "ip_network", value) -- and write "ip_network"
        end
 end
+function ipn6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 - ip_url (default "checkip.dyndns.com") -- #############################
+-- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
        translate("URL to detect") .. " [IPv4]",
        translate("Defines the Web page to read systems IPv4-Address from") )
@@ -668,8 +882,11 @@ function iurl4.write(self, section, value)
                return self.map:set(section, "ip_url", value)   -- and write "ip_url"
        end
 end
+function iurl4.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ###########################
+-- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
        translate("URL to detect") .. " [IPv6]" )
 iurl6:depends("ipv6_source", "web")
@@ -716,8 +933,11 @@ function iurl6.write(self, section, value)
                return self.map:set(section, "ip_url", value)   -- and write "ip_url"
        end
 end
+function iurl6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 + IPv6 - ip_interface -- ###############################################
+-- IPv4 + IPv6 - ip_interface -- ##############################################
 ipi = ns:taboption("advanced", ListValue, "ip_interface",
        translate("Interface"),
        translate("Defines the interface to read systems IP-Address from") )
@@ -732,16 +952,18 @@ for _, v in pairs(SYS.net.devices()) do
        end
 end
 function ipi.validate(self, value)
-       if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface")
-       or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then
+       local fusev6 = usev6:formvalue(section)
+       if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
+       or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
                return ""
        else
                return value
        end
 end
 function ipi.write(self, section, value)
-       if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface")
-       or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then
+       local fusev6 = usev6:formvalue(section)
+       if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
+       or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
                return true
        else
                -- get network from device to
@@ -751,21 +973,24 @@ function ipi.write(self, section, value)
                return self.map:set(section, self.option, value)
        end
 end
+function ipi.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 + IPv6 - ip_script (NEW) -- ############################################
+-- IPv4 + IPv6 - ip_script -- #################################################
 ips = ns:taboption("advanced", Value, "ip_script",
        translate("Script"),
        translate("User defined script to read systems IP-Address") )
 ips:depends("ipv4_source", "script")   -- IPv4
 ips:depends("ipv6_source", "script")   -- or IPv6
-ips.rmempty    = false
 ips.placeholder = "/path/to/script.sh"
 function ips.validate(self, value)
+       local fusev6 = usev6:formvalue(section)
        local split
        if value then split = UTIL.split(value, " ") end
 
-       if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script")
-       or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then
+       if (fusev6 == "0" and src4:formvalue(section) ~= "script")
+       or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
                return ""
        elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
                return nil, err_tab_adv(self) ..
@@ -775,15 +1000,19 @@ function ips.validate(self, value)
        end
 end
 function ips.write(self, section, value)
-       if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script")
-       or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then
+       local fusev6 = usev6:formvalue(section)
+       if (fusev6 == "0" and src4:formvalue(section) ~= "script")
+       or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
                return true
        else
                return self.map:set(section, self.option, value)
        end
 end
+function ips.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 - interface - default "wan" -- #########################################
+-- IPv4 - interface - default "wan" -- ########################################
 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
 -- only needs to be set if "ip_source"="web" or "script"
 -- if "ip_source"="network" or "interface" we use their network
@@ -798,27 +1027,32 @@ function eif4.cfgvalue(self, section)
        return DDNS.read_value(self, section, "interface")
 end
 function eif4.validate(self, value)
+       local fsrc4 = src4:formvalue(section)
        if usev6:formvalue(section) == "1"
-        or src4:formvalue(section) == "network"
-        or src4:formvalue(section) == "interface" then
+        or fsrc4 == "network"
+        or fsrc4 == "interface" then
                return ""       -- ignore IPv6, network, interface
        else
                return value
        end
 end
 function eif4.write(self, section, value)
+       local fsrc4 = src4:formvalue(section)
        if usev6:formvalue(section) == "1"
-        or src4:formvalue(section) == "network"
-        or src4:formvalue(section) == "interface" then
+        or fsrc4 == "network"
+        or fsrc4 == "interface" then
                return true     -- ignore IPv6, network, interface
        else
                self.map:del(section, self.option)               -- delete "ipv4_interface" helper
                return self.map:set(section, "interface", value) -- and write "interface"
        end
 end
+function eif4.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv6 - interface (NEW) - default "wan6" -- ##################################
--- event network to monitor changes/hotplug (NEW)
+-- IPv6 - interface - default "wan6" -- #######################################
+-- event network to monitor changes/hotplug
 -- only needs to be set if "ip_source"="web" or "script"
 -- if "ip_source"="network" or "interface" we use their network
 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
@@ -836,9 +1070,10 @@ function eif6.cfgvalue(self, section)
        return DDNS.read_value(self, section, "interface")
 end
 function eif6.validate(self, value)
+       local fsrc6 = src6:formvalue(section)
        if usev6:formvalue(section) == "0"
-        or src4:formvalue(section) == "network"
-        or src4:formvalue(section) == "interface" then
+        or fsrc6 == "network"
+        or fsrc6 == "interface" then
                return ""       -- ignore IPv4, network, interface
        elseif not has_ipv6 then
                return nil, err_tab_adv(self) .. err_ipv6_plain
@@ -847,23 +1082,26 @@ function eif6.validate(self, value)
        end
 end
 function eif6.write(self, section, value)
+       local fsrc6 = src6:formvalue(section)
        if usev6:formvalue(section) == "0"
-        or src4:formvalue(section) == "network"
-        or src4:formvalue(section) == "interface" then
+        or fsrc6 == "network"
+        or fsrc6 == "interface" then
                return true     -- ignore IPv4, network, interface
        else
                self.map:del(section, self.option)               -- delete "ipv6_interface" helper
                return self.map:set(section, "interface", value) -- and write "interface"
        end
 end
+function eif6.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4/IPv6 - bind_network -- #################################################
+-- IPv4/IPv6 - bind_network -- ################################################
 if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
        bnet = ns:taboption("advanced", ListValue, "bind_network",
                translate("Bind Network") )
        bnet:depends("ipv4_source", "web")
        bnet:depends("ipv6_source", "web")
-       bnet.rmempty = true
        bnet.default = ""
        bnet:value("", translate("-- default --"))
        WADM.cbi_add_networks(bnet)
@@ -883,9 +1121,12 @@ if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
                if (value ~= "" and has_ssl ) or value == "" then return value end
                return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
        end
+       function bnet.parse(self, section, novld)
+               DDNS.value_parse(self, section, novld)
+       end
 end
 
--- IPv4 + IPv6 - force_ipversion (NEW) -- ######################################
+-- IPv4 + IPv6 - force_ipversion -- ###########################################
 -- optional to force wget/curl and host to use only selected IP version
 -- command parameter "-4" or "-6"
 if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
@@ -907,19 +1148,9 @@ if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
                if (value == "1" and has_force) or value == "0" then return value end
                return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
        end
-       function fipv.parse(self, section)
-               DDNS.flag_parse(self, section)
-       end
-       function fipv.write(self, section, value)
-               if value == "1" then
-                       return self.map:set(section, self.option, value)
-               else
-                       return self.map:del(section, self.option)
-               end
-       end
 end
 
--- IPv4 + IPv6 - dns_server (NEW) -- ###########################################
+-- IPv4 + IPv6 - dns_server -- ################################################
 -- optional DNS Server to use resolving my IP if "ip_source"="web"
 dns = ns:taboption("advanced", Value, "dns_server",
        translate("DNS-Server"),
@@ -928,7 +1159,7 @@ dns = ns:taboption("advanced", Value, "dns_server",
 dns.placeholder = "mydns.lan"
 function dns.validate(self, value)
        -- if .datatype is set, then it is checked before calling this function
-       if not value then
+       if not value or (#value == 0) then
                return ""       -- ignore on empty
        elseif not DTYP.host(value) then
                return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
@@ -946,8 +1177,11 @@ function dns.validate(self, value)
                end
        end
 end
+function dns.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- IPv4 + IPv6 - force_dnstcp (NEW) -- #########################################
+-- IPv4 + IPv6 - force_dnstcp -- ##############################################
 if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
        tcp = ns:taboption("advanced", Flag, "force_dnstcp",
                translate("Force TCP on DNS") )
@@ -969,12 +1203,9 @@ if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
                end
                return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
        end
-       function tcp.parse(self, section)
-               DDNS.flag_parse(self, section)
-       end
 end
 
--- IPv4 + IPv6 - proxy (NEW) -- ################################################
+-- IPv4 + IPv6 - proxy -- #####################################################
 -- optional Proxy to use for http/https requests  [user:password@]proxyhost[:port]
 if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
        pxy = ns:taboption("advanced", Value, "proxy",
@@ -996,7 +1227,7 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
        end
        function pxy.validate(self, value)
                -- if .datatype is set, then it is checked before calling this function
-               if not value then
+               if not value or (#value == 0) then
                        return ""       -- ignore on empty
                elseif has_proxy then
                        local ipv6  = usev6:formvalue(section) or "0"
@@ -1015,9 +1246,12 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
                        return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
                end
        end
+       function pxy.parse(self, section, novld)
+               DDNS.value_parse(self, section, novld)
+       end
 end
 
--- use_syslog -- ###############################################################
+-- use_syslog -- ##############################################################
 slog = ns:taboption("advanced", ListValue, "use_syslog",
        translate("Log to syslog"),
        translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
@@ -1027,26 +1261,24 @@ slog:value("1", translate("Info"))
 slog:value("2", translate("Notice"))
 slog:value("3", translate("Warning"))
 slog:value("4", translate("Error"))
+function slog.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- use_logfile (NEW) -- ########################################################
+-- use_logfile -- #############################################################
 logf = ns:taboption("advanced", Flag, "use_logfile",
        translate("Log to file"),
        translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
        translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] )
 logf.orientation = "horizontal"
-logf.rmempty = false   -- we want to save in /etc/config/ddns file on "0" because
-logf.default = "1"     -- if not defined write to log by default
-function logf.parse(self, section)
-       DDNS.flag_parse(self, section)
-end
+logf.default     = "1"         -- if not defined write to log by default
 
--- TAB: Timer  #####################################################################################
--- check_interval -- ###########################################################
+-- TAB: Timer  ####################################################################################
+-- check_interval -- ##########################################################
 ci = ns:taboption("timer", Value, "check_interval",
        translate("Check Interval") )
 ci.template = "ddns/detail_value"
-ci.default  = 10
-ci.rmempty = false     -- validate ourselves for translatable error messages
+ci.default  = "10"
 function ci.validate(self, value)
        if not DTYP.uinteger(value)
        or tonumber(value) < 1 then
@@ -1061,7 +1293,7 @@ function ci.validate(self, value)
        end
 end
 function ci.write(self, section, value)
-       -- simulate rmempty=true remove default
+       -- remove when default
        local secs = DDNS.calc_seconds(value, cu:formvalue(section))
        if secs ~= 600 then     --default 10 minutes
                return self.map:set(section, self.option, value)
@@ -1070,20 +1302,22 @@ function ci.write(self, section, value)
                return self.map:del(section, self.option)
        end
 end
+function ci.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- check_unit -- ###############################################################
+-- check_unit -- ##############################################################
 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
        translate("Interval to check for changed IP" .. "<br />" ..
                "Values below 5 minutes == 300 seconds are not supported") )
 cu.template = "ddns/detail_lvalue"
 cu.default  = "minutes"
-cu.rmempty  = false    -- want to control write process
 cu:value("seconds", translate("seconds"))
 cu:value("minutes", translate("minutes"))
 cu:value("hours", translate("hours"))
 --cu:value("days", translate("days"))
 function cu.write(self, section, value)
-       -- simulate rmempty=true remove default
+       -- remove when default
        local secs = DDNS.calc_seconds(ci:formvalue(section), value)
        if secs ~= 600 then     --default 10 minutes
                return self.map:set(section, self.option, value)
@@ -1091,13 +1325,16 @@ function cu.write(self, section, value)
                return true
        end
 end
+function cu.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- force_interval (modified) -- ################################################
+-- force_interval (modified) -- ###############################################
 fi = ns:taboption("timer", Value, "force_interval",
        translate("Force Interval") )
 fi.template = "ddns/detail_value"
-fi.default  = 72       -- see dynamic_dns_updater.sh script
-fi.rmempty = false     -- validate ourselves for translatable error messages
+fi.default  = "72"     -- see dynamic_dns_updater.sh script
+--fi.rmempty = false   -- validate ourselves for translatable error messages
 function fi.validate(self, value)
        if not DTYP.uinteger(value)
        or tonumber(value) < 0 then
@@ -1131,15 +1368,18 @@ function fi.write(self, section, value)
                return self.map:del(section, self.option)
        end
 end
+function fi.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- force_unit -- ###############################################################
+-- force_unit -- ##############################################################
 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
        translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
                "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
                "Values lower 'Check Interval' except '0' are not supported") )
 fu.template = "ddns/detail_lvalue"
 fu.default  = "hours"
-fu.rmempty  = false    -- want to control write process
+--fu.rmempty  = false  -- want to control write process
 --fu:value("seconds", translate("seconds"))
 fu:value("minutes", translate("minutes"))
 fu:value("hours", translate("hours"))
@@ -1153,15 +1393,17 @@ function fu.write(self, section, value)
                return true
        end
 end
+function fu.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- retry_count (NEW) -- ########################################################
+-- retry_count -- #############################################################
 rc = ns:taboption("timer", Value, "retry_count")
 rc.title       = translate("Error Retry Counter")
 rc.description = translate("On Error the script will stop execution after given number of retrys")
                .. "<br />"
                .. translate("The default setting of '0' will retry infinite.")
-rc.default     = 0
-rc.rmempty     = false -- validate ourselves for translatable error messages
+rc.default     = "0"
 function rc.validate(self, value)
        if not DTYP.uinteger(value) then
                return nil, err_tab_timer(self) .. translate("minimum value '0'")
@@ -1169,21 +1411,15 @@ function rc.validate(self, value)
                return value
        end
 end
-function rc.write(self, section, value)
-       -- simulate rmempty=true remove default
-       if tonumber(value) ~= self.default then
-               return self.map:set(section, self.option, value)
-       else
-               return self.map:del(section, self.option)
-       end
+function rc.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
--- retry_interval -- ###########################################################
+-- retry_interval -- ##########################################################
 ri = ns:taboption("timer", Value, "retry_interval",
        translate("Error Retry Interval") )
 ri.template = "ddns/detail_value"
-ri.default  = 60
-ri.rmempty  = false    -- validate ourselves for translatable error messages
+ri.default  = "60"
 function ri.validate(self, value)
        if not DTYP.uinteger(value)
        or tonumber(value) < 1 then
@@ -1202,13 +1438,16 @@ function ri.write(self, section, value)
                return self.map:del(section, self.option)
        end
 end
+function ri.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- retry_unit -- ###############################################################
+-- retry_unit -- ##############################################################
 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
        translate("On Error the script will retry the failed action after given time") )
 ru.template = "ddns/detail_lvalue"
 ru.default  = "seconds"
-ru.rmempty  = false    -- want to control write process
+--ru.rmempty  = false  -- want to control write process
 ru:value("seconds", translate("seconds"))
 ru:value("minutes", translate("minutes"))
 --ru:value("hours", translate("hours"))
@@ -1222,8 +1461,11 @@ function ru.write(self, section, value)
                return true -- will be deleted by retry_interval
        end
 end
+function ru.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
--- TAB: LogView  (NEW) #############################################################################
+-- TAB: LogView  ##################################################################################
 lv = ns:taboption("logview", DummyValue, "_logview")
 lv.template = "ddns/detail_logview"
 lv.inputtitle = translate("Read / Reread log file")
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 CTRL = require "luci.controller.ddns"    -- this application's controller
 local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
 
 -- cbi-map definition -- #######################################################
 local m = Map("ddns")
-
-m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] 
-       .. translate("Dynamic DNS") .. [[</a>]]
-
-m.description = translate("Dynamic DNS allows that your router can be reached with " ..
-                       "a fixed hostname while having a dynamically changing IP address.")
-
-m.redirect = DISP.build_url("admin", "services", "ddns")
+m.title                = CTRL.app_title_back()
+m.description  = CTRL.app_description()
+m.redirect     = DISP.build_url("admin", "services", "ddns")
 
 function m.commit_handler(self)
        if self.changed then    -- changes ?
@@ -52,17 +48,7 @@ ali.description = translate("Non-public and by default blocked IP's") .. ":"
                .. "0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16"
                .. [[<br /><strong>IPv6: </strong>]]
                .. "::/32, f000::/4"
-ali.reempty    = true
 ali.default    = "0"
-function ali.parse(self, section)
-       DDNS.flag_parse(self, section)
-end
-function ali.validate(self, value)
-       if value == self.default then
-               return "" -- default = empty
-       end
-       return value
-end
 
 -- date_format  -- #############################################################
 local df       = ns:option(Value, "date_format")
@@ -71,7 +57,6 @@ df.description        = [[<a href="http://www.cplusplus.com/reference/ctime/strftime/"
                .. translate("For supported codes look here") 
                .. [[</a>]]
 df.template    = "ddns/global_value"
-df.rmempty     = true
 df.default     = "%F %R"
 df.date_string = ""
 function df.cfgvalue(self, section)
@@ -80,55 +65,45 @@ function df.cfgvalue(self, section)
        self.date_string = DDNS.epoch2date(epoch, value)
        return value
 end
-function df.validate(self, value)
-       if value == self.default then
-               return "" -- default = empty
-       end
-       return value
+function df.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
 -- run_dir  -- #################################################################
 local rd       = ns:option(Value, "run_dir")
 rd.title       = translate("Status directory")
 rd.description = translate("Directory contains PID and other status information for each running section")
-rd.rmempty     = true
 rd.default     = "/var/run/ddns"
-function rd.validate(self, value)
-       if value == self.default then
-               return "" -- default = empty
-       end
-       return value
+-- no need to validate. if empty default is used everything else created by dns-scripts
+function rd.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
 -- log_dir  -- #################################################################
 local ld       = ns:option(Value, "log_dir")
 ld.title       = translate("Log directory")
 ld.description = translate("Directory contains Log files for each running section")
-ld.rmempty     = true
 ld.default     = "/var/log/ddns"
-function ld.validate(self, value)
-       if value == self.default then
-               return "" -- default = empty
-       end
-       return value
+-- no need to validate. if empty default is used everything else created by dns-scripts
+function ld.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
 end
 
 -- log_lines  -- ###############################################################
 local ll       = ns:option(Value, "log_lines")
 ll.title       = translate("Log length")
 ll.description = translate("Number of last lines stored in log files")
-ll.rmempty     = true
 ll.default     = "250"
 function ll.validate(self, value)
        local n = tonumber(value)
        if not n or math.floor(n) ~= n or n < 1 then
                return nil, self.title .. ": " .. translate("minimum value '1'")
        end
-       if value == self.default then
-               return "" -- default = empty
-       end
        return value
 end
+function ll.parse(self, section, novld)
+       DDNS.value_parse(self, section, novld)
+end
 
 -- use_curl  -- ################################################################
 if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) 
@@ -139,17 +114,7 @@ and NXFS.access("/usr/bin/curl") then
                .. [[<br />]]
                .. translate("To use cURL activate this option.")
        pc.orientation  = "horizontal"
-       pc.rmempty      = true
        pc.default      = "0"
-       function pc.parse(self, section)
-               DDNS.flag_parse(self, section)
-       end
-       function pc.validate(self, value)
-               if value == self.default then
-                       return "" -- default = empty
-               end
-               return value
-       end
 end
 
 return m
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.
 
-local CTRL = require "luci.controller.ddns"    -- this application's controller
 local DISP = require "luci.dispatcher"
 local SYS  = require "luci.sys"
+local CTRL = require "luci.controller.ddns"    -- this application's controller
 local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
 
 -- check supported options -- ##################################################
@@ -11,8 +11,6 @@ local DDNS = require "luci.tools.ddns"                -- ddns multiused functions
 has_ssl     = DDNS.check_ssl()         -- HTTPS support and --bind-network / --interface
 has_proxy   = DDNS.check_proxy()       -- Proxy support
 has_dnstcp  = DDNS.check_bind_host()   -- DNS TCP support
--- correct ddns-scripts version
-need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN)
 
 -- html constants
 font_red = [[<font color="red">]]
@@ -22,15 +20,9 @@ bold_off = [[</strong>]]
 
 -- cbi-map definition -- #######################################################
 m = Map("ddns")
-
-m.title = [[<a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] ..
-               translate("Dynamic DNS") .. [[</a>]]
-
-m.description = translate("Dynamic DNS allows that your router can be reached with " ..
-                       "a fixed hostname while having a dynamically changing " ..
-                       "IP address.")
-
-m.redirect = DISP.build_url("admin", "services", "ddns")
+m.title                = CTRL.app_title_back()
+m.description  = CTRL.app_description()
+m.redirect     = DISP.build_url("admin", "services", "ddns")
 
 -- SimpleSection definition -- #################################################
 -- show Hints to optimize installation and script usage
@@ -39,7 +31,7 @@ s = m:section( SimpleSection,
        translate("Below a list of configuration tips for your system to run Dynamic DNS updates without limitations") )
 
 -- ddns_scripts needs to be updated for full functionality
-if need_update then
+if not CTRL.service_ok() then
        local dv = s:option(DummyValue, "_update_needed")
        dv.titleref = DISP.build_url("admin", "system", "packages")
        dv.rawhtml  = true
index 5b5925b..46fc0a4 100644 (file)
@@ -2,10 +2,10 @@
 -- Licensed to the public under the Apache License 2.0.
 
 local NXFS = require "nixio.fs"
-local CTRL = require "luci.controller.ddns"    -- this application's controller
 local DISP = require "luci.dispatcher"
 local HTTP = require "luci.http"
 local SYS  = require "luci.sys"
+local CTRL = require "luci.controller.ddns"    -- this application's controller
 local DDNS = require "luci.tools.ddns"         -- ddns multiused functions
 
 -- show hints ?
@@ -15,7 +15,7 @@ show_hints = not (DDNS.check_ipv6()           -- IPv6 support
                and DDNS.check_bind_host()      -- DNS TCP support
                )
 -- correct ddns-scripts version
-need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN)
+need_update = not CTRL.service_ok()
 
 -- html constants
 font_red = [[<font color="red">]]
@@ -25,22 +25,8 @@ bold_off = [[</strong>]]
 
 -- cbi-map definition -- #######################################################
 m = Map("ddns")
-
-m.title        = [[<a href="javascript:alert(']]
-               .. translate("Version Information")
-               .. [[\n\nluci-app-ddns]]
-               .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("luci-app-ddns")
-               .. [[\n\nddns-scripts ]] .. translate("required") .. [[:]]
-               .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.DDNS_MIN .. [[ ]] .. translate("or higher")
-               .. [[\n\nddns-scripts ]] .. translate("installed") .. [[:]]
-               .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("ddns-scripts")
-               .. [[\n\n]]
-       .. [[')">]]
-       .. translate("Dynamic DNS") .. [[</a>]]
-
-m.description = translate("Dynamic DNS allows that your router can be reached with " ..
-                       "a fixed hostname while having a dynamically changing " ..
-                       "IP address.")
+m.title                = CTRL.app_title_main()
+m.description  = CTRL.app_description()
 
 m.on_after_commit = function(self)
        if self.changed then    -- changes ?
@@ -122,21 +108,21 @@ function ts.create(self, name)
        HTTP.redirect( self.extedit:format(name) )
 end
 
--- Domain and registered IP -- #################################################
-dom = ts:option(DummyValue, "_domainIP",
-       translate("Hostname/Domain") .. "<br />" .. translate("Registered IP") )
+-- Lookup_Host and registered IP -- #################################################
+dom = ts:option(DummyValue, "_lookupIP",
+       translate("Lookup Hostname") .. "<br />" .. translate("Registered IP") )
 dom.template = "ddns/overview_doubleline"
 function dom.set_one(self, section)
-       local domain = self.map:get(section, "domain") or ""
-       if domain ~= "" then
-               return domain
+       local lookup = self.map:get(section, "lookup_host") or ""
+       if lookup ~= "" then
+               return lookup
        else
                return [[<em>]] .. translate("config error") .. [[</em>]]
        end
 end
 function dom.set_two(self, section)
-       local domain = self.map:get(section, "domain") or ""
-       if domain == "" then return "" end
+       local lookup = self.map:get(section, "lookup_host") or ""
+       if lookup == "" then return "" end
        local dnsserver = self.map:get(section, "dnsserver") or ""
        local use_ipv6 = tonumber(self.map:get(section, "use_ipv6") or 0)
        local force_ipversion = tonumber(self.map:get(section, "force_ipversion") or 0)
@@ -145,7 +131,7 @@ function dom.set_two(self, section)
        if not NXFS.access(command, "rwx", "rx", "rx") then
                NXFS.chmod(command, 755)
        end
-       command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 ..
+       command = command .. [[ get_registered_ip ]] .. lookup .. [[ ]] .. use_ipv6 ..
                [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver
        local ip = SYS.exec(command)
        if ip == "" then ip = translate("no data") end
@@ -157,9 +143,6 @@ ena = ts:option( Flag, "enabled",
        translate("Enabled"))
 ena.template = "ddns/overview_enabled"
 ena.rmempty = false
-function ena.parse(self, section)
-       DDNS.flag_parse(self, section)
-end
 
 -- show PID and next update
 upd = ts:option( DummyValue, "_update",
index 4466063..ecc4131 100644 (file)
@@ -96,58 +96,6 @@ function get_pid(section)
        return pid
 end
 
--- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>"
-function ipkg_ver_compare(ver1, comp, ver2)
-       if not ver1 or not ver2
-       or not comp or not (#comp > 0) then return nil end
-       -- correct compare string
-       if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
-       elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
-       elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
-       elseif comp == "="  or comp == "==" then comp = "=="
-       elseif comp == "<<" then comp = "<"
-       elseif comp == ">>" then comp = ">"
-       else return nil end
-
-       local av1 = UTIL.split(ver1, "[%.%-]", nil, true)
-       local av2 = UTIL.split(ver2, "[%.%-]", nil, true)
-
-       for i = 1, math.max(table.getn(av1),table.getn(av2)), 1  do
-               local s1 = av1[i] or ""
-               local s2 = av2[i] or ""
-
-               -- first "not equal" found return true
-               if comp == "~=" and (s1 ~= s2) then return true end
-               -- first "lower" found return true
-               if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
-               -- first "greater" found return true
-               if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
-               -- not equal then return false
-               if (s1 ~= s2) then return false end
-       end
-
-       -- all equal and not compare greater or lower then true
-       return not (comp == "<" or comp == ">")
-end
-
--- read version information for given package if installed
-function ipkg_ver_installed(pkg)
-       local version = nil
-       local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r")
-       if control then
-               local ln
-               repeat
-                       ln = control:read("*l")
-                       if ln and ln:match("^Version: ") then
-                               version = ln:gsub("^Version: ", "")
-                               break
-                       end
-               until not ln
-               control:close()
-       end
-       return version
-end
-
 -- replacement of build-in read of UCI option
 -- modified AbstractValue.cfgvalue(self, section) from cbi.lua
 -- needed to read from other option then current value definition
@@ -172,24 +120,77 @@ function read_value(self, section, option)
        end
 end
 
--- replacement of build-in Flag.parse of cbi.lua
--- modified to mark section as changed if value changes
--- current parse did not do this, but it is done AbstaractValue.parse()
-function flag_parse(self, section)
-       local fexists = self.map:formvalue(
-               luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
+-- replacement of build-in parse of "Value"
+-- modified AbstractValue.parse(self, section, novld) from cbi.lua
+-- validate is called if rmempty/optional true or false
+-- before write check if forcewrite, value eq default, and more
+function value_parse(self, section, novld)
+       local fvalue = self:formvalue(section)
+       local fexist = ( fvalue and (#fvalue > 0) )     -- not "nil" and "not empty"
+       local cvalue = self:cfgvalue(section)
+       local rm_opt = ( self.rmempty or self.optional )
+       local eq_cfg                                    -- flag: equal cfgvalue
 
-       if fexists then
-               local fvalue = self:formvalue(section) and self.enabled or self.disabled
-               local cvalue = self:cfgvalue(section)
-               if fvalue ~= self.default or (not self.optional and not self.rmempty) then
-                       self:write(section, fvalue)
-               else
-                       self:remove(section)
+       -- If favlue and cvalue are both tables and have the same content
+       -- make them identical
+       if type(fvalue) == "table" and type(cvalue) == "table" then
+               eq_cfg = (#fvalue == #cvalue)
+               if eq_cfg then
+                       for i=1, #fvalue do
+                               if cvalue[i] ~= fvalue[i] then
+                                       eq_cfg = false
+                               end
+                       end
                end
-               if (fvalue ~= cvalue) then self.section.changed = true end
-       else
-               self:remove(section)
+               if eq_cfg then
+                       fvalue = cvalue
+               end
+       end
+
+       -- removed parameter "section" from function call because used/accepted nowhere
+       -- also removed call to function "transfer"
+       local vvalue, errtxt = self:validate(fvalue)
+
+       -- error handling; validate return "nil"
+       if not vvalue then
+               if novld then           -- and "novld" set
+                       return          -- then exit without raising an error
+               end
+
+               if fexist then          -- and there is a formvalue
+                       self:add_error(section, "invalid", errtxt)
+                       return          -- so data are invalid
+
+               elseif not rm_opt then  -- and empty formvalue but NOT (rmempty or optional) set
+                       self:add_error(section, "missing", errtxt)
+                       return          -- so data is missing
+               end
+       end
+       -- for whatever reason errtxt set and not handled above
+       assert( not (errtxt and (#errtxt > 0)), "unhandled validate error" )
+
+       -- lets continue with value returned from validate
+       eq_cfg  = ( vvalue == cvalue )                  -- update equal_config flag
+       local vexist = ( vvalue and (#vvalue > 0) )     -- not "nil" and "not empty"
+       local eq_def = ( vvalue == self.default )       -- equal_default flag
+
+       -- not forcewrite and (rmempty or optional)
+       -- and (no data or equal_default)
+       if not self.forcewrite and rm_opt
+         and (not vexist or eq_def) then
+               if self:remove(section) then            -- remove data from UCI
+                       self.section.changed = true     -- and push events
+               end
+               return
+       end
+
+       -- not forcewrite and no changes, so nothing to write
+       if not self.forcewrite and eq_cfg then
+               return
+       end
+
+       -- write data to UCI; raise event only on changes
+       if self:write(section, vvalue) and not eq_cfg then
                self.section.changed = true
        end
 end
index 4dcb7bb..fd1d5be 100644 (file)
@@ -6,7 +6,7 @@
                var txt = document.getElementById("cbid.ddns." + section + "._logview.txt");    // TextArea
                if ( !txt ) { return; } // security check
 
-               XHR.get('<%=url('admin/services/ddns/logview')%>/' + section, null,
+               XHR.get('<%=url([[admin]], [[services]], [[ddns]], [[logview]])%>/' + section, null,
                        function(x) {
                                if (x.responseText == "_nodata_")
                                        txt.value = "<%:File not found or empty%>";
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 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
                                        break;
                        }
 
-                       // domain
-                       // (data[i].domain ignored here
+                       // lookup
+                       // (data[i].lookup ignored here
 
                        // registered IP
                        // rip.innerHTML = "Registered IP";
-                       if (data[i].domain == "_nodomain_")
+                       if (data[i].lookup == "_nolookup_")
                                rip.innerHTML = '';
                        else if (data[i].reg_ip == "_nodata_")
                                rip.innerHTML = '<em><%:No data%></em>';
 
                // 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;"
        }
 
        // 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); }
                }
        // 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); }
                }
index 06dca48..5bdcb03 100644 (file)
                                        break;
                        }
 
-                       // domain
-                       if (data[j].domain == "_nodomain_")
+                       // lookup
+                       if (data[j].lookup == "_nolookup_")
                                tr.insertCell(-1).innerHTML = '<em><%:config error%></em>';
                        else
-                               tr.insertCell(-1).innerHTML = data[j].domain;
+                               tr.insertCell(-1).innerHTML = data[j].lookup;
 
                        // registered IP
                        switch (data[j].reg_ip) {
-                               case "_nodomain_":
+                               case "_nolookup_":
                                        tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>';
                                        break;
                                case "_nodata_":
        }
 
        // force to immediate show status (not waiting for XHR.poll)
-       XHR.get('<%=url('admin/services/ddns/status')%>', null,
+       XHR.get('<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null,
                function(x, data) {
                        if (data) { _data2elements(x, data); }
                }
        );
 
-       XHR.poll(5, '<%=url('admin/services/ddns/status')%>', null,
+       XHR.poll(15, '<%=url([[admin]], [[services]], [[ddns]], [[status]])%>', null,
                function(x, data) {
                        if (data) { _data2elements(x, data); }
                }
                <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>
index 6ffde5d..e662adc 100644 (file)
@@ -1,15 +1,15 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: luci-app-ddns\n"
-"POT-Creation-Date: 2015-05-08 21:29+0100\n"
-"PO-Revision-Date: 2015-05-08 21:47+0100\n"
+"POT-Creation-Date: 2015-11-04 19:10-0100\n"
+"PO-Revision-Date: 2015-11-14 18:31+0100\n"
 "Last-Translator: Christian Schoenebeck <christian.schoenebeck@gmail.com>\n"
 "Language-Team: \n"
 "Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.7.5\n"
+"X-Generator: Poedit 1.8.6\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Poedit-SourceCharset: UTF-8\n"
 "X-Poedit-Basepath: .\n"
@@ -70,6 +70,9 @@ msgstr ""
 msgid "Casual users should not change this setting"
 msgstr "Standard Benutzer sollten diese Einstellung nicht ändern."
 
+msgid "Change provider"
+msgstr "Anbieter wechseln"
+
 msgid "Check Interval"
 msgstr "Prüfinterval"
 
@@ -125,6 +128,12 @@ msgstr "Eigenes Update-Skript"
 msgid "DDNS Autostart disabled"
 msgstr "DDNS Autostart deaktiviert"
 
+msgid "DDNS Client Configuration"
+msgstr "DDNS Client Konfiguration"
+
+msgid "DDNS Client Documentation"
+msgstr "DDNS Client Dokumentation"
+
 msgid "DDNS Service provider"
 msgstr "DDNS-Dienstanbieter"
 
@@ -196,6 +205,9 @@ msgstr ""
 msgid "Disabled"
 msgstr "Deaktiviert"
 
+msgid "Domain"
+msgstr "Domäne"
+
 msgid "Dynamic DNS"
 msgstr "Dynamisches DNS"
 
@@ -284,8 +296,10 @@ msgstr "HTTPS nicht unterstützt"
 msgid "Hints"
 msgstr "Hinweise"
 
-msgid "Hostname/Domain"
-msgstr "Rechnername/Domäne"
+msgid "Hostname/FQDN to validate, if IP update happen or necessary"
+msgstr ""
+"Hostname/FQDN um zu überprüfen, ob eine Aktualisierung stattgefunden hat "
+"oder notwendig ist"
 
 msgid "IP address source"
 msgstr "IP-Adressquelle"
@@ -385,6 +399,12 @@ msgstr "Protokoll in Datei schreiben"
 msgid "Log to syslog"
 msgstr "Systemprotokoll verwenden"
 
+msgid "Lookup Hostname"
+msgstr "Nachschlage-Hostname"
+
+msgid "NOT installed"
+msgstr "NICHT installiert"
+
 msgid ""
 "Neither GNU Wget with SSL nor cURL installed to select a network to use for "
 "communication."
@@ -454,6 +474,21 @@ msgstr ""
 msgid "On Error the script will stop execution after given number of retrys"
 msgstr "Das Skript wird nach der gegebenen Anzahl von Fehlversuchen beendet."
 
+msgid "OpenWrt Wiki"
+msgstr "OpenWrt Wiki"
+
+msgid "Optional Encoded Parameter"
+msgstr "Optionaler codierten Parameter"
+
+msgid "Optional Parameter"
+msgstr "Optionaler Parameter"
+
+msgid "Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"
+msgstr "Optional: Ersetzt [PARAMENC] in der Update-URL (URL-codiert)"
+
+msgid "Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"
+msgstr "Optional: Ersetzt [PARAMENC] in der Update-URL (NICHT URL-codiert)"
+
 msgid "Overview"
 msgstr "Übersicht"
 
@@ -484,17 +519,20 @@ msgstr "Prozess ID"
 msgid "Read / Reread log file"
 msgstr "Protokolldatei (neu) einlesen"
 
+msgid "Really change DDNS provider?"
+msgstr "Wirklich DDNS-Anbieter wechseln?"
+
 msgid "Registered IP"
 msgstr "Registrierte IP"
 
 msgid "Replaces [DOMAIN] in Update-URL"
 msgstr "Ersetzt [DOMAIN] in der Update-URL"
 
-msgid "Replaces [PASSWORD] in Update-URL"
-msgstr "Ersetzt [PASSWORD] in der Update-URL"
+msgid "Replaces [PASSWORD] in Update-URL (URL-encoded)"
+msgstr "Ersetzt [PASSWORD] in der Update-URL (URL-codiert)"
 
-msgid "Replaces [USERNAME] in Update-URL"
-msgstr "Ersetzt [USERNAME] in der Update-URL"
+msgid "Replaces [USERNAME] in Update-URL (URL-encoded)"
+msgstr "Ersetzt [USERNAME] in der Update-URL (URL-codiert)"
 
 msgid "Run once"
 msgstr "Einmalig ausführen"
@@ -528,7 +566,7 @@ msgstr ""
 "Optionen."
 
 msgid "The default setting of '0' will retry infinite."
-msgstr "Der Standard-Wert von '0' wird es endlosen erneut versuchen."
+msgstr "Beim Standard-Wert von '0' wird es endlos erneut versucht."
 
 msgid "There is no service configured."
 msgstr "Kein Dienst konfiguriert"
@@ -661,8 +699,8 @@ msgstr "Stunden"
 msgid "installed"
 msgstr "installiert"
 
-msgid "invalid - Sample"
-msgstr "ungültig - Beispiel"
+msgid "invalid FQDN / required - Sample"
+msgstr "ungültige FQDN / Pflichtfeld - Beispiel"
 
 msgid "minimum value '0'"
 msgstr "Minimum Wert '0'"
index 3538680..7ab51dd 100644 (file)
@@ -50,6 +50,9 @@ msgstr ""
 msgid "Casual users should not change this setting"
 msgstr ""
 
+msgid "Change provider"
+msgstr ""
+
 msgid "Check Interval"
 msgstr ""
 
@@ -96,6 +99,12 @@ msgstr ""
 msgid "DDNS Autostart disabled"
 msgstr ""
 
+msgid "DDNS Client Configuration"
+msgstr ""
+
+msgid "DDNS Client Documentation"
+msgstr ""
+
 msgid "DDNS Service provider"
 msgstr ""
 
@@ -149,6 +158,9 @@ msgstr ""
 msgid "Disabled"
 msgstr ""
 
+msgid "Domain"
+msgstr ""
+
 msgid "Dynamic DNS"
 msgstr ""
 
@@ -230,7 +242,7 @@ msgstr ""
 msgid "Hints"
 msgstr ""
 
-msgid "Hostname/Domain"
+msgid "Hostname/FQDN to validate, if IP update happen or necessary"
 msgstr ""
 
 msgid "IP address source"
@@ -315,6 +327,12 @@ msgstr ""
 msgid "Log to syslog"
 msgstr ""
 
+msgid "Lookup Hostname"
+msgstr ""
+
+msgid "NOT installed"
+msgstr ""
+
 msgid ""
 "Neither GNU Wget with SSL nor cURL installed to select a network to use for "
 "communication."
@@ -373,6 +391,21 @@ msgstr ""
 msgid "On Error the script will stop execution after given number of retrys"
 msgstr ""
 
+msgid "OpenWrt Wiki"
+msgstr ""
+
+msgid "Optional Encoded Parameter"
+msgstr ""
+
+msgid "Optional Parameter"
+msgstr ""
+
+msgid "Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)"
+msgstr ""
+
+msgid "Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)"
+msgstr ""
+
 msgid "Overview"
 msgstr ""
 
@@ -403,16 +436,19 @@ msgstr ""
 msgid "Read / Reread log file"
 msgstr ""
 
+msgid "Really change DDNS provider?"
+msgstr ""
+
 msgid "Registered IP"
 msgstr ""
 
 msgid "Replaces [DOMAIN] in Update-URL"
 msgstr ""
 
-msgid "Replaces [PASSWORD] in Update-URL"
+msgid "Replaces [PASSWORD] in Update-URL (URL-encoded)"
 msgstr ""
 
-msgid "Replaces [USERNAME] in Update-URL"
+msgid "Replaces [USERNAME] in Update-URL (URL-encoded)"
 msgstr ""
 
 msgid "Run once"
@@ -563,7 +599,7 @@ msgstr ""
 msgid "installed"
 msgstr ""
 
-msgid "invalid - Sample"
+msgid "invalid FQDN / required - Sample"
 msgstr ""
 
 msgid "minimum value '0'"