1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
3 -- Copyright 2013 Manuel Munz <freifunk at somakoma dot de>
4 -- Copyright 2014-2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
5 -- Licensed to the public under the Apache License 2.0.
7 local NX = require "nixio"
8 local NXFS = require "nixio.fs"
9 local SYS = require "luci.sys"
10 local UTIL = require "luci.util"
11 local HTTP = require "luci.http"
12 local DISP = require "luci.dispatcher"
13 local WADM = require "luci.tools.webadmin"
14 local DTYP = require "luci.cbi.datatypes"
15 local CTRL = require "luci.controller.ddns" -- this application's controller
16 local DDNS = require "luci.tools.ddns" -- ddns multiused functions
18 -- takeover arguments -- #######################################################
19 local section = arg[1]
21 -- check supported options -- ##################################################
22 -- saved to local vars here because doing multiple os calls slow down the system
23 local has_ipv6 = DDNS.check_ipv6() -- IPv6 support
24 local has_ssl = DDNS.check_ssl() -- HTTPS support
25 local has_proxy = DDNS.check_proxy() -- Proxy support
26 local has_dnstcp = DDNS.check_bind_host() -- DNS TCP support
27 local has_force = has_ssl and has_dnstcp -- Force IP Protocoll
29 -- html constants -- ###########################################################
30 local font_red = "<font color='red'>"
31 local font_off = "</font>"
32 local bold_on = "<strong>"
33 local bold_off = "</strong>"
35 -- error text constants -- #####################################################
36 local err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
37 translate("please select 'IPv4' address version")
38 local err_ipv6_basic = bold_on ..
40 translate("IPv6 not supported") ..
42 "<br />" .. translate("please select 'IPv4' address version") ..
44 local err_ipv6_other = bold_on ..
46 translate("IPv6 not supported") ..
48 "<br />" .. translate("please select 'IPv4' address version in") .. " " ..
50 DISP.build_url("admin", "services", "ddns", "detail", section) ..
51 "?tab.dns." .. section .. "=basic" ..
53 translate("Basic Settings") ..
57 local function err_tab_basic(self)
58 return translate("Basic Settings") .. " - " .. self.title .. ": "
60 local function err_tab_adv(self)
61 return translate("Advanced Settings") .. " - " .. self.title .. ": "
63 local function err_tab_timer(self)
64 return translate("Timer Settings") .. " - " .. self.title .. ": "
67 -- read services/services_ipv6 files -- ########################################
68 local services4 = { } -- IPv4 --
69 local fd4 = io.open("/usr/lib/ddns/services", "r")
74 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
75 s = s and s:gsub('"','') -- remove "
76 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
77 if t then services4[t[1]]=t[2] end
82 local services6 = { } -- IPv6 --
83 local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r")
88 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
89 s = s and s:gsub('"','') -- remove "
90 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
91 if t then services6[t[1]]=t[2] end
96 -- multi-used functions -- ####################################################
97 -- function to verify settings around ip_source
98 -- will use dynamic_dns_lucihelper to check if
99 -- local IP can be read
100 local function _verify_ip_source()
101 -- section is globally defined here be calling agrument (see above)
104 local _interface = "-"
108 local _ipv6 = usev6:formvalue(section)
109 local _source = (_ipv6 == "1")
110 and src6:formvalue(section)
111 or src4:formvalue(section)
112 if _source == "network" then
113 _network = (_ipv6 == "1")
114 and ipn6:formvalue(section)
115 or ipn4:formvalue(section)
116 elseif _source == "web" then
117 _url = (_ipv6 == "1")
118 and iurl6:formvalue(section)
119 or iurl4:formvalue(section)
120 -- proxy only needed for checking url
121 _proxy = (pxy) and pxy:formvalue(section) or ""
122 elseif _source == "interface" then
123 _interface = ipi:formvalue(section)
124 elseif _source == "script" then
125 _script = ips:formvalue(section)
128 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] ..
129 _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] ..
130 _url .. [[ ]] .. _interface .. [[ ']] .. _script.. [[' ]] .. _proxy
131 local ret = SYS.call(command)
136 return nil -- invalid
140 -- function to check if option is used inside url or script
141 -- return -1 on error, 0 NOT required, 1 required
142 local function _option_used(option, urlscript)
143 local surl -- search string for url
144 local ssh -- search string for script
145 local required -- option used inside url or script
147 if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain'
148 elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username'
149 elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password'
150 elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc'
151 elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt'
153 error("undefined option")
154 return -1 -- return on error
157 local required = false
159 if urlscript:find('http') then
160 required = ( urlscript:find(surl) )
163 if not urlscript:find("/") then
164 -- might be inside ddns-scripts directory
165 urlscript = "/usr/lib/ddns/" .. urlscript
167 -- problem with script exit here
168 if not NXFS.access(urlscript) then return -1 end
170 local f = io.input(urlscript)
171 -- still problem with script exit here
172 if not f then return -1 end
173 for l in f:lines() do
175 if l:find('^#') then break end -- continue on comment lines
176 required = ( l:find(surl) or l:find(ssh) )
178 if required then break end
182 return (required and 1 or 0)
185 -- function to verify if option is valid
186 local function _option_validate(self, value)
187 -- section is globally defined here be calling agrument (see above)
188 local fusev6 = usev6:formvalue(section)
189 local fsvc4 = svc4:formvalue(section)
190 local fsvc6 = svc6:formvalue(section)
193 -- IP-Version dependent custom service selected
194 if (fusev6 == "0" and fsvc4 == "-") or
195 (fusev6 == "1" and fsvc6 == "-") then
197 urlsh = uurl:formvalue(section)
198 -- no url then read custom script
199 if not urlsh or (#urlsh == 0) then
200 urlsh = ush:formvalue(section)
202 -- IPv4 read from services4 table
203 elseif (fusev6 == "0") then
204 urlsh = services4[fsvc4]
205 -- IPv6 read from services6 table
207 urlsh = services6[fsvc6]
209 -- problem with url or script exit here
210 -- error handled somewhere else
211 if not urlsh or (#urlsh == 0) then return "" end
213 used = _option_used(self.option, urlsh)
214 -- on error or not used return empty sting
215 if used < 1 then return "" end
216 -- needed but no data then return error
217 if not value or (#value == 0) then
218 return nil, err_tab_basic(self) .. translate("missing / required")
223 -- cbi-map definition -- #######################################################
224 local m = Map("ddns")
225 m.title = CTRL.app_title_back()
226 m.description = CTRL.app_description()
227 m.redirect = DISP.build_url("admin", "services", "ddns")
229 m.on_after_commit = function(self)
230 if self.changed then -- changes ?
231 local pid = DDNS.get_pid(section)
232 if pid > 0 then -- running ?
233 local tmp = NX.kill(pid, 1) -- send SIGHUP
238 -- provider switch was requested, save and reload page
239 if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1]
241 local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section)
242 if fusev6 == "1" then
243 fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section)
245 fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section)
248 if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed
249 m:set(section, "use_ipv6", fusev6) -- save it
252 if fsvc ~= "-" then -- NOT "custom"
253 m:set(section, "service_name", fsvc) -- save it
255 m:del(section, "service_name") -- delete it
260 HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
264 -- read application settings -- ################################################
265 -- date format; if not set use ISO format
266 local date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
268 local log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
270 -- cbi-section definition -- ###################################################
271 local ns = m:section( NamedSection, section, "service",
272 translate("Details for") .. ([[: <strong>%s</strong>]] % section),
273 translate("Configure here the details for selected Dynamic DNS service.") )
274 ns.instance = section -- arg [1]
275 ns:tab("basic", translate("Basic Settings"), nil )
276 ns:tab("advanced", translate("Advanced Settings"), nil )
277 ns:tab("timer", translate("Timer Settings"), nil )
278 ns:tab("logview", translate("Log File Viewer"), nil )
280 -- TAB: Basic #####################################################################################
281 -- enabled -- #################################################################
282 en = ns:taboption("basic", Flag, "enabled",
283 translate("Enabled"),
284 translate("If this service section is disabled it could not be started." .. "<br />" ..
285 "Neither from LuCI interface nor from console") )
286 en.orientation = "horizontal"
288 -- IPv4/IPv6 - lookup_host -- #################################################
289 luh = ns:taboption("basic", Value, "lookup_host",
290 translate("Lookup Hostname"),
291 translate("Hostname/FQDN to validate, if IP update happen or necessary") )
293 luh.placeholder = "myhost.example.com"
294 function luh.validate(self, value)
297 or not DTYP.hostname(value) then
298 return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
300 return UTIL.trim(value)
303 function luh.parse(self, section, novld)
304 DDNS.value_parse(self, section, novld)
307 -- use_ipv6 -- ################################################################
308 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
309 translate("IP address version"),
310 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
311 usev6.widget = "radio"
313 usev6:value("0", translate("IPv4-Address") )
314 function usev6.cfgvalue(self, section)
315 local value = AbstractValue.cfgvalue(self, section)
316 if has_ipv6 or (value == "1" and not has_ipv6) then
317 self:value("1", translate("IPv6-Address") )
319 if value == "1" and not has_ipv6 then
320 self.description = err_ipv6_basic
324 function usev6.validate(self, value)
325 if (value == "1" and has_ipv6) or value == "0" then
328 return nil, err_tab_basic(self) .. err_ipv6_plain
330 function usev6.parse(self, section, novld)
331 DDNS.value_parse(self, section, novld)
334 -- IPv4 - service_name -- #####################################################
335 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
336 translate("DDNS Service provider") .. " [IPv4]" )
338 svc4:depends("use_ipv6", "0") -- only show on IPv4
339 function svc4.cfgvalue(self, section)
340 local v = DDNS.read_value(self, section, "service_name")
341 if not v or #v == 0 then
347 function svc4.validate(self, value)
348 if usev6:formvalue(section) == "0" then -- do only on IPv4
351 return "" -- supress validate error
354 function svc4.write(self, section, value)
355 if usev6:formvalue(section) == "0" then -- do only IPv4 here
356 self.map:del(section, self.option) -- to be shure
357 if value ~= "-" then -- and write "service_name
358 self.map:del(section, "update_url") -- delete update_url
359 self.map:del(section, "update_script") -- delete update_script
360 return self.map:set(section, "service_name", value)
362 return self.map:del(section, "service_name")
366 function svc4.parse(self, section, novld)
367 DDNS.value_parse(self, section, novld)
370 -- IPv6 - service_name -- #####################################################
371 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
372 translate("DDNS Service provider") .. " [IPv6]" )
374 svc6:depends("use_ipv6", "1") -- only show on IPv6
376 svc6.description = err_ipv6_basic
378 function svc6.cfgvalue(self, section)
379 local v = DDNS.read_value(self, section, "service_name")
380 if not v or #v == 0 then
386 function svc6.validate(self, value)
387 if usev6:formvalue(section) == "1" then -- do only on IPv6
388 if has_ipv6 then return value end
389 return nil, err_tab_basic(self) .. err_ipv6_plain
391 return "" -- supress validate error
394 function svc6.write(self, section, value)
395 if usev6:formvalue(section) == "1" then -- do only when IPv6
396 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
397 if value ~= "-" then -- and write "service_name
398 self.map:del(section, "update_url") -- delete update_url
399 self.map:del(section, "update_script") -- delete update_script
400 return self.map:set(section, "service_name", value)
402 return self.map:del(section, "service_name")
406 function svc6.parse(self, section, novld)
407 DDNS.value_parse(self, section, novld)
410 -- IPv4/IPv6 - change Provider -- #############################################
411 svs = ns:taboption("basic", Button, "_switch")
412 svs.title = translate("Really change DDNS provider?")
413 svs.inputtitle = translate("Change provider")
414 svs.inputstyle = "apply"
416 -- IPv4/IPv6 - update_url -- ##################################################
417 uurl = ns:taboption("basic", Value, "update_url",
418 translate("Custom update-URL"),
419 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
420 "Follow instructions you will find on their WEB page.") )
421 function uurl.validate(self, value)
422 local fush = ush:formvalue(section)
423 local fusev6 = usev6:formvalue(section)
425 if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
426 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
427 return "" -- suppress validate error
428 elseif not value then
429 if not fush or (#fush == 0) then
430 return nil, err_tab_basic(self) .. translate("missing / required")
432 return "" -- suppress validate error / update_script is given
434 elseif (#fush > 0) then
435 return nil, err_tab_basic(self) .. translate("either url or script could be set")
438 local url = DDNS.parse_url(value)
439 if not url.scheme == "http" then
440 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
441 elseif not url.query then
442 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
443 elseif not url.host then
444 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
445 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
446 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
451 function uurl.parse(self, section, novld)
452 DDNS.value_parse(self, section, novld)
455 -- IPv4/IPv6 - update_script -- ###############################################
456 ush = ns:taboption("basic", Value, "update_script",
457 translate("Custom update-script"),
458 translate("Custom update script to be used for updating your DDNS Provider.") )
459 function ush.validate(self, value)
460 local fuurl = uurl:formvalue(section)
461 local fusev6 = usev6:formvalue(section)
463 if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
464 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
465 return "" -- suppress validate error
466 elseif not value then
467 if not fuurl or (#fuurl == 0) then
468 return nil, err_tab_basic(self) .. translate("missing / required")
470 return "" -- suppress validate error / update_url is given
472 elseif (#fuurl > 0) then
473 return nil, err_tab_basic(self) .. translate("either url or script could be set")
474 elseif not NXFS.access(value) then
475 return nil, err_tab_basic(self) .. translate("File not found")
479 function ush.parse(self, section, novld)
480 DDNS.value_parse(self, section, novld)
483 -- IPv4/IPv6 - domain -- ######################################################
484 dom = ns:taboption("basic", Value, "domain",
486 translate("Replaces [DOMAIN] in Update-URL") )
487 dom.placeholder = "myhost.example.com"
488 function dom.validate(self, value)
489 return _option_validate(self, value)
491 function dom.parse(self, section, novld)
492 DDNS.value_parse(self, section, novld)
495 -- IPv4/IPv6 - username -- ####################################################
496 user = ns:taboption("basic", Value, "username",
497 translate("Username"),
498 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
499 function user.validate(self, value)
500 return _option_validate(self, value)
502 function user.parse(self, section, novld)
503 DDNS.value_parse(self, section, novld)
506 -- IPv4/IPv6 - password -- ####################################################
507 pw = ns:taboption("basic", Value, "password",
508 translate("Password"),
509 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
511 function pw.validate(self, value)
512 return _option_validate(self, value)
514 function pw.parse(self, section, novld)
515 DDNS.value_parse(self, section, novld)
518 -- IPv4/IPv6 - param_enc -- ###################################################
519 pe = ns:taboption("basic", Value, "param_enc",
520 translate("Optional Encoded Parameter"),
521 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
522 function pe.validate(self, value)
523 return _option_validate(self, value)
525 function pe.parse(self, section, novld)
526 DDNS.value_parse(self, section, novld)
529 -- IPv4/IPv6 - param_enc -- ###################################################
530 po = ns:taboption("basic", Value, "param_opt",
531 translate("Optional Parameter"),
532 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
533 function po.validate(self, value)
534 return _option_validate(self, value)
536 function po.parse(self, section, novld)
537 DDNS.value_parse(self, section, novld)
540 -- handled service dependent show/display -- ##################################
542 local cv4 = svc4:cfgvalue(section)
544 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
545 ush:depends ("ipv4_service_name", "?")
546 uurl:depends("ipv4_service_name", "?")
548 uurl:depends("ipv4_service_name", "-")
549 ush:depends ("ipv4_service_name", "-")
550 dom:depends("ipv4_service_name", "-" )
551 user:depends("ipv4_service_name", "-" )
552 pw:depends("ipv4_service_name", "-" )
553 pe:depends("ipv4_service_name", "-" )
554 po:depends("ipv4_service_name", "-" )
556 for s, u in UTIL.kspairs(services4) do
557 svc4:value(s) -- fill DropDown-List
559 svs:depends("ipv4_service_name", s )
561 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
562 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
563 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
564 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
565 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
568 svc4:value("-", translate("-- custom --") )
571 local cv6 = svc6:cfgvalue(section)
573 svs:depends ("ipv6_service_name", "-" )
574 uurl:depends("ipv6_service_name", "?")
575 ush:depends ("ipv6_service_name", "?")
577 uurl:depends("ipv6_service_name", "-")
578 ush:depends ("ipv6_service_name", "-")
579 dom:depends("ipv6_service_name", "-" )
580 user:depends("ipv6_service_name", "-" )
581 pw:depends("ipv6_service_name", "-" )
582 pe:depends("ipv6_service_name", "-" )
583 po:depends("ipv6_service_name", "-" )
585 for s, u in UTIL.kspairs(services6) do
586 svc6:value(s) -- fill DropDown-List
588 svs:depends("ipv6_service_name", s )
590 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
591 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
592 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
593 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
594 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
597 svc6:value("-", translate("-- custom --") )
599 -- IPv4/IPv6 - use_https -- ###################################################
600 if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
601 https = ns:taboption("basic", Flag, "use_https",
602 translate("Use HTTP Secure") )
603 https.orientation = "horizontal"
604 function https.cfgvalue(self, section)
605 local value = AbstractValue.cfgvalue(self, section)
606 if not has_ssl and value == "1" then
607 self.description = bold_on .. font_red ..
608 translate("HTTPS not supported") .. font_off .. "<br />" ..
609 translate("please disable") .. " !" .. bold_off
611 self.description = translate("Enable secure communication with DDNS provider")
615 function https.validate(self, value)
616 if (value == "1" and has_ssl ) or value == "0" then return value end
617 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
619 function https.write(self, section, value)
621 return self.map:set(section, self.option, value)
623 self.map:del(section, "cacert")
624 return self.map:del(section, self.option)
629 -- IPv4/IPv6 - cacert -- ######################################################
631 cert = ns:taboption("basic", Value, "cacert",
632 translate("Path to CA-Certificate"),
633 translate("directory or path/file") .. "<br />" ..
634 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
635 translate("to run HTTPS without verification of server certificates (insecure)") )
636 cert:depends("use_https", "1")
637 cert.default = "/etc/ssl/certs"
638 cert.forcewrite = true
639 function cert.validate(self, value)
640 if https:formvalue(section) == "0" then
641 return "" -- supress validate error if NOT https
643 if value then -- otherwise errors in datatype check
644 if DTYP.directory(value)
646 or value == "IGNORE" then
650 return nil, err_tab_basic(self) ..
651 translate("file or directory not found or not 'IGNORE'") .. " !"
653 function cert.parse(self, section, novld)
654 DDNS.value_parse(self, section, novld)
658 -- TAB: Advanced #################################################################################
659 -- IPv4 - ip_source -- ########################################################
660 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
661 translate("IP address source") .. " [IPv4]",
662 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
663 src4:depends("use_ipv6", "0") -- IPv4 selected
664 src4.default = "network"
665 src4:value("network", translate("Network"))
666 src4:value("web", translate("URL"))
667 src4:value("interface", translate("Interface"))
668 src4:value("script", translate("Script"))
669 function src4.cfgvalue(self, section)
670 return DDNS.read_value(self, section, "ip_source")
672 function src4.validate(self, value)
673 if usev6:formvalue(section) == "1" then
674 return "" -- ignore on IPv6 selected
675 elseif not _verify_ip_source() then
676 return nil, err_tab_adv(self) ..
677 translate("can not detect local IP. Please select a different Source combination")
682 function src4.write(self, section, value)
683 if usev6:formvalue(section) == "1" then
684 return true -- ignore on IPv6 selected
685 elseif value == "network" then
686 self.map:del(section, "ip_url") -- delete not need parameters
687 self.map:del(section, "ip_interface")
688 self.map:del(section, "ip_script")
689 elseif value == "web" then
690 self.map:del(section, "ip_network") -- delete not need parameters
691 self.map:del(section, "ip_interface")
692 self.map:del(section, "ip_script")
693 elseif value == "interface" then
694 self.map:del(section, "ip_network") -- delete not need parameters
695 self.map:del(section, "ip_url")
696 self.map:del(section, "ip_script")
697 elseif value == "script" then
698 self.map:del(section, "ip_network")
699 self.map:del(section, "ip_url") -- delete not need parameters
700 self.map:del(section, "ip_interface")
702 self.map:del(section, self.option) -- delete "ipv4_source" helper
703 return self.map:set(section, "ip_source", value) -- and write "ip_source
705 function src4.parse(self, section, novld)
706 DDNS.value_parse(self, section, novld)
709 -- IPv6 - ip_source -- ########################################################
710 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
711 translate("IP address source") .. " [IPv6]",
712 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
713 src6:depends("use_ipv6", 1) -- IPv6 selected
714 src6.default = "network"
715 src6:value("network", translate("Network"))
716 src6:value("web", translate("URL"))
717 src6:value("interface", translate("Interface"))
718 src6:value("script", translate("Script"))
720 src6.description = err_ipv6_other
722 function src6.cfgvalue(self, section)
723 return DDNS.read_value(self, section, "ip_source")
725 function src6.validate(self, value)
726 if usev6:formvalue(section) == "0" then
727 return "" -- ignore on IPv4 selected
728 elseif not has_ipv6 then
729 return nil, err_tab_adv(self) .. err_ipv6_plain
730 elseif not _verify_ip_source() then
731 return nil, err_tab_adv(self) ..
732 translate("can not detect local IP. Please select a different Source combination")
737 function src6.write(self, section, value)
738 if usev6:formvalue(section) == "0" then
739 return true -- ignore on IPv4 selected
740 elseif value == "network" then
741 self.map:del(section, "ip_url") -- delete not need parameters
742 self.map:del(section, "ip_interface")
743 self.map:del(section, "ip_script")
744 elseif value == "web" then
745 self.map:del(section, "ip_network") -- delete not need parameters
746 self.map:del(section, "ip_interface")
747 self.map:del(section, "ip_script")
748 elseif value == "interface" then
749 self.map:del(section, "ip_network") -- delete not need parameters
750 self.map:del(section, "ip_url")
751 self.map:del(section, "ip_script")
752 elseif value == "script" then
753 self.map:del(section, "ip_network")
754 self.map:del(section, "ip_url") -- delete not need parameters
755 self.map:del(section, "ip_interface")
757 self.map:del(section, self.option) -- delete "ipv4_source" helper
758 return self.map:set(section, "ip_source", value) -- and write "ip_source
760 function src6.parse(self, section, novld)
761 DDNS.value_parse(self, section, novld)
764 -- IPv4 - ip_network (default "wan") -- #######################################
765 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
766 translate("Network") .. " [IPv4]",
767 translate("Defines the network to read systems IPv4-Address from") )
768 ipn4:depends("ipv4_source", "network")
770 WADM.cbi_add_networks(ipn4)
771 function ipn4.cfgvalue(self, section)
772 return DDNS.read_value(self, section, "ip_network")
774 function ipn4.validate(self, value)
775 if usev6:formvalue(section) == "1"
776 or src4:formvalue(section) ~= "network" then
777 -- ignore if IPv6 selected OR
778 -- ignore everything except "network"
784 function ipn4.write(self, section, value)
785 if usev6:formvalue(section) == "1"
786 or src4:formvalue(section) ~= "network" then
787 -- ignore if IPv6 selected OR
788 -- ignore everything except "network"
791 -- set also as "interface" for monitoring events changes/hot-plug
792 self.map:set(section, "interface", value)
793 self.map:del(section, self.option) -- delete "ipv4_network" helper
794 return self.map:set(section, "ip_network", value) -- and write "ip_network"
797 function ipn4.parse(self, section, novld)
798 DDNS.value_parse(self, section, novld)
801 -- IPv6 - ip_network (default "wan6") -- ######################################
802 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
803 translate("Network") .. " [IPv6]" )
804 ipn6:depends("ipv6_source", "network")
805 ipn6.default = "wan6"
806 WADM.cbi_add_networks(ipn6)
808 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
810 ipn6.description = err_ipv6_other
812 function ipn6.cfgvalue(self, section)
813 return DDNS.read_value(self, section, "ip_network")
815 function ipn6.validate(self, value)
816 if usev6:formvalue(section) == "0"
817 or src6:formvalue(section) ~= "network" then
818 -- ignore if IPv4 selected OR
819 -- ignore everything except "network"
824 return nil, err_tab_adv(self) .. err_ipv6_plain
827 function ipn6.write(self, section, value)
828 if usev6:formvalue(section) == "0"
829 or src6:formvalue(section) ~= "network" then
830 -- ignore if IPv4 selected OR
831 -- ignore everything except "network"
834 -- set also as "interface" for monitoring events changes/hotplug
835 self.map:set(section, "interface", value)
836 self.map:del(section, self.option) -- delete "ipv6_network" helper
837 return self.map:set(section, "ip_network", value) -- and write "ip_network"
840 function ipn6.parse(self, section, novld)
841 DDNS.value_parse(self, section, novld)
844 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
845 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
846 translate("URL to detect") .. " [IPv4]",
847 translate("Defines the Web page to read systems IPv4-Address from") )
848 iurl4:depends("ipv4_source", "web")
849 iurl4.default = "http://checkip.dyndns.com"
850 function iurl4.cfgvalue(self, section)
851 return DDNS.read_value(self, section, "ip_url")
853 function iurl4.validate(self, value)
854 if usev6:formvalue(section) == "1"
855 or src4:formvalue(section) ~= "web" then
856 -- ignore if IPv6 selected OR
857 -- ignore everything except "web"
859 elseif not value or #value == 0 then
860 return nil, err_tab_adv(self) .. translate("missing / required")
863 local url = DDNS.parse_url(value)
864 if not (url.scheme == "http" or url.scheme == "https") then
865 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
866 elseif not url.host then
867 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
868 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
869 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
874 function iurl4.write(self, section, value)
875 if usev6:formvalue(section) == "1"
876 or src4:formvalue(section) ~= "web" then
877 -- ignore if IPv6 selected OR
878 -- ignore everything except "web"
881 self.map:del(section, self.option) -- delete "ipv4_url" helper
882 return self.map:set(section, "ip_url", value) -- and write "ip_url"
885 function iurl4.parse(self, section, novld)
886 DDNS.value_parse(self, section, novld)
889 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
890 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
891 translate("URL to detect") .. " [IPv6]" )
892 iurl6:depends("ipv6_source", "web")
893 iurl6.default = "http://checkipv6.dyndns.com"
895 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
897 iurl6.description = err_ipv6_other
899 function iurl6.cfgvalue(self, section)
900 return DDNS.read_value(self, section, "ip_url")
902 function iurl6.validate(self, value)
903 if usev6:formvalue(section) == "0"
904 or src6:formvalue(section) ~= "web" then
905 -- ignore if IPv4 selected OR
906 -- ignore everything except "web"
908 elseif not has_ipv6 then
909 return nil, err_tab_adv(self) .. err_ipv6_plain
910 elseif not value or #value == 0 then
911 return nil, err_tab_adv(self) .. translate("missing / required")
914 local url = DDNS.parse_url(value)
915 if not (url.scheme == "http" or url.scheme == "https") then
916 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
917 elseif not url.host then
918 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
919 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
920 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
925 function iurl6.write(self, section, value)
926 if usev6:formvalue(section) == "0"
927 or src6:formvalue(section) ~= "web" then
928 -- ignore if IPv4 selected OR
929 -- ignore everything except "web"
932 self.map:del(section, self.option) -- delete "ipv6_url" helper
933 return self.map:set(section, "ip_url", value) -- and write "ip_url"
936 function iurl6.parse(self, section, novld)
937 DDNS.value_parse(self, section, novld)
940 -- IPv4 + IPv6 - ip_interface -- ##############################################
941 ipi = ns:taboption("advanced", ListValue, "ip_interface",
942 translate("Interface"),
943 translate("Defines the interface to read systems IP-Address from") )
944 ipi:depends("ipv4_source", "interface") -- IPv4
945 ipi:depends("ipv6_source", "interface") -- or IPv6
946 for _, v in pairs(SYS.net.devices()) do
947 -- show only interface set to a network
948 -- and ignore loopback
949 net = WADM.iface_get_network(v)
950 if net and net ~= "loopback" then
954 function ipi.validate(self, value)
955 local fusev6 = usev6:formvalue(section)
956 if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
957 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
963 function ipi.write(self, section, value)
964 local fusev6 = usev6:formvalue(section)
965 if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
966 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
969 -- get network from device to
970 -- set also as "interface" for monitoring events changes/hotplug
971 local net = WADM.iface_get_network(value)
972 self.map:set(section, "interface", net)
973 return self.map:set(section, self.option, value)
976 function ipi.parse(self, section, novld)
977 DDNS.value_parse(self, section, novld)
980 -- IPv4 + IPv6 - ip_script -- #################################################
981 ips = ns:taboption("advanced", Value, "ip_script",
983 translate("User defined script to read systems IP-Address") )
984 ips:depends("ipv4_source", "script") -- IPv4
985 ips:depends("ipv6_source", "script") -- or IPv6
986 ips.placeholder = "/path/to/script.sh"
987 function ips.validate(self, value)
988 local fusev6 = usev6:formvalue(section)
990 if value then split = UTIL.split(value, " ") end
992 if (fusev6 == "0" and src4:formvalue(section) ~= "script")
993 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
995 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
996 return nil, err_tab_adv(self) ..
997 translate("not found or not executable - Sample: '/path/to/script.sh'")
1002 function ips.write(self, section, value)
1003 local fusev6 = usev6:formvalue(section)
1004 if (fusev6 == "0" and src4:formvalue(section) ~= "script")
1005 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
1008 return self.map:set(section, self.option, value)
1011 function ips.parse(self, section, novld)
1012 DDNS.value_parse(self, section, novld)
1015 -- IPv4 - interface - default "wan" -- ########################################
1016 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1017 -- only needs to be set if "ip_source"="web" or "script"
1018 -- if "ip_source"="network" or "interface" we use their network
1019 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1020 translate("Event Network") .. " [IPv4]",
1021 translate("Network on which the ddns-updater scripts will be started") )
1022 eif4:depends("ipv4_source", "web")
1023 eif4:depends("ipv4_source", "script")
1024 eif4.default = "wan"
1025 WADM.cbi_add_networks(eif4)
1026 function eif4.cfgvalue(self, section)
1027 return DDNS.read_value(self, section, "interface")
1029 function eif4.validate(self, value)
1030 local fsrc4 = src4:formvalue(section)
1031 if usev6:formvalue(section) == "1"
1032 or fsrc4 == "network"
1033 or fsrc4 == "interface" then
1034 return "" -- ignore IPv6, network, interface
1039 function eif4.write(self, section, value)
1040 local fsrc4 = src4:formvalue(section)
1041 if usev6:formvalue(section) == "1"
1042 or fsrc4 == "network"
1043 or fsrc4 == "interface" then
1044 return true -- ignore IPv6, network, interface
1046 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1047 return self.map:set(section, "interface", value) -- and write "interface"
1050 function eif4.parse(self, section, novld)
1051 DDNS.value_parse(self, section, novld)
1054 -- IPv6 - interface - default "wan6" -- #######################################
1055 -- event network to monitor changes/hotplug
1056 -- only needs to be set if "ip_source"="web" or "script"
1057 -- if "ip_source"="network" or "interface" we use their network
1058 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1059 translate("Event Network") .. " [IPv6]" )
1060 eif6:depends("ipv6_source", "web")
1061 eif6:depends("ipv6_source", "script")
1062 eif6.default = "wan6"
1063 WADM.cbi_add_networks(eif6)
1064 if not has_ipv6 then
1065 eif6.description = err_ipv6_other
1067 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1069 function eif6.cfgvalue(self, section)
1070 return DDNS.read_value(self, section, "interface")
1072 function eif6.validate(self, value)
1073 local fsrc6 = src6:formvalue(section)
1074 if usev6:formvalue(section) == "0"
1075 or fsrc6 == "network"
1076 or fsrc6 == "interface" then
1077 return "" -- ignore IPv4, network, interface
1078 elseif not has_ipv6 then
1079 return nil, err_tab_adv(self) .. err_ipv6_plain
1084 function eif6.write(self, section, value)
1085 local fsrc6 = src6:formvalue(section)
1086 if usev6:formvalue(section) == "0"
1087 or fsrc6 == "network"
1088 or fsrc6 == "interface" then
1089 return true -- ignore IPv4, network, interface
1091 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1092 return self.map:set(section, "interface", value) -- and write "interface"
1095 function eif6.parse(self, section, novld)
1096 DDNS.value_parse(self, section, novld)
1099 -- IPv4/IPv6 - bind_network -- ################################################
1100 if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1101 bnet = ns:taboption("advanced", ListValue, "bind_network",
1102 translate("Bind Network") )
1103 bnet:depends("ipv4_source", "web")
1104 bnet:depends("ipv6_source", "web")
1106 bnet:value("", translate("-- default --"))
1107 WADM.cbi_add_networks(bnet)
1108 function bnet.cfgvalue(self, section)
1109 local value = AbstractValue.cfgvalue(self, section)
1110 if not has_ssl and value ~= "" then
1111 self.description = bold_on .. font_red ..
1112 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1113 translate("please set to 'default'") .. " !" .. bold_off
1115 self.description = translate("OPTIONAL: Network to use for communication") ..
1116 "<br />" .. translate("Casual users should not change this setting")
1120 function bnet.validate(self, value)
1121 if (value ~= "" and has_ssl ) or value == "" then return value end
1122 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1124 function bnet.parse(self, section, novld)
1125 DDNS.value_parse(self, section, novld)
1129 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1130 -- optional to force wget/curl and host to use only selected IP version
1131 -- command parameter "-4" or "-6"
1132 if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1133 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1134 translate("Force IP Version") )
1135 fipv.orientation = "horizontal"
1136 function fipv.cfgvalue(self, section)
1137 local value = AbstractValue.cfgvalue(self, section)
1138 if not has_force and value ~= "0" then
1139 self.description = bold_on .. font_red ..
1140 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1141 translate("please disable") .. " !" .. bold_off
1143 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1147 function fipv.validate(self, value)
1148 if (value == "1" and has_force) or value == "0" then return value end
1149 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1153 -- IPv4 + IPv6 - dns_server -- ################################################
1154 -- optional DNS Server to use resolving my IP if "ip_source"="web"
1155 dns = ns:taboption("advanced", Value, "dns_server",
1156 translate("DNS-Server"),
1157 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1158 translate("Format: IP or FQDN"))
1159 dns.placeholder = "mydns.lan"
1160 function dns.validate(self, value)
1161 -- if .datatype is set, then it is checked before calling this function
1162 if not value or (#value == 0) then
1163 return "" -- ignore on empty
1164 elseif not DTYP.host(value) then
1165 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1167 local ipv6 = usev6:formvalue(section)
1168 local force = (fipv) and fipv:formvalue(section) or "0"
1169 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] ..
1170 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1171 local ret = SYS.call(command)
1172 if ret == 0 then return value -- everything OK
1173 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1174 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1175 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1176 else return nil, err_tab_adv(self) .. translate("unspecific error")
1180 function dns.parse(self, section, novld)
1181 DDNS.value_parse(self, section, novld)
1184 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1185 if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1186 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1187 translate("Force TCP on DNS") )
1188 tcp.orientation = "horizontal"
1189 function tcp.cfgvalue(self, section)
1190 local value = AbstractValue.cfgvalue(self, section)
1191 if not has_dnstcp and value ~= "0" then
1192 self.description = bold_on .. font_red ..
1193 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1194 translate("please disable") .. " !" .. bold_off
1196 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1200 function tcp.validate(self, value)
1201 if (value == "1" and has_dnstcp ) or value == "0" then
1204 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1208 -- IPv4 + IPv6 - proxy -- #####################################################
1209 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1210 if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1211 pxy = ns:taboption("advanced", Value, "proxy",
1212 translate("PROXY-Server") )
1213 pxy.placeholder="user:password@myproxy.lan:8080"
1214 function pxy.cfgvalue(self, section)
1215 local value = AbstractValue.cfgvalue(self, section)
1216 if not has_proxy and value ~= "" then
1217 self.description = bold_on .. font_red ..
1218 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1219 translate("please remove entry") .. "!" .. bold_off
1221 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1222 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1223 translate("IPv6 address must be given in square brackets") .. ": " ..
1224 bold_on .. " [2001:db8::1]:8080" .. bold_off
1228 function pxy.validate(self, value)
1229 -- if .datatype is set, then it is checked before calling this function
1230 if not value or (#value == 0) then
1231 return "" -- ignore on empty
1232 elseif has_proxy then
1233 local ipv6 = usev6:formvalue(section) or "0"
1234 local force = (fipv) and fipv:formvalue(section) or "0"
1235 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] ..
1236 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1237 local ret = SYS.call(command)
1238 if ret == 0 then return value
1239 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1240 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1241 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1242 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1243 else return nil, err_tab_adv(self) .. translate("unspecific error")
1246 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1249 function pxy.parse(self, section, novld)
1250 DDNS.value_parse(self, section, novld)
1254 -- use_syslog -- ##############################################################
1255 slog = ns:taboption("advanced", ListValue, "use_syslog",
1256 translate("Log to syslog"),
1257 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1259 slog:value("0", translate("No logging"))
1260 slog:value("1", translate("Info"))
1261 slog:value("2", translate("Notice"))
1262 slog:value("3", translate("Warning"))
1263 slog:value("4", translate("Error"))
1264 function slog.parse(self, section, novld)
1265 DDNS.value_parse(self, section, novld)
1268 -- use_logfile -- #############################################################
1269 logf = ns:taboption("advanced", Flag, "use_logfile",
1270 translate("Log to file"),
1271 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1272 translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] )
1273 logf.orientation = "horizontal"
1274 logf.default = "1" -- if not defined write to log by default
1276 -- TAB: Timer ####################################################################################
1277 -- check_interval -- ##########################################################
1278 ci = ns:taboption("timer", Value, "check_interval",
1279 translate("Check Interval") )
1280 ci.template = "ddns/detail_value"
1282 function ci.validate(self, value)
1283 if not DTYP.uinteger(value)
1284 or tonumber(value) < 1 then
1285 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1288 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1292 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1295 function ci.write(self, section, value)
1296 -- remove when default
1297 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1298 if secs ~= 600 then --default 10 minutes
1299 return self.map:set(section, self.option, value)
1301 self.map:del(section, "check_unit")
1302 return self.map:del(section, self.option)
1305 function ci.parse(self, section, novld)
1306 DDNS.value_parse(self, section, novld)
1309 -- check_unit -- ##############################################################
1310 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1311 translate("Interval to check for changed IP" .. "<br />" ..
1312 "Values below 5 minutes == 300 seconds are not supported") )
1313 cu.template = "ddns/detail_lvalue"
1314 cu.default = "minutes"
1315 cu:value("seconds", translate("seconds"))
1316 cu:value("minutes", translate("minutes"))
1317 cu:value("hours", translate("hours"))
1318 --cu:value("days", translate("days"))
1319 function cu.write(self, section, value)
1320 -- remove when default
1321 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1322 if secs ~= 600 then --default 10 minutes
1323 return self.map:set(section, self.option, value)
1328 function cu.parse(self, section, novld)
1329 DDNS.value_parse(self, section, novld)
1332 -- force_interval (modified) -- ###############################################
1333 fi = ns:taboption("timer", Value, "force_interval",
1334 translate("Force Interval") )
1335 fi.template = "ddns/detail_value"
1336 fi.default = "72" -- see dynamic_dns_updater.sh script
1337 --fi.rmempty = false -- validate ourselves for translatable error messages
1338 function fi.validate(self, value)
1339 if not DTYP.uinteger(value)
1340 or tonumber(value) < 0 then
1341 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1344 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1345 if force_s == 0 then
1349 local ci_value = ci:formvalue(section)
1350 if not DTYP.uinteger(ci_value) then
1351 return "" -- ignore because error in check_interval above
1354 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1355 if force_s >= check_s then
1359 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1361 function fi.write(self, section, value)
1362 -- simulate rmempty=true remove default
1363 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1364 if secs ~= 259200 then --default 72 hours == 3 days
1365 return self.map:set(section, self.option, value)
1367 self.map:del(section, "force_unit")
1368 return self.map:del(section, self.option)
1371 function fi.parse(self, section, novld)
1372 DDNS.value_parse(self, section, novld)
1375 -- force_unit -- ##############################################################
1376 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1377 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1378 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1379 "Values lower 'Check Interval' except '0' are not supported") )
1380 fu.template = "ddns/detail_lvalue"
1381 fu.default = "hours"
1382 --fu.rmempty = false -- want to control write process
1383 --fu:value("seconds", translate("seconds"))
1384 fu:value("minutes", translate("minutes"))
1385 fu:value("hours", translate("hours"))
1386 fu:value("days", translate("days"))
1387 function fu.write(self, section, value)
1388 -- simulate rmempty=true remove default
1389 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1390 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1391 return self.map:set(section, self.option, value)
1396 function fu.parse(self, section, novld)
1397 DDNS.value_parse(self, section, novld)
1400 -- retry_count -- #############################################################
1401 rc = ns:taboption("timer", Value, "retry_count")
1402 rc.title = translate("Error Retry Counter")
1403 rc.description = translate("On Error the script will stop execution after given number of retrys")
1405 .. translate("The default setting of '0' will retry infinite.")
1407 function rc.validate(self, value)
1408 if not DTYP.uinteger(value) then
1409 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1414 function rc.parse(self, section, novld)
1415 DDNS.value_parse(self, section, novld)
1418 -- retry_interval -- ##########################################################
1419 ri = ns:taboption("timer", Value, "retry_interval",
1420 translate("Error Retry Interval") )
1421 ri.template = "ddns/detail_value"
1423 function ri.validate(self, value)
1424 if not DTYP.uinteger(value)
1425 or tonumber(value) < 1 then
1426 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1431 function ri.write(self, section, value)
1432 -- simulate rmempty=true remove default
1433 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1434 if secs ~= 60 then --default 60seconds
1435 return self.map:set(section, self.option, value)
1437 self.map:del(section, "retry_unit")
1438 return self.map:del(section, self.option)
1441 function ri.parse(self, section, novld)
1442 DDNS.value_parse(self, section, novld)
1445 -- retry_unit -- ##############################################################
1446 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1447 translate("On Error the script will retry the failed action after given time") )
1448 ru.template = "ddns/detail_lvalue"
1449 ru.default = "seconds"
1450 --ru.rmempty = false -- want to control write process
1451 ru:value("seconds", translate("seconds"))
1452 ru:value("minutes", translate("minutes"))
1453 --ru:value("hours", translate("hours"))
1454 --ru:value("days", translate("days"))
1455 function ru.write(self, section, value)
1456 -- simulate rmempty=true remove default
1457 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1458 if secs ~= 60 then --default 60seconds
1459 return self.map:set(section, self.option, value)
1461 return true -- will be deleted by retry_interval
1464 function ru.parse(self, section, novld)
1465 DDNS.value_parse(self, section, novld)
1468 -- TAB: LogView ##################################################################################
1469 lv = ns:taboption("logview", DummyValue, "_logview")
1470 lv.template = "ddns/detail_logview"
1471 lv.inputtitle = translate("Read / Reread log file")
1473 function lv.cfgvalue(self, section)
1474 local lfile=log_dir .. "/" .. section .. ".log"
1475 if NXFS.access(lfile) then
1476 return lfile .. "\n" .. translate("Please press [Read] button")
1478 return lfile .. "\n" .. translate("File not found or empty")