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-2016 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 -- html constants -- ###########################################################
22 local font_red = "<font color='red'>"
23 local font_off = "</font>"
24 local bold_on = "<strong>"
25 local bold_off = "</strong>"
27 -- error text constants -- #####################################################
28 local err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
29 translate("please select 'IPv4' address version")
30 local err_ipv6_basic = bold_on ..
32 translate("IPv6 not supported") ..
34 "<br />" .. translate("please select 'IPv4' address version") ..
36 local err_ipv6_other = bold_on ..
38 translate("IPv6 not supported") ..
40 "<br />" .. translate("please select 'IPv4' address version in") .. " " ..
42 DISP.build_url("admin", "services", "ddns", "detail", section) ..
43 "?tab.dns." .. section .. "=basic" ..
45 translate("Basic Settings") ..
49 function err_tab_basic(self)
50 return translate("Basic Settings") .. " - " .. self.title .. ": "
52 function err_tab_adv(self)
53 return translate("Advanced Settings") .. " - " .. self.title .. ": "
55 function err_tab_timer(self)
56 return translate("Timer Settings") .. " - " .. self.title .. ": "
59 -- read services/services_ipv6 files -- ########################################
60 local services4 = { } -- IPv4 --
61 local fd4 = io.open("/etc/ddns/services", "r")
66 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
67 s = s and s:gsub('"','') -- remove "
68 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
69 if t then services4[t[1]]=t[2] end
74 local services6 = { } -- IPv6 --
75 local fd6 = io.open("/etc/ddns/services_ipv6", "r")
80 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
81 s = s and s:gsub('"','') -- remove "
82 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
83 if t then services6[t[1]]=t[2] end
88 -- multi-used functions -- ####################################################
89 -- function to verify settings around ip_source
90 -- will use dynamic_dns_lucihelper to check if
91 -- local IP can be read
92 local function _verify_ip_source()
93 -- section is globally defined here be calling argument (see above)
96 local _ipv6 = usev6:formvalue(section)
97 local _source = (_ipv6 == "1")
98 and src6:formvalue(section)
99 or src4:formvalue(section)
101 local command = CTRL.luci_helper .. [[ -]]
102 if (_ipv6 == "1") then command = command .. [[6]] end
104 if _source == "network" then
105 _arg = (_ipv6 == "1")
106 and ipn6:formvalue(section)
107 or ipn4:formvalue(section)
108 command = command .. [[n ]] .. _arg
109 elseif _source == "web" then
110 _arg = (_ipv6 == "1")
111 and iurl6:formvalue(section)
112 or iurl4:formvalue(section)
113 command = command .. [[u ]] .. _arg
115 -- proxy only needed for checking url
116 _arg = (pxy) and pxy:formvalue(section) or ""
117 if (_arg and #_arg > 0) then
118 command = command .. [[ -p ]] .. _arg
120 elseif _source == "interface" then
121 command = command .. [[i ]] .. ipi:formvalue(section)
122 elseif _source == "script" then
123 command = command .. [[s ]] .. ips:formvalue(section)
125 command = command .. [[ -- get_local_ip]]
126 return (SYS.call(command) == 0)
129 -- function to check if option is used inside url or script
130 -- return -1 on error, 0 NOT required, 1 required
131 local function _option_used(option, urlscript)
132 local surl -- search string for url
133 local ssh -- search string for script
134 local required -- option used inside url or script
136 if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain'
137 elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username'
138 elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password'
139 elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc'
140 elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt'
142 error("undefined option")
143 return -1 -- return on error
146 local required = false
148 if urlscript:find('http') then
149 required = ( urlscript:find(surl) )
152 if not urlscript:find("/") then
153 -- might be inside ddns-scripts directory
154 urlscript = "/usr/lib/ddns/" .. urlscript
156 -- problem with script exit here
157 if not NXFS.access(urlscript) then return -1 end
159 local f = io.input(urlscript)
160 -- still problem with script exit here
161 if not f then return -1 end
162 for l in f:lines() do
164 if l:find('^#') then break end -- continue on comment lines
165 required = ( l:find(surl) or l:find(ssh) )
167 if required then break end
171 return (required and 1 or 0)
174 -- function to verify if option is valid
175 local function _option_validate(self, value, optional)
176 -- section is globally defined here be calling argument (see above)
177 local fusev6 = usev6:formvalue(section) or "0"
178 local fsvc4 = svc4:formvalue(section) or "-"
179 local fsvc6 = svc6:formvalue(section) or "-"
182 -- IP-Version dependent custom service selected
183 if (fusev6 == "0" and fsvc4 == "-") or
184 (fusev6 == "1" and fsvc6 == "-") then
186 urlsh = uurl:formvalue(section) or ""
187 -- no url then read custom script
188 if (#urlsh == 0) then
189 urlsh = ush:formvalue(section) or ""
191 -- IPv4 read from services4 table
192 elseif (fusev6 == "0") then
193 urlsh = services4[fsvc4] or ""
194 -- IPv6 read from services6 table
196 urlsh = services6[fsvc6] or ""
198 -- problem with url or script exit here
199 -- error handled somewhere else
200 if (#urlsh == 0) then return "" end
202 used = _option_used(self.option, urlsh)
203 -- on error or not used return empty string
204 if used < 1 then return "" end
205 -- needed but no data then return error
206 if not value or (#value == 0) then
207 if optional then return nil end
208 return nil, err_tab_basic(self) .. translate("missing / required")
213 -- cbi-map definition -- #######################################################
214 local m = Map("ddns")
215 m.title = CTRL.app_title_back()
216 m.description = CTRL.app_description()
217 m.redirect = DISP.build_url("admin", "services", "ddns")
219 m.on_after_commit = function(self)
220 if self.changed then -- changes ?
221 local pid = DDNS.get_pid(section)
222 if pid > 0 then -- running ?
223 local tmp = NX.kill(pid, 1) -- send SIGHUP
228 -- provider switch was requested, save and reload page
229 if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1]
231 local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section) or "0"
232 if fusev6 == "1" then
233 fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section) or ""
235 fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section) or ""
238 if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed
239 m:set(section, "use_ipv6", fusev6) -- save it
242 if fsvc ~= "-" then -- NOT "custom"
243 m:set(section, "service_name", fsvc) -- save it
245 m:del(section, "service_name") -- delete it
250 HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
254 -- read application settings -- ################################################
256 local logdir = m.uci:get(m.config, "global", "ddns_logdir") or "/var/log/ddns"
258 -- cbi-section definition -- ###################################################
259 local ns = m:section( NamedSection, section, "service",
260 translate("Details for") .. ([[: <strong>%s</strong>]] % section),
261 translate("Configure here the details for selected Dynamic DNS service.") )
262 ns.instance = section -- arg [1]
263 ns:tab("basic", translate("Basic Settings"), nil )
264 ns:tab("advanced", translate("Advanced Settings"), nil )
265 ns:tab("timer", translate("Timer Settings"), nil )
266 ns:tab("logview", translate("Log File Viewer"), nil )
268 -- TAB: Basic #####################################################################################
269 -- enabled -- #################################################################
270 en = ns:taboption("basic", Flag, "enabled",
271 translate("Enabled"),
272 translate("If this service section is disabled it could not be started." .. "<br />" ..
273 "Neither from LuCI interface nor from console") )
274 en.orientation = "horizontal"
276 -- IPv4/IPv6 - lookup_host -- #################################################
277 luh = ns:taboption("basic", Value, "lookup_host",
278 translate("Lookup Hostname"),
279 translate("Hostname/FQDN to validate, if IP update happen or necessary") )
281 luh.placeholder = "myhost.example.com"
282 function luh.validate(self, value)
285 or not DTYP.hostname(value) then
286 return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
288 return UTIL.trim(value)
291 function luh.parse(self, section, novld)
292 DDNS.value_parse(self, section, novld)
295 -- use_ipv6 -- ################################################################
297 --We call it globally as it's called 11 times even outside specific function, saves 11 os.execute slow command!
298 local has_ipv6 = DDNS.env_info("has_ipv6")
300 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
301 translate("IP address version"),
302 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
303 usev6.widget = "radio"
305 usev6:value("0", translate("IPv4-Address") )
306 function usev6.cfgvalue(self, section)
307 local value = AbstractValue.cfgvalue(self, section) or "0"
308 if has_ipv6 or (value == "1" and not has_ipv6) then
309 self:value("1", translate("IPv6-Address") )
311 if value == "1" and not has_ipv6 then
312 self.description = err_ipv6_basic
316 function usev6.validate(self, value)
317 if (value == "1" and has_ipv6) or value == "0" then
320 return nil, err_tab_basic(self) .. err_ipv6_plain
322 function usev6.parse(self, section, novld)
323 DDNS.value_parse(self, section, novld)
326 -- IPv4 - service_name -- #####################################################
327 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
328 translate("DDNS Service provider") .. " [IPv4]" )
330 svc4:depends("use_ipv6", "0") -- only show on IPv4
331 function svc4.cfgvalue(self, section)
332 local v = DDNS.read_value(self, section, "service_name")
333 if v and (#v > 0) then
334 for s, u in UTIL.kspairs(services4) do
335 if v == s then return v end
340 function svc4.validate(self, value)
341 if usev6:formvalue(section) ~= "1" then -- do only on IPv4
344 return "" -- suppress validate error
347 function svc4.write(self, section, value)
348 if usev6:formvalue(section) ~= "1" then -- do only IPv4 here
349 self.map:del(section, self.option) -- to be shure
350 if value ~= "-" then -- and write "service_name
351 self.map:del(section, "update_url") -- delete update_url
352 self.map:del(section, "update_script") -- delete update_script
353 return self.map:set(section, "service_name", value)
355 return self.map:del(section, "service_name")
359 function svc4.parse(self, section, novld)
360 DDNS.value_parse(self, section, novld)
363 -- IPv6 - service_name -- #####################################################
364 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
365 translate("DDNS Service provider") .. " [IPv6]" )
367 svc6:depends("use_ipv6", "1") -- only show on IPv6
369 svc6.description = err_ipv6_basic
371 function svc6.cfgvalue(self, section)
372 local v = DDNS.read_value(self, section, "service_name")
373 if v and (#v > 0) then
374 for s, u in UTIL.kspairs(services4) do
375 if v == s then return v end
380 function svc6.validate(self, value)
381 if usev6:formvalue(section) == "1" then -- do only on IPv6
382 if has_ipv6 then return value end
383 return nil, err_tab_basic(self) .. err_ipv6_plain
385 return "" -- suppress validate error
388 function svc6.write(self, section, value)
389 if usev6:formvalue(section) == "1" then -- do only when IPv6
390 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
391 if value ~= "-" then -- and write "service_name
392 self.map:del(section, "update_url") -- delete update_url
393 self.map:del(section, "update_script") -- delete update_script
394 return self.map:set(section, "service_name", value)
396 return self.map:del(section, "service_name")
400 function svc6.parse(self, section, novld)
401 DDNS.value_parse(self, section, novld)
404 -- IPv4/IPv6 - change Provider -- #############################################
405 svs = ns:taboption("basic", Button, "_switch")
406 svs.title = translate("Really change DDNS provider?")
407 svs.inputtitle = translate("Change provider")
408 svs.inputstyle = "apply"
410 -- IPv4/IPv6 - update_url -- ##################################################
411 uurl = ns:taboption("basic", Value, "update_url",
412 translate("Custom update-URL"),
413 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
414 "Follow instructions you will find on their WEB page.") )
415 function uurl.validate(self, value)
416 local fush = ush:formvalue(section)
417 local fusev6 = usev6:formvalue(section)
419 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
420 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
421 return "" -- suppress validate error
422 elseif not value or (#value == 0) then
423 if not fush or (#fush == 0) then
424 return nil, err_tab_basic(self) .. translate("missing / required")
426 return "" -- suppress validate error / update_script is given
428 elseif (#fush > 0) then
429 return nil, err_tab_basic(self) .. translate("either url or script could be set")
432 local url = DDNS.parse_url(value)
433 if not url.scheme == "http" then
434 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
435 elseif not url.query then
436 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
437 elseif not url.host then
438 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
439 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
440 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
445 function uurl.parse(self, section, novld)
446 DDNS.value_parse(self, section, novld)
449 -- IPv4/IPv6 - update_script -- ###############################################
450 ush = ns:taboption("basic", Value, "update_script",
451 translate("Custom update-script"),
452 translate("Custom update script to be used for updating your DDNS Provider.") )
453 function ush.validate(self, value)
454 local fuurl = uurl:formvalue(section)
455 local fusev6 = usev6:formvalue(section)
457 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
458 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
459 return "" -- suppress validate error
460 elseif not value or (#value == 0) then
461 if not fuurl or (#fuurl == 0) then
462 return nil, err_tab_basic(self) .. translate("missing / required")
464 return "" -- suppress validate error / update_url is given
466 elseif (#fuurl > 0) then
467 return nil, err_tab_basic(self) .. translate("either url or script could be set")
468 elseif not NXFS.access(value) then
469 return nil, err_tab_basic(self) .. translate("File not found")
473 function ush.parse(self, section, novld)
474 DDNS.value_parse(self, section, novld)
477 -- IPv4/IPv6 - domain -- ######################################################
478 dom = ns:taboption("basic", Value, "domain",
480 translate("Replaces [DOMAIN] in Update-URL") )
481 dom.placeholder = "myhost.example.com"
482 function dom.validate(self, value)
483 return _option_validate(self, value)
485 function dom.parse(self, section, novld)
486 DDNS.value_parse(self, section, novld)
489 -- IPv4/IPv6 - username -- ####################################################
490 user = ns:taboption("basic", Value, "username",
491 translate("Username"),
492 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
493 function user.validate(self, value)
494 return _option_validate(self, value)
496 function user.parse(self, section, novld)
497 DDNS.value_parse(self, section, novld)
500 -- IPv4/IPv6 - password -- ####################################################
501 pw = ns:taboption("basic", Value, "password",
502 translate("Password"),
503 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
505 function pw.validate(self, value)
506 return _option_validate(self, value)
508 function pw.parse(self, section, novld)
509 DDNS.value_parse(self, section, novld)
512 -- IPv4/IPv6 - param_enc -- ###################################################
513 pe = ns:taboption("basic", Value, "param_enc",
514 translate("Optional Encoded Parameter"),
515 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
516 function pe.validate(self, value)
517 return _option_validate(self, value, true)
519 function pe.parse(self, section, novld)
520 DDNS.value_parse(self, section, novld)
523 -- IPv4/IPv6 - param_opt -- ###################################################
524 po = ns:taboption("basic", Value, "param_opt",
525 translate("Optional Parameter"),
526 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
527 function po.validate(self, value)
528 return _option_validate(self, value, true)
530 function po.parse(self, section, novld)
531 DDNS.value_parse(self, section, novld)
534 -- handled service dependent show/display -- ##################################
536 local cv4 = svc4:cfgvalue(section)
538 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
539 ush:depends ("ipv4_service_name", "?")
540 uurl:depends("ipv4_service_name", "?")
542 uurl:depends("ipv4_service_name", "-")
543 ush:depends ("ipv4_service_name", "-")
544 dom:depends("ipv4_service_name", "-" )
545 user:depends("ipv4_service_name", "-" )
546 pw:depends("ipv4_service_name", "-" )
547 pe:depends("ipv4_service_name", "-" )
548 po:depends("ipv4_service_name", "-" )
550 for s, u in UTIL.kspairs(services4) do
551 svc4:value(s) -- fill DropDown-List
553 svs:depends("ipv4_service_name", s )
555 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
556 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
557 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
558 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
559 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
562 svc4:value("-", translate("-- custom --") )
565 local cv6 = svc6:cfgvalue(section)
567 svs:depends ("ipv6_service_name", "-" )
568 uurl:depends("ipv6_service_name", "?")
569 ush:depends ("ipv6_service_name", "?")
571 uurl:depends("ipv6_service_name", "-")
572 ush:depends ("ipv6_service_name", "-")
573 dom:depends("ipv6_service_name", "-" )
574 user:depends("ipv6_service_name", "-" )
575 pw:depends("ipv6_service_name", "-" )
576 pe:depends("ipv6_service_name", "-" )
577 po:depends("ipv6_service_name", "-" )
579 for s, u in UTIL.kspairs(services6) do
580 svc6:value(s) -- fill DropDown-List
582 svs:depends("ipv6_service_name", s )
584 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
585 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
586 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
587 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
588 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
591 svc6:value("-", translate("-- custom --") )
593 -- IPv4/IPv6 - use_https -- ###################################################
595 --We call it globally as it's called 4 times outside specific function.
596 local has_ssl = DDNS.env_info("has_ssl")
598 if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
599 https = ns:taboption("basic", Flag, "use_https",
600 translate("Use HTTP Secure") )
601 https.orientation = "horizontal"
602 function https.cfgvalue(self, section)
603 local value = AbstractValue.cfgvalue(self, section)
604 if not has_ssl and value == "1" then
605 self.description = bold_on .. font_red ..
606 translate("HTTPS not supported") .. font_off .. "<br />" ..
607 translate("please disable") .. " !" .. bold_off
609 self.description = translate("Enable secure communication with DDNS provider")
613 function https.validate(self, value)
614 if (value == "1" and has_ssl ) or value == "0" then return value end
615 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
617 function https.write(self, section, value)
619 return self.map:set(section, self.option, value)
621 self.map:del(section, "cacert")
622 return self.map:del(section, self.option)
627 -- IPv4/IPv6 - cacert -- ######################################################
629 cert = ns:taboption("basic", Value, "cacert",
630 translate("Path to CA-Certificate"),
631 translate("directory or path/file") .. "<br />" ..
632 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
633 translate("to run HTTPS without verification of server certificates (insecure)") )
634 cert:depends("use_https", "1")
635 cert.placeholder = "/etc/ssl/certs"
636 cert.forcewrite = true
637 function cert.validate(self, value)
638 if https:formvalue(section) ~= "1" then
639 return "" -- suppress validate error if NOT https
641 if value then -- otherwise errors in datatype check
642 if DTYP.directory(value)
644 or (value == "IGNORE")
645 or (#value == 0) then
649 return nil, err_tab_basic(self) ..
650 translate("file or directory not found or not 'IGNORE'") .. " !"
652 function cert.parse(self, section, novld)
653 DDNS.value_parse(self, section, novld)
657 -- TAB: Advanced #################################################################################
658 -- IPv4 - ip_source -- ########################################################
659 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
660 translate("IP address source") .. " [IPv4]",
661 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
662 src4:depends("use_ipv6", "0") -- IPv4 selected
663 src4.default = "network"
664 src4:value("network", translate("Network"))
665 src4:value("web", translate("URL"))
666 src4:value("interface", translate("Interface"))
667 src4:value("script", translate("Script"))
668 function src4.cfgvalue(self, section)
669 return DDNS.read_value(self, section, "ip_source")
671 function src4.validate(self, value)
672 if usev6:formvalue(section) == "1" then
673 return "" -- ignore on IPv6 selected
674 elseif not _verify_ip_source() then
675 return nil, err_tab_adv(self) ..
676 translate("can not detect local IP. Please select a different Source combination")
681 function src4.write(self, section, value)
682 if usev6:formvalue(section) == "1" then
683 return true -- ignore on IPv6 selected
684 elseif value == "network" then
685 self.map:del(section, "ip_url") -- delete not need parameters
686 self.map:del(section, "ip_interface")
687 self.map:del(section, "ip_script")
688 elseif value == "web" then
689 self.map:del(section, "ip_network") -- delete not need parameters
690 self.map:del(section, "ip_interface")
691 self.map:del(section, "ip_script")
692 elseif value == "interface" then
693 self.map:del(section, "ip_network") -- delete not need parameters
694 self.map:del(section, "ip_url")
695 self.map:del(section, "ip_script")
696 elseif value == "script" then
697 self.map:del(section, "ip_network")
698 self.map:del(section, "ip_url") -- delete not need parameters
699 self.map:del(section, "ip_interface")
701 self.map:del(section, self.option) -- delete "ipv4_source" helper
702 return self.map:set(section, "ip_source", value) -- and write "ip_source
704 function src4.parse(self, section, novld)
705 DDNS.value_parse(self, section, novld)
708 -- IPv6 - ip_source -- ########################################################
709 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
710 translate("IP address source") .. " [IPv6]",
711 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
712 src6:depends("use_ipv6", 1) -- IPv6 selected
713 src6.default = "network"
714 src6:value("network", translate("Network"))
715 src6:value("web", translate("URL"))
716 src6:value("interface", translate("Interface"))
717 src6:value("script", translate("Script"))
719 src6.description = err_ipv6_other
721 function src6.cfgvalue(self, section)
722 return DDNS.read_value(self, section, "ip_source")
724 function src6.validate(self, value)
725 if usev6:formvalue(section) ~= "1" then
726 return "" -- ignore on IPv4 selected
727 elseif not has_ipv6 then
728 return nil, err_tab_adv(self) .. err_ipv6_plain
729 elseif not _verify_ip_source() then
730 return nil, err_tab_adv(self) ..
731 translate("can not detect local IP. Please select a different Source combination")
736 function src6.write(self, section, value)
737 if usev6:formvalue(section) ~= "1" then
738 return true -- ignore on IPv4 selected
739 elseif value == "network" then
740 self.map:del(section, "ip_url") -- delete not need parameters
741 self.map:del(section, "ip_interface")
742 self.map:del(section, "ip_script")
743 elseif value == "web" then
744 self.map:del(section, "ip_network") -- delete not need parameters
745 self.map:del(section, "ip_interface")
746 self.map:del(section, "ip_script")
747 elseif value == "interface" then
748 self.map:del(section, "ip_network") -- delete not need parameters
749 self.map:del(section, "ip_url")
750 self.map:del(section, "ip_script")
751 elseif value == "script" then
752 self.map:del(section, "ip_network")
753 self.map:del(section, "ip_url") -- delete not need parameters
754 self.map:del(section, "ip_interface")
756 self.map:del(section, self.option) -- delete "ipv4_source" helper
757 return self.map:set(section, "ip_source", value) -- and write "ip_source
759 function src6.parse(self, section, novld)
760 DDNS.value_parse(self, section, novld)
763 -- IPv4 - ip_network (default "wan") -- #######################################
764 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
765 translate("Network") .. " [IPv4]",
766 translate("Defines the network to read systems IPv4-Address from") )
767 ipn4:depends("ipv4_source", "network")
769 WADM.cbi_add_networks(ipn4)
770 function ipn4.cfgvalue(self, section)
771 return DDNS.read_value(self, section, "ip_network")
773 function ipn4.validate(self, value)
774 if usev6:formvalue(section) == "1"
775 or src4:formvalue(section) ~= "network" then
776 -- ignore if IPv6 selected OR
777 -- ignore everything except "network"
783 function ipn4.write(self, section, value)
784 if usev6:formvalue(section) == "1"
785 or src4:formvalue(section) ~= "network" then
786 -- ignore if IPv6 selected OR
787 -- ignore everything except "network"
790 -- set also as "interface" for monitoring events changes/hot-plug
791 self.map:set(section, "interface", value)
792 self.map:del(section, self.option) -- delete "ipv4_network" helper
793 return self.map:set(section, "ip_network", value) -- and write "ip_network"
796 function ipn4.parse(self, section, novld)
797 DDNS.value_parse(self, section, novld)
800 -- IPv6 - ip_network (default "wan6") -- ######################################
801 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
802 translate("Network") .. " [IPv6]" )
803 ipn6:depends("ipv6_source", "network")
804 ipn6.default = "wan6"
805 WADM.cbi_add_networks(ipn6)
807 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
809 ipn6.description = err_ipv6_other
811 function ipn6.cfgvalue(self, section)
812 return DDNS.read_value(self, section, "ip_network")
814 function ipn6.validate(self, value)
815 if usev6:formvalue(section) ~= "1"
816 or src6:formvalue(section) ~= "network" then
817 -- ignore if IPv4 selected OR
818 -- ignore everything except "network"
823 return nil, err_tab_adv(self) .. err_ipv6_plain
826 function ipn6.write(self, section, value)
827 if usev6:formvalue(section) ~= "1"
828 or src6:formvalue(section) ~= "network" then
829 -- ignore if IPv4 selected OR
830 -- ignore everything except "network"
833 -- set also as "interface" for monitoring events changes/hotplug
834 self.map:set(section, "interface", value)
835 self.map:del(section, self.option) -- delete "ipv6_network" helper
836 return self.map:set(section, "ip_network", value) -- and write "ip_network"
839 function ipn6.parse(self, section, novld)
840 DDNS.value_parse(self, section, novld)
843 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
844 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
845 translate("URL to detect") .. " [IPv4]",
846 translate("Defines the Web page to read systems IPv4-Address from") )
847 iurl4:depends("ipv4_source", "web")
848 iurl4.default = "http://checkip.dyndns.com"
849 function iurl4.cfgvalue(self, section)
850 return DDNS.read_value(self, section, "ip_url")
852 function iurl4.validate(self, value)
853 if usev6:formvalue(section) == "1"
854 or src4:formvalue(section) ~= "web" then
855 -- ignore if IPv6 selected OR
856 -- ignore everything except "web"
858 elseif not value or #value == 0 then
859 return nil, err_tab_adv(self) .. translate("missing / required")
862 local url = DDNS.parse_url(value)
863 if not (url.scheme == "http" or url.scheme == "https") then
864 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
865 elseif not url.host then
866 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
867 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
868 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
873 function iurl4.write(self, section, value)
874 if usev6:formvalue(section) == "1"
875 or src4:formvalue(section) ~= "web" then
876 -- ignore if IPv6 selected OR
877 -- ignore everything except "web"
880 self.map:del(section, self.option) -- delete "ipv4_url" helper
881 return self.map:set(section, "ip_url", value) -- and write "ip_url"
884 function iurl4.parse(self, section, novld)
885 DDNS.value_parse(self, section, novld)
888 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
889 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
890 translate("URL to detect") .. " [IPv6]" )
891 iurl6:depends("ipv6_source", "web")
892 iurl6.default = "http://checkipv6.dyndns.com"
894 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
896 iurl6.description = err_ipv6_other
898 function iurl6.cfgvalue(self, section)
899 return DDNS.read_value(self, section, "ip_url")
901 function iurl6.validate(self, value)
902 if usev6:formvalue(section) ~= "1"
903 or src6:formvalue(section) ~= "web" then
904 -- ignore if IPv4 selected OR
905 -- ignore everything except "web"
907 elseif not has_ipv6 then
908 return nil, err_tab_adv(self) .. err_ipv6_plain
909 elseif not value or #value == 0 then
910 return nil, err_tab_adv(self) .. translate("missing / required")
913 local url = DDNS.parse_url(value)
914 if not (url.scheme == "http" or url.scheme == "https") then
915 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
916 elseif not url.host then
917 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
918 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
919 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
924 function iurl6.write(self, section, value)
925 if usev6:formvalue(section) ~= "1"
926 or src6:formvalue(section) ~= "web" then
927 -- ignore if IPv4 selected OR
928 -- ignore everything except "web"
931 self.map:del(section, self.option) -- delete "ipv6_url" helper
932 return self.map:set(section, "ip_url", value) -- and write "ip_url"
935 function iurl6.parse(self, section, novld)
936 DDNS.value_parse(self, section, novld)
939 -- IPv4 + IPv6 - ip_interface -- ##############################################
940 ipi = ns:taboption("advanced", ListValue, "ip_interface",
941 translate("Interface"),
942 translate("Defines the interface to read systems IP-Address from") )
943 ipi:depends("ipv4_source", "interface") -- IPv4
944 ipi:depends("ipv6_source", "interface") -- or IPv6
945 for _, v in pairs(SYS.net.devices()) do
946 -- show only interface set to a network
947 -- and ignore loopback
948 net = WADM.iface_get_network(v)
949 if net and net ~= "loopback" then
953 function ipi.validate(self, value)
954 local fusev6 = usev6:formvalue(section)
955 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
956 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
962 function ipi.write(self, section, value)
963 local fusev6 = usev6:formvalue(section)
964 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
965 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
968 -- get network from device to
969 -- set also as "interface" for monitoring events changes/hotplug
970 local net = WADM.iface_get_network(value)
971 self.map:set(section, "interface", net)
972 return self.map:set(section, self.option, value)
975 function ipi.parse(self, section, novld)
976 DDNS.value_parse(self, section, novld)
979 -- IPv4 + IPv6 - ip_script -- #################################################
980 ips = ns:taboption("advanced", Value, "ip_script",
982 translate("User defined script to read systems IP-Address") )
983 ips:depends("ipv4_source", "script") -- IPv4
984 ips:depends("ipv6_source", "script") -- or IPv6
985 ips.placeholder = "/path/to/script.sh"
986 function ips.validate(self, value)
987 local fusev6 = usev6:formvalue(section)
989 if value then split = UTIL.split(value, " ") end
991 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
992 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
994 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
995 return nil, err_tab_adv(self) ..
996 translate("not found or not executable - Sample: '/path/to/script.sh'")
1001 function ips.write(self, section, value)
1002 local fusev6 = usev6:formvalue(section)
1003 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
1004 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
1007 return self.map:set(section, self.option, value)
1010 function ips.parse(self, section, novld)
1011 DDNS.value_parse(self, section, novld)
1014 -- IPv4 - interface - default "wan" -- ########################################
1015 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1016 -- only needs to be set if "ip_source"="web" or "script"
1017 -- if "ip_source"="network" or "interface" we use their network
1018 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1019 translate("Event Network") .. " [IPv4]",
1020 translate("Network on which the ddns-updater scripts will be started") )
1021 eif4:depends("ipv4_source", "web")
1022 eif4:depends("ipv4_source", "script")
1023 eif4.default = "wan"
1024 WADM.cbi_add_networks(eif4)
1025 function eif4.cfgvalue(self, section)
1026 return DDNS.read_value(self, section, "interface")
1028 function eif4.validate(self, value)
1029 local fsrc4 = src4:formvalue(section) or ""
1030 if usev6:formvalue(section) == "1"
1031 or fsrc4 == "network"
1032 or fsrc4 == "interface" then
1033 return "" -- ignore IPv6, network, interface
1038 function eif4.write(self, section, value)
1039 local fsrc4 = src4:formvalue(section) or ""
1040 if usev6:formvalue(section) == "1"
1041 or fsrc4 == "network"
1042 or fsrc4 == "interface" then
1043 return true -- ignore IPv6, network, interface
1045 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1046 return self.map:set(section, "interface", value) -- and write "interface"
1049 function eif4.parse(self, section, novld)
1050 DDNS.value_parse(self, section, novld)
1053 -- IPv6 - interface - default "wan6" -- #######################################
1054 -- event network to monitor changes/hotplug
1055 -- only needs to be set if "ip_source"="web" or "script"
1056 -- if "ip_source"="network" or "interface" we use their network
1057 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1058 translate("Event Network") .. " [IPv6]" )
1059 eif6:depends("ipv6_source", "web")
1060 eif6:depends("ipv6_source", "script")
1061 eif6.default = "wan6"
1062 WADM.cbi_add_networks(eif6)
1063 if not has_ipv6 then
1064 eif6.description = err_ipv6_other
1066 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1068 function eif6.cfgvalue(self, section)
1069 return DDNS.read_value(self, section, "interface")
1071 function eif6.validate(self, value)
1072 local fsrc6 = src6:formvalue(section) or ""
1073 if usev6:formvalue(section) ~= "1"
1074 or fsrc6 == "network"
1075 or fsrc6 == "interface" then
1076 return "" -- ignore IPv4, network, interface
1077 elseif not has_ipv6 then
1078 return nil, err_tab_adv(self) .. err_ipv6_plain
1083 function eif6.write(self, section, value)
1084 local fsrc6 = src6:formvalue(section) or ""
1085 if usev6:formvalue(section) ~= "1"
1086 or fsrc6 == "network"
1087 or fsrc6 == "interface" then
1088 return true -- ignore IPv4, network, interface
1090 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1091 return self.map:set(section, "interface", value) -- and write "interface"
1094 function eif6.parse(self, section, novld)
1095 DDNS.value_parse(self, section, novld)
1098 -- IPv4/IPv6 - bind_network -- ################################################
1100 local has_bindnet = DDNS.env_info("has_bindnet")
1102 if has_bindnet or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1103 bnet = ns:taboption("advanced", ListValue, "bind_network",
1104 translate("Bind Network") )
1105 bnet:depends("ipv4_source", "web")
1106 bnet:depends("ipv6_source", "web")
1108 bnet:value("", translate("-- default --"))
1109 WADM.cbi_add_networks(bnet)
1110 function bnet.cfgvalue(self, section)
1111 local value = AbstractValue.cfgvalue(self, section)
1112 if not has_bindnet and value ~= "" then
1113 self.description = bold_on .. font_red ..
1114 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1115 translate("please set to 'default'") .. " !" .. bold_off
1117 self.description = translate("OPTIONAL: Network to use for communication") ..
1118 "<br />" .. translate("Casual users should not change this setting")
1122 function bnet.validate(self, value)
1123 if ( (value ~= "") and has_bindnet ) or (value == "") then return value end
1124 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1126 function bnet.parse(self, section, novld)
1127 DDNS.value_parse(self, section, novld)
1131 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1132 -- optional to force wget/curl and host to use only selected IP version
1133 -- command parameter "-4" or "-6"
1135 local has_forceip = DDNS.env_info("has_forceip")
1137 if has_forceip or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1138 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1139 translate("Force IP Version") )
1140 fipv.orientation = "horizontal"
1141 function fipv.cfgvalue(self, section)
1142 local value = AbstractValue.cfgvalue(self, section)
1143 if not has_forceip and value ~= "0" then
1144 self.description = bold_on .. font_red ..
1145 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1146 translate("please disable") .. " !" .. bold_off
1148 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1152 function fipv.validate(self, value)
1153 if (value == "1" and has_forceip) or value == "0" then return value end
1154 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1158 -- IPv4 + IPv6 - dns_server -- ################################################
1159 -- optional DNS Server to use resolving my IP
1161 local has_dnsserver = DDNS.env_info("has_dnsserver")
1163 if has_dnsserver or ( ( m:get(section, "dns_server") or "" ) ~= "" ) then
1164 dns = ns:taboption("advanced", Value, "dns_server",
1165 translate("DNS-Server"),
1166 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1167 translate("Format: IP or FQDN"))
1168 dns.placeholder = "mydns.lan"
1169 function dns.validate(self, value)
1170 -- if .datatype is set, then it is checked before calling this function
1171 if not value or (#value == 0) then
1172 return "" -- ignore on empty
1173 elseif not has_dnsserver then
1174 return nil, err_tab_adv(self) .. translate("Specifying a DNS-Server is not supported")
1175 elseif not DTYP.host(value) then
1176 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1178 local ipv6 = usev6:formvalue(section) or "0"
1179 local force = fipv:formvalue(section) or "0"
1180 local command = CTRL.luci_helper .. [[ -]]
1181 if (ipv6 == 1) then command = command .. [[6]] end
1182 if (force == 1) then command = command .. [[f]] end
1183 command = command .. [[d ]] .. value .. [[ -- verify_dns]]
1185 local ret = SYS.call(command)
1186 if ret == 0 then return value -- everything OK
1187 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1188 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1189 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1190 else return nil, err_tab_adv(self) .. translate("unspecific error")
1194 function dns.parse(self, section, novld)
1195 DDNS.value_parse(self, section, novld)
1199 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1201 local has_bindhost = DDNS.env_info("has_bindhost")
1203 if has_bindhost or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1204 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1205 translate("Force TCP on DNS") )
1206 tcp.orientation = "horizontal"
1207 function tcp.cfgvalue(self, section)
1208 local value = AbstractValue.cfgvalue(self, section)
1209 if not has_bindhost and value ~= "0" then
1210 self.description = bold_on .. font_red ..
1211 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1212 translate("please disable") .. " !" .. bold_off
1214 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1218 function tcp.validate(self, value)
1219 if (value == "1" and has_bindhost ) or value == "0" then
1222 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1226 -- IPv4 + IPv6 - proxy -- #####################################################
1227 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1229 local has_proxy = DDNS.env_info("has_proxy")
1231 if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1232 pxy = ns:taboption("advanced", Value, "proxy",
1233 translate("PROXY-Server") )
1234 pxy.placeholder="user:password@myproxy.lan:8080"
1235 function pxy.cfgvalue(self, section)
1236 local value = AbstractValue.cfgvalue(self, section)
1237 if not has_proxy and value ~= "" then
1238 self.description = bold_on .. font_red ..
1239 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1240 translate("please remove entry") .. "!" .. bold_off
1242 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1243 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1244 translate("IPv6 address must be given in square brackets") .. ": " ..
1245 bold_on .. " [2001:db8::1]:8080" .. bold_off
1249 function pxy.validate(self, value)
1250 -- if .datatype is set, then it is checked before calling this function
1251 if not value or (#value == 0) then
1252 return "" -- ignore on empty
1253 elseif has_proxy then
1254 local ipv6 = usev6:formvalue(section) or "0"
1255 local force = fipv:formvalue(section) or "0"
1256 local command = CTRL.luci_helper .. [[ -]]
1257 if (ipv6 == 1) then command = command .. [[6]] end
1258 if (force == 1) then command = command .. [[f]] end
1259 command = command .. [[p ]] .. value .. [[ -- verify_proxy]]
1260 local ret = SYS.call(command)
1261 if ret == 0 then return value
1262 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1263 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1264 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1265 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1266 else return nil, err_tab_adv(self) .. translate("unspecific error")
1269 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1272 function pxy.parse(self, section, novld)
1273 DDNS.value_parse(self, section, novld)
1277 -- use_syslog -- ##############################################################
1278 slog = ns:taboption("advanced", ListValue, "use_syslog",
1279 translate("Log to syslog"),
1280 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1282 slog:value("0", translate("No logging"))
1283 slog:value("1", translate("Info"))
1284 slog:value("2", translate("Notice"))
1285 slog:value("3", translate("Warning"))
1286 slog:value("4", translate("Error"))
1287 function slog.parse(self, section, novld)
1288 DDNS.value_parse(self, section, novld)
1291 -- use_logfile -- #############################################################
1292 logf = ns:taboption("advanced", Flag, "use_logfile",
1293 translate("Log to file"),
1294 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1295 translate("File") .. [[: "]] .. logdir .. [[/]] .. section .. [[.log"]] )
1296 logf.orientation = "horizontal"
1297 logf.default = "1" -- if not defined write to log by default
1299 -- TAB: Timer ####################################################################################
1300 -- check_interval -- ##########################################################
1301 ci = ns:taboption("timer", Value, "check_interval",
1302 translate("Check Interval") )
1303 ci.template = "ddns/detail_value"
1305 function ci.validate(self, value)
1306 if not DTYP.uinteger(value)
1307 or tonumber(value) < 1 then
1308 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1311 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1315 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1318 function ci.write(self, section, value)
1319 -- remove when default
1320 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1321 if secs ~= 600 then --default 10 minutes
1322 return self.map:set(section, self.option, value)
1324 self.map:del(section, "check_unit")
1325 return self.map:del(section, self.option)
1328 function ci.parse(self, section, novld)
1329 DDNS.value_parse(self, section, novld)
1332 -- check_unit -- ##############################################################
1333 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1334 translate("Interval to check for changed IP" .. "<br />" ..
1335 "Values below 5 minutes == 300 seconds are not supported") )
1336 cu.template = "ddns/detail_lvalue"
1337 cu.default = "minutes"
1338 cu:value("seconds", translate("seconds"))
1339 cu:value("minutes", translate("minutes"))
1340 cu:value("hours", translate("hours"))
1341 --cu:value("days", translate("days"))
1342 function cu.write(self, section, value)
1343 -- remove when default
1344 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1345 if secs ~= 600 then --default 10 minutes
1346 return self.map:set(section, self.option, value)
1351 function cu.parse(self, section, novld)
1352 DDNS.value_parse(self, section, novld)
1355 -- force_interval (modified) -- ###############################################
1356 fi = ns:taboption("timer", Value, "force_interval",
1357 translate("Force Interval") )
1358 fi.template = "ddns/detail_value"
1359 fi.default = "72" -- see dynamic_dns_updater.sh script
1360 --fi.rmempty = false -- validate ourselves for translatable error messages
1361 function fi.validate(self, value)
1362 if not DTYP.uinteger(value)
1363 or tonumber(value) < 0 then
1364 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1367 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1368 if force_s == 0 then
1372 local ci_value = ci:formvalue(section)
1373 if not DTYP.uinteger(ci_value) then
1374 return "" -- ignore because error in check_interval above
1377 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1378 if force_s >= check_s then
1382 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1384 function fi.write(self, section, value)
1385 -- simulate rmempty=true remove default
1386 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1387 if secs ~= 259200 then --default 72 hours == 3 days
1388 return self.map:set(section, self.option, value)
1390 self.map:del(section, "force_unit")
1391 return self.map:del(section, self.option)
1394 function fi.parse(self, section, novld)
1395 DDNS.value_parse(self, section, novld)
1398 -- force_unit -- ##############################################################
1399 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1400 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1401 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1402 "Values lower 'Check Interval' except '0' are not supported") )
1403 fu.template = "ddns/detail_lvalue"
1404 fu.default = "hours"
1405 --fu.rmempty = false -- want to control write process
1406 --fu:value("seconds", translate("seconds"))
1407 fu:value("minutes", translate("minutes"))
1408 fu:value("hours", translate("hours"))
1409 fu:value("days", translate("days"))
1410 function fu.write(self, section, value)
1411 -- simulate rmempty=true remove default
1412 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1413 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1414 return self.map:set(section, self.option, value)
1419 function fu.parse(self, section, novld)
1420 DDNS.value_parse(self, section, novld)
1423 -- retry_count -- #############################################################
1424 rc = ns:taboption("timer", Value, "retry_count")
1425 rc.title = translate("Error Retry Counter")
1426 rc.description = translate("On Error the script will stop execution after given number of retrys")
1428 .. translate("The default setting of '0' will retry infinite.")
1430 function rc.validate(self, value)
1431 if not DTYP.uinteger(value) then
1432 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1437 function rc.parse(self, section, novld)
1438 DDNS.value_parse(self, section, novld)
1441 -- retry_interval -- ##########################################################
1442 ri = ns:taboption("timer", Value, "retry_interval",
1443 translate("Error Retry Interval") )
1444 ri.template = "ddns/detail_value"
1446 function ri.validate(self, value)
1447 if not DTYP.uinteger(value)
1448 or tonumber(value) < 1 then
1449 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1454 function ri.write(self, section, value)
1455 -- simulate rmempty=true remove default
1456 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1457 if secs ~= 60 then --default 60seconds
1458 return self.map:set(section, self.option, value)
1460 self.map:del(section, "retry_unit")
1461 return self.map:del(section, self.option)
1464 function ri.parse(self, section, novld)
1465 DDNS.value_parse(self, section, novld)
1468 -- retry_unit -- ##############################################################
1469 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1470 translate("On Error the script will retry the failed action after given time") )
1471 ru.template = "ddns/detail_lvalue"
1472 ru.default = "seconds"
1473 --ru.rmempty = false -- want to control write process
1474 ru:value("seconds", translate("seconds"))
1475 ru:value("minutes", translate("minutes"))
1476 --ru:value("hours", translate("hours"))
1477 --ru:value("days", translate("days"))
1478 function ru.write(self, section, value)
1479 -- simulate rmempty=true remove default
1480 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1481 if secs ~= 60 then --default 60seconds
1482 return self.map:set(section, self.option, value)
1484 return true -- will be deleted by retry_interval
1487 function ru.parse(self, section, novld)
1488 DDNS.value_parse(self, section, novld)
1491 -- TAB: LogView ##################################################################################
1492 lv = ns:taboption("logview", DummyValue, "_logview")
1493 lv.template = "ddns/detail_logview"
1494 lv.inputtitle = translate("Read / Reread log file")
1496 function lv.cfgvalue(self, section)
1497 local lfile=logdir .. "/" .. section .. ".log"
1498 if NXFS.access(lfile) then
1499 return lfile .. "\n" .. translate("Please press [Read] button")
1501 return lfile .. "\n" .. translate("File not found or empty")