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 -- ################################################################
295 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
296 translate("IP address version"),
297 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
298 usev6.widget = "radio"
300 usev6:value("0", translate("IPv4-Address") )
301 function usev6.cfgvalue(self, section)
302 local value = AbstractValue.cfgvalue(self, section) or "0"
303 if DDNS.has_ipv6 or (value == "1" and not DDNS.has_ipv6) then
304 self:value("1", translate("IPv6-Address") )
306 if value == "1" and not DDNS.has_ipv6 then
307 self.description = err_ipv6_basic
311 function usev6.validate(self, value)
312 if (value == "1" and DDNS.has_ipv6) or value == "0" then
315 return nil, err_tab_basic(self) .. err_ipv6_plain
317 function usev6.parse(self, section, novld)
318 DDNS.value_parse(self, section, novld)
321 -- IPv4 - service_name -- #####################################################
322 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
323 translate("DDNS Service provider") .. " [IPv4]" )
325 svc4:depends("use_ipv6", "0") -- only show on IPv4
326 function svc4.cfgvalue(self, section)
327 local v = DDNS.read_value(self, section, "service_name")
328 if v and (#v > 0) then
329 for s, u in UTIL.kspairs(services4) do
330 if v == s then return v end
335 function svc4.validate(self, value)
336 if usev6:formvalue(section) ~= "1" then -- do only on IPv4
339 return "" -- suppress validate error
342 function svc4.write(self, section, value)
343 if usev6:formvalue(section) ~= "1" then -- do only IPv4 here
344 self.map:del(section, self.option) -- to be shure
345 if value ~= "-" then -- and write "service_name
346 self.map:del(section, "update_url") -- delete update_url
347 self.map:del(section, "update_script") -- delete update_script
348 return self.map:set(section, "service_name", value)
350 return self.map:del(section, "service_name")
354 function svc4.parse(self, section, novld)
355 DDNS.value_parse(self, section, novld)
358 -- IPv6 - service_name -- #####################################################
359 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
360 translate("DDNS Service provider") .. " [IPv6]" )
362 svc6:depends("use_ipv6", "1") -- only show on IPv6
363 if not DDNS.has_ipv6 then
364 svc6.description = err_ipv6_basic
366 function svc6.cfgvalue(self, section)
367 local v = DDNS.read_value(self, section, "service_name")
368 if v and (#v > 0) then
369 for s, u in UTIL.kspairs(services4) do
370 if v == s then return v end
375 function svc6.validate(self, value)
376 if usev6:formvalue(section) == "1" then -- do only on IPv6
377 if DDNS.has_ipv6 then return value end
378 return nil, err_tab_basic(self) .. err_ipv6_plain
380 return "" -- suppress validate error
383 function svc6.write(self, section, value)
384 if usev6:formvalue(section) == "1" then -- do only when IPv6
385 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
386 if value ~= "-" then -- and write "service_name
387 self.map:del(section, "update_url") -- delete update_url
388 self.map:del(section, "update_script") -- delete update_script
389 return self.map:set(section, "service_name", value)
391 return self.map:del(section, "service_name")
395 function svc6.parse(self, section, novld)
396 DDNS.value_parse(self, section, novld)
399 -- IPv4/IPv6 - change Provider -- #############################################
400 svs = ns:taboption("basic", Button, "_switch")
401 svs.title = translate("Really change DDNS provider?")
402 svs.inputtitle = translate("Change provider")
403 svs.inputstyle = "apply"
405 -- IPv4/IPv6 - update_url -- ##################################################
406 uurl = ns:taboption("basic", Value, "update_url",
407 translate("Custom update-URL"),
408 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
409 "Follow instructions you will find on their WEB page.") )
410 function uurl.validate(self, value)
411 local fush = ush:formvalue(section)
412 local fusev6 = usev6:formvalue(section)
414 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
415 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
416 return "" -- suppress validate error
417 elseif not value or (#value == 0) then
418 if not fush or (#fush == 0) then
419 return nil, err_tab_basic(self) .. translate("missing / required")
421 return "" -- suppress validate error / update_script is given
423 elseif (#fush > 0) then
424 return nil, err_tab_basic(self) .. translate("either url or script could be set")
427 local url = DDNS.parse_url(value)
428 if not url.scheme == "http" then
429 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
430 elseif not url.query then
431 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
432 elseif not url.host then
433 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
434 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
435 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
440 function uurl.parse(self, section, novld)
441 DDNS.value_parse(self, section, novld)
444 -- IPv4/IPv6 - update_script -- ###############################################
445 ush = ns:taboption("basic", Value, "update_script",
446 translate("Custom update-script"),
447 translate("Custom update script to be used for updating your DDNS Provider.") )
448 function ush.validate(self, value)
449 local fuurl = uurl:formvalue(section)
450 local fusev6 = usev6:formvalue(section)
452 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
453 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
454 return "" -- suppress validate error
455 elseif not value or (#value == 0) then
456 if not fuurl or (#fuurl == 0) then
457 return nil, err_tab_basic(self) .. translate("missing / required")
459 return "" -- suppress validate error / update_url is given
461 elseif (#fuurl > 0) then
462 return nil, err_tab_basic(self) .. translate("either url or script could be set")
463 elseif not NXFS.access(value) then
464 return nil, err_tab_basic(self) .. translate("File not found")
468 function ush.parse(self, section, novld)
469 DDNS.value_parse(self, section, novld)
472 -- IPv4/IPv6 - domain -- ######################################################
473 dom = ns:taboption("basic", Value, "domain",
475 translate("Replaces [DOMAIN] in Update-URL") )
476 dom.placeholder = "myhost.example.com"
477 function dom.validate(self, value)
478 return _option_validate(self, value)
480 function dom.parse(self, section, novld)
481 DDNS.value_parse(self, section, novld)
484 -- IPv4/IPv6 - username -- ####################################################
485 user = ns:taboption("basic", Value, "username",
486 translate("Username"),
487 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
488 function user.validate(self, value)
489 return _option_validate(self, value)
491 function user.parse(self, section, novld)
492 DDNS.value_parse(self, section, novld)
495 -- IPv4/IPv6 - password -- ####################################################
496 pw = ns:taboption("basic", Value, "password",
497 translate("Password"),
498 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
500 function pw.validate(self, value)
501 return _option_validate(self, value)
503 function pw.parse(self, section, novld)
504 DDNS.value_parse(self, section, novld)
507 -- IPv4/IPv6 - param_enc -- ###################################################
508 pe = ns:taboption("basic", Value, "param_enc",
509 translate("Optional Encoded Parameter"),
510 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
511 function pe.validate(self, value)
512 return _option_validate(self, value)
514 function pe.parse(self, section, novld)
515 DDNS.value_parse(self, section, novld)
518 -- IPv4/IPv6 - param_enc -- ###################################################
519 po = ns:taboption("basic", Value, "param_opt",
520 translate("Optional Parameter"),
521 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
522 function po.validate(self, value)
523 return _option_validate(self, value)
525 function po.parse(self, section, novld)
526 DDNS.value_parse(self, section, novld)
529 -- handled service dependent show/display -- ##################################
531 local cv4 = svc4:cfgvalue(section)
533 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
534 ush:depends ("ipv4_service_name", "?")
535 uurl:depends("ipv4_service_name", "?")
537 uurl:depends("ipv4_service_name", "-")
538 ush:depends ("ipv4_service_name", "-")
539 dom:depends("ipv4_service_name", "-" )
540 user:depends("ipv4_service_name", "-" )
541 pw:depends("ipv4_service_name", "-" )
542 pe:depends("ipv4_service_name", "-" )
543 po:depends("ipv4_service_name", "-" )
545 for s, u in UTIL.kspairs(services4) do
546 svc4:value(s) -- fill DropDown-List
548 svs:depends("ipv4_service_name", s )
550 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
551 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
552 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
553 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
554 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
557 svc4:value("-", translate("-- custom --") )
560 local cv6 = svc6:cfgvalue(section)
562 svs:depends ("ipv6_service_name", "-" )
563 uurl:depends("ipv6_service_name", "?")
564 ush:depends ("ipv6_service_name", "?")
566 uurl:depends("ipv6_service_name", "-")
567 ush:depends ("ipv6_service_name", "-")
568 dom:depends("ipv6_service_name", "-" )
569 user:depends("ipv6_service_name", "-" )
570 pw:depends("ipv6_service_name", "-" )
571 pe:depends("ipv6_service_name", "-" )
572 po:depends("ipv6_service_name", "-" )
574 for s, u in UTIL.kspairs(services6) do
575 svc6:value(s) -- fill DropDown-List
577 svs:depends("ipv6_service_name", s )
579 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
580 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
581 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
582 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
583 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
586 svc6:value("-", translate("-- custom --") )
588 -- IPv4/IPv6 - use_https -- ###################################################
589 if DDNS.has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
590 https = ns:taboption("basic", Flag, "use_https",
591 translate("Use HTTP Secure") )
592 https.orientation = "horizontal"
593 function https.cfgvalue(self, section)
594 local value = AbstractValue.cfgvalue(self, section)
595 if not DDNS.has_ssl and value == "1" then
596 self.description = bold_on .. font_red ..
597 translate("HTTPS not supported") .. font_off .. "<br />" ..
598 translate("please disable") .. " !" .. bold_off
600 self.description = translate("Enable secure communication with DDNS provider")
604 function https.validate(self, value)
605 if (value == "1" and DDNS.has_ssl ) or value == "0" then return value end
606 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
608 function https.write(self, section, value)
610 return self.map:set(section, self.option, value)
612 self.map:del(section, "cacert")
613 return self.map:del(section, self.option)
618 -- IPv4/IPv6 - cacert -- ######################################################
620 cert = ns:taboption("basic", Value, "cacert",
621 translate("Path to CA-Certificate"),
622 translate("directory or path/file") .. "<br />" ..
623 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
624 translate("to run HTTPS without verification of server certificates (insecure)") )
625 cert:depends("use_https", "1")
626 cert.placeholder = "/etc/ssl/certs"
627 cert.forcewrite = true
628 function cert.validate(self, value)
629 if https:formvalue(section) ~= "1" then
630 return "" -- suppress validate error if NOT https
632 if value then -- otherwise errors in datatype check
633 if DTYP.directory(value)
635 or (value == "IGNORE")
636 or (#value == 0) then
640 return nil, err_tab_basic(self) ..
641 translate("file or directory not found or not 'IGNORE'") .. " !"
643 function cert.parse(self, section, novld)
644 DDNS.value_parse(self, section, novld)
648 -- TAB: Advanced #################################################################################
649 -- IPv4 - ip_source -- ########################################################
650 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
651 translate("IP address source") .. " [IPv4]",
652 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
653 src4:depends("use_ipv6", "0") -- IPv4 selected
654 src4.default = "network"
655 src4:value("network", translate("Network"))
656 src4:value("web", translate("URL"))
657 src4:value("interface", translate("Interface"))
658 src4:value("script", translate("Script"))
659 function src4.cfgvalue(self, section)
660 return DDNS.read_value(self, section, "ip_source")
662 function src4.validate(self, value)
663 if usev6:formvalue(section) == "1" then
664 return "" -- ignore on IPv6 selected
665 elseif not _verify_ip_source() then
666 return nil, err_tab_adv(self) ..
667 translate("can not detect local IP. Please select a different Source combination")
672 function src4.write(self, section, value)
673 if usev6:formvalue(section) == "1" then
674 return true -- ignore on IPv6 selected
675 elseif value == "network" then
676 self.map:del(section, "ip_url") -- delete not need parameters
677 self.map:del(section, "ip_interface")
678 self.map:del(section, "ip_script")
679 elseif value == "web" then
680 self.map:del(section, "ip_network") -- delete not need parameters
681 self.map:del(section, "ip_interface")
682 self.map:del(section, "ip_script")
683 elseif value == "interface" then
684 self.map:del(section, "ip_network") -- delete not need parameters
685 self.map:del(section, "ip_url")
686 self.map:del(section, "ip_script")
687 elseif value == "script" then
688 self.map:del(section, "ip_network")
689 self.map:del(section, "ip_url") -- delete not need parameters
690 self.map:del(section, "ip_interface")
692 self.map:del(section, self.option) -- delete "ipv4_source" helper
693 return self.map:set(section, "ip_source", value) -- and write "ip_source
695 function src4.parse(self, section, novld)
696 DDNS.value_parse(self, section, novld)
699 -- IPv6 - ip_source -- ########################################################
700 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
701 translate("IP address source") .. " [IPv6]",
702 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
703 src6:depends("use_ipv6", 1) -- IPv6 selected
704 src6.default = "network"
705 src6:value("network", translate("Network"))
706 src6:value("web", translate("URL"))
707 src6:value("interface", translate("Interface"))
708 src6:value("script", translate("Script"))
709 if not DDNS.has_ipv6 then
710 src6.description = err_ipv6_other
712 function src6.cfgvalue(self, section)
713 return DDNS.read_value(self, section, "ip_source")
715 function src6.validate(self, value)
716 if usev6:formvalue(section) ~= "1" then
717 return "" -- ignore on IPv4 selected
718 elseif not DDNS.has_ipv6 then
719 return nil, err_tab_adv(self) .. err_ipv6_plain
720 elseif not _verify_ip_source() then
721 return nil, err_tab_adv(self) ..
722 translate("can not detect local IP. Please select a different Source combination")
727 function src6.write(self, section, value)
728 if usev6:formvalue(section) ~= "1" then
729 return true -- ignore on IPv4 selected
730 elseif value == "network" then
731 self.map:del(section, "ip_url") -- delete not need parameters
732 self.map:del(section, "ip_interface")
733 self.map:del(section, "ip_script")
734 elseif value == "web" then
735 self.map:del(section, "ip_network") -- delete not need parameters
736 self.map:del(section, "ip_interface")
737 self.map:del(section, "ip_script")
738 elseif value == "interface" then
739 self.map:del(section, "ip_network") -- delete not need parameters
740 self.map:del(section, "ip_url")
741 self.map:del(section, "ip_script")
742 elseif value == "script" then
743 self.map:del(section, "ip_network")
744 self.map:del(section, "ip_url") -- delete not need parameters
745 self.map:del(section, "ip_interface")
747 self.map:del(section, self.option) -- delete "ipv4_source" helper
748 return self.map:set(section, "ip_source", value) -- and write "ip_source
750 function src6.parse(self, section, novld)
751 DDNS.value_parse(self, section, novld)
754 -- IPv4 - ip_network (default "wan") -- #######################################
755 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
756 translate("Network") .. " [IPv4]",
757 translate("Defines the network to read systems IPv4-Address from") )
758 ipn4:depends("ipv4_source", "network")
760 WADM.cbi_add_networks(ipn4)
761 function ipn4.cfgvalue(self, section)
762 return DDNS.read_value(self, section, "ip_network")
764 function ipn4.validate(self, value)
765 if usev6:formvalue(section) == "1"
766 or src4:formvalue(section) ~= "network" then
767 -- ignore if IPv6 selected OR
768 -- ignore everything except "network"
774 function ipn4.write(self, section, value)
775 if usev6:formvalue(section) == "1"
776 or src4:formvalue(section) ~= "network" then
777 -- ignore if IPv6 selected OR
778 -- ignore everything except "network"
781 -- set also as "interface" for monitoring events changes/hot-plug
782 self.map:set(section, "interface", value)
783 self.map:del(section, self.option) -- delete "ipv4_network" helper
784 return self.map:set(section, "ip_network", value) -- and write "ip_network"
787 function ipn4.parse(self, section, novld)
788 DDNS.value_parse(self, section, novld)
791 -- IPv6 - ip_network (default "wan6") -- ######################################
792 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
793 translate("Network") .. " [IPv6]" )
794 ipn6:depends("ipv6_source", "network")
795 ipn6.default = "wan6"
796 WADM.cbi_add_networks(ipn6)
797 if DDNS.has_ipv6 then
798 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
800 ipn6.description = err_ipv6_other
802 function ipn6.cfgvalue(self, section)
803 return DDNS.read_value(self, section, "ip_network")
805 function ipn6.validate(self, value)
806 if usev6:formvalue(section) ~= "1"
807 or src6:formvalue(section) ~= "network" then
808 -- ignore if IPv4 selected OR
809 -- ignore everything except "network"
811 elseif DDNS.has_ipv6 then
814 return nil, err_tab_adv(self) .. err_ipv6_plain
817 function ipn6.write(self, section, value)
818 if usev6:formvalue(section) ~= "1"
819 or src6:formvalue(section) ~= "network" then
820 -- ignore if IPv4 selected OR
821 -- ignore everything except "network"
824 -- set also as "interface" for monitoring events changes/hotplug
825 self.map:set(section, "interface", value)
826 self.map:del(section, self.option) -- delete "ipv6_network" helper
827 return self.map:set(section, "ip_network", value) -- and write "ip_network"
830 function ipn6.parse(self, section, novld)
831 DDNS.value_parse(self, section, novld)
834 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
835 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
836 translate("URL to detect") .. " [IPv4]",
837 translate("Defines the Web page to read systems IPv4-Address from") )
838 iurl4:depends("ipv4_source", "web")
839 iurl4.default = "http://checkip.dyndns.com"
840 function iurl4.cfgvalue(self, section)
841 return DDNS.read_value(self, section, "ip_url")
843 function iurl4.validate(self, value)
844 if usev6:formvalue(section) == "1"
845 or src4:formvalue(section) ~= "web" then
846 -- ignore if IPv6 selected OR
847 -- ignore everything except "web"
849 elseif not value or #value == 0 then
850 return nil, err_tab_adv(self) .. translate("missing / required")
853 local url = DDNS.parse_url(value)
854 if not (url.scheme == "http" or url.scheme == "https") then
855 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
856 elseif not url.host then
857 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
858 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
859 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
864 function iurl4.write(self, section, value)
865 if usev6:formvalue(section) == "1"
866 or src4:formvalue(section) ~= "web" then
867 -- ignore if IPv6 selected OR
868 -- ignore everything except "web"
871 self.map:del(section, self.option) -- delete "ipv4_url" helper
872 return self.map:set(section, "ip_url", value) -- and write "ip_url"
875 function iurl4.parse(self, section, novld)
876 DDNS.value_parse(self, section, novld)
879 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
880 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
881 translate("URL to detect") .. " [IPv6]" )
882 iurl6:depends("ipv6_source", "web")
883 iurl6.default = "http://checkipv6.dyndns.com"
884 if DDNS.has_ipv6 then
885 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
887 iurl6.description = err_ipv6_other
889 function iurl6.cfgvalue(self, section)
890 return DDNS.read_value(self, section, "ip_url")
892 function iurl6.validate(self, value)
893 if usev6:formvalue(section) ~= "1"
894 or src6:formvalue(section) ~= "web" then
895 -- ignore if IPv4 selected OR
896 -- ignore everything except "web"
898 elseif not DDNS.has_ipv6 then
899 return nil, err_tab_adv(self) .. err_ipv6_plain
900 elseif not value or #value == 0 then
901 return nil, err_tab_adv(self) .. translate("missing / required")
904 local url = DDNS.parse_url(value)
905 if not (url.scheme == "http" or url.scheme == "https") then
906 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
907 elseif not url.host then
908 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
909 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
910 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
915 function iurl6.write(self, section, value)
916 if usev6:formvalue(section) ~= "1"
917 or src6:formvalue(section) ~= "web" then
918 -- ignore if IPv4 selected OR
919 -- ignore everything except "web"
922 self.map:del(section, self.option) -- delete "ipv6_url" helper
923 return self.map:set(section, "ip_url", value) -- and write "ip_url"
926 function iurl6.parse(self, section, novld)
927 DDNS.value_parse(self, section, novld)
930 -- IPv4 + IPv6 - ip_interface -- ##############################################
931 ipi = ns:taboption("advanced", ListValue, "ip_interface",
932 translate("Interface"),
933 translate("Defines the interface to read systems IP-Address from") )
934 ipi:depends("ipv4_source", "interface") -- IPv4
935 ipi:depends("ipv6_source", "interface") -- or IPv6
936 for _, v in pairs(SYS.net.devices()) do
937 -- show only interface set to a network
938 -- and ignore loopback
939 net = WADM.iface_get_network(v)
940 if net and net ~= "loopback" then
944 function ipi.validate(self, value)
945 local fusev6 = usev6:formvalue(section)
946 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
947 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
953 function ipi.write(self, section, 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
959 -- get network from device to
960 -- set also as "interface" for monitoring events changes/hotplug
961 local net = WADM.iface_get_network(value)
962 self.map:set(section, "interface", net)
963 return self.map:set(section, self.option, value)
966 function ipi.parse(self, section, novld)
967 DDNS.value_parse(self, section, novld)
970 -- IPv4 + IPv6 - ip_script -- #################################################
971 ips = ns:taboption("advanced", Value, "ip_script",
973 translate("User defined script to read systems IP-Address") )
974 ips:depends("ipv4_source", "script") -- IPv4
975 ips:depends("ipv6_source", "script") -- or IPv6
976 ips.placeholder = "/path/to/script.sh"
977 function ips.validate(self, value)
978 local fusev6 = usev6:formvalue(section)
980 if value then split = UTIL.split(value, " ") end
982 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
983 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
985 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
986 return nil, err_tab_adv(self) ..
987 translate("not found or not executable - Sample: '/path/to/script.sh'")
992 function ips.write(self, section, value)
993 local fusev6 = usev6:formvalue(section)
994 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
995 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
998 return self.map:set(section, self.option, value)
1001 function ips.parse(self, section, novld)
1002 DDNS.value_parse(self, section, novld)
1005 -- IPv4 - interface - default "wan" -- ########################################
1006 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1007 -- only needs to be set if "ip_source"="web" or "script"
1008 -- if "ip_source"="network" or "interface" we use their network
1009 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1010 translate("Event Network") .. " [IPv4]",
1011 translate("Network on which the ddns-updater scripts will be started") )
1012 eif4:depends("ipv4_source", "web")
1013 eif4:depends("ipv4_source", "script")
1014 eif4.default = "wan"
1015 WADM.cbi_add_networks(eif4)
1016 function eif4.cfgvalue(self, section)
1017 return DDNS.read_value(self, section, "interface")
1019 function eif4.validate(self, value)
1020 local fsrc4 = src4:formvalue(section) or ""
1021 if usev6:formvalue(section) == "1"
1022 or fsrc4 == "network"
1023 or fsrc4 == "interface" then
1024 return "" -- ignore IPv6, network, interface
1029 function eif4.write(self, section, value)
1030 local fsrc4 = src4:formvalue(section) or ""
1031 if usev6:formvalue(section) == "1"
1032 or fsrc4 == "network"
1033 or fsrc4 == "interface" then
1034 return true -- ignore IPv6, network, interface
1036 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1037 return self.map:set(section, "interface", value) -- and write "interface"
1040 function eif4.parse(self, section, novld)
1041 DDNS.value_parse(self, section, novld)
1044 -- IPv6 - interface - default "wan6" -- #######################################
1045 -- event network to monitor changes/hotplug
1046 -- only needs to be set if "ip_source"="web" or "script"
1047 -- if "ip_source"="network" or "interface" we use their network
1048 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1049 translate("Event Network") .. " [IPv6]" )
1050 eif6:depends("ipv6_source", "web")
1051 eif6:depends("ipv6_source", "script")
1052 eif6.default = "wan6"
1053 WADM.cbi_add_networks(eif6)
1054 if not DDNS.has_ipv6 then
1055 eif6.description = err_ipv6_other
1057 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1059 function eif6.cfgvalue(self, section)
1060 return DDNS.read_value(self, section, "interface")
1062 function eif6.validate(self, value)
1063 local fsrc6 = src6:formvalue(section) or ""
1064 if usev6:formvalue(section) ~= "1"
1065 or fsrc6 == "network"
1066 or fsrc6 == "interface" then
1067 return "" -- ignore IPv4, network, interface
1068 elseif not DDNS.has_ipv6 then
1069 return nil, err_tab_adv(self) .. err_ipv6_plain
1074 function eif6.write(self, section, value)
1075 local fsrc6 = src6:formvalue(section) or ""
1076 if usev6:formvalue(section) ~= "1"
1077 or fsrc6 == "network"
1078 or fsrc6 == "interface" then
1079 return true -- ignore IPv4, network, interface
1081 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1082 return self.map:set(section, "interface", value) -- and write "interface"
1085 function eif6.parse(self, section, novld)
1086 DDNS.value_parse(self, section, novld)
1089 -- IPv4/IPv6 - bind_network -- ################################################
1090 if DDNS.has_bindnet or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1091 bnet = ns:taboption("advanced", ListValue, "bind_network",
1092 translate("Bind Network") )
1093 bnet:depends("ipv4_source", "web")
1094 bnet:depends("ipv6_source", "web")
1096 bnet:value("", translate("-- default --"))
1097 WADM.cbi_add_networks(bnet)
1098 function bnet.cfgvalue(self, section)
1099 local value = AbstractValue.cfgvalue(self, section)
1100 if not DDNS.has_bindnet and value ~= "" then
1101 self.description = bold_on .. font_red ..
1102 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1103 translate("please set to 'default'") .. " !" .. bold_off
1105 self.description = translate("OPTIONAL: Network to use for communication") ..
1106 "<br />" .. translate("Casual users should not change this setting")
1110 function bnet.validate(self, value)
1111 if ( (value ~= "") and DDNS.has_bindnet ) or (value == "") then return value end
1112 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1114 function bnet.parse(self, section, novld)
1115 DDNS.value_parse(self, section, novld)
1119 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1120 -- optional to force wget/curl and host to use only selected IP version
1121 -- command parameter "-4" or "-6"
1122 if DDNS.has_forceip or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1123 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1124 translate("Force IP Version") )
1125 fipv.orientation = "horizontal"
1126 function fipv.cfgvalue(self, section)
1127 local value = AbstractValue.cfgvalue(self, section)
1128 if not DDNS.has_forceip and value ~= "0" then
1129 self.description = bold_on .. font_red ..
1130 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1131 translate("please disable") .. " !" .. bold_off
1133 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1137 function fipv.validate(self, value)
1138 if (value == "1" and DDNS.has_forceip) or value == "0" then return value end
1139 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1143 -- IPv4 + IPv6 - dns_server -- ################################################
1144 -- optional DNS Server to use resolving my IP
1145 if DDNS.has_dnsserver or ( ( m:get(section, "dns_server") or "" ) ~= "" ) then
1146 dns = ns:taboption("advanced", Value, "dns_server",
1147 translate("DNS-Server"),
1148 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1149 translate("Format: IP or FQDN"))
1150 dns.placeholder = "mydns.lan"
1151 function dns.validate(self, value)
1152 -- if .datatype is set, then it is checked before calling this function
1153 if not value or (#value == 0) then
1154 return "" -- ignore on empty
1155 elseif not DDNS.has_dnsserver then
1156 return nil, err_tab_adv(self) .. translate("Specifying a DNS-Server is not supported")
1157 elseif not DTYP.host(value) then
1158 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1160 local ipv6 = usev6:formvalue(section) or "0"
1161 local force = fipv:formvalue(section) or "0"
1162 local command = CTRL.luci_helper .. [[ -]]
1163 if (ipv6 == 1) then command = command .. [[6]] end
1164 if (force == 1) then command = command .. [[f]] end
1165 command = command .. [[d ]] .. value .. [[ -- verify_dns]]
1167 local ret = SYS.call(command)
1168 if ret == 0 then return value -- everything OK
1169 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1170 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1171 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1172 else return nil, err_tab_adv(self) .. translate("unspecific error")
1176 function dns.parse(self, section, novld)
1177 DDNS.value_parse(self, section, novld)
1181 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1182 if DDNS.has_bindhost or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1183 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1184 translate("Force TCP on DNS") )
1185 tcp.orientation = "horizontal"
1186 function tcp.cfgvalue(self, section)
1187 local value = AbstractValue.cfgvalue(self, section)
1188 if not DDNS.has_bindhost and value ~= "0" then
1189 self.description = bold_on .. font_red ..
1190 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1191 translate("please disable") .. " !" .. bold_off
1193 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1197 function tcp.validate(self, value)
1198 if (value == "1" and DDNS.has_bindhost ) or value == "0" then
1201 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1205 -- IPv4 + IPv6 - proxy -- #####################################################
1206 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1207 if DDNS.has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1208 pxy = ns:taboption("advanced", Value, "proxy",
1209 translate("PROXY-Server") )
1210 pxy.placeholder="user:password@myproxy.lan:8080"
1211 function pxy.cfgvalue(self, section)
1212 local value = AbstractValue.cfgvalue(self, section)
1213 if not DDNS.has_proxy and value ~= "" then
1214 self.description = bold_on .. font_red ..
1215 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1216 translate("please remove entry") .. "!" .. bold_off
1218 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1219 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1220 translate("IPv6 address must be given in square brackets") .. ": " ..
1221 bold_on .. " [2001:db8::1]:8080" .. bold_off
1225 function pxy.validate(self, value)
1226 -- if .datatype is set, then it is checked before calling this function
1227 if not value or (#value == 0) then
1228 return "" -- ignore on empty
1229 elseif DDNS.has_proxy then
1230 local ipv6 = usev6:formvalue(section) or "0"
1231 local force = fipv:formvalue(section) or "0"
1232 local command = CRTL.luci_helper .. [[ -]]
1233 if (ipv6 == 1) then command = command .. [[6]] end
1234 if (force == 1) then command = command .. [[f]] end
1235 command = command .. [[p ]] .. value .. [[ -- verify_proxy]]
1236 local ret = SYS.call(command)
1237 if ret == 0 then return value
1238 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1239 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1240 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1241 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1242 else return nil, err_tab_adv(self) .. translate("unspecific error")
1245 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1248 function pxy.parse(self, section, novld)
1249 DDNS.value_parse(self, section, novld)
1253 -- use_syslog -- ##############################################################
1254 slog = ns:taboption("advanced", ListValue, "use_syslog",
1255 translate("Log to syslog"),
1256 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1258 slog:value("0", translate("No logging"))
1259 slog:value("1", translate("Info"))
1260 slog:value("2", translate("Notice"))
1261 slog:value("3", translate("Warning"))
1262 slog:value("4", translate("Error"))
1263 function slog.parse(self, section, novld)
1264 DDNS.value_parse(self, section, novld)
1267 -- use_logfile -- #############################################################
1268 logf = ns:taboption("advanced", Flag, "use_logfile",
1269 translate("Log to file"),
1270 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1271 translate("File") .. [[: "]] .. logdir .. [[/]] .. section .. [[.log"]] )
1272 logf.orientation = "horizontal"
1273 logf.default = "1" -- if not defined write to log by default
1275 -- TAB: Timer ####################################################################################
1276 -- check_interval -- ##########################################################
1277 ci = ns:taboption("timer", Value, "check_interval",
1278 translate("Check Interval") )
1279 ci.template = "ddns/detail_value"
1281 function ci.validate(self, value)
1282 if not DTYP.uinteger(value)
1283 or tonumber(value) < 1 then
1284 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1287 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1291 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1294 function ci.write(self, section, value)
1295 -- remove when default
1296 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1297 if secs ~= 600 then --default 10 minutes
1298 return self.map:set(section, self.option, value)
1300 self.map:del(section, "check_unit")
1301 return self.map:del(section, self.option)
1304 function ci.parse(self, section, novld)
1305 DDNS.value_parse(self, section, novld)
1308 -- check_unit -- ##############################################################
1309 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1310 translate("Interval to check for changed IP" .. "<br />" ..
1311 "Values below 5 minutes == 300 seconds are not supported") )
1312 cu.template = "ddns/detail_lvalue"
1313 cu.default = "minutes"
1314 cu:value("seconds", translate("seconds"))
1315 cu:value("minutes", translate("minutes"))
1316 cu:value("hours", translate("hours"))
1317 --cu:value("days", translate("days"))
1318 function cu.write(self, section, value)
1319 -- remove when default
1320 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1321 if secs ~= 600 then --default 10 minutes
1322 return self.map:set(section, self.option, value)
1327 function cu.parse(self, section, novld)
1328 DDNS.value_parse(self, section, novld)
1331 -- force_interval (modified) -- ###############################################
1332 fi = ns:taboption("timer", Value, "force_interval",
1333 translate("Force Interval") )
1334 fi.template = "ddns/detail_value"
1335 fi.default = "72" -- see dynamic_dns_updater.sh script
1336 --fi.rmempty = false -- validate ourselves for translatable error messages
1337 function fi.validate(self, value)
1338 if not DTYP.uinteger(value)
1339 or tonumber(value) < 0 then
1340 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1343 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1344 if force_s == 0 then
1348 local ci_value = ci:formvalue(section)
1349 if not DTYP.uinteger(ci_value) then
1350 return "" -- ignore because error in check_interval above
1353 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1354 if force_s >= check_s then
1358 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1360 function fi.write(self, section, value)
1361 -- simulate rmempty=true remove default
1362 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1363 if secs ~= 259200 then --default 72 hours == 3 days
1364 return self.map:set(section, self.option, value)
1366 self.map:del(section, "force_unit")
1367 return self.map:del(section, self.option)
1370 function fi.parse(self, section, novld)
1371 DDNS.value_parse(self, section, novld)
1374 -- force_unit -- ##############################################################
1375 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1376 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1377 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1378 "Values lower 'Check Interval' except '0' are not supported") )
1379 fu.template = "ddns/detail_lvalue"
1380 fu.default = "hours"
1381 --fu.rmempty = false -- want to control write process
1382 --fu:value("seconds", translate("seconds"))
1383 fu:value("minutes", translate("minutes"))
1384 fu:value("hours", translate("hours"))
1385 fu:value("days", translate("days"))
1386 function fu.write(self, section, value)
1387 -- simulate rmempty=true remove default
1388 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1389 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1390 return self.map:set(section, self.option, value)
1395 function fu.parse(self, section, novld)
1396 DDNS.value_parse(self, section, novld)
1399 -- retry_count -- #############################################################
1400 rc = ns:taboption("timer", Value, "retry_count")
1401 rc.title = translate("Error Retry Counter")
1402 rc.description = translate("On Error the script will stop execution after given number of retrys")
1404 .. translate("The default setting of '0' will retry infinite.")
1406 function rc.validate(self, value)
1407 if not DTYP.uinteger(value) then
1408 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1413 function rc.parse(self, section, novld)
1414 DDNS.value_parse(self, section, novld)
1417 -- retry_interval -- ##########################################################
1418 ri = ns:taboption("timer", Value, "retry_interval",
1419 translate("Error Retry Interval") )
1420 ri.template = "ddns/detail_value"
1422 function ri.validate(self, value)
1423 if not DTYP.uinteger(value)
1424 or tonumber(value) < 1 then
1425 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1430 function ri.write(self, section, value)
1431 -- simulate rmempty=true remove default
1432 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1433 if secs ~= 60 then --default 60seconds
1434 return self.map:set(section, self.option, value)
1436 self.map:del(section, "retry_unit")
1437 return self.map:del(section, self.option)
1440 function ri.parse(self, section, novld)
1441 DDNS.value_parse(self, section, novld)
1444 -- retry_unit -- ##############################################################
1445 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1446 translate("On Error the script will retry the failed action after given time") )
1447 ru.template = "ddns/detail_lvalue"
1448 ru.default = "seconds"
1449 --ru.rmempty = false -- want to control write process
1450 ru:value("seconds", translate("seconds"))
1451 ru:value("minutes", translate("minutes"))
1452 --ru:value("hours", translate("hours"))
1453 --ru:value("days", translate("days"))
1454 function ru.write(self, section, value)
1455 -- simulate rmempty=true remove default
1456 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1457 if secs ~= 60 then --default 60seconds
1458 return self.map:set(section, self.option, value)
1460 return true -- will be deleted by retry_interval
1463 function ru.parse(self, section, novld)
1464 DDNS.value_parse(self, section, novld)
1467 -- TAB: LogView ##################################################################################
1468 lv = ns:taboption("logview", DummyValue, "_logview")
1469 lv.template = "ddns/detail_logview"
1470 lv.inputtitle = translate("Read / Reread log file")
1472 function lv.cfgvalue(self, section)
1473 local lfile=logdir .. "/" .. section .. ".log"
1474 if NXFS.access(lfile) then
1475 return lfile .. "\n" .. translate("Please press [Read] button")
1477 return lfile .. "\n" .. translate("File not found or empty")