Merge pull request #534 from yousong/add-luci-proto-pppossh
[project/luci.git] / applications / luci-app-ddns / luasrc / model / cbi / ddns / detail.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
3 -- Copyright 2013 Manuel Munz <freifunk at somakoma dot de>
4 -- Copyright 2014-2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
5 -- Licensed to the public under the Apache License 2.0.
6
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
17
18 -- takeover arguments -- #######################################################
19 local section = arg[1]
20
21 -- check supported options -- ##################################################
22 -- saved to local vars here because doing multiple os calls slow down the system
23 local has_ipv6 = DDNS.check_ipv6() -- IPv6 support
24 local has_ssl = DDNS.check_ssl() -- HTTPS support
25 local has_proxy = DDNS.check_proxy() -- Proxy support
26 local has_dnstcp = DDNS.check_bind_host() -- DNS TCP support
27 local has_force = has_ssl and has_dnstcp -- Force IP Protocoll
28
29 -- html constants -- ###########################################################
30 local font_red = "<font color='red'>"
31 local font_off = "</font>"
32 local bold_on = "<strong>"
33 local bold_off = "</strong>"
34
35 -- error text constants -- #####################################################
36 local err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
37 translate("please select 'IPv4' address version")
38 local err_ipv6_basic = bold_on ..
39 font_red ..
40 translate("IPv6 not supported") ..
41 font_off ..
42 "<br />" .. translate("please select 'IPv4' address version") ..
43 bold_off
44 local err_ipv6_other = bold_on ..
45 font_red ..
46 translate("IPv6 not supported") ..
47 font_off ..
48 "<br />" .. translate("please select 'IPv4' address version in") .. " " ..
49 [[<a href="]] ..
50 DISP.build_url("admin", "services", "ddns", "detail", section) ..
51 "?tab.dns." .. section .. "=basic" ..
52 [[">]] ..
53 translate("Basic Settings") ..
54 [[</a>]] ..
55 bold_off
56
57 local function err_tab_basic(self)
58 return translate("Basic Settings") .. " - " .. self.title .. ": "
59 end
60 local function err_tab_adv(self)
61 return translate("Advanced Settings") .. " - " .. self.title .. ": "
62 end
63 local function err_tab_timer(self)
64 return translate("Timer Settings") .. " - " .. self.title .. ": "
65 end
66
67 -- read services/services_ipv6 files -- ########################################
68 local services4 = { } -- IPv4 --
69 local fd4 = io.open("/usr/lib/ddns/services", "r")
70 if fd4 then
71 local ln, s, t
72 repeat
73 ln = fd4:read("*l")
74 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
75 s = s and s:gsub('"','') -- remove "
76 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
77 if t then services4[t[1]]=t[2] end
78 until not ln
79 fd4:close()
80 end
81
82 local services6 = { } -- IPv6 --
83 local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r")
84 if fd6 then
85 local ln, s, t
86 repeat
87 ln = fd6:read("*l")
88 s = ln and ln:match('^%s*".*') -- only handle lines beginning with "
89 s = s and s:gsub('"','') -- remove "
90 t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces
91 if t then services6[t[1]]=t[2] end
92 until not ln
93 fd6:close()
94 end
95
96 -- multi-used functions -- ####################################################
97 -- function to verify settings around ip_source
98 -- will use dynamic_dns_lucihelper to check if
99 -- local IP can be read
100 local function _verify_ip_source()
101 -- section is globally defined here be calling agrument (see above)
102 local _network = "-"
103 local _url = "-"
104 local _interface = "-"
105 local _script = "-"
106 local _proxy = ""
107
108 local _ipv6 = usev6:formvalue(section)
109 local _source = (_ipv6 == "1")
110 and src6:formvalue(section)
111 or src4:formvalue(section)
112 if _source == "network" then
113 _network = (_ipv6 == "1")
114 and ipn6:formvalue(section)
115 or ipn4:formvalue(section)
116 elseif _source == "web" then
117 _url = (_ipv6 == "1")
118 and iurl6:formvalue(section)
119 or iurl4:formvalue(section)
120 -- proxy only needed for checking url
121 _proxy = (pxy) and pxy:formvalue(section) or ""
122 elseif _source == "interface" then
123 _interface = ipi:formvalue(section)
124 elseif _source == "script" then
125 _script = ips:formvalue(section)
126 end
127
128 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] ..
129 _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] ..
130 _url .. [[ ]] .. _interface .. [[ ']] .. _script.. [[' ]] .. _proxy
131 local ret = SYS.call(command)
132
133 if ret == 0 then
134 return true -- valid
135 else
136 return nil -- invalid
137 end
138 end
139
140 -- function to check if option is used inside url or script
141 -- return -1 on error, 0 NOT required, 1 required
142 local function _option_used(option, urlscript)
143 local surl -- search string for url
144 local ssh -- search string for script
145 local required -- option used inside url or script
146
147 if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain'
148 elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username'
149 elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password'
150 elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc'
151 elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt'
152 else
153 error("undefined option")
154 return -1 -- return on error
155 end
156
157 local required = false
158 -- handle url
159 if urlscript:find('http') then
160 required = ( urlscript:find(surl) )
161 -- handle script
162 else
163 if not urlscript:find("/") then
164 -- might be inside ddns-scripts directory
165 urlscript = "/usr/lib/ddns/" .. urlscript
166 end
167 -- problem with script exit here
168 if not NXFS.access(urlscript) then return -1 end
169
170 local f = io.input(urlscript)
171 -- still problem with script exit here
172 if not f then return -1 end
173 for l in f:lines() do
174 repeat
175 if l:find('^#') then break end -- continue on comment lines
176 required = ( l:find(surl) or l:find(ssh) )
177 until true
178 if required then break end
179 end
180 f:close()
181 end
182 return (required and 1 or 0)
183 end
184
185 -- function to verify if option is valid
186 local function _option_validate(self, value)
187 -- section is globally defined here be calling agrument (see above)
188 local fusev6 = usev6:formvalue(section)
189 local fsvc4 = svc4:formvalue(section)
190 local fsvc6 = svc6:formvalue(section)
191 local urlsh, used
192
193 -- IP-Version dependent custom service selected
194 if (fusev6 == "0" and fsvc4 == "-") or
195 (fusev6 == "1" and fsvc6 == "-") then
196 -- read custom url
197 urlsh = uurl:formvalue(section)
198 -- no url then read custom script
199 if not urlsh or (#urlsh == 0) then
200 urlsh = ush:formvalue(section)
201 end
202 -- IPv4 read from services4 table
203 elseif (fusev6 == "0") then
204 urlsh = services4[fsvc4]
205 -- IPv6 read from services6 table
206 else
207 urlsh = services6[fsvc6]
208 end
209 -- problem with url or script exit here
210 -- error handled somewhere else
211 if not urlsh or (#urlsh == 0) then return "" end
212
213 used = _option_used(self.option, urlsh)
214 -- on error or not used return empty sting
215 if used < 1 then return "" end
216 -- needed but no data then return error
217 if not value or (#value == 0) then
218 return nil, err_tab_basic(self) .. translate("missing / required")
219 end
220 return value
221 end
222
223 -- cbi-map definition -- #######################################################
224 local m = Map("ddns")
225 m.title = CTRL.app_title_back()
226 m.description = CTRL.app_description()
227 m.redirect = DISP.build_url("admin", "services", "ddns")
228
229 m.on_after_commit = function(self)
230 if self.changed then -- changes ?
231 local pid = DDNS.get_pid(section)
232 if pid > 0 then -- running ?
233 local tmp = NX.kill(pid, 1) -- send SIGHUP
234 end
235 end
236 end
237
238 -- provider switch was requested, save and reload page
239 if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1]
240 local fsvc
241 local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section)
242 if fusev6 == "1" then
243 fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section)
244 else
245 fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section)
246 end
247
248 if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed
249 m:set(section, "use_ipv6", fusev6) -- save it
250 end
251
252 if fsvc ~= "-" then -- NOT "custom"
253 m:set(section, "service_name", fsvc) -- save it
254 else -- else
255 m:del(section, "service_name") -- delete it
256 end
257 m.uci:save(m.config)
258
259 -- reload page
260 HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) )
261 return
262 end
263
264 -- read application settings -- ################################################
265 -- date format; if not set use ISO format
266 local date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
267 -- log directory
268 local log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
269
270 -- cbi-section definition -- ###################################################
271 local ns = m:section( NamedSection, section, "service",
272 translate("Details for") .. ([[: <strong>%s</strong>]] % section),
273 translate("Configure here the details for selected Dynamic DNS service.") )
274 ns.instance = section -- arg [1]
275 ns:tab("basic", translate("Basic Settings"), nil )
276 ns:tab("advanced", translate("Advanced Settings"), nil )
277 ns:tab("timer", translate("Timer Settings"), nil )
278 ns:tab("logview", translate("Log File Viewer"), nil )
279
280 -- TAB: Basic #####################################################################################
281 -- enabled -- #################################################################
282 en = ns:taboption("basic", Flag, "enabled",
283 translate("Enabled"),
284 translate("If this service section is disabled it could not be started." .. "<br />" ..
285 "Neither from LuCI interface nor from console") )
286 en.orientation = "horizontal"
287
288 -- IPv4/IPv6 - lookup_host -- #################################################
289 luh = ns:taboption("basic", Value, "lookup_host",
290 translate("Lookup Hostname"),
291 translate("Hostname/FQDN to validate, if IP update happen or necessary") )
292 luh.rmempty = false
293 luh.placeholder = "myhost.example.com"
294 function luh.validate(self, value)
295 if not value
296 or not (#value > 0)
297 or not DTYP.hostname(value) then
298 return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'"
299 else
300 return UTIL.trim(value)
301 end
302 end
303 function luh.parse(self, section, novld)
304 DDNS.value_parse(self, section, novld)
305 end
306
307 -- use_ipv6 -- ################################################################
308 usev6 = ns:taboption("basic", ListValue, "use_ipv6",
309 translate("IP address version"),
310 translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
311 usev6.widget = "radio"
312 usev6.default = "0"
313 usev6:value("0", translate("IPv4-Address") )
314 function usev6.cfgvalue(self, section)
315 local value = AbstractValue.cfgvalue(self, section)
316 if has_ipv6 or (value == "1" and not has_ipv6) then
317 self:value("1", translate("IPv6-Address") )
318 end
319 if value == "1" and not has_ipv6 then
320 self.description = err_ipv6_basic
321 end
322 return value
323 end
324 function usev6.validate(self, value)
325 if (value == "1" and has_ipv6) or value == "0" then
326 return value
327 end
328 return nil, err_tab_basic(self) .. err_ipv6_plain
329 end
330 function usev6.parse(self, section, novld)
331 DDNS.value_parse(self, section, novld)
332 end
333
334 -- IPv4 - service_name -- #####################################################
335 svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
336 translate("DDNS Service provider") .. " [IPv4]" )
337 svc4.default = "-"
338 svc4:depends("use_ipv6", "0") -- only show on IPv4
339 function svc4.cfgvalue(self, section)
340 local v = DDNS.read_value(self, section, "service_name")
341 if not v or #v == 0 then
342 return "-"
343 else
344 return v
345 end
346 end
347 function svc4.validate(self, value)
348 if usev6:formvalue(section) == "0" then -- do only on IPv4
349 return value
350 else
351 return "" -- supress validate error
352 end
353 end
354 function svc4.write(self, section, value)
355 if usev6:formvalue(section) == "0" then -- do only IPv4 here
356 self.map:del(section, self.option) -- to be shure
357 if value ~= "-" then -- and write "service_name
358 self.map:del(section, "update_url") -- delete update_url
359 self.map:del(section, "update_script") -- delete update_script
360 return self.map:set(section, "service_name", value)
361 else
362 return self.map:del(section, "service_name")
363 end
364 end
365 end
366 function svc4.parse(self, section, novld)
367 DDNS.value_parse(self, section, novld)
368 end
369
370 -- IPv6 - service_name -- #####################################################
371 svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
372 translate("DDNS Service provider") .. " [IPv6]" )
373 svc6.default = "-"
374 svc6:depends("use_ipv6", "1") -- only show on IPv6
375 if not has_ipv6 then
376 svc6.description = err_ipv6_basic
377 end
378 function svc6.cfgvalue(self, section)
379 local v = DDNS.read_value(self, section, "service_name")
380 if not v or #v == 0 then
381 return "-"
382 else
383 return v
384 end
385 end
386 function svc6.validate(self, value)
387 if usev6:formvalue(section) == "1" then -- do only on IPv6
388 if has_ipv6 then return value end
389 return nil, err_tab_basic(self) .. err_ipv6_plain
390 else
391 return "" -- supress validate error
392 end
393 end
394 function svc6.write(self, section, value)
395 if usev6:formvalue(section) == "1" then -- do only when IPv6
396 self.map:del(section, self.option) -- delete "ipv6_service_name" helper
397 if value ~= "-" then -- and write "service_name
398 self.map:del(section, "update_url") -- delete update_url
399 self.map:del(section, "update_script") -- delete update_script
400 return self.map:set(section, "service_name", value)
401 else
402 return self.map:del(section, "service_name")
403 end
404 end
405 end
406 function svc6.parse(self, section, novld)
407 DDNS.value_parse(self, section, novld)
408 end
409
410 -- IPv4/IPv6 - change Provider -- #############################################
411 svs = ns:taboption("basic", Button, "_switch")
412 svs.title = translate("Really change DDNS provider?")
413 svs.inputtitle = translate("Change provider")
414 svs.inputstyle = "apply"
415
416 -- IPv4/IPv6 - update_url -- ##################################################
417 uurl = ns:taboption("basic", Value, "update_url",
418 translate("Custom update-URL"),
419 translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
420 "Follow instructions you will find on their WEB page.") )
421 function uurl.validate(self, value)
422 local fush = ush:formvalue(section)
423 local fusev6 = usev6:formvalue(section)
424
425 if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
426 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
427 return "" -- suppress validate error
428 elseif not value then
429 if not fush or (#fush == 0) then
430 return nil, err_tab_basic(self) .. translate("missing / required")
431 else
432 return "" -- suppress validate error / update_script is given
433 end
434 elseif (#fush > 0) then
435 return nil, err_tab_basic(self) .. translate("either url or script could be set")
436 end
437
438 local url = DDNS.parse_url(value)
439 if not url.scheme == "http" then
440 return nil, err_tab_basic(self) .. translate("must start with 'http://'")
441 elseif not url.query then
442 return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
443 elseif not url.host then
444 return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
445 elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
446 return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
447 end
448
449 return value
450 end
451 function uurl.parse(self, section, novld)
452 DDNS.value_parse(self, section, novld)
453 end
454
455 -- IPv4/IPv6 - update_script -- ###############################################
456 ush = ns:taboption("basic", Value, "update_script",
457 translate("Custom update-script"),
458 translate("Custom update script to be used for updating your DDNS Provider.") )
459 function ush.validate(self, value)
460 local fuurl = uurl:formvalue(section)
461 local fusev6 = usev6:formvalue(section)
462
463 if (fusev6 == "0" and svc4:formvalue(section) ~= "-") or
464 (fusev6 == "1" and svc6:formvalue(section) ~= "-") then
465 return "" -- suppress validate error
466 elseif not value then
467 if not fuurl or (#fuurl == 0) then
468 return nil, err_tab_basic(self) .. translate("missing / required")
469 else
470 return "" -- suppress validate error / update_url is given
471 end
472 elseif (#fuurl > 0) then
473 return nil, err_tab_basic(self) .. translate("either url or script could be set")
474 elseif not NXFS.access(value) then
475 return nil, err_tab_basic(self) .. translate("File not found")
476 end
477 return value
478 end
479 function ush.parse(self, section, novld)
480 DDNS.value_parse(self, section, novld)
481 end
482
483 -- IPv4/IPv6 - domain -- ######################################################
484 dom = ns:taboption("basic", Value, "domain",
485 translate("Domain"),
486 translate("Replaces [DOMAIN] in Update-URL") )
487 dom.placeholder = "myhost.example.com"
488 function dom.validate(self, value)
489 return _option_validate(self, value)
490 end
491 function dom.parse(self, section, novld)
492 DDNS.value_parse(self, section, novld)
493 end
494
495 -- IPv4/IPv6 - username -- ####################################################
496 user = ns:taboption("basic", Value, "username",
497 translate("Username"),
498 translate("Replaces [USERNAME] in Update-URL (URL-encoded)") )
499 function user.validate(self, value)
500 return _option_validate(self, value)
501 end
502 function user.parse(self, section, novld)
503 DDNS.value_parse(self, section, novld)
504 end
505
506 -- IPv4/IPv6 - password -- ####################################################
507 pw = ns:taboption("basic", Value, "password",
508 translate("Password"),
509 translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") )
510 pw.password = true
511 function pw.validate(self, value)
512 return _option_validate(self, value)
513 end
514 function pw.parse(self, section, novld)
515 DDNS.value_parse(self, section, novld)
516 end
517
518 -- IPv4/IPv6 - param_enc -- ###################################################
519 pe = ns:taboption("basic", Value, "param_enc",
520 translate("Optional Encoded Parameter"),
521 translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") )
522 function pe.validate(self, value)
523 return _option_validate(self, value)
524 end
525 function pe.parse(self, section, novld)
526 DDNS.value_parse(self, section, novld)
527 end
528
529 -- IPv4/IPv6 - param_enc -- ###################################################
530 po = ns:taboption("basic", Value, "param_opt",
531 translate("Optional Parameter"),
532 translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") )
533 function po.validate(self, value)
534 return _option_validate(self, value)
535 end
536 function po.parse(self, section, novld)
537 DDNS.value_parse(self, section, novld)
538 end
539
540 -- handled service dependent show/display -- ##################################
541 -- IPv4 --
542 local cv4 = svc4:cfgvalue(section)
543 if cv4 ~= "-" then
544 svs:depends ("ipv4_service_name", "-" ) -- show only if "-"
545 ush:depends ("ipv4_service_name", "?")
546 uurl:depends("ipv4_service_name", "?")
547 else
548 uurl:depends("ipv4_service_name", "-")
549 ush:depends ("ipv4_service_name", "-")
550 dom:depends("ipv4_service_name", "-" )
551 user:depends("ipv4_service_name", "-" )
552 pw:depends("ipv4_service_name", "-" )
553 pe:depends("ipv4_service_name", "-" )
554 po:depends("ipv4_service_name", "-" )
555 end
556 for s, u in UTIL.kspairs(services4) do
557 svc4:value(s) -- fill DropDown-List
558 if cv4 ~= s then
559 svs:depends("ipv4_service_name", s )
560 else
561 dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
562 user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
563 pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
564 pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
565 po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
566 end
567 end
568 svc4:value("-", translate("-- custom --") )
569
570 -- IPv6 --
571 local cv6 = svc6:cfgvalue(section)
572 if cv6 ~= "-" then
573 svs:depends ("ipv6_service_name", "-" )
574 uurl:depends("ipv6_service_name", "?")
575 ush:depends ("ipv6_service_name", "?")
576 else
577 uurl:depends("ipv6_service_name", "-")
578 ush:depends ("ipv6_service_name", "-")
579 dom:depends("ipv6_service_name", "-" )
580 user:depends("ipv6_service_name", "-" )
581 pw:depends("ipv6_service_name", "-" )
582 pe:depends("ipv6_service_name", "-" )
583 po:depends("ipv6_service_name", "-" )
584 end
585 for s, u in UTIL.kspairs(services6) do
586 svc6:value(s) -- fill DropDown-List
587 if cv6 ~= s then
588 svs:depends("ipv6_service_name", s )
589 else
590 dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") )
591 user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") )
592 pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") )
593 pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") )
594 po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") )
595 end
596 end
597 svc6:value("-", translate("-- custom --") )
598
599 -- IPv4/IPv6 - use_https -- ###################################################
600 if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
601 https = ns:taboption("basic", Flag, "use_https",
602 translate("Use HTTP Secure") )
603 https.orientation = "horizontal"
604 function https.cfgvalue(self, section)
605 local value = AbstractValue.cfgvalue(self, section)
606 if not has_ssl and value == "1" then
607 self.description = bold_on .. font_red ..
608 translate("HTTPS not supported") .. font_off .. "<br />" ..
609 translate("please disable") .. " !" .. bold_off
610 else
611 self.description = translate("Enable secure communication with DDNS provider")
612 end
613 return value
614 end
615 function https.validate(self, value)
616 if (value == "1" and has_ssl ) or value == "0" then return value end
617 return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
618 end
619 function https.write(self, section, value)
620 if value == "1" then
621 return self.map:set(section, self.option, value)
622 else
623 self.map:del(section, "cacert")
624 return self.map:del(section, self.option)
625 end
626 end
627 end
628
629 -- IPv4/IPv6 - cacert -- ######################################################
630 if has_ssl then
631 cert = ns:taboption("basic", Value, "cacert",
632 translate("Path to CA-Certificate"),
633 translate("directory or path/file") .. "<br />" ..
634 translate("or") .. bold_on .. " IGNORE " .. bold_off ..
635 translate("to run HTTPS without verification of server certificates (insecure)") )
636 cert:depends("use_https", "1")
637 cert.default = "/etc/ssl/certs"
638 cert.forcewrite = true
639 function cert.validate(self, value)
640 if https:formvalue(section) == "0" then
641 return "" -- supress validate error if NOT https
642 end
643 if value then -- otherwise errors in datatype check
644 if DTYP.directory(value)
645 or DTYP.file(value)
646 or value == "IGNORE" then
647 return value
648 end
649 end
650 return nil, err_tab_basic(self) ..
651 translate("file or directory not found or not 'IGNORE'") .. " !"
652 end
653 function cert.parse(self, section, novld)
654 DDNS.value_parse(self, section, novld)
655 end
656 end
657
658 -- TAB: Advanced #################################################################################
659 -- IPv4 - ip_source -- ########################################################
660 src4 = ns:taboption("advanced", ListValue, "ipv4_source",
661 translate("IP address source") .. " [IPv4]",
662 translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
663 src4:depends("use_ipv6", "0") -- IPv4 selected
664 src4.default = "network"
665 src4:value("network", translate("Network"))
666 src4:value("web", translate("URL"))
667 src4:value("interface", translate("Interface"))
668 src4:value("script", translate("Script"))
669 function src4.cfgvalue(self, section)
670 return DDNS.read_value(self, section, "ip_source")
671 end
672 function src4.validate(self, value)
673 if usev6:formvalue(section) == "1" then
674 return "" -- ignore on IPv6 selected
675 elseif not _verify_ip_source() then
676 return nil, err_tab_adv(self) ..
677 translate("can not detect local IP. Please select a different Source combination")
678 else
679 return value
680 end
681 end
682 function src4.write(self, section, value)
683 if usev6:formvalue(section) == "1" then
684 return true -- ignore on IPv6 selected
685 elseif value == "network" then
686 self.map:del(section, "ip_url") -- delete not need parameters
687 self.map:del(section, "ip_interface")
688 self.map:del(section, "ip_script")
689 elseif value == "web" then
690 self.map:del(section, "ip_network") -- delete not need parameters
691 self.map:del(section, "ip_interface")
692 self.map:del(section, "ip_script")
693 elseif value == "interface" then
694 self.map:del(section, "ip_network") -- delete not need parameters
695 self.map:del(section, "ip_url")
696 self.map:del(section, "ip_script")
697 elseif value == "script" then
698 self.map:del(section, "ip_network")
699 self.map:del(section, "ip_url") -- delete not need parameters
700 self.map:del(section, "ip_interface")
701 end
702 self.map:del(section, self.option) -- delete "ipv4_source" helper
703 return self.map:set(section, "ip_source", value) -- and write "ip_source
704 end
705 function src4.parse(self, section, novld)
706 DDNS.value_parse(self, section, novld)
707 end
708
709 -- IPv6 - ip_source -- ########################################################
710 src6 = ns:taboption("advanced", ListValue, "ipv6_source",
711 translate("IP address source") .. " [IPv6]",
712 translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
713 src6:depends("use_ipv6", 1) -- IPv6 selected
714 src6.default = "network"
715 src6:value("network", translate("Network"))
716 src6:value("web", translate("URL"))
717 src6:value("interface", translate("Interface"))
718 src6:value("script", translate("Script"))
719 if not has_ipv6 then
720 src6.description = err_ipv6_other
721 end
722 function src6.cfgvalue(self, section)
723 return DDNS.read_value(self, section, "ip_source")
724 end
725 function src6.validate(self, value)
726 if usev6:formvalue(section) == "0" then
727 return "" -- ignore on IPv4 selected
728 elseif not has_ipv6 then
729 return nil, err_tab_adv(self) .. err_ipv6_plain
730 elseif not _verify_ip_source() then
731 return nil, err_tab_adv(self) ..
732 translate("can not detect local IP. Please select a different Source combination")
733 else
734 return value
735 end
736 end
737 function src6.write(self, section, value)
738 if usev6:formvalue(section) == "0" then
739 return true -- ignore on IPv4 selected
740 elseif value == "network" then
741 self.map:del(section, "ip_url") -- delete not need parameters
742 self.map:del(section, "ip_interface")
743 self.map:del(section, "ip_script")
744 elseif value == "web" then
745 self.map:del(section, "ip_network") -- delete not need parameters
746 self.map:del(section, "ip_interface")
747 self.map:del(section, "ip_script")
748 elseif value == "interface" then
749 self.map:del(section, "ip_network") -- delete not need parameters
750 self.map:del(section, "ip_url")
751 self.map:del(section, "ip_script")
752 elseif value == "script" then
753 self.map:del(section, "ip_network")
754 self.map:del(section, "ip_url") -- delete not need parameters
755 self.map:del(section, "ip_interface")
756 end
757 self.map:del(section, self.option) -- delete "ipv4_source" helper
758 return self.map:set(section, "ip_source", value) -- and write "ip_source
759 end
760 function src6.parse(self, section, novld)
761 DDNS.value_parse(self, section, novld)
762 end
763
764 -- IPv4 - ip_network (default "wan") -- #######################################
765 ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
766 translate("Network") .. " [IPv4]",
767 translate("Defines the network to read systems IPv4-Address from") )
768 ipn4:depends("ipv4_source", "network")
769 ipn4.default = "wan"
770 WADM.cbi_add_networks(ipn4)
771 function ipn4.cfgvalue(self, section)
772 return DDNS.read_value(self, section, "ip_network")
773 end
774 function ipn4.validate(self, value)
775 if usev6:formvalue(section) == "1"
776 or src4:formvalue(section) ~= "network" then
777 -- ignore if IPv6 selected OR
778 -- ignore everything except "network"
779 return ""
780 else
781 return value
782 end
783 end
784 function ipn4.write(self, section, value)
785 if usev6:formvalue(section) == "1"
786 or src4:formvalue(section) ~= "network" then
787 -- ignore if IPv6 selected OR
788 -- ignore everything except "network"
789 return true
790 else
791 -- set also as "interface" for monitoring events changes/hot-plug
792 self.map:set(section, "interface", value)
793 self.map:del(section, self.option) -- delete "ipv4_network" helper
794 return self.map:set(section, "ip_network", value) -- and write "ip_network"
795 end
796 end
797 function ipn4.parse(self, section, novld)
798 DDNS.value_parse(self, section, novld)
799 end
800
801 -- IPv6 - ip_network (default "wan6") -- ######################################
802 ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
803 translate("Network") .. " [IPv6]" )
804 ipn6:depends("ipv6_source", "network")
805 ipn6.default = "wan6"
806 WADM.cbi_add_networks(ipn6)
807 if has_ipv6 then
808 ipn6.description = translate("Defines the network to read systems IPv6-Address from")
809 else
810 ipn6.description = err_ipv6_other
811 end
812 function ipn6.cfgvalue(self, section)
813 return DDNS.read_value(self, section, "ip_network")
814 end
815 function ipn6.validate(self, value)
816 if usev6:formvalue(section) == "0"
817 or src6:formvalue(section) ~= "network" then
818 -- ignore if IPv4 selected OR
819 -- ignore everything except "network"
820 return ""
821 elseif has_ipv6 then
822 return value
823 else
824 return nil, err_tab_adv(self) .. err_ipv6_plain
825 end
826 end
827 function ipn6.write(self, section, value)
828 if usev6:formvalue(section) == "0"
829 or src6:formvalue(section) ~= "network" then
830 -- ignore if IPv4 selected OR
831 -- ignore everything except "network"
832 return true
833 else
834 -- set also as "interface" for monitoring events changes/hotplug
835 self.map:set(section, "interface", value)
836 self.map:del(section, self.option) -- delete "ipv6_network" helper
837 return self.map:set(section, "ip_network", value) -- and write "ip_network"
838 end
839 end
840 function ipn6.parse(self, section, novld)
841 DDNS.value_parse(self, section, novld)
842 end
843
844 -- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################
845 iurl4 = ns:taboption("advanced", Value, "ipv4_url",
846 translate("URL to detect") .. " [IPv4]",
847 translate("Defines the Web page to read systems IPv4-Address from") )
848 iurl4:depends("ipv4_source", "web")
849 iurl4.default = "http://checkip.dyndns.com"
850 function iurl4.cfgvalue(self, section)
851 return DDNS.read_value(self, section, "ip_url")
852 end
853 function iurl4.validate(self, value)
854 if usev6:formvalue(section) == "1"
855 or src4:formvalue(section) ~= "web" then
856 -- ignore if IPv6 selected OR
857 -- ignore everything except "web"
858 return ""
859 elseif not value or #value == 0 then
860 return nil, err_tab_adv(self) .. translate("missing / required")
861 end
862
863 local url = DDNS.parse_url(value)
864 if not (url.scheme == "http" or url.scheme == "https") then
865 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
866 elseif not url.host then
867 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
868 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
869 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
870 else
871 return value
872 end
873 end
874 function iurl4.write(self, section, value)
875 if usev6:formvalue(section) == "1"
876 or src4:formvalue(section) ~= "web" then
877 -- ignore if IPv6 selected OR
878 -- ignore everything except "web"
879 return true
880 else
881 self.map:del(section, self.option) -- delete "ipv4_url" helper
882 return self.map:set(section, "ip_url", value) -- and write "ip_url"
883 end
884 end
885 function iurl4.parse(self, section, novld)
886 DDNS.value_parse(self, section, novld)
887 end
888
889 -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ##########################
890 iurl6 = ns:taboption("advanced", Value, "ipv6_url",
891 translate("URL to detect") .. " [IPv6]" )
892 iurl6:depends("ipv6_source", "web")
893 iurl6.default = "http://checkipv6.dyndns.com"
894 if has_ipv6 then
895 iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
896 else
897 iurl6.description = err_ipv6_other
898 end
899 function iurl6.cfgvalue(self, section)
900 return DDNS.read_value(self, section, "ip_url")
901 end
902 function iurl6.validate(self, value)
903 if usev6:formvalue(section) == "0"
904 or src6:formvalue(section) ~= "web" then
905 -- ignore if IPv4 selected OR
906 -- ignore everything except "web"
907 return ""
908 elseif not has_ipv6 then
909 return nil, err_tab_adv(self) .. err_ipv6_plain
910 elseif not value or #value == 0 then
911 return nil, err_tab_adv(self) .. translate("missing / required")
912 end
913
914 local url = DDNS.parse_url(value)
915 if not (url.scheme == "http" or url.scheme == "https") then
916 return nil, err_tab_adv(self) .. translate("must start with 'http://'")
917 elseif not url.host then
918 return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
919 elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
920 return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
921 else
922 return value
923 end
924 end
925 function iurl6.write(self, section, value)
926 if usev6:formvalue(section) == "0"
927 or src6:formvalue(section) ~= "web" then
928 -- ignore if IPv4 selected OR
929 -- ignore everything except "web"
930 return true
931 else
932 self.map:del(section, self.option) -- delete "ipv6_url" helper
933 return self.map:set(section, "ip_url", value) -- and write "ip_url"
934 end
935 end
936 function iurl6.parse(self, section, novld)
937 DDNS.value_parse(self, section, novld)
938 end
939
940 -- IPv4 + IPv6 - ip_interface -- ##############################################
941 ipi = ns:taboption("advanced", ListValue, "ip_interface",
942 translate("Interface"),
943 translate("Defines the interface to read systems IP-Address from") )
944 ipi:depends("ipv4_source", "interface") -- IPv4
945 ipi:depends("ipv6_source", "interface") -- or IPv6
946 for _, v in pairs(SYS.net.devices()) do
947 -- show only interface set to a network
948 -- and ignore loopback
949 net = WADM.iface_get_network(v)
950 if net and net ~= "loopback" then
951 ipi:value(v)
952 end
953 end
954 function ipi.validate(self, value)
955 local fusev6 = usev6:formvalue(section)
956 if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
957 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
958 return ""
959 else
960 return value
961 end
962 end
963 function ipi.write(self, section, value)
964 local fusev6 = usev6:formvalue(section)
965 if (fusev6 == "0" and src4:formvalue(section) ~= "interface")
966 or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then
967 return true
968 else
969 -- get network from device to
970 -- set also as "interface" for monitoring events changes/hotplug
971 local net = WADM.iface_get_network(value)
972 self.map:set(section, "interface", net)
973 return self.map:set(section, self.option, value)
974 end
975 end
976 function ipi.parse(self, section, novld)
977 DDNS.value_parse(self, section, novld)
978 end
979
980 -- IPv4 + IPv6 - ip_script -- #################################################
981 ips = ns:taboption("advanced", Value, "ip_script",
982 translate("Script"),
983 translate("User defined script to read systems IP-Address") )
984 ips:depends("ipv4_source", "script") -- IPv4
985 ips:depends("ipv6_source", "script") -- or IPv6
986 ips.placeholder = "/path/to/script.sh"
987 function ips.validate(self, value)
988 local fusev6 = usev6:formvalue(section)
989 local split
990 if value then split = UTIL.split(value, " ") end
991
992 if (fusev6 == "0" and src4:formvalue(section) ~= "script")
993 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
994 return ""
995 elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
996 return nil, err_tab_adv(self) ..
997 translate("not found or not executable - Sample: '/path/to/script.sh'")
998 else
999 return value
1000 end
1001 end
1002 function ips.write(self, section, value)
1003 local fusev6 = usev6:formvalue(section)
1004 if (fusev6 == "0" and src4:formvalue(section) ~= "script")
1005 or (fusev6 == "1" and src6:formvalue(section) ~= "script") then
1006 return true
1007 else
1008 return self.map:set(section, self.option, value)
1009 end
1010 end
1011 function ips.parse(self, section, novld)
1012 DDNS.value_parse(self, section, novld)
1013 end
1014
1015 -- IPv4 - interface - default "wan" -- ########################################
1016 -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
1017 -- only needs to be set if "ip_source"="web" or "script"
1018 -- if "ip_source"="network" or "interface" we use their network
1019 eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
1020 translate("Event Network") .. " [IPv4]",
1021 translate("Network on which the ddns-updater scripts will be started") )
1022 eif4:depends("ipv4_source", "web")
1023 eif4:depends("ipv4_source", "script")
1024 eif4.default = "wan"
1025 WADM.cbi_add_networks(eif4)
1026 function eif4.cfgvalue(self, section)
1027 return DDNS.read_value(self, section, "interface")
1028 end
1029 function eif4.validate(self, value)
1030 local fsrc4 = src4:formvalue(section)
1031 if usev6:formvalue(section) == "1"
1032 or fsrc4 == "network"
1033 or fsrc4 == "interface" then
1034 return "" -- ignore IPv6, network, interface
1035 else
1036 return value
1037 end
1038 end
1039 function eif4.write(self, section, value)
1040 local fsrc4 = src4:formvalue(section)
1041 if usev6:formvalue(section) == "1"
1042 or fsrc4 == "network"
1043 or fsrc4 == "interface" then
1044 return true -- ignore IPv6, network, interface
1045 else
1046 self.map:del(section, self.option) -- delete "ipv4_interface" helper
1047 return self.map:set(section, "interface", value) -- and write "interface"
1048 end
1049 end
1050 function eif4.parse(self, section, novld)
1051 DDNS.value_parse(self, section, novld)
1052 end
1053
1054 -- IPv6 - interface - default "wan6" -- #######################################
1055 -- event network to monitor changes/hotplug
1056 -- only needs to be set if "ip_source"="web" or "script"
1057 -- if "ip_source"="network" or "interface" we use their network
1058 eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
1059 translate("Event Network") .. " [IPv6]" )
1060 eif6:depends("ipv6_source", "web")
1061 eif6:depends("ipv6_source", "script")
1062 eif6.default = "wan6"
1063 WADM.cbi_add_networks(eif6)
1064 if not has_ipv6 then
1065 eif6.description = err_ipv6_other
1066 else
1067 eif6.description = translate("Network on which the ddns-updater scripts will be started")
1068 end
1069 function eif6.cfgvalue(self, section)
1070 return DDNS.read_value(self, section, "interface")
1071 end
1072 function eif6.validate(self, value)
1073 local fsrc6 = src6:formvalue(section)
1074 if usev6:formvalue(section) == "0"
1075 or fsrc6 == "network"
1076 or fsrc6 == "interface" then
1077 return "" -- ignore IPv4, network, interface
1078 elseif not has_ipv6 then
1079 return nil, err_tab_adv(self) .. err_ipv6_plain
1080 else
1081 return value
1082 end
1083 end
1084 function eif6.write(self, section, value)
1085 local fsrc6 = src6:formvalue(section)
1086 if usev6:formvalue(section) == "0"
1087 or fsrc6 == "network"
1088 or fsrc6 == "interface" then
1089 return true -- ignore IPv4, network, interface
1090 else
1091 self.map:del(section, self.option) -- delete "ipv6_interface" helper
1092 return self.map:set(section, "interface", value) -- and write "interface"
1093 end
1094 end
1095 function eif6.parse(self, section, novld)
1096 DDNS.value_parse(self, section, novld)
1097 end
1098
1099 -- IPv4/IPv6 - bind_network -- ################################################
1100 if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
1101 bnet = ns:taboption("advanced", ListValue, "bind_network",
1102 translate("Bind Network") )
1103 bnet:depends("ipv4_source", "web")
1104 bnet:depends("ipv6_source", "web")
1105 bnet.default = ""
1106 bnet:value("", translate("-- default --"))
1107 WADM.cbi_add_networks(bnet)
1108 function bnet.cfgvalue(self, section)
1109 local value = AbstractValue.cfgvalue(self, section)
1110 if not has_ssl and value ~= "" then
1111 self.description = bold_on .. font_red ..
1112 translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
1113 translate("please set to 'default'") .. " !" .. bold_off
1114 else
1115 self.description = translate("OPTIONAL: Network to use for communication") ..
1116 "<br />" .. translate("Casual users should not change this setting")
1117 end
1118 return value
1119 end
1120 function bnet.validate(self, value)
1121 if (value ~= "" and has_ssl ) or value == "" then return value end
1122 return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
1123 end
1124 function bnet.parse(self, section, novld)
1125 DDNS.value_parse(self, section, novld)
1126 end
1127 end
1128
1129 -- IPv4 + IPv6 - force_ipversion -- ###########################################
1130 -- optional to force wget/curl and host to use only selected IP version
1131 -- command parameter "-4" or "-6"
1132 if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
1133 fipv = ns:taboption("advanced", Flag, "force_ipversion",
1134 translate("Force IP Version") )
1135 fipv.orientation = "horizontal"
1136 function fipv.cfgvalue(self, section)
1137 local value = AbstractValue.cfgvalue(self, section)
1138 if not has_force and value ~= "0" then
1139 self.description = bold_on .. font_red ..
1140 translate("Force IP Version not supported") .. font_off .. "<br />" ..
1141 translate("please disable") .. " !" .. bold_off
1142 else
1143 self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
1144 end
1145 return value
1146 end
1147 function fipv.validate(self, value)
1148 if (value == "1" and has_force) or value == "0" then return value end
1149 return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
1150 end
1151 end
1152
1153 -- IPv4 + IPv6 - dns_server -- ################################################
1154 -- optional DNS Server to use resolving my IP if "ip_source"="web"
1155 dns = ns:taboption("advanced", Value, "dns_server",
1156 translate("DNS-Server"),
1157 translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
1158 translate("Format: IP or FQDN"))
1159 dns.placeholder = "mydns.lan"
1160 function dns.validate(self, value)
1161 -- if .datatype is set, then it is checked before calling this function
1162 if not value or (#value == 0) then
1163 return "" -- ignore on empty
1164 elseif not DTYP.host(value) then
1165 return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
1166 else
1167 local ipv6 = usev6:formvalue(section)
1168 local force = (fipv) and fipv:formvalue(section) or "0"
1169 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] ..
1170 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1171 local ret = SYS.call(command)
1172 if ret == 0 then return value -- everything OK
1173 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1174 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1175 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1176 else return nil, err_tab_adv(self) .. translate("unspecific error")
1177 end
1178 end
1179 end
1180 function dns.parse(self, section, novld)
1181 DDNS.value_parse(self, section, novld)
1182 end
1183
1184 -- IPv4 + IPv6 - force_dnstcp -- ##############################################
1185 if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
1186 tcp = ns:taboption("advanced", Flag, "force_dnstcp",
1187 translate("Force TCP on DNS") )
1188 tcp.orientation = "horizontal"
1189 function tcp.cfgvalue(self, section)
1190 local value = AbstractValue.cfgvalue(self, section)
1191 if not has_dnstcp and value ~= "0" then
1192 self.description = bold_on .. font_red ..
1193 translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
1194 translate("please disable") .. " !" .. bold_off
1195 else
1196 self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
1197 end
1198 return value
1199 end
1200 function tcp.validate(self, value)
1201 if (value == "1" and has_dnstcp ) or value == "0" then
1202 return value
1203 end
1204 return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
1205 end
1206 end
1207
1208 -- IPv4 + IPv6 - proxy -- #####################################################
1209 -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
1210 if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
1211 pxy = ns:taboption("advanced", Value, "proxy",
1212 translate("PROXY-Server") )
1213 pxy.placeholder="user:password@myproxy.lan:8080"
1214 function pxy.cfgvalue(self, section)
1215 local value = AbstractValue.cfgvalue(self, section)
1216 if not has_proxy and value ~= "" then
1217 self.description = bold_on .. font_red ..
1218 translate("PROXY-Server not supported") .. font_off .. "<br />" ..
1219 translate("please remove entry") .. "!" .. bold_off
1220 else
1221 self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
1222 translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
1223 translate("IPv6 address must be given in square brackets") .. ": " ..
1224 bold_on .. " [2001:db8::1]:8080" .. bold_off
1225 end
1226 return value
1227 end
1228 function pxy.validate(self, value)
1229 -- if .datatype is set, then it is checked before calling this function
1230 if not value or (#value == 0) then
1231 return "" -- ignore on empty
1232 elseif has_proxy then
1233 local ipv6 = usev6:formvalue(section) or "0"
1234 local force = (fipv) and fipv:formvalue(section) or "0"
1235 local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] ..
1236 value .. [[ ]] .. ipv6 .. [[ ]] .. force
1237 local ret = SYS.call(command)
1238 if ret == 0 then return value
1239 elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
1240 elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
1241 elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
1242 elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
1243 else return nil, err_tab_adv(self) .. translate("unspecific error")
1244 end
1245 else
1246 return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
1247 end
1248 end
1249 function pxy.parse(self, section, novld)
1250 DDNS.value_parse(self, section, novld)
1251 end
1252 end
1253
1254 -- use_syslog -- ##############################################################
1255 slog = ns:taboption("advanced", ListValue, "use_syslog",
1256 translate("Log to syslog"),
1257 translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
1258 slog.default = "2"
1259 slog:value("0", translate("No logging"))
1260 slog:value("1", translate("Info"))
1261 slog:value("2", translate("Notice"))
1262 slog:value("3", translate("Warning"))
1263 slog:value("4", translate("Error"))
1264 function slog.parse(self, section, novld)
1265 DDNS.value_parse(self, section, novld)
1266 end
1267
1268 -- use_logfile -- #############################################################
1269 logf = ns:taboption("advanced", Flag, "use_logfile",
1270 translate("Log to file"),
1271 translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
1272 translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] )
1273 logf.orientation = "horizontal"
1274 logf.default = "1" -- if not defined write to log by default
1275
1276 -- TAB: Timer ####################################################################################
1277 -- check_interval -- ##########################################################
1278 ci = ns:taboption("timer", Value, "check_interval",
1279 translate("Check Interval") )
1280 ci.template = "ddns/detail_value"
1281 ci.default = "10"
1282 function ci.validate(self, value)
1283 if not DTYP.uinteger(value)
1284 or tonumber(value) < 1 then
1285 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1286 end
1287
1288 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1289 if secs >= 300 then
1290 return value
1291 else
1292 return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
1293 end
1294 end
1295 function ci.write(self, section, value)
1296 -- remove when default
1297 local secs = DDNS.calc_seconds(value, cu:formvalue(section))
1298 if secs ~= 600 then --default 10 minutes
1299 return self.map:set(section, self.option, value)
1300 else
1301 self.map:del(section, "check_unit")
1302 return self.map:del(section, self.option)
1303 end
1304 end
1305 function ci.parse(self, section, novld)
1306 DDNS.value_parse(self, section, novld)
1307 end
1308
1309 -- check_unit -- ##############################################################
1310 cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
1311 translate("Interval to check for changed IP" .. "<br />" ..
1312 "Values below 5 minutes == 300 seconds are not supported") )
1313 cu.template = "ddns/detail_lvalue"
1314 cu.default = "minutes"
1315 cu:value("seconds", translate("seconds"))
1316 cu:value("minutes", translate("minutes"))
1317 cu:value("hours", translate("hours"))
1318 --cu:value("days", translate("days"))
1319 function cu.write(self, section, value)
1320 -- remove when default
1321 local secs = DDNS.calc_seconds(ci:formvalue(section), value)
1322 if secs ~= 600 then --default 10 minutes
1323 return self.map:set(section, self.option, value)
1324 else
1325 return true
1326 end
1327 end
1328 function cu.parse(self, section, novld)
1329 DDNS.value_parse(self, section, novld)
1330 end
1331
1332 -- force_interval (modified) -- ###############################################
1333 fi = ns:taboption("timer", Value, "force_interval",
1334 translate("Force Interval") )
1335 fi.template = "ddns/detail_value"
1336 fi.default = "72" -- see dynamic_dns_updater.sh script
1337 --fi.rmempty = false -- validate ourselves for translatable error messages
1338 function fi.validate(self, value)
1339 if not DTYP.uinteger(value)
1340 or tonumber(value) < 0 then
1341 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1342 end
1343
1344 local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
1345 if force_s == 0 then
1346 return value
1347 end
1348
1349 local ci_value = ci:formvalue(section)
1350 if not DTYP.uinteger(ci_value) then
1351 return "" -- ignore because error in check_interval above
1352 end
1353
1354 local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
1355 if force_s >= check_s then
1356 return value
1357 end
1358
1359 return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
1360 end
1361 function fi.write(self, section, value)
1362 -- simulate rmempty=true remove default
1363 local secs = DDNS.calc_seconds(value, fu:formvalue(section))
1364 if secs ~= 259200 then --default 72 hours == 3 days
1365 return self.map:set(section, self.option, value)
1366 else
1367 self.map:del(section, "force_unit")
1368 return self.map:del(section, self.option)
1369 end
1370 end
1371 function fi.parse(self, section, novld)
1372 DDNS.value_parse(self, section, novld)
1373 end
1374
1375 -- force_unit -- ##############################################################
1376 fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
1377 translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
1378 "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
1379 "Values lower 'Check Interval' except '0' are not supported") )
1380 fu.template = "ddns/detail_lvalue"
1381 fu.default = "hours"
1382 --fu.rmempty = false -- want to control write process
1383 --fu:value("seconds", translate("seconds"))
1384 fu:value("minutes", translate("minutes"))
1385 fu:value("hours", translate("hours"))
1386 fu:value("days", translate("days"))
1387 function fu.write(self, section, value)
1388 -- simulate rmempty=true remove default
1389 local secs = DDNS.calc_seconds(fi:formvalue(section), value)
1390 if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
1391 return self.map:set(section, self.option, value)
1392 else
1393 return true
1394 end
1395 end
1396 function fu.parse(self, section, novld)
1397 DDNS.value_parse(self, section, novld)
1398 end
1399
1400 -- retry_count -- #############################################################
1401 rc = ns:taboption("timer", Value, "retry_count")
1402 rc.title = translate("Error Retry Counter")
1403 rc.description = translate("On Error the script will stop execution after given number of retrys")
1404 .. "<br />"
1405 .. translate("The default setting of '0' will retry infinite.")
1406 rc.default = "0"
1407 function rc.validate(self, value)
1408 if not DTYP.uinteger(value) then
1409 return nil, err_tab_timer(self) .. translate("minimum value '0'")
1410 else
1411 return value
1412 end
1413 end
1414 function rc.parse(self, section, novld)
1415 DDNS.value_parse(self, section, novld)
1416 end
1417
1418 -- retry_interval -- ##########################################################
1419 ri = ns:taboption("timer", Value, "retry_interval",
1420 translate("Error Retry Interval") )
1421 ri.template = "ddns/detail_value"
1422 ri.default = "60"
1423 function ri.validate(self, value)
1424 if not DTYP.uinteger(value)
1425 or tonumber(value) < 1 then
1426 return nil, err_tab_timer(self) .. translate("minimum value '1'")
1427 else
1428 return value
1429 end
1430 end
1431 function ri.write(self, section, value)
1432 -- simulate rmempty=true remove default
1433 local secs = DDNS.calc_seconds(value, ru:formvalue(section))
1434 if secs ~= 60 then --default 60seconds
1435 return self.map:set(section, self.option, value)
1436 else
1437 self.map:del(section, "retry_unit")
1438 return self.map:del(section, self.option)
1439 end
1440 end
1441 function ri.parse(self, section, novld)
1442 DDNS.value_parse(self, section, novld)
1443 end
1444
1445 -- retry_unit -- ##############################################################
1446 ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
1447 translate("On Error the script will retry the failed action after given time") )
1448 ru.template = "ddns/detail_lvalue"
1449 ru.default = "seconds"
1450 --ru.rmempty = false -- want to control write process
1451 ru:value("seconds", translate("seconds"))
1452 ru:value("minutes", translate("minutes"))
1453 --ru:value("hours", translate("hours"))
1454 --ru:value("days", translate("days"))
1455 function ru.write(self, section, value)
1456 -- simulate rmempty=true remove default
1457 local secs = DDNS.calc_seconds(ri:formvalue(section), value)
1458 if secs ~= 60 then --default 60seconds
1459 return self.map:set(section, self.option, value)
1460 else
1461 return true -- will be deleted by retry_interval
1462 end
1463 end
1464 function ru.parse(self, section, novld)
1465 DDNS.value_parse(self, section, novld)
1466 end
1467
1468 -- TAB: LogView ##################################################################################
1469 lv = ns:taboption("logview", DummyValue, "_logview")
1470 lv.template = "ddns/detail_logview"
1471 lv.inputtitle = translate("Read / Reread log file")
1472 lv.rows = 50
1473 function lv.cfgvalue(self, section)
1474 local lfile=log_dir .. "/" .. section .. ".log"
1475 if NXFS.access(lfile) then
1476 return lfile .. "\n" .. translate("Please press [Read] button")
1477 end
1478 return lfile .. "\n" .. translate("File not found or empty")
1479 end
1480
1481 return m