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("/usr/lib/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("/usr/lib/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 _interface = "-"
100 local _ipv6 = usev6:formvalue(section)
101 local _source = (_ipv6 == "1")
102 and src6:formvalue(section)
103 or src4:formvalue(section)
104 if _source == "network" then
105 _network = (_ipv6 == "1")
106 and ipn6:formvalue(section)
107 or ipn4:formvalue(section)
108 elseif _source == "web" then
109 _url = (_ipv6 == "1")
110 and iurl6:formvalue(section)
111 or iurl4:formvalue(section)
112 -- proxy only needed for checking url
113 _proxy = (pxy) and pxy:formvalue(section) or ""
114 elseif _source == "interface" then
115 _interface = ipi:formvalue(section)
116 elseif _source == "script" then
117 _script = ips:formvalue(section)
120 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] ..
121 _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] ..
122 _url .. [[ ]] .. _interface .. [[ ']] .. _script.. [[' ]] .. _proxy
123 return (SYS.call(command) == 0)
126 -- function to check if option is used inside url or script
127 -- return -1 on error, 0 NOT required, 1 required
128 local function _option_used(option, urlscript)
129 local surl -- search string for url
130 local ssh -- search string for script
131 local required -- option used inside url or script
133 if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain'
134 elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username'
135 elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password'
136 elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc'
137 elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt'
139 error("undefined option")
140 return -1 -- return on error
143 local required = false
145 if urlscript:find('http') then
146 required = ( urlscript:find(surl) )
149 if not urlscript:find("/") then
150 -- might be inside ddns-scripts directory
151 urlscript = "/usr/lib/ddns/" .. urlscript
153 -- problem with script exit here
154 if not NXFS.access(urlscript) then return -1 end
156 local f = io.input(urlscript)
157 -- still problem with script exit here
158 if not f then return -1 end
159 for l in f:lines() do
161 if l:find('^#') then break end -- continue on comment lines
162 required = ( l:find(surl) or l:find(ssh) )
164 if required then break end
168 return (required and 1 or 0)
171 -- function to verify if option is valid
172 local function _option_validate(self, value)
173 -- section is globally defined here be calling agrument (see above)
174 local fusev6 = usev6:formvalue(section) or "0"
175 local fsvc4 = svc4:formvalue(section) or "-"
176 local fsvc6 = svc6:formvalue(section) or "-"
179 -- IP-Version dependent custom service selected
180 if (fusev6 == "0" and fsvc4 == "-") or
181 (fusev6 == "1" and fsvc6 == "-") then
183 urlsh = uurl:formvalue(section) or ""
184 -- no url then read custom script
185 if (#urlsh == 0) then
186 urlsh = ush:formvalue(section) or ""
188 -- IPv4 read from services4 table
189 elseif (fusev6 == "0") then
190 urlsh = services4[fsvc4] or ""
191 -- IPv6 read from services6 table
193 urlsh = services6[fsvc6] or ""
195 -- problem with url or script exit here
196 -- error handled somewhere else
197 if (#urlsh == 0) then return "" end
199 used = _option_used(self.option, urlsh)
200 -- on error or not used return empty sting
201 if used < 1 then return "" end
202 -- needed but no data then return error
203 if not value or (#value == 0) then
204 return nil, err_tab_basic(self) .. translate("missing / required")
209 -- cbi-map definition -- #######################################################
210 local m = Map("ddns")
211 m.title = CTRL.app_title_back()
212 m.description = CTRL.app_description()
213 m.redirect = DISP.build_url("admin", "services", "ddns")
215 m.on_after_commit = function(self)
216 if self.changed then -- changes ?
217 local pid = DDNS.get_pid(section)
218 if pid > 0 then -- running ?
219 local tmp = NX.kill(pid, 1) -- send SIGHUP
224 -- provider switch was requested, save and reload page
225 if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1]
227 local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section) or "0"
228 if fusev6 == "1" then
229 fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section) or ""
231 fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section) or ""
234 if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed
235 m:set(section, "use_ipv6", fusev6) -- save it
238 if fsvc ~= "-" then -- NOT "custom"
239 m:set(section, "service_name", fsvc) -- save it
241 m:del(section, "service_name") -- delete it
246 HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
250 -- read application settings -- ################################################
251 -- date format; if not set use ISO format
252 local date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
254 local log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
256 -- cbi-section definition -- ###################################################
257 local ns = m:section( NamedSection, section, "service",
258 translate("Details for") .. ([[: <strong>%s</strong>]] % section),
259 translate("Configure here the details for selected Dynamic DNS service.") )
260 ns.instance = section -- arg [1]
261 ns:tab("basic", translate("Basic Settings"), nil )
262 ns:tab("advanced", translate("Advanced Settings"), nil )
263 ns:tab("timer", translate("Timer Settings"), nil )
264 ns:tab("logview", translate("Log File Viewer"), nil )
266 -- TAB: Basic #####################################################################################
267 -- enabled -- #################################################################
268 en = ns:taboption("basic", Flag, "enabled",
269 translate("Enabled"),
270 translate("If this service section is disabled it could not be started." .. "<br />" ..
271 "Neither from LuCI interface nor from console") )
272 en.orientation = "horizontal"
274 -- IPv4/IPv6 - lookup_host -- #################################################
275 luh = ns:taboption("basic", Value, "lookup_host",
276 translate("Lookup Hostname"),
277 translate("Hostname/FQDN to validate, if IP update happen or necessary") )
279 luh.placeholder = "myhost.example.com"
280 function luh.validate(self, value)
283 or not DTYP.hostname(value) then
284 return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
286 return UTIL.trim(value)
289 function luh.parse(self, section, novld)
290 DDNS.value_parse(self, section, novld)
293 -- use_ipv6 -- ################################################################
294 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
295 translate("IP address version"),
296 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
297 usev6.widget = "radio"
299 usev6:value("0", translate("IPv4-Address") )
300 function usev6.cfgvalue(self, section)
301 local value = AbstractValue.cfgvalue(self, section) or "0"
302 if DDNS.has_ipv6 or (value == "1" and not DDNS.has_ipv6) then
303 self:value("1", translate("IPv6-Address") )
305 if value == "1" and not DDNS.has_ipv6 then
306 self.description = err_ipv6_basic
310 function usev6.validate(self, value)
311 if (value == "1" and DDNS.has_ipv6) or value == "0" then
314 return nil, err_tab_basic(self) .. err_ipv6_plain
316 function usev6.parse(self, section, novld)
317 DDNS.value_parse(self, section, novld)
320 -- IPv4 - service_name -- #####################################################
321 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
322 translate("DDNS Service provider") .. " [IPv4]" )
324 svc4:depends("use_ipv6", "0") -- only show on IPv4
325 function svc4.cfgvalue(self, section)
326 local v = DDNS.read_value(self, section, "service_name")
327 if v and (#v > 0) then
328 for s, u in UTIL.kspairs(services4) do
329 if v == s then return v end
334 function svc4.validate(self, value)
335 if usev6:formvalue(section) ~= "1" then -- do only on IPv4
338 return "" -- suppress validate error
341 function svc4.write(self, section, value)
342 if usev6:formvalue(section) ~= "1" then -- do only IPv4 here
343 self.map:del(section, self.option) -- to be shure
344 if value ~= "-" then -- and write "service_name
345 self.map:del(section, "update_url") -- delete update_url
346 self.map:del(section, "update_script") -- delete update_script
347 return self.map:set(section, "service_name", value)
349 return self.map:del(section, "service_name")
353 function svc4.parse(self, section, novld)
354 DDNS.value_parse(self, section, novld)
357 -- IPv6 - service_name -- #####################################################
358 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
359 translate("DDNS Service provider") .. " [IPv6]" )
361 svc6:depends("use_ipv6", "1") -- only show on IPv6
362 if not DDNS.has_ipv6 then
363 svc6.description = err_ipv6_basic
365 function svc6.cfgvalue(self, section)
366 local v = DDNS.read_value(self, section, "service_name")
367 if v and (#v > 0) then
368 for s, u in UTIL.kspairs(services4) do
369 if v == s then return v end
374 function svc6.validate(self, value)
375 if usev6:formvalue(section) == "1" then -- do only on IPv6
376 if DDNS.has_ipv6 then return value end
377 return nil, err_tab_basic(self) .. err_ipv6_plain
379 return "" -- suppress validate error
382 function svc6.write(self, section, value)
383 if usev6:formvalue(section) == "1" then -- do only when IPv6
384 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
385 if value ~= "-" then -- and write "service_name
386 self.map:del(section, "update_url") -- delete update_url
387 self.map:del(section, "update_script") -- delete update_script
388 return self.map:set(section, "service_name", value)
390 return self.map:del(section, "service_name")
394 function svc6.parse(self, section, novld)
395 DDNS.value_parse(self, section, novld)
398 -- IPv4/IPv6 - change Provider -- #############################################
399 svs = ns:taboption("basic", Button, "_switch")
400 svs.title = translate("Really change DDNS provider?")
401 svs.inputtitle = translate("Change provider")
402 svs.inputstyle = "apply"
404 -- IPv4/IPv6 - update_url -- ##################################################
405 uurl = ns:taboption("basic", Value, "update_url",
406 translate("Custom update-URL"),
407 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
408 "Follow instructions you will find on their WEB page.") )
409 function uurl.validate(self, value)
410 local fush = ush:formvalue(section)
411 local fusev6 = usev6:formvalue(section)
413 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
414 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
415 return "" -- suppress validate error
416 elseif not value or (#value == 0) then
417 if not fush or (#fush == 0) then
418 return nil, err_tab_basic(self) .. translate("missing / required")
420 return "" -- suppress validate error / update_script is given
422 elseif (#fush > 0) then
423 return nil, err_tab_basic(self) .. translate("either url or script could be set")
426 local url = DDNS.parse_url(value)
427 if not url.scheme == "http" then
428 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
429 elseif not url.query then
430 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
431 elseif not url.host then
432 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
433 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
434 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
439 function uurl.parse(self, section, novld)
440 DDNS.value_parse(self, section, novld)
443 -- IPv4/IPv6 - update_script -- ###############################################
444 ush = ns:taboption("basic", Value, "update_script",
445 translate("Custom update-script"),
446 translate("Custom update script to be used for updating your DDNS Provider.") )
447 function ush.validate(self, value)
448 local fuurl = uurl:formvalue(section)
449 local fusev6 = usev6:formvalue(section)
451 if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or
452 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
453 return "" -- suppress validate error
454 elseif not value or (#value == 0) then
455 if not fuurl or (#fuurl == 0) then
456 return nil, err_tab_basic(self) .. translate("missing / required")
458 return "" -- suppress validate error / update_url is given
460 elseif (#fuurl > 0) then
461 return nil, err_tab_basic(self) .. translate("either url or script could be set")
462 elseif not NXFS.access(value) then
463 return nil, err_tab_basic(self) .. translate("File not found")
467 function ush.parse(self, section, novld)
468 DDNS.value_parse(self, section, novld)
471 -- IPv4/IPv6 - domain -- ######################################################
472 dom = ns:taboption("basic", Value, "domain",
474 translate("Replaces [DOMAIN] in Update-URL") )
475 dom.placeholder = "myhost.example.com"
476 function dom.validate(self, value)
477 return _option_validate(self, value)
479 function dom.parse(self, section, novld)
480 DDNS.value_parse(self, section, novld)
483 -- IPv4/IPv6 - username -- ####################################################
484 user = ns:taboption("basic", Value, "username",
485 translate("Username"),
486 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
487 function user.validate(self, value)
488 return _option_validate(self, value)
490 function user.parse(self, section, novld)
491 DDNS.value_parse(self, section, novld)
494 -- IPv4/IPv6 - password -- ####################################################
495 pw = ns:taboption("basic", Value, "password",
496 translate("Password"),
497 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
499 function pw.validate(self, value)
500 return _option_validate(self, value)
502 function pw.parse(self, section, novld)
503 DDNS.value_parse(self, section, novld)
506 -- IPv4/IPv6 - param_enc -- ###################################################
507 pe = ns:taboption("basic", Value, "param_enc",
508 translate("Optional Encoded Parameter"),
509 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
510 function pe.validate(self, value)
511 return _option_validate(self, value)
513 function pe.parse(self, section, novld)
514 DDNS.value_parse(self, section, novld)
517 -- IPv4/IPv6 - param_enc -- ###################################################
518 po = ns:taboption("basic", Value, "param_opt",
519 translate("Optional Parameter"),
520 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
521 function po.validate(self, value)
522 return _option_validate(self, value)
524 function po.parse(self, section, novld)
525 DDNS.value_parse(self, section, novld)
528 -- handled service dependent show/display -- ##################################
530 local cv4 = svc4:cfgvalue(section)
532 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
533 ush:depends ("ipv4_service_name", "?")
534 uurl:depends("ipv4_service_name", "?")
536 uurl:depends("ipv4_service_name", "-")
537 ush:depends ("ipv4_service_name", "-")
538 dom:depends("ipv4_service_name", "-" )
539 user:depends("ipv4_service_name", "-" )
540 pw:depends("ipv4_service_name", "-" )
541 pe:depends("ipv4_service_name", "-" )
542 po:depends("ipv4_service_name", "-" )
544 for s, u in UTIL.kspairs(services4) do
545 svc4:value(s) -- fill DropDown-List
547 svs:depends("ipv4_service_name", s )
549 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
550 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
551 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
552 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
553 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
556 svc4:value("-", translate("-- custom --") )
559 local cv6 = svc6:cfgvalue(section)
561 svs:depends ("ipv6_service_name", "-" )
562 uurl:depends("ipv6_service_name", "?")
563 ush:depends ("ipv6_service_name", "?")
565 uurl:depends("ipv6_service_name", "-")
566 ush:depends ("ipv6_service_name", "-")
567 dom:depends("ipv6_service_name", "-" )
568 user:depends("ipv6_service_name", "-" )
569 pw:depends("ipv6_service_name", "-" )
570 pe:depends("ipv6_service_name", "-" )
571 po:depends("ipv6_service_name", "-" )
573 for s, u in UTIL.kspairs(services6) do
574 svc6:value(s) -- fill DropDown-List
576 svs:depends("ipv6_service_name", s )
578 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
579 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
580 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
581 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
582 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
585 svc6:value("-", translate("-- custom --") )
587 -- IPv4/IPv6 - use_https -- ###################################################
588 if DDNS.has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
589 https = ns:taboption("basic", Flag, "use_https",
590 translate("Use HTTP Secure") )
591 https.orientation = "horizontal"
592 function https.cfgvalue(self, section)
593 local value = AbstractValue.cfgvalue(self, section)
594 if not DDNS.has_ssl and value == "1" then
595 self.description = bold_on .. font_red ..
596 translate("HTTPS not supported") .. font_off .. "<br />" ..
597 translate("please disable") .. " !" .. bold_off
599 self.description = translate("Enable secure communication with DDNS provider")
603 function https.validate(self, value)
604 if (value == "1" and DDNS.has_ssl ) or value == "0" then return value end
605 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
607 function https.write(self, section, value)
609 return self.map:set(section, self.option, value)
611 self.map:del(section, "cacert")
612 return self.map:del(section, self.option)
617 -- IPv4/IPv6 - cacert -- ######################################################
619 cert = ns:taboption("basic", Value, "cacert",
620 translate("Path to CA-Certificate"),
621 translate("directory or path/file") .. "<br />" ..
622 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
623 translate("to run HTTPS without verification of server certificates (insecure)") )
624 cert:depends("use_https", "1")
625 cert.placeholder = "/etc/ssl/certs"
626 cert.forcewrite = true
627 function cert.validate(self, value)
628 if https:formvalue(section) ~= "1" then
629 return "" -- suppress validate error if NOT https
631 if value then -- otherwise errors in datatype check
632 if DTYP.directory(value)
634 or (value == "IGNORE")
635 or (#value == 0) then
639 return nil, err_tab_basic(self) ..
640 translate("file or directory not found or not 'IGNORE'") .. " !"
642 function cert.parse(self, section, novld)
643 DDNS.value_parse(self, section, novld)
647 -- TAB: Advanced #################################################################################
648 -- IPv4 - ip_source -- ########################################################
649 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
650 translate("IP address source") .. " [IPv4]",
651 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
652 src4:depends("use_ipv6", "0") -- IPv4 selected
653 src4.default = "network"
654 src4:value("network", translate("Network"))
655 src4:value("web", translate("URL"))
656 src4:value("interface", translate("Interface"))
657 src4:value("script", translate("Script"))
658 function src4.cfgvalue(self, section)
659 return DDNS.read_value(self, section, "ip_source")
661 function src4.validate(self, value)
662 if usev6:formvalue(section) == "1" then
663 return "" -- ignore on IPv6 selected
664 elseif not _verify_ip_source() then
665 return nil, err_tab_adv(self) ..
666 translate("can not detect local IP. Please select a different Source combination")
671 function src4.write(self, section, value)
672 if usev6:formvalue(section) == "1" then
673 return true -- ignore on IPv6 selected
674 elseif value == "network" then
675 self.map:del(section, "ip_url") -- delete not need parameters
676 self.map:del(section, "ip_interface")
677 self.map:del(section, "ip_script")
678 elseif value == "web" then
679 self.map:del(section, "ip_network") -- delete not need parameters
680 self.map:del(section, "ip_interface")
681 self.map:del(section, "ip_script")
682 elseif value == "interface" then
683 self.map:del(section, "ip_network") -- delete not need parameters
684 self.map:del(section, "ip_url")
685 self.map:del(section, "ip_script")
686 elseif value == "script" then
687 self.map:del(section, "ip_network")
688 self.map:del(section, "ip_url") -- delete not need parameters
689 self.map:del(section, "ip_interface")
691 self.map:del(section, self.option) -- delete "ipv4_source" helper
692 return self.map:set(section, "ip_source", value) -- and write "ip_source
694 function src4.parse(self, section, novld)
695 DDNS.value_parse(self, section, novld)
698 -- IPv6 - ip_source -- ########################################################
699 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
700 translate("IP address source") .. " [IPv6]",
701 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
702 src6:depends("use_ipv6", 1) -- IPv6 selected
703 src6.default = "network"
704 src6:value("network", translate("Network"))
705 src6:value("web", translate("URL"))
706 src6:value("interface", translate("Interface"))
707 src6:value("script", translate("Script"))
708 if not DDNS.has_ipv6 then
709 src6.description = err_ipv6_other
711 function src6.cfgvalue(self, section)
712 return DDNS.read_value(self, section, "ip_source")
714 function src6.validate(self, value)
715 if usev6:formvalue(section) ~= "1" then
716 return "" -- ignore on IPv4 selected
717 elseif not DDNS.has_ipv6 then
718 return nil, err_tab_adv(self) .. err_ipv6_plain
719 elseif not _verify_ip_source() then
720 return nil, err_tab_adv(self) ..
721 translate("can not detect local IP. Please select a different Source combination")
726 function src6.write(self, section, value)
727 if usev6:formvalue(section) ~= "1" then
728 return true -- ignore on IPv4 selected
729 elseif value == "network" then
730 self.map:del(section, "ip_url") -- delete not need parameters
731 self.map:del(section, "ip_interface")
732 self.map:del(section, "ip_script")
733 elseif value == "web" then
734 self.map:del(section, "ip_network") -- delete not need parameters
735 self.map:del(section, "ip_interface")
736 self.map:del(section, "ip_script")
737 elseif value == "interface" then
738 self.map:del(section, "ip_network") -- delete not need parameters
739 self.map:del(section, "ip_url")
740 self.map:del(section, "ip_script")
741 elseif value == "script" then
742 self.map:del(section, "ip_network")
743 self.map:del(section, "ip_url") -- delete not need parameters
744 self.map:del(section, "ip_interface")
746 self.map:del(section, self.option) -- delete "ipv4_source" helper
747 return self.map:set(section, "ip_source", value) -- and write "ip_source
749 function src6.parse(self, section, novld)
750 DDNS.value_parse(self, section, novld)
753 -- IPv4 - ip_network (default "wan") -- #######################################
754 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
755 translate("Network") .. " [IPv4]",
756 translate("Defines the network to read systems IPv4-Address from") )
757 ipn4:depends("ipv4_source", "network")
759 WADM.cbi_add_networks(ipn4)
760 function ipn4.cfgvalue(self, section)
761 return DDNS.read_value(self, section, "ip_network")
763 function ipn4.validate(self, value)
764 if usev6:formvalue(section) == "1"
765 or src4:formvalue(section) ~= "network" then
766 -- ignore if IPv6 selected OR
767 -- ignore everything except "network"
773 function ipn4.write(self, section, value)
774 if usev6:formvalue(section) == "1"
775 or src4:formvalue(section) ~= "network" then
776 -- ignore if IPv6 selected OR
777 -- ignore everything except "network"
780 -- set also as "interface" for monitoring events changes/hot-plug
781 self.map:set(section, "interface", value)
782 self.map:del(section, self.option) -- delete "ipv4_network" helper
783 return self.map:set(section, "ip_network", value) -- and write "ip_network"
786 function ipn4.parse(self, section, novld)
787 DDNS.value_parse(self, section, novld)
790 -- IPv6 - ip_network (default "wan6") -- ######################################
791 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
792 translate("Network") .. " [IPv6]" )
793 ipn6:depends("ipv6_source", "network")
794 ipn6.default = "wan6"
795 WADM.cbi_add_networks(ipn6)
796 if DDNS.has_ipv6 then
797 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
799 ipn6.description = err_ipv6_other
801 function ipn6.cfgvalue(self, section)
802 return DDNS.read_value(self, section, "ip_network")
804 function ipn6.validate(self, value)
805 if usev6:formvalue(section) ~= "1"
806 or src6:formvalue(section) ~= "network" then
807 -- ignore if IPv4 selected OR
808 -- ignore everything except "network"
810 elseif DDNS.has_ipv6 then
813 return nil, err_tab_adv(self) .. err_ipv6_plain
816 function ipn6.write(self, section, value)
817 if usev6:formvalue(section) ~= "1"
818 or src6:formvalue(section) ~= "network" then
819 -- ignore if IPv4 selected OR
820 -- ignore everything except "network"
823 -- set also as "interface" for monitoring events changes/hotplug
824 self.map:set(section, "interface", value)
825 self.map:del(section, self.option) -- delete "ipv6_network" helper
826 return self.map:set(section, "ip_network", value) -- and write "ip_network"
829 function ipn6.parse(self, section, novld)
830 DDNS.value_parse(self, section, novld)
833 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
834 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
835 translate("URL to detect") .. " [IPv4]",
836 translate("Defines the Web page to read systems IPv4-Address from") )
837 iurl4:depends("ipv4_source", "web")
838 iurl4.default = "http://checkip.dyndns.com"
839 function iurl4.cfgvalue(self, section)
840 return DDNS.read_value(self, section, "ip_url")
842 function iurl4.validate(self, value)
843 if usev6:formvalue(section) == "1"
844 or src4:formvalue(section) ~= "web" then
845 -- ignore if IPv6 selected OR
846 -- ignore everything except "web"
848 elseif not value or #value == 0 then
849 return nil, err_tab_adv(self) .. translate("missing / required")
852 local url = DDNS.parse_url(value)
853 if not (url.scheme == "http" or url.scheme == "https") then
854 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
855 elseif not url.host then
856 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
857 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
858 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
863 function iurl4.write(self, section, value)
864 if usev6:formvalue(section) == "1"
865 or src4:formvalue(section) ~= "web" then
866 -- ignore if IPv6 selected OR
867 -- ignore everything except "web"
870 self.map:del(section, self.option) -- delete "ipv4_url" helper
871 return self.map:set(section, "ip_url", value) -- and write "ip_url"
874 function iurl4.parse(self, section, novld)
875 DDNS.value_parse(self, section, novld)
878 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
879 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
880 translate("URL to detect") .. " [IPv6]" )
881 iurl6:depends("ipv6_source", "web")
882 iurl6.default = "http://checkipv6.dyndns.com"
883 if DDNS.has_ipv6 then
884 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
886 iurl6.description = err_ipv6_other
888 function iurl6.cfgvalue(self, section)
889 return DDNS.read_value(self, section, "ip_url")
891 function iurl6.validate(self, value)
892 if usev6:formvalue(section) ~= "1"
893 or src6:formvalue(section) ~= "web" then
894 -- ignore if IPv4 selected OR
895 -- ignore everything except "web"
897 elseif not DDNS.has_ipv6 then
898 return nil, err_tab_adv(self) .. err_ipv6_plain
899 elseif not value or #value == 0 then
900 return nil, err_tab_adv(self) .. translate("missing / required")
903 local url = DDNS.parse_url(value)
904 if not (url.scheme == "http" or url.scheme == "https") then
905 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
906 elseif not url.host then
907 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
908 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
909 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
914 function iurl6.write(self, section, value)
915 if usev6:formvalue(section) ~= "1"
916 or src6:formvalue(section) ~= "web" then
917 -- ignore if IPv4 selected OR
918 -- ignore everything except "web"
921 self.map:del(section, self.option) -- delete "ipv6_url" helper
922 return self.map:set(section, "ip_url", value) -- and write "ip_url"
925 function iurl6.parse(self, section, novld)
926 DDNS.value_parse(self, section, novld)
929 -- IPv4 + IPv6 - ip_interface -- ##############################################
930 ipi = ns:taboption("advanced", ListValue, "ip_interface",
931 translate("Interface"),
932 translate("Defines the interface to read systems IP-Address from") )
933 ipi:depends("ipv4_source", "interface") -- IPv4
934 ipi:depends("ipv6_source", "interface") -- or IPv6
935 for _, v in pairs(SYS.net.devices()) do
936 -- show only interface set to a network
937 -- and ignore loopback
938 net = WADM.iface_get_network(v)
939 if net and net ~= "loopback" then
943 function ipi.validate(self, value)
944 local fusev6 = usev6:formvalue(section)
945 if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface")
946 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
952 function ipi.write(self, section, 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
958 -- get network from device to
959 -- set also as "interface" for monitoring events changes/hotplug
960 local net = WADM.iface_get_network(value)
961 self.map:set(section, "interface", net)
962 return self.map:set(section, self.option, value)
965 function ipi.parse(self, section, novld)
966 DDNS.value_parse(self, section, novld)
969 -- IPv4 + IPv6 - ip_script -- #################################################
970 ips = ns:taboption("advanced", Value, "ip_script",
972 translate("User defined script to read systems IP-Address") )
973 ips:depends("ipv4_source", "script") -- IPv4
974 ips:depends("ipv6_source", "script") -- or IPv6
975 ips.placeholder = "/path/to/script.sh"
976 function ips.validate(self, value)
977 local fusev6 = usev6:formvalue(section)
979 if value then split = UTIL.split(value, " ") end
981 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
982 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
984 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
985 return nil, err_tab_adv(self) ..
986 translate("not found or not executable - Sample: '/path/to/script.sh'")
991 function ips.write(self, section, value)
992 local fusev6 = usev6:formvalue(section)
993 if (fusev6 ~= "1" and src4:formvalue(section) ~= "script")
994 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
997 return self.map:set(section, self.option, value)
1000 function ips.parse(self, section, novld)
1001 DDNS.value_parse(self, section, novld)
1004 -- IPv4 - interface - default "wan" -- ########################################
1005 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1006 -- only needs to be set if "ip_source"="web" or "script"
1007 -- if "ip_source"="network" or "interface" we use their network
1008 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1009 translate("Event Network") .. " [IPv4]",
1010 translate("Network on which the ddns-updater scripts will be started") )
1011 eif4:depends("ipv4_source", "web")
1012 eif4:depends("ipv4_source", "script")
1013 eif4.default = "wan"
1014 WADM.cbi_add_networks(eif4)
1015 function eif4.cfgvalue(self, section)
1016 return DDNS.read_value(self, section, "interface")
1018 function eif4.validate(self, value)
1019 local fsrc4 = src4:formvalue(section) or ""
1020 if usev6:formvalue(section) == "1"
1021 or fsrc4 == "network"
1022 or fsrc4 == "interface" then
1023 return "" -- ignore IPv6, network, interface
1028 function eif4.write(self, section, value)
1029 local fsrc4 = src4:formvalue(section) or ""
1030 if usev6:formvalue(section) == "1"
1031 or fsrc4 == "network"
1032 or fsrc4 == "interface" then
1033 return true -- ignore IPv6, network, interface
1035 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1036 return self.map:set(section, "interface", value) -- and write "interface"
1039 function eif4.parse(self, section, novld)
1040 DDNS.value_parse(self, section, novld)
1043 -- IPv6 - interface - default "wan6" -- #######################################
1044 -- event network to monitor changes/hotplug
1045 -- only needs to be set if "ip_source"="web" or "script"
1046 -- if "ip_source"="network" or "interface" we use their network
1047 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1048 translate("Event Network") .. " [IPv6]" )
1049 eif6:depends("ipv6_source", "web")
1050 eif6:depends("ipv6_source", "script")
1051 eif6.default = "wan6"
1052 WADM.cbi_add_networks(eif6)
1053 if not DDNS.has_ipv6 then
1054 eif6.description = err_ipv6_other
1056 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1058 function eif6.cfgvalue(self, section)
1059 return DDNS.read_value(self, section, "interface")
1061 function eif6.validate(self, value)
1062 local fsrc6 = src6:formvalue(section) or ""
1063 if usev6:formvalue(section) ~= "1"
1064 or fsrc6 == "network"
1065 or fsrc6 == "interface" then
1066 return "" -- ignore IPv4, network, interface
1067 elseif not DDNS.has_ipv6 then
1068 return nil, err_tab_adv(self) .. err_ipv6_plain
1073 function eif6.write(self, section, value)
1074 local fsrc6 = src6:formvalue(section) or ""
1075 if usev6:formvalue(section) ~= "1"
1076 or fsrc6 == "network"
1077 or fsrc6 == "interface" then
1078 return true -- ignore IPv4, network, interface
1080 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1081 return self.map:set(section, "interface", value) -- and write "interface"
1084 function eif6.parse(self, section, novld)
1085 DDNS.value_parse(self, section, novld)
1088 -- IPv4/IPv6 - bind_network -- ################################################
1089 if DDNS.has_bindnet or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1090 bnet = ns:taboption("advanced", ListValue, "bind_network",
1091 translate("Bind Network") )
1092 bnet:depends("ipv4_source", "web")
1093 bnet:depends("ipv6_source", "web")
1095 bnet:value("", translate("-- default --"))
1096 WADM.cbi_add_networks(bnet)
1097 function bnet.cfgvalue(self, section)
1098 local value = AbstractValue.cfgvalue(self, section)
1099 if not DDNS.has_bindnet and value ~= "" then
1100 self.description = bold_on .. font_red ..
1101 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1102 translate("please set to 'default'") .. " !" .. bold_off
1104 self.description = translate("OPTIONAL: Network to use for communication") ..
1105 "<br />" .. translate("Casual users should not change this setting")
1109 function bnet.validate(self, value)
1110 if ( (value ~= "") and DDNS.has_bindnet ) or (value == "") then return value end
1111 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1113 function bnet.parse(self, section, novld)
1114 DDNS.value_parse(self, section, novld)
1118 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1119 -- optional to force wget/curl and host to use only selected IP version
1120 -- command parameter "-4" or "-6"
1121 if DDNS.has_forceip or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1122 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1123 translate("Force IP Version") )
1124 fipv.orientation = "horizontal"
1125 function fipv.cfgvalue(self, section)
1126 local value = AbstractValue.cfgvalue(self, section)
1127 if not DDNS.has_forceip and value ~= "0" then
1128 self.description = bold_on .. font_red ..
1129 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1130 translate("please disable") .. " !" .. bold_off
1132 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1136 function fipv.validate(self, value)
1137 if (value == "1" and DDNS.has_forceip) or value == "0" then return value end
1138 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1142 -- IPv4 + IPv6 - dns_server -- ################################################
1143 -- optional DNS Server to use resolving my IP
1144 if DDNS.has_dnsserver or ( ( m:get(section, "dns_server") or "" ) ~= "" ) then
1145 dns = ns:taboption("advanced", Value, "dns_server",
1146 translate("DNS-Server"),
1147 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1148 translate("Format: IP or FQDN"))
1149 dns.placeholder = "mydns.lan"
1150 function dns.validate(self, value)
1151 -- if .datatype is set, then it is checked before calling this function
1152 if not value or (#value == 0) then
1153 return "" -- ignore on empty
1154 elseif not DDNS.has_dnsserver then
1155 return nil, err_tab_adv(self) .. translate("Specifying a DNS-Server is not supported")
1156 elseif not DTYP.host(value) then
1157 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1159 local ipv6 = usev6:formvalue(section) or "0"
1160 local force = fipv:formvalue(section) or "0"
1161 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] ..
1162 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1163 local ret = SYS.call(command)
1164 if ret == 0 then return value -- everything OK
1165 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1166 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1167 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1168 else return nil, err_tab_adv(self) .. translate("unspecific error")
1172 function dns.parse(self, section, novld)
1173 DDNS.value_parse(self, section, novld)
1177 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1178 if DDNS.has_bindhost or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1179 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1180 translate("Force TCP on DNS") )
1181 tcp.orientation = "horizontal"
1182 function tcp.cfgvalue(self, section)
1183 local value = AbstractValue.cfgvalue(self, section)
1184 if not DDNS.has_bindhost and value ~= "0" then
1185 self.description = bold_on .. font_red ..
1186 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1187 translate("please disable") .. " !" .. bold_off
1189 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1193 function tcp.validate(self, value)
1194 if (value == "1" and DDNS.has_bindhost ) or value == "0" then
1197 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1201 -- IPv4 + IPv6 - proxy -- #####################################################
1202 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1203 if DDNS.has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1204 pxy = ns:taboption("advanced", Value, "proxy",
1205 translate("PROXY-Server") )
1206 pxy.placeholder="user:password@myproxy.lan:8080"
1207 function pxy.cfgvalue(self, section)
1208 local value = AbstractValue.cfgvalue(self, section)
1209 if not DDNS.has_proxy and value ~= "" then
1210 self.description = bold_on .. font_red ..
1211 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1212 translate("please remove entry") .. "!" .. bold_off
1214 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1215 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1216 translate("IPv6 address must be given in square brackets") .. ": " ..
1217 bold_on .. " [2001:db8::1]:8080" .. bold_off
1221 function pxy.validate(self, value)
1222 -- if .datatype is set, then it is checked before calling this function
1223 if not value or (#value == 0) then
1224 return "" -- ignore on empty
1225 elseif DDNS.has_proxy then
1226 local ipv6 = usev6:formvalue(section) or "0"
1227 local force = fipv:formvalue(section) or "0"
1228 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] ..
1229 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1230 local ret = SYS.call(command)
1231 if ret == 0 then return value
1232 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1233 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1234 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1235 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1236 else return nil, err_tab_adv(self) .. translate("unspecific error")
1239 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1242 function pxy.parse(self, section, novld)
1243 DDNS.value_parse(self, section, novld)
1247 -- use_syslog -- ##############################################################
1248 slog = ns:taboption("advanced", ListValue, "use_syslog",
1249 translate("Log to syslog"),
1250 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1252 slog:value("0", translate("No logging"))
1253 slog:value("1", translate("Info"))
1254 slog:value("2", translate("Notice"))
1255 slog:value("3", translate("Warning"))
1256 slog:value("4", translate("Error"))
1257 function slog.parse(self, section, novld)
1258 DDNS.value_parse(self, section, novld)
1261 -- use_logfile -- #############################################################
1262 logf = ns:taboption("advanced", Flag, "use_logfile",
1263 translate("Log to file"),
1264 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1265 translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] )
1266 logf.orientation = "horizontal"
1267 logf.default = "1" -- if not defined write to log by default
1269 -- TAB: Timer ####################################################################################
1270 -- check_interval -- ##########################################################
1271 ci = ns:taboption("timer", Value, "check_interval",
1272 translate("Check Interval") )
1273 ci.template = "ddns/detail_value"
1275 function ci.validate(self, value)
1276 if not DTYP.uinteger(value)
1277 or tonumber(value) < 1 then
1278 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1281 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1285 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1288 function ci.write(self, section, value)
1289 -- remove when default
1290 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1291 if secs ~= 600 then --default 10 minutes
1292 return self.map:set(section, self.option, value)
1294 self.map:del(section, "check_unit")
1295 return self.map:del(section, self.option)
1298 function ci.parse(self, section, novld)
1299 DDNS.value_parse(self, section, novld)
1302 -- check_unit -- ##############################################################
1303 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1304 translate("Interval to check for changed IP" .. "<br />" ..
1305 "Values below 5 minutes == 300 seconds are not supported") )
1306 cu.template = "ddns/detail_lvalue"
1307 cu.default = "minutes"
1308 cu:value("seconds", translate("seconds"))
1309 cu:value("minutes", translate("minutes"))
1310 cu:value("hours", translate("hours"))
1311 --cu:value("days", translate("days"))
1312 function cu.write(self, section, value)
1313 -- remove when default
1314 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1315 if secs ~= 600 then --default 10 minutes
1316 return self.map:set(section, self.option, value)
1321 function cu.parse(self, section, novld)
1322 DDNS.value_parse(self, section, novld)
1325 -- force_interval (modified) -- ###############################################
1326 fi = ns:taboption("timer", Value, "force_interval",
1327 translate("Force Interval") )
1328 fi.template = "ddns/detail_value"
1329 fi.default = "72" -- see dynamic_dns_updater.sh script
1330 --fi.rmempty = false -- validate ourselves for translatable error messages
1331 function fi.validate(self, value)
1332 if not DTYP.uinteger(value)
1333 or tonumber(value) < 0 then
1334 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1337 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1338 if force_s == 0 then
1342 local ci_value = ci:formvalue(section)
1343 if not DTYP.uinteger(ci_value) then
1344 return "" -- ignore because error in check_interval above
1347 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1348 if force_s >= check_s then
1352 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1354 function fi.write(self, section, value)
1355 -- simulate rmempty=true remove default
1356 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1357 if secs ~= 259200 then --default 72 hours == 3 days
1358 return self.map:set(section, self.option, value)
1360 self.map:del(section, "force_unit")
1361 return self.map:del(section, self.option)
1364 function fi.parse(self, section, novld)
1365 DDNS.value_parse(self, section, novld)
1368 -- force_unit -- ##############################################################
1369 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1370 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1371 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1372 "Values lower 'Check Interval' except '0' are not supported") )
1373 fu.template = "ddns/detail_lvalue"
1374 fu.default = "hours"
1375 --fu.rmempty = false -- want to control write process
1376 --fu:value("seconds", translate("seconds"))
1377 fu:value("minutes", translate("minutes"))
1378 fu:value("hours", translate("hours"))
1379 fu:value("days", translate("days"))
1380 function fu.write(self, section, value)
1381 -- simulate rmempty=true remove default
1382 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1383 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1384 return self.map:set(section, self.option, value)
1389 function fu.parse(self, section, novld)
1390 DDNS.value_parse(self, section, novld)
1393 -- retry_count -- #############################################################
1394 rc = ns:taboption("timer", Value, "retry_count")
1395 rc.title = translate("Error Retry Counter")
1396 rc.description = translate("On Error the script will stop execution after given number of retrys")
1398 .. translate("The default setting of '0' will retry infinite.")
1400 function rc.validate(self, value)
1401 if not DTYP.uinteger(value) then
1402 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1407 function rc.parse(self, section, novld)
1408 DDNS.value_parse(self, section, novld)
1411 -- retry_interval -- ##########################################################
1412 ri = ns:taboption("timer", Value, "retry_interval",
1413 translate("Error Retry Interval") )
1414 ri.template = "ddns/detail_value"
1416 function ri.validate(self, value)
1417 if not DTYP.uinteger(value)
1418 or tonumber(value) < 1 then
1419 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1424 function ri.write(self, section, value)
1425 -- simulate rmempty=true remove default
1426 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1427 if secs ~= 60 then --default 60seconds
1428 return self.map:set(section, self.option, value)
1430 self.map:del(section, "retry_unit")
1431 return self.map:del(section, self.option)
1434 function ri.parse(self, section, novld)
1435 DDNS.value_parse(self, section, novld)
1438 -- retry_unit -- ##############################################################
1439 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1440 translate("On Error the script will retry the failed action after given time") )
1441 ru.template = "ddns/detail_lvalue"
1442 ru.default = "seconds"
1443 --ru.rmempty = false -- want to control write process
1444 ru:value("seconds", translate("seconds"))
1445 ru:value("minutes", translate("minutes"))
1446 --ru:value("hours", translate("hours"))
1447 --ru:value("days", translate("days"))
1448 function ru.write(self, section, value)
1449 -- simulate rmempty=true remove default
1450 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1451 if secs ~= 60 then --default 60seconds
1452 return self.map:set(section, self.option, value)
1454 return true -- will be deleted by retry_interval
1457 function ru.parse(self, section, novld)
1458 DDNS.value_parse(self, section, novld)
1461 -- TAB: LogView ##################################################################################
1462 lv = ns:taboption("logview", DummyValue, "_logview")
1463 lv.template = "ddns/detail_logview"
1464 lv.inputtitle = translate("Read / Reread log file")
1466 function lv.cfgvalue(self, section)
1467 local lfile=log_dir .. "/" .. section .. ".log"
1468 if NXFS.access(lfile) then
1469 return lfile .. "\n" .. translate("Please press [Read] button")
1471 return lfile .. "\n" .. translate("File not found or empty")