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