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 agrument (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)
176 -- section is globally defined here be calling agrument (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 sting
204 if used < 1 then return "" end
205 -- needed but no data then return error
206 if not value or (#value == 0) then
207 return nil, err_tab_basic(self) .. translate("missing / required")
212 -- cbi-map definition -- #######################################################
213 local m = Map("ddns")
214 m.title = CTRL.app_title_back()
215 m.description = CTRL.app_description()
216 m.redirect = DISP.build_url("admin", "services", "ddns")
218 m.on_after_commit = function(self)
219 if self.changed then -- changes ?
220 local pid = DDNS.get_pid(section)
221 if pid > 0 then -- running ?
222 local tmp = NX.kill(pid, 1) -- send SIGHUP
227 -- provider switch was requested, save and reload page
228 if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1]
230 local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section) or "0"
231 if fusev6 == "1" then
232 fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section) or ""
234 fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section) or ""
237 if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed
238 m:set(section, "use_ipv6", fusev6) -- save it
241 if fsvc ~= "-" then -- NOT "custom"
242 m:set(section, "service_name", fsvc) -- save it
244 m:del(section, "service_name") -- delete it
249 HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
253 -- read application settings -- ################################################
255 local logdir = m.uci:get(m.config, "global", "ddns_logdir") or "/var/log/ddns"
257 -- cbi-section definition -- ###################################################
258 local ns = m:section( NamedSection, section, "service",
259 translate("Details for") .. ([[: <strong>%s</strong>]] % section),
260 translate("Configure here the details for selected Dynamic DNS service.") )
261 ns.instance = section -- arg [1]
262 ns:tab("basic", translate("Basic Settings"), nil )
263 ns:tab("advanced", translate("Advanced Settings"), nil )
264 ns:tab("timer", translate("Timer Settings"), nil )
265 ns:tab("logview", translate("Log File Viewer"), nil )
267 -- TAB: Basic #####################################################################################
268 -- enabled -- #################################################################
269 en = ns:taboption("basic", Flag, "enabled",
270 translate("Enabled"),
271 translate("If this service section is disabled it could not be started." .. "<br />" ..
272 "Neither from LuCI interface nor from console") )
273 en.orientation = "horizontal"
275 -- IPv4/IPv6 - lookup_host -- #################################################
276 luh = ns:taboption("basic", Value, "lookup_host",
277 translate("Lookup Hostname"),
278 translate("Hostname/FQDN to validate, if IP update happen or necessary") )
280 luh.placeholder = "myhost.example.com"
281 function luh.validate(self, value)
284 or not DTYP.hostname(value) then
285 return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
287 return UTIL.trim(value)
290 function luh.parse(self, section, novld)
291 DDNS.value_parse(self, section, novld)
294 -- use_ipv6 -- ################################################################
296 --We call it globally as it's called 11 times even outside specific function, saves 11 os.execute slow command!
297 local has_ipv6 = DDNS.env_info("has_ipv6")
299 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
300 translate("IP address version"),
301 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
302 usev6.widget = "radio"
304 usev6:value("0", translate("IPv4-Address") )
305 function usev6.cfgvalue(self, section)
306 local value = AbstractValue.cfgvalue(self, section) or "0"
307 if has_ipv6 or (value == "1" and not has_ipv6) then
308 self:value("1", translate("IPv6-Address") )
310 if value == "1" and not has_ipv6 then
311 self.description = err_ipv6_basic
315 function usev6.validate(self, value)
316 if (value == "1" and has_ipv6) or value == "0" then
319 return nil, err_tab_basic(self) .. err_ipv6_plain
321 function usev6.parse(self, section, novld)
322 DDNS.value_parse(self, section, novld)
325 -- IPv4 - service_name -- #####################################################
326 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
327 translate("DDNS Service provider") .. " [IPv4]" )
329 svc4:depends("use_ipv6", "0") -- only show on IPv4
330 function svc4.cfgvalue(self, section)
331 local v = DDNS.read_value(self, section, "service_name")
332 if v and (#v > 0) then
333 for s, u in UTIL.kspairs(services4) do
334 if v == s then return v end
339 function svc4.validate(self, value)
340 if usev6:formvalue(section) ~= "1" then -- do only on IPv4
343 return "" -- suppress validate error
346 function svc4.write(self, section, value)
347 if usev6:formvalue(section) ~= "1" then -- do only IPv4 here
348 self.map:del(section, self.option) -- to be shure
349 if value ~= "-" then -- and write "service_name
350 self.map:del(section, "update_url") -- delete update_url
351 self.map:del(section, "update_script") -- delete update_script
352 return self.map:set(section, "service_name", value)
354 return self.map:del(section, "service_name")
358 function svc4.parse(self, section, novld)
359 DDNS.value_parse(self, section, novld)
362 -- IPv6 - service_name -- #####################################################
363 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
364 translate("DDNS Service provider") .. " [IPv6]" )
366 svc6:depends("use_ipv6", "1") -- only show on IPv6
368 svc6.description = err_ipv6_basic
370 function svc6.cfgvalue(self, section)
371 local v = DDNS.read_value(self, section, "service_name")
372 if v and (#v > 0) then
373 for s, u in UTIL.kspairs(services4) do
374 if v == s then return v end
379 function svc6.validate(self, value)
380 if usev6:formvalue(section) == "1" then -- do only on IPv6
381 if has_ipv6 then return value end
382 return nil, err_tab_basic(self) .. err_ipv6_plain
384 return "" -- suppress validate error
387 function svc6.write(self, section, value)
388 if usev6:formvalue(section) == "1" then -- do only when IPv6
389 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
390 if value ~= "-" then -- and write "service_name
391 self.map:del(section, "update_url") -- delete update_url
392 self.map:del(section, "update_script") -- delete update_script
393 return self.map:set(section, "service_name", value)
395 return self.map:del(section, "service_name")
399 function svc6.parse(self, section, novld)
400 DDNS.value_parse(self, section, novld)
403 -- IPv4/IPv6 - change Provider -- #############################################
404 svs = ns:taboption("basic", Button, "_switch")
405 svs.title = translate("Really change DDNS provider?")
406 svs.inputtitle = translate("Change provider")
407 svs.inputstyle = "apply"
409 -- IPv4/IPv6 - update_url -- ##################################################
410 uurl = ns:taboption("basic", Value, "update_url",
411 translate("Custom update-URL"),
412 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
413 "Follow instructions you will find on their WEB page.") )
414 function uurl.validate(self, value)
415 local fush = ush:formvalue(section)
416 local fusev6 = usev6:formvalue(section)
418 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
419 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
420 return "" -- suppress validate error
421 elseif not value or (#value == 0) then
422 if not fush or (#fush == 0) then
423 return nil, err_tab_basic(self) .. translate("missing / required")
425 return "" -- suppress validate error / update_script is given
427 elseif (#fush > 0) then
428 return nil, err_tab_basic(self) .. translate("either url or script could be set")
431 local url = DDNS.parse_url(value)
432 if not url.scheme == "http" then
433 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
434 elseif not url.query then
435 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
436 elseif not url.host then
437 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
438 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
439 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
444 function uurl.parse(self, section, novld)
445 DDNS.value_parse(self, section, novld)
448 -- IPv4/IPv6 - update_script -- ###############################################
449 ush = ns:taboption("basic", Value, "update_script",
450 translate("Custom update-script"),
451 translate("Custom update script to be used for updating your DDNS Provider.") )
452 function ush.validate(self, value)
453 local fuurl = uurl:formvalue(section)
454 local fusev6 = usev6:formvalue(section)
456 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
457 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
458 return "" -- suppress validate error
459 elseif not value or (#value == 0) then
460 if not fuurl or (#fuurl == 0) then
461 return nil, err_tab_basic(self) .. translate("missing / required")
463 return "" -- suppress validate error / update_url is given
465 elseif (#fuurl > 0) then
466 return nil, err_tab_basic(self) .. translate("either url or script could be set")
467 elseif not NXFS.access(value) then
468 return nil, err_tab_basic(self) .. translate("File not found")
472 function ush.parse(self, section, novld)
473 DDNS.value_parse(self, section, novld)
476 -- IPv4/IPv6 - domain -- ######################################################
477 dom = ns:taboption("basic", Value, "domain",
479 translate("Replaces [DOMAIN] in Update-URL") )
480 dom.placeholder = "myhost.example.com"
481 function dom.validate(self, value)
482 return _option_validate(self, value)
484 function dom.parse(self, section, novld)
485 DDNS.value_parse(self, section, novld)
488 -- IPv4/IPv6 - username -- ####################################################
489 user = ns:taboption("basic", Value, "username",
490 translate("Username"),
491 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
492 function user.validate(self, value)
493 return _option_validate(self, value)
495 function user.parse(self, section, novld)
496 DDNS.value_parse(self, section, novld)
499 -- IPv4/IPv6 - password -- ####################################################
500 pw = ns:taboption("basic", Value, "password",
501 translate("Password"),
502 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
504 function pw.validate(self, value)
505 return _option_validate(self, value)
507 function pw.parse(self, section, novld)
508 DDNS.value_parse(self, section, novld)
511 -- IPv4/IPv6 - param_enc -- ###################################################
512 pe = ns:taboption("basic", Value, "param_enc",
513 translate("Optional Encoded Parameter"),
514 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
515 function pe.validate(self, value)
516 return _option_validate(self, value)
518 function pe.parse(self, section, novld)
519 DDNS.value_parse(self, section, novld)
522 -- IPv4/IPv6 - param_enc -- ###################################################
523 po = ns:taboption("basic", Value, "param_opt",
524 translate("Optional Parameter"),
525 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
526 function po.validate(self, value)
527 return _option_validate(self, value)
529 function po.parse(self, section, novld)
530 DDNS.value_parse(self, section, novld)
533 -- handled service dependent show/display -- ##################################
535 local cv4 = svc4:cfgvalue(section)
537 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
538 ush:depends ("ipv4_service_name", "?")
539 uurl:depends("ipv4_service_name", "?")
541 uurl:depends("ipv4_service_name", "-")
542 ush:depends ("ipv4_service_name", "-")
543 dom:depends("ipv4_service_name", "-" )
544 user:depends("ipv4_service_name", "-" )
545 pw:depends("ipv4_service_name", "-" )
546 pe:depends("ipv4_service_name", "-" )
547 po:depends("ipv4_service_name", "-" )
549 for s, u in UTIL.kspairs(services4) do
550 svc4:value(s) -- fill DropDown-List
552 svs:depends("ipv4_service_name", s )
554 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
555 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
556 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
557 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
558 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
561 svc4:value("-", translate("-- custom --") )
564 local cv6 = svc6:cfgvalue(section)
566 svs:depends ("ipv6_service_name", "-" )
567 uurl:depends("ipv6_service_name", "?")
568 ush:depends ("ipv6_service_name", "?")
570 uurl:depends("ipv6_service_name", "-")
571 ush:depends ("ipv6_service_name", "-")
572 dom:depends("ipv6_service_name", "-" )
573 user:depends("ipv6_service_name", "-" )
574 pw:depends("ipv6_service_name", "-" )
575 pe:depends("ipv6_service_name", "-" )
576 po:depends("ipv6_service_name", "-" )
578 for s, u in UTIL.kspairs(services6) do
579 svc6:value(s) -- fill DropDown-List
581 svs:depends("ipv6_service_name", s )
583 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
584 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
585 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
586 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
587 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
590 svc6:value("-", translate("-- custom --") )
592 -- IPv4/IPv6 - use_https -- ###################################################
594 --We call it globally as it's called 4 times outside specific function.
595 local has_ssl = DDNS.env_info("has_ssl")
597 if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
598 https = ns:taboption("basic", Flag, "use_https",
599 translate("Use HTTP Secure") )
600 https.orientation = "horizontal"
601 function https.cfgvalue(self, section)
602 local value = AbstractValue.cfgvalue(self, section)
603 if not has_ssl and value == "1" then
604 self.description = bold_on .. font_red ..
605 translate("HTTPS not supported") .. font_off .. "<br />" ..
606 translate("please disable") .. " !" .. bold_off
608 self.description = translate("Enable secure communication with DDNS provider")
612 function https.validate(self, value)
613 if (value == "1" and has_ssl ) or value == "0" then return value end
614 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
616 function https.write(self, section, value)
618 return self.map:set(section, self.option, value)
620 self.map:del(section, "cacert")
621 return self.map:del(section, self.option)
626 -- IPv4/IPv6 - cacert -- ######################################################
628 cert = ns:taboption("basic", Value, "cacert",
629 translate("Path to CA-Certificate"),
630 translate("directory or path/file") .. "<br />" ..
631 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
632 translate("to run HTTPS without verification of server certificates (insecure)") )
633 cert:depends("use_https", "1")
634 cert.placeholder = "/etc/ssl/certs"
635 cert.forcewrite = true
636 function cert.validate(self, value)
637 if https:formvalue(section) ~= "1" then
638 return "" -- suppress validate error if NOT https
640 if value then -- otherwise errors in datatype check
641 if DTYP.directory(value)
643 or (value == "IGNORE")
644 or (#value == 0) then
648 return nil, err_tab_basic(self) ..
649 translate("file or directory not found or not 'IGNORE'") .. " !"
651 function cert.parse(self, section, novld)
652 DDNS.value_parse(self, section, novld)
656 -- TAB: Advanced #################################################################################
657 -- IPv4 - ip_source -- ########################################################
658 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
659 translate("IP address source") .. " [IPv4]",
660 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
661 src4:depends("use_ipv6", "0") -- IPv4 selected
662 src4.default = "network"
663 src4:value("network", translate("Network"))
664 src4:value("web", translate("URL"))
665 src4:value("interface", translate("Interface"))
666 src4:value("script", translate("Script"))
667 function src4.cfgvalue(self, section)
668 return DDNS.read_value(self, section, "ip_source")
670 function src4.validate(self, value)
671 if usev6:formvalue(section) == "1" then
672 return "" -- ignore on IPv6 selected
673 elseif not _verify_ip_source() then
674 return nil, err_tab_adv(self) ..
675 translate("can not detect local IP. Please select a different Source combination")
680 function src4.write(self, section, value)
681 if usev6:formvalue(section) == "1" then
682 return true -- ignore on IPv6 selected
683 elseif value == "network" then
684 self.map:del(section, "ip_url") -- delete not need parameters
685 self.map:del(section, "ip_interface")
686 self.map:del(section, "ip_script")
687 elseif value == "web" then
688 self.map:del(section, "ip_network") -- delete not need parameters
689 self.map:del(section, "ip_interface")
690 self.map:del(section, "ip_script")
691 elseif value == "interface" then
692 self.map:del(section, "ip_network") -- delete not need parameters
693 self.map:del(section, "ip_url")
694 self.map:del(section, "ip_script")
695 elseif value == "script" then
696 self.map:del(section, "ip_network")
697 self.map:del(section, "ip_url") -- delete not need parameters
698 self.map:del(section, "ip_interface")
700 self.map:del(section, self.option) -- delete "ipv4_source" helper
701 return self.map:set(section, "ip_source", value) -- and write "ip_source
703 function src4.parse(self, section, novld)
704 DDNS.value_parse(self, section, novld)
707 -- IPv6 - ip_source -- ########################################################
708 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
709 translate("IP address source") .. " [IPv6]",
710 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
711 src6:depends("use_ipv6", 1) -- IPv6 selected
712 src6.default = "network"
713 src6:value("network", translate("Network"))
714 src6:value("web", translate("URL"))
715 src6:value("interface", translate("Interface"))
716 src6:value("script", translate("Script"))
718 src6.description = err_ipv6_other
720 function src6.cfgvalue(self, section)
721 return DDNS.read_value(self, section, "ip_source")
723 function src6.validate(self, value)
724 if usev6:formvalue(section) ~= "1" then
725 return "" -- ignore on IPv4 selected
726 elseif not has_ipv6 then
727 return nil, err_tab_adv(self) .. err_ipv6_plain
728 elseif not _verify_ip_source() then
729 return nil, err_tab_adv(self) ..
730 translate("can not detect local IP. Please select a different Source combination")
735 function src6.write(self, section, value)
736 if usev6:formvalue(section) ~= "1" then
737 return true -- ignore on IPv4 selected
738 elseif value == "network" then
739 self.map:del(section, "ip_url") -- delete not need parameters
740 self.map:del(section, "ip_interface")
741 self.map:del(section, "ip_script")
742 elseif value == "web" then
743 self.map:del(section, "ip_network") -- delete not need parameters
744 self.map:del(section, "ip_interface")
745 self.map:del(section, "ip_script")
746 elseif value == "interface" then
747 self.map:del(section, "ip_network") -- delete not need parameters
748 self.map:del(section, "ip_url")
749 self.map:del(section, "ip_script")
750 elseif value == "script" then
751 self.map:del(section, "ip_network")
752 self.map:del(section, "ip_url") -- delete not need parameters
753 self.map:del(section, "ip_interface")
755 self.map:del(section, self.option) -- delete "ipv4_source" helper
756 return self.map:set(section, "ip_source", value) -- and write "ip_source
758 function src6.parse(self, section, novld)
759 DDNS.value_parse(self, section, novld)
762 -- IPv4 - ip_network (default "wan") -- #######################################
763 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
764 translate("Network") .. " [IPv4]",
765 translate("Defines the network to read systems IPv4-Address from") )
766 ipn4:depends("ipv4_source", "network")
768 WADM.cbi_add_networks(ipn4)
769 function ipn4.cfgvalue(self, section)
770 return DDNS.read_value(self, section, "ip_network")
772 function ipn4.validate(self, value)
773 if usev6:formvalue(section) == "1"
774 or src4:formvalue(section) ~= "network" then
775 -- ignore if IPv6 selected OR
776 -- ignore everything except "network"
782 function ipn4.write(self, section, value)
783 if usev6:formvalue(section) == "1"
784 or src4:formvalue(section) ~= "network" then
785 -- ignore if IPv6 selected OR
786 -- ignore everything except "network"
789 -- set also as "interface" for monitoring events changes/hot-plug
790 self.map:set(section, "interface", value)
791 self.map:del(section, self.option) -- delete "ipv4_network" helper
792 return self.map:set(section, "ip_network", value) -- and write "ip_network"
795 function ipn4.parse(self, section, novld)
796 DDNS.value_parse(self, section, novld)
799 -- IPv6 - ip_network (default "wan6") -- ######################################
800 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
801 translate("Network") .. " [IPv6]" )
802 ipn6:depends("ipv6_source", "network")
803 ipn6.default = "wan6"
804 WADM.cbi_add_networks(ipn6)
806 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
808 ipn6.description = err_ipv6_other
810 function ipn6.cfgvalue(self, section)
811 return DDNS.read_value(self, section, "ip_network")
813 function ipn6.validate(self, value)
814 if usev6:formvalue(section) ~= "1"
815 or src6:formvalue(section) ~= "network" then
816 -- ignore if IPv4 selected OR
817 -- ignore everything except "network"
822 return nil, err_tab_adv(self) .. err_ipv6_plain
825 function ipn6.write(self, section, value)
826 if usev6:formvalue(section) ~= "1"
827 or src6:formvalue(section) ~= "network" then
828 -- ignore if IPv4 selected OR
829 -- ignore everything except "network"
832 -- set also as "interface" for monitoring events changes/hotplug
833 self.map:set(section, "interface", value)
834 self.map:del(section, self.option) -- delete "ipv6_network" helper
835 return self.map:set(section, "ip_network", value) -- and write "ip_network"
838 function ipn6.parse(self, section, novld)
839 DDNS.value_parse(self, section, novld)
842 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
843 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
844 translate("URL to detect") .. " [IPv4]",
845 translate("Defines the Web page to read systems IPv4-Address from") )
846 iurl4:depends("ipv4_source", "web")
847 iurl4.default = "http://checkip.dyndns.com"
848 function iurl4.cfgvalue(self, section)
849 return DDNS.read_value(self, section, "ip_url")
851 function iurl4.validate(self, value)
852 if usev6:formvalue(section) == "1"
853 or src4:formvalue(section) ~= "web" then
854 -- ignore if IPv6 selected OR
855 -- ignore everything except "web"
857 elseif not value or #value == 0 then
858 return nil, err_tab_adv(self) .. translate("missing / required")
861 local url = DDNS.parse_url(value)
862 if not (url.scheme == "http" or url.scheme == "https") then
863 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
864 elseif not url.host then
865 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
866 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
867 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
872 function iurl4.write(self, section, value)
873 if usev6:formvalue(section) == "1"
874 or src4:formvalue(section) ~= "web" then
875 -- ignore if IPv6 selected OR
876 -- ignore everything except "web"
879 self.map:del(section, self.option) -- delete "ipv4_url" helper
880 return self.map:set(section, "ip_url", value) -- and write "ip_url"
883 function iurl4.parse(self, section, novld)
884 DDNS.value_parse(self, section, novld)
887 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
888 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
889 translate("URL to detect") .. " [IPv6]" )
890 iurl6:depends("ipv6_source", "web")
891 iurl6.default = "http://checkipv6.dyndns.com"
893 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
895 iurl6.description = err_ipv6_other
897 function iurl6.cfgvalue(self, section)
898 return DDNS.read_value(self, section, "ip_url")
900 function iurl6.validate(self, value)
901 if usev6:formvalue(section) ~= "1"
902 or src6:formvalue(section) ~= "web" then
903 -- ignore if IPv4 selected OR
904 -- ignore everything except "web"
906 elseif not has_ipv6 then
907 return nil, err_tab_adv(self) .. err_ipv6_plain
908 elseif not value or #value == 0 then
909 return nil, err_tab_adv(self) .. translate("missing / required")
912 local url = DDNS.parse_url(value)
913 if not (url.scheme == "http" or url.scheme == "https") then
914 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
915 elseif not url.host then
916 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
917 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
918 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
923 function iurl6.write(self, section, value)
924 if usev6:formvalue(section) ~= "1"
925 or src6:formvalue(section) ~= "web" then
926 -- ignore if IPv4 selected OR
927 -- ignore everything except "web"
930 self.map:del(section, self.option) -- delete "ipv6_url" helper
931 return self.map:set(section, "ip_url", value) -- and write "ip_url"
934 function iurl6.parse(self, section, novld)
935 DDNS.value_parse(self, section, novld)
938 -- IPv4 + IPv6 - ip_interface -- ##############################################
939 ipi = ns:taboption("advanced", ListValue, "ip_interface",
940 translate("Interface"),
941 translate("Defines the interface to read systems IP-Address from") )
942 ipi:depends("ipv4_source", "interface") -- IPv4
943 ipi:depends("ipv6_source", "interface") -- or IPv6
944 for _, v in pairs(SYS.net.devices()) do
945 -- show only interface set to a network
946 -- and ignore loopback
947 net = WADM.iface_get_network(v)
948 if net and net ~= "loopback" then
952 function ipi.validate(self, value)
953 local fusev6 = usev6:formvalue(section)
954 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
955 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
961 function ipi.write(self, section, value)
962 local fusev6 = usev6:formvalue(section)
963 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
964 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
967 -- get network from device to
968 -- set also as "interface" for monitoring events changes/hotplug
969 local net = WADM.iface_get_network(value)
970 self.map:set(section, "interface", net)
971 return self.map:set(section, self.option, value)
974 function ipi.parse(self, section, novld)
975 DDNS.value_parse(self, section, novld)
978 -- IPv4 + IPv6 - ip_script -- #################################################
979 ips = ns:taboption("advanced", Value, "ip_script",
981 translate("User defined script to read systems IP-Address") )
982 ips:depends("ipv4_source", "script") -- IPv4
983 ips:depends("ipv6_source", "script") -- or IPv6
984 ips.placeholder = "/path/to/script.sh"
985 function ips.validate(self, value)
986 local fusev6 = usev6:formvalue(section)
988 if value then split = UTIL.split(value, " ") end
990 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
991 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
993 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
994 return nil, err_tab_adv(self) ..
995 translate("not found or not executable - Sample: '/path/to/script.sh'")
1000 function ips.write(self, section, value)
1001 local fusev6 = usev6:formvalue(section)
1002 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
1003 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
1006 return self.map:set(section, self.option, value)
1009 function ips.parse(self, section, novld)
1010 DDNS.value_parse(self, section, novld)
1013 -- IPv4 - interface - default "wan" -- ########################################
1014 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1015 -- only needs to be set if "ip_source"="web" or "script"
1016 -- if "ip_source"="network" or "interface" we use their network
1017 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1018 translate("Event Network") .. " [IPv4]",
1019 translate("Network on which the ddns-updater scripts will be started") )
1020 eif4:depends("ipv4_source", "web")
1021 eif4:depends("ipv4_source", "script")
1022 eif4.default = "wan"
1023 WADM.cbi_add_networks(eif4)
1024 function eif4.cfgvalue(self, section)
1025 return DDNS.read_value(self, section, "interface")
1027 function eif4.validate(self, value)
1028 local fsrc4 = src4:formvalue(section) or ""
1029 if usev6:formvalue(section) == "1"
1030 or fsrc4 == "network"
1031 or fsrc4 == "interface" then
1032 return "" -- ignore IPv6, network, interface
1037 function eif4.write(self, section, value)
1038 local fsrc4 = src4:formvalue(section) or ""
1039 if usev6:formvalue(section) == "1"
1040 or fsrc4 == "network"
1041 or fsrc4 == "interface" then
1042 return true -- ignore IPv6, network, interface
1044 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1045 return self.map:set(section, "interface", value) -- and write "interface"
1048 function eif4.parse(self, section, novld)
1049 DDNS.value_parse(self, section, novld)
1052 -- IPv6 - interface - default "wan6" -- #######################################
1053 -- event network to monitor changes/hotplug
1054 -- only needs to be set if "ip_source"="web" or "script"
1055 -- if "ip_source"="network" or "interface" we use their network
1056 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1057 translate("Event Network") .. " [IPv6]" )
1058 eif6:depends("ipv6_source", "web")
1059 eif6:depends("ipv6_source", "script")
1060 eif6.default = "wan6"
1061 WADM.cbi_add_networks(eif6)
1062 if not has_ipv6 then
1063 eif6.description = err_ipv6_other
1065 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1067 function eif6.cfgvalue(self, section)
1068 return DDNS.read_value(self, section, "interface")
1070 function eif6.validate(self, value)
1071 local fsrc6 = src6:formvalue(section) or ""
1072 if usev6:formvalue(section) ~= "1"
1073 or fsrc6 == "network"
1074 or fsrc6 == "interface" then
1075 return "" -- ignore IPv4, network, interface
1076 elseif not has_ipv6 then
1077 return nil, err_tab_adv(self) .. err_ipv6_plain
1082 function eif6.write(self, section, value)
1083 local fsrc6 = src6:formvalue(section) or ""
1084 if usev6:formvalue(section) ~= "1"
1085 or fsrc6 == "network"
1086 or fsrc6 == "interface" then
1087 return true -- ignore IPv4, network, interface
1089 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1090 return self.map:set(section, "interface", value) -- and write "interface"
1093 function eif6.parse(self, section, novld)
1094 DDNS.value_parse(self, section, novld)
1097 -- IPv4/IPv6 - bind_network -- ################################################
1099 local has_bindnet = DDNS.env_info("has_bindnet")
1101 if has_bindnet or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1102 bnet = ns:taboption("advanced", ListValue, "bind_network",
1103 translate("Bind Network") )
1104 bnet:depends("ipv4_source", "web")
1105 bnet:depends("ipv6_source", "web")
1107 bnet:value("", translate("-- default --"))
1108 WADM.cbi_add_networks(bnet)
1109 function bnet.cfgvalue(self, section)
1110 local value = AbstractValue.cfgvalue(self, section)
1111 if not has_bindnet and value ~= "" then
1112 self.description = bold_on .. font_red ..
1113 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1114 translate("please set to 'default'") .. " !" .. bold_off
1116 self.description = translate("OPTIONAL: Network to use for communication") ..
1117 "<br />" .. translate("Casual users should not change this setting")
1121 function bnet.validate(self, value)
1122 if ( (value ~= "") and has_bindnet ) or (value == "") then return value end
1123 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1125 function bnet.parse(self, section, novld)
1126 DDNS.value_parse(self, section, novld)
1130 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1131 -- optional to force wget/curl and host to use only selected IP version
1132 -- command parameter "-4" or "-6"
1134 local has_forceip = DDNS.env_info("has_forceip")
1136 if has_forceip or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1137 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1138 translate("Force IP Version") )
1139 fipv.orientation = "horizontal"
1140 function fipv.cfgvalue(self, section)
1141 local value = AbstractValue.cfgvalue(self, section)
1142 if not has_forceip and value ~= "0" then
1143 self.description = bold_on .. font_red ..
1144 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1145 translate("please disable") .. " !" .. bold_off
1147 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1151 function fipv.validate(self, value)
1152 if (value == "1" and has_forceip) or value == "0" then return value end
1153 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1157 -- IPv4 + IPv6 - dns_server -- ################################################
1158 -- optional DNS Server to use resolving my IP
1160 local has_dnsserver = DDNS.env_info("has_dnsserver")
1162 if has_dnsserver or ( ( m:get(section, "dns_server") or "" ) ~= "" ) then
1163 dns = ns:taboption("advanced", Value, "dns_server",
1164 translate("DNS-Server"),
1165 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1166 translate("Format: IP or FQDN"))
1167 dns.placeholder = "mydns.lan"
1168 function dns.validate(self, value)
1169 -- if .datatype is set, then it is checked before calling this function
1170 if not value or (#value == 0) then
1171 return "" -- ignore on empty
1172 elseif not has_dnsserver then
1173 return nil, err_tab_adv(self) .. translate("Specifying a DNS-Server is not supported")
1174 elseif not DTYP.host(value) then
1175 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1177 local ipv6 = usev6:formvalue(section) or "0"
1178 local force = fipv:formvalue(section) or "0"
1179 local command = CTRL.luci_helper .. [[ -]]
1180 if (ipv6 == 1) then command = command .. [[6]] end
1181 if (force == 1) then command = command .. [[f]] end
1182 command = command .. [[d ]] .. value .. [[ -- verify_dns]]
1184 local ret = SYS.call(command)
1185 if ret == 0 then return value -- everything OK
1186 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1187 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1188 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1189 else return nil, err_tab_adv(self) .. translate("unspecific error")
1193 function dns.parse(self, section, novld)
1194 DDNS.value_parse(self, section, novld)
1198 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1200 local has_bindhost = DDNS.env_info("has_bindhost")
1202 if has_bindhost or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1203 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1204 translate("Force TCP on DNS") )
1205 tcp.orientation = "horizontal"
1206 function tcp.cfgvalue(self, section)
1207 local value = AbstractValue.cfgvalue(self, section)
1208 if not has_bindhost and value ~= "0" then
1209 self.description = bold_on .. font_red ..
1210 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1211 translate("please disable") .. " !" .. bold_off
1213 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1217 function tcp.validate(self, value)
1218 if (value == "1" and has_bindhost ) or value == "0" then
1221 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1225 -- IPv4 + IPv6 - proxy -- #####################################################
1226 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1228 local has_proxy = DDNS.env_info("has_proxy")
1230 if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1231 pxy = ns:taboption("advanced", Value, "proxy",
1232 translate("PROXY-Server") )
1233 pxy.placeholder="user:password@myproxy.lan:8080"
1234 function pxy.cfgvalue(self, section)
1235 local value = AbstractValue.cfgvalue(self, section)
1236 if not has_proxy and value ~= "" then
1237 self.description = bold_on .. font_red ..
1238 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1239 translate("please remove entry") .. "!" .. bold_off
1241 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1242 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1243 translate("IPv6 address must be given in square brackets") .. ": " ..
1244 bold_on .. " [2001:db8::1]:8080" .. bold_off
1248 function pxy.validate(self, value)
1249 -- if .datatype is set, then it is checked before calling this function
1250 if not value or (#value == 0) then
1251 return "" -- ignore on empty
1252 elseif has_proxy then
1253 local ipv6 = usev6:formvalue(section) or "0"
1254 local force = fipv:formvalue(section) or "0"
1255 local command = CTRL.luci_helper .. [[ -]]
1256 if (ipv6 == 1) then command = command .. [[6]] end
1257 if (force == 1) then command = command .. [[f]] end
1258 command = command .. [[p ]] .. value .. [[ -- verify_proxy]]
1259 local ret = SYS.call(command)
1260 if ret == 0 then return value
1261 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1262 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1263 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1264 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1265 else return nil, err_tab_adv(self) .. translate("unspecific error")
1268 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1271 function pxy.parse(self, section, novld)
1272 DDNS.value_parse(self, section, novld)
1276 -- use_syslog -- ##############################################################
1277 slog = ns:taboption("advanced", ListValue, "use_syslog",
1278 translate("Log to syslog"),
1279 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1281 slog:value("0", translate("No logging"))
1282 slog:value("1", translate("Info"))
1283 slog:value("2", translate("Notice"))
1284 slog:value("3", translate("Warning"))
1285 slog:value("4", translate("Error"))
1286 function slog.parse(self, section, novld)
1287 DDNS.value_parse(self, section, novld)
1290 -- use_logfile -- #############################################################
1291 logf = ns:taboption("advanced", Flag, "use_logfile",
1292 translate("Log to file"),
1293 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1294 translate("File") .. [[: "]] .. logdir .. [[/]] .. section .. [[.log"]] )
1295 logf.orientation = "horizontal"
1296 logf.default = "1" -- if not defined write to log by default
1298 -- TAB: Timer ####################################################################################
1299 -- check_interval -- ##########################################################
1300 ci = ns:taboption("timer", Value, "check_interval",
1301 translate("Check Interval") )
1302 ci.template = "ddns/detail_value"
1304 function ci.validate(self, value)
1305 if not DTYP.uinteger(value)
1306 or tonumber(value) < 1 then
1307 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1310 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1314 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1317 function ci.write(self, section, value)
1318 -- remove when default
1319 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1320 if secs ~= 600 then --default 10 minutes
1321 return self.map:set(section, self.option, value)
1323 self.map:del(section, "check_unit")
1324 return self.map:del(section, self.option)
1327 function ci.parse(self, section, novld)
1328 DDNS.value_parse(self, section, novld)
1331 -- check_unit -- ##############################################################
1332 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1333 translate("Interval to check for changed IP" .. "<br />" ..
1334 "Values below 5 minutes == 300 seconds are not supported") )
1335 cu.template = "ddns/detail_lvalue"
1336 cu.default = "minutes"
1337 cu:value("seconds", translate("seconds"))
1338 cu:value("minutes", translate("minutes"))
1339 cu:value("hours", translate("hours"))
1340 --cu:value("days", translate("days"))
1341 function cu.write(self, section, value)
1342 -- remove when default
1343 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1344 if secs ~= 600 then --default 10 minutes
1345 return self.map:set(section, self.option, value)
1350 function cu.parse(self, section, novld)
1351 DDNS.value_parse(self, section, novld)
1354 -- force_interval (modified) -- ###############################################
1355 fi = ns:taboption("timer", Value, "force_interval",
1356 translate("Force Interval") )
1357 fi.template = "ddns/detail_value"
1358 fi.default = "72" -- see dynamic_dns_updater.sh script
1359 --fi.rmempty = false -- validate ourselves for translatable error messages
1360 function fi.validate(self, value)
1361 if not DTYP.uinteger(value)
1362 or tonumber(value) < 0 then
1363 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1366 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1367 if force_s == 0 then
1371 local ci_value = ci:formvalue(section)
1372 if not DTYP.uinteger(ci_value) then
1373 return "" -- ignore because error in check_interval above
1376 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1377 if force_s >= check_s then
1381 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1383 function fi.write(self, section, value)
1384 -- simulate rmempty=true remove default
1385 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1386 if secs ~= 259200 then --default 72 hours == 3 days
1387 return self.map:set(section, self.option, value)
1389 self.map:del(section, "force_unit")
1390 return self.map:del(section, self.option)
1393 function fi.parse(self, section, novld)
1394 DDNS.value_parse(self, section, novld)
1397 -- force_unit -- ##############################################################
1398 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1399 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1400 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1401 "Values lower 'Check Interval' except '0' are not supported") )
1402 fu.template = "ddns/detail_lvalue"
1403 fu.default = "hours"
1404 --fu.rmempty = false -- want to control write process
1405 --fu:value("seconds", translate("seconds"))
1406 fu:value("minutes", translate("minutes"))
1407 fu:value("hours", translate("hours"))
1408 fu:value("days", translate("days"))
1409 function fu.write(self, section, value)
1410 -- simulate rmempty=true remove default
1411 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1412 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1413 return self.map:set(section, self.option, value)
1418 function fu.parse(self, section, novld)
1419 DDNS.value_parse(self, section, novld)
1422 -- retry_count -- #############################################################
1423 rc = ns:taboption("timer", Value, "retry_count")
1424 rc.title = translate("Error Retry Counter")
1425 rc.description = translate("On Error the script will stop execution after given number of retrys")
1427 .. translate("The default setting of '0' will retry infinite.")
1429 function rc.validate(self, value)
1430 if not DTYP.uinteger(value) then
1431 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1436 function rc.parse(self, section, novld)
1437 DDNS.value_parse(self, section, novld)
1440 -- retry_interval -- ##########################################################
1441 ri = ns:taboption("timer", Value, "retry_interval",
1442 translate("Error Retry Interval") )
1443 ri.template = "ddns/detail_value"
1445 function ri.validate(self, value)
1446 if not DTYP.uinteger(value)
1447 or tonumber(value) < 1 then
1448 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1453 function ri.write(self, section, value)
1454 -- simulate rmempty=true remove default
1455 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1456 if secs ~= 60 then --default 60seconds
1457 return self.map:set(section, self.option, value)
1459 self.map:del(section, "retry_unit")
1460 return self.map:del(section, self.option)
1463 function ri.parse(self, section, novld)
1464 DDNS.value_parse(self, section, novld)
1467 -- retry_unit -- ##############################################################
1468 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1469 translate("On Error the script will retry the failed action after given time") )
1470 ru.template = "ddns/detail_lvalue"
1471 ru.default = "seconds"
1472 --ru.rmempty = false -- want to control write process
1473 ru:value("seconds", translate("seconds"))
1474 ru:value("minutes", translate("minutes"))
1475 --ru:value("hours", translate("hours"))
1476 --ru:value("days", translate("days"))
1477 function ru.write(self, section, value)
1478 -- simulate rmempty=true remove default
1479 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1480 if secs ~= 60 then --default 60seconds
1481 return self.map:set(section, self.option, value)
1483 return true -- will be deleted by retry_interval
1486 function ru.parse(self, section, novld)
1487 DDNS.value_parse(self, section, novld)
1490 -- TAB: LogView ##################################################################################
1491 lv = ns:taboption("logview", DummyValue, "_logview")
1492 lv.template = "ddns/detail_logview"
1493 lv.inputtitle = translate("Read / Reread log file")
1495 function lv.cfgvalue(self, section)
1496 local lfile=logdir .. "/" .. section .. ".log"
1497 if NXFS.access(lfile) then
1498 return lfile .. "\n" .. translate("Please press [Read] button")
1500 return lfile .. "\n" .. translate("File not found or empty")