detail.lua 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240
  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. local NX = require "nixio"
  7. local NXFS = require "nixio.fs"
  8. local SYS = require "luci.sys"
  9. local UTIL = require "luci.util"
  10. local DISP = require "luci.dispatcher"
  11. local WADM = require "luci.tools.webadmin"
  12. local DTYP = require "luci.cbi.datatypes"
  13. local DDNS = require "luci.tools.ddns" -- ddns multiused functions
  14. -- takeover arguments -- #######################################################
  15. local section = arg[1]
  16. -- check supported options -- ##################################################
  17. -- saved to local vars here because doing multiple os calls slow down the system
  18. local has_ipv6 = DDNS.check_ipv6() -- IPv6 support
  19. local has_ssl = DDNS.check_ssl() -- HTTPS support
  20. local has_proxy = DDNS.check_proxy() -- Proxy support
  21. local has_dnstcp = DDNS.check_bind_host() -- DNS TCP support
  22. local has_force = has_ssl and has_dnstcp -- Force IP Protocoll
  23. -- html constants -- ###########################################################
  24. local font_red = "<font color='red'>"
  25. local font_off = "</font>"
  26. local bold_on = "<strong>"
  27. local bold_off = "</strong>"
  28. -- error text constants -- #####################################################
  29. err_ipv6_plain = translate("IPv6 not supported") .. " - " ..
  30. translate("please select 'IPv4' address version")
  31. err_ipv6_basic = bold_on ..
  32. font_red ..
  33. translate("IPv6 not supported") ..
  34. font_off ..
  35. "<br />" .. translate("please select 'IPv4' address version") ..
  36. bold_off
  37. err_ipv6_other = bold_on ..
  38. font_red ..
  39. translate("IPv6 not supported") ..
  40. font_off ..
  41. "<br />" .. translate("please select 'IPv4' address version in") .. " " ..
  42. [[<a href="]] ..
  43. DISP.build_url("admin", "services", "ddns", "detail", section) ..
  44. "?tab.dns." .. section .. "=basic" ..
  45. [[">]] ..
  46. translate("Basic Settings") ..
  47. [[</a>]] ..
  48. bold_off
  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. -- function to verify settings around ip_source
  59. -- will use dynamic_dns_lucihelper to check if
  60. -- local IP can be read
  61. local function _verify_ip_source()
  62. -- section is globally defined here be calling agrument (see above)
  63. local _network = "-"
  64. local _url = "-"
  65. local _interface = "-"
  66. local _script = "-"
  67. local _proxy = ""
  68. local _ipv6 = usev6:formvalue(section)
  69. local _source = (_ipv6 == "1")
  70. and src6:formvalue(section)
  71. or src4:formvalue(section)
  72. if _source == "network" then
  73. _network = (_ipv6 == "1")
  74. and ipn6:formvalue(section)
  75. or ipn4:formvalue(section)
  76. elseif _source == "web" then
  77. _url = (_ipv6 == "1")
  78. and iurl6:formvalue(section)
  79. or iurl4:formvalue(section)
  80. -- proxy only needed for checking url
  81. _proxy = (pxy) and pxy:formvalue(section) or ""
  82. elseif _source == "interface" then
  83. _interface = ipi:formvalue(section)
  84. elseif _source == "script" then
  85. _script = ips:formvalue(section)
  86. end
  87. local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] ..
  88. _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] ..
  89. _url .. [[ ]] .. _interface .. [[ ']] .. _script.. [[' ]] .. _proxy
  90. local ret = SYS.call(command)
  91. if ret == 0 then
  92. return true -- valid
  93. else
  94. return nil -- invalid
  95. end
  96. end
  97. -- cbi-map definition -- #######################################################
  98. m = Map("ddns")
  99. -- first need to close <a> from cbi map template our <a> closed by template
  100. m.title = [[</a><a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] ..
  101. translate("Dynamic DNS")
  102. m.description = translate("Dynamic DNS allows that your router can be reached with " ..
  103. "a fixed hostname while having a dynamically changing " ..
  104. "IP address.")
  105. m.redirect = DISP.build_url("admin", "services", "ddns")
  106. m.on_after_commit = function(self)
  107. if self.changed then -- changes ?
  108. local pid = DDNS.get_pid(section)
  109. if pid > 0 then -- running ?
  110. local tmp = NX.kill(pid, 1) -- send SIGHUP
  111. end
  112. end
  113. end
  114. -- read application settings -- ################################################
  115. -- date format; if not set use ISO format
  116. date_format = m.uci:get(m.config, "global", "date_format") or "%F %R"
  117. -- log directory
  118. log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns"
  119. -- cbi-section definition -- ###################################################
  120. ns = m:section( NamedSection, section, "service",
  121. translate("Details for") .. ([[: <strong>%s</strong>]] % section),
  122. translate("Configure here the details for selected Dynamic DNS service.")
  123. .. [[<br /><a href="http://wiki.openwrt.org/doc/uci/ddns#version_1x" target="_blank">]]
  124. .. translate("For detailed information about parameter settings look here.")
  125. .. [[</a>]] )
  126. ns.instance = section -- arg [1]
  127. ns:tab("basic", translate("Basic Settings"), nil )
  128. ns:tab("advanced", translate("Advanced Settings"), nil )
  129. ns:tab("timer", translate("Timer Settings"), nil )
  130. ns:tab("logview", translate("Log File Viewer"), nil )
  131. -- TAB: Basic #####################################################################################
  132. -- enabled -- #################################################################
  133. en = ns:taboption("basic", Flag, "enabled",
  134. translate("Enabled"),
  135. translate("If this service section is disabled it could not be started." .. "<br />" ..
  136. "Neither from LuCI interface nor from console") )
  137. en.orientation = "horizontal"
  138. function en.parse(self, section)
  139. DDNS.flag_parse(self, section)
  140. end
  141. -- use_ipv6 (NEW) -- ##########################################################
  142. usev6 = ns:taboption("basic", ListValue, "use_ipv6",
  143. translate("IP address version"),
  144. translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") )
  145. usev6.widget = "radio"
  146. usev6.default = "0"
  147. usev6:value("0", translate("IPv4-Address") )
  148. function usev6.cfgvalue(self, section)
  149. local value = AbstractValue.cfgvalue(self, section)
  150. if has_ipv6 or (value == "1" and not has_ipv6) then
  151. self:value("1", translate("IPv6-Address") )
  152. end
  153. if value == "1" and not has_ipv6 then
  154. self.description = err_ipv6_basic
  155. end
  156. return value
  157. end
  158. function usev6.validate(self, value)
  159. if (value == "1" and has_ipv6) or value == "0" then
  160. return value
  161. end
  162. return nil, err_tab_basic(self) .. err_ipv6_plain
  163. end
  164. function usev6.write(self, section, value)
  165. if value == "0" then -- force rmempty
  166. return self.map:del(section, self.option)
  167. else
  168. return self.map:set(section, self.option, value)
  169. end
  170. end
  171. -- IPv4 - service_name -- ######################################################
  172. svc4 = ns:taboption("basic", ListValue, "ipv4_service_name",
  173. translate("DDNS Service provider") .. " [IPv4]" )
  174. svc4.default = "-"
  175. svc4:depends("use_ipv6", "0") -- only show on IPv4
  176. local services4 = { }
  177. local fd4 = io.open("/usr/lib/ddns/services", "r")
  178. if fd4 then
  179. local ln
  180. repeat
  181. ln = fd4:read("*l")
  182. local s = ln and ln:match('^%s*"([^"]+)"')
  183. if s then services4[#services4+1] = s end
  184. until not ln
  185. fd4:close()
  186. end
  187. for _, v in UTIL.vspairs(services4) do svc4:value(v) end
  188. svc4:value("-", translate("-- custom --") )
  189. function svc4.cfgvalue(self, section)
  190. local v = DDNS.read_value(self, section, "service_name")
  191. if not v or #v == 0 then
  192. return "-"
  193. else
  194. return v
  195. end
  196. end
  197. function svc4.validate(self, value)
  198. if usev6:formvalue(section) == "0" then -- do only on IPv4
  199. return value
  200. else
  201. return "" -- supress validate error
  202. end
  203. end
  204. function svc4.write(self, section, value)
  205. if usev6:formvalue(section) == "0" then -- do only IPv4 here
  206. self.map:del(section, self.option) -- to be shure
  207. if value ~= "-" then -- and write "service_name
  208. self.map:del(section, "update_url") -- delete update_url
  209. return self.map:set(section, "service_name", value)
  210. else
  211. return self.map:del(section, "service_name")
  212. end
  213. end
  214. end
  215. -- IPv6 - service_name -- ######################################################
  216. svc6 = ns:taboption("basic", ListValue, "ipv6_service_name",
  217. translate("DDNS Service provider") .. " [IPv6]" )
  218. svc6.default = "-"
  219. svc6:depends("use_ipv6", "1") -- only show on IPv6
  220. if not has_ipv6 then
  221. svc6.description = err_ipv6_basic
  222. end
  223. local services6 = { }
  224. local fd6 = io.open("/usr/lib/ddns/services_ipv6", "r")
  225. if fd6 then
  226. local ln
  227. repeat
  228. ln = fd6:read("*l")
  229. local s = ln and ln:match('^%s*"([^"]+)"')
  230. if s then services6[#services6+1] = s end
  231. until not ln
  232. fd6:close()
  233. end
  234. for _, v in UTIL.vspairs(services6) do svc6:value(v) end
  235. svc6:value("-", translate("-- custom --") )
  236. function svc6.cfgvalue(self, section)
  237. local v = DDNS.read_value(self, section, "service_name")
  238. if not v or #v == 0 then
  239. return "-"
  240. else
  241. return v
  242. end
  243. end
  244. function svc6.validate(self, value)
  245. if usev6:formvalue(section) == "1" then -- do only on IPv6
  246. if has_ipv6 then return value end
  247. return nil, err_tab_basic(self) .. err_ipv6_plain
  248. else
  249. return "" -- supress validate error
  250. end
  251. end
  252. function svc6.write(self, section, value)
  253. if usev6:formvalue(section) == "1" then -- do only when IPv6
  254. self.map:del(section, self.option) -- delete "ipv6_service_name" helper
  255. if value ~= "-" then -- and write "service_name
  256. self.map:del(section, "update_url") -- delete update_url
  257. return self.map:set(section, "service_name", value)
  258. else
  259. return self.map:del(section, "service_name")
  260. end
  261. end
  262. end
  263. -- IPv4/IPv6 - update_url -- ###################################################
  264. uurl = ns:taboption("basic", Value, "update_url",
  265. translate("Custom update-URL"),
  266. translate("Update URL to be used for updating your DDNS Provider." .. "<br />" ..
  267. "Follow instructions you will find on their WEB page.") )
  268. uurl:depends("ipv4_service_name", "-")
  269. uurl:depends("ipv6_service_name", "-")
  270. function uurl.validate(self, value)
  271. local script = ush:formvalue(section)
  272. if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or
  273. (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then
  274. return "" -- suppress validate error
  275. elseif not value then
  276. if not script or not (#script > 0) then
  277. return nil, err_tab_basic(self) .. translate("missing / required")
  278. else
  279. return "" -- suppress validate error / update_script is given
  280. end
  281. elseif (#script > 0) then
  282. return nil, err_tab_basic(self) .. translate("either url or script could be set")
  283. end
  284. local url = DDNS.parse_url(value)
  285. if not url.scheme == "http" then
  286. return nil, err_tab_basic(self) .. translate("must start with 'http://'")
  287. elseif not url.query then
  288. return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required")
  289. elseif not url.host then
  290. return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required")
  291. elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then
  292. return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host
  293. end
  294. return value
  295. end
  296. -- IPv4/IPv6 - update_script -- ################################################
  297. ush = ns:taboption("basic", Value, "update_script",
  298. translate("Custom update-script"),
  299. translate("Custom update script to be used for updating your DDNS Provider.") )
  300. ush:depends("ipv4_service_name", "-")
  301. ush:depends("ipv6_service_name", "-")
  302. function ush.validate(self, value)
  303. local url = uurl:formvalue(section)
  304. if (usev6:formvalue(section) == "0" and svc4:formvalue(section) ~= "-") or
  305. (usev6:formvalue(section) == "1" and svc6:formvalue(section) ~= "-") then
  306. return "" -- suppress validate error
  307. elseif not value then
  308. if not url or not (#url > 0) then
  309. return nil, err_tab_basic(self) .. translate("missing / required")
  310. else
  311. return "" -- suppress validate error / update_url is given
  312. end
  313. elseif (#url > 0) then
  314. return nil, err_tab_basic(self) .. translate("either url or script could be set")
  315. elseif not NXFS.access(value) then
  316. return nil, err_tab_basic(self) .. translate("File not found")
  317. end
  318. return value
  319. end
  320. -- IPv4/IPv6 - domain -- #######################################################
  321. dom = ns:taboption("basic", Value, "domain",
  322. translate("Hostname/Domain"),
  323. translate("Replaces [DOMAIN] in Update-URL") )
  324. dom.rmempty = false
  325. dom.placeholder = "mypersonaldomain.dyndns.org"
  326. function dom.validate(self, value)
  327. if not value
  328. or not (#value > 0)
  329. or not DTYP.hostname(value) then
  330. return nil, err_tab_basic(self) .. translate("invalid - Sample") .. ": 'mypersonaldomain.dyndns.org'"
  331. else
  332. return value
  333. end
  334. end
  335. -- IPv4/IPv6 - username -- #####################################################
  336. user = ns:taboption("basic", Value, "username",
  337. translate("Username"),
  338. translate("Replaces [USERNAME] in Update-URL") )
  339. user.rmempty = false
  340. function user.validate(self, value)
  341. if not value then
  342. return nil, err_tab_basic(self) .. translate("missing / required")
  343. end
  344. return value
  345. end
  346. -- IPv4/IPv6 - password -- #####################################################
  347. pw = ns:taboption("basic", Value, "password",
  348. translate("Password"),
  349. translate("Replaces [PASSWORD] in Update-URL") )
  350. pw.rmempty = false
  351. pw.password = true
  352. function pw.validate(self, value)
  353. if not value then
  354. return nil, err_tab_basic(self) .. translate("missing / required")
  355. end
  356. return value
  357. end
  358. -- IPv4/IPv6 - use_https (NEW) -- ##############################################
  359. if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then
  360. https = ns:taboption("basic", Flag, "use_https",
  361. translate("Use HTTP Secure") )
  362. https.orientation = "horizontal"
  363. https.rmempty = false -- force validate function
  364. function https.cfgvalue(self, section)
  365. local value = AbstractValue.cfgvalue(self, section)
  366. if not has_ssl and value == "1" then
  367. self.description = bold_on .. font_red ..
  368. translate("HTTPS not supported") .. font_off .. "<br />" ..
  369. translate("please disable") .. " !" .. bold_off
  370. else
  371. self.description = translate("Enable secure communication with DDNS provider")
  372. end
  373. return value
  374. end
  375. function https.parse(self, section)
  376. DDNS.flag_parse(self, section)
  377. end
  378. function https.validate(self, value)
  379. if (value == "1" and has_ssl ) or value == "0" then return value end
  380. return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !"
  381. end
  382. function https.write(self, section, value)
  383. if value == "1" then
  384. return self.map:set(section, self.option, value)
  385. else
  386. self.map:del(section, "cacert")
  387. return self.map:del(section, self.option)
  388. end
  389. end
  390. end
  391. -- IPv4/IPv6 - cacert (NEW) -- #################################################
  392. if has_ssl then
  393. cert = ns:taboption("basic", Value, "cacert",
  394. translate("Path to CA-Certificate"),
  395. translate("directory or path/file") .. "<br />" ..
  396. translate("or") .. bold_on .. " IGNORE " .. bold_off ..
  397. translate("to run HTTPS without verification of server certificates (insecure)") )
  398. cert:depends("use_https", "1")
  399. cert.rmempty = false -- force validate function
  400. cert.default = "/etc/ssl/certs"
  401. function cert.validate(self, value)
  402. if https:formvalue(section) == "0" then
  403. return "" -- supress validate error if NOT https
  404. end
  405. if value then -- otherwise errors in datatype check
  406. if DTYP.directory(value)
  407. or DTYP.file(value)
  408. or value == "IGNORE" then
  409. return value
  410. end
  411. end
  412. return nil, err_tab_basic(self) ..
  413. translate("file or directory not found or not 'IGNORE'") .. " !"
  414. end
  415. end
  416. -- TAB: Advanced ##################################################################################
  417. -- IPv4 - ip_source -- #########################################################
  418. src4 = ns:taboption("advanced", ListValue, "ipv4_source",
  419. translate("IP address source") .. " [IPv4]",
  420. translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") )
  421. src4:depends("use_ipv6", "0") -- IPv4 selected
  422. src4.default = "network"
  423. src4:value("network", translate("Network"))
  424. src4:value("web", translate("URL"))
  425. src4:value("interface", translate("Interface"))
  426. src4:value("script", translate("Script"))
  427. function src4.cfgvalue(self, section)
  428. return DDNS.read_value(self, section, "ip_source")
  429. end
  430. function src4.validate(self, value)
  431. if usev6:formvalue(section) == "1" then
  432. return "" -- ignore on IPv6 selected
  433. elseif not _verify_ip_source() then
  434. return nil, err_tab_adv(self) ..
  435. translate("can not detect local IP. Please select a different Source combination")
  436. else
  437. return value
  438. end
  439. end
  440. function src4.write(self, section, value)
  441. if usev6:formvalue(section) == "1" then
  442. return true -- ignore on IPv6 selected
  443. elseif value == "network" then
  444. self.map:del(section, "ip_url") -- delete not need parameters
  445. self.map:del(section, "ip_interface")
  446. self.map:del(section, "ip_script")
  447. elseif value == "web" then
  448. self.map:del(section, "ip_network") -- delete not need parameters
  449. self.map:del(section, "ip_interface")
  450. self.map:del(section, "ip_script")
  451. elseif value == "interface" then
  452. self.map:del(section, "ip_network") -- delete not need parameters
  453. self.map:del(section, "ip_url")
  454. self.map:del(section, "ip_script")
  455. elseif value == "script" then
  456. self.map:del(section, "ip_network")
  457. self.map:del(section, "ip_url") -- delete not need parameters
  458. self.map:del(section, "ip_interface")
  459. end
  460. self.map:del(section, self.option) -- delete "ipv4_source" helper
  461. return self.map:set(section, "ip_source", value) -- and write "ip_source
  462. end
  463. -- IPv6 - ip_source -- #########################################################
  464. src6 = ns:taboption("advanced", ListValue, "ipv6_source",
  465. translate("IP address source") .. " [IPv6]",
  466. translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") )
  467. src6:depends("use_ipv6", 1) -- IPv6 selected
  468. src6.default = "network"
  469. src6:value("network", translate("Network"))
  470. src6:value("web", translate("URL"))
  471. src6:value("interface", translate("Interface"))
  472. src6:value("script", translate("Script"))
  473. if not has_ipv6 then
  474. src6.description = err_ipv6_other
  475. end
  476. function src6.cfgvalue(self, section)
  477. return DDNS.read_value(self, section, "ip_source")
  478. end
  479. function src6.validate(self, value)
  480. if usev6:formvalue(section) == "0" then
  481. return "" -- ignore on IPv4 selected
  482. elseif not has_ipv6 then
  483. return nil, err_tab_adv(self) .. err_ipv6_plain
  484. elseif not _verify_ip_source() then
  485. return nil, err_tab_adv(self) ..
  486. translate("can not detect local IP. Please select a different Source combination")
  487. else
  488. return value
  489. end
  490. end
  491. function src6.write(self, section, value)
  492. if usev6:formvalue(section) == "0" then
  493. return true -- ignore on IPv4 selected
  494. elseif value == "network" then
  495. self.map:del(section, "ip_url") -- delete not need parameters
  496. self.map:del(section, "ip_interface")
  497. self.map:del(section, "ip_script")
  498. elseif value == "web" then
  499. self.map:del(section, "ip_network") -- delete not need parameters
  500. self.map:del(section, "ip_interface")
  501. self.map:del(section, "ip_script")
  502. elseif value == "interface" then
  503. self.map:del(section, "ip_network") -- delete not need parameters
  504. self.map:del(section, "ip_url")
  505. self.map:del(section, "ip_script")
  506. elseif value == "script" then
  507. self.map:del(section, "ip_network")
  508. self.map:del(section, "ip_url") -- delete not need parameters
  509. self.map:del(section, "ip_interface")
  510. end
  511. self.map:del(section, self.option) -- delete "ipv4_source" helper
  512. return self.map:set(section, "ip_source", value) -- and write "ip_source
  513. end
  514. -- IPv4 - ip_network (default "wan") -- ########################################
  515. ipn4 = ns:taboption("advanced", ListValue, "ipv4_network",
  516. translate("Network") .. " [IPv4]",
  517. translate("Defines the network to read systems IPv4-Address from") )
  518. ipn4:depends("ipv4_source", "network")
  519. ipn4.default = "wan"
  520. WADM.cbi_add_networks(ipn4)
  521. function ipn4.cfgvalue(self, section)
  522. return DDNS.read_value(self, section, "ip_network")
  523. end
  524. function ipn4.validate(self, value)
  525. if usev6:formvalue(section) == "1"
  526. or src4:formvalue(section) ~= "network" then
  527. -- ignore if IPv6 selected OR
  528. -- ignore everything except "network"
  529. return ""
  530. else
  531. return value
  532. end
  533. end
  534. function ipn4.write(self, section, value)
  535. if usev6:formvalue(section) == "1"
  536. or src4:formvalue(section) ~= "network" then
  537. -- ignore if IPv6 selected OR
  538. -- ignore everything except "network"
  539. return true
  540. else
  541. -- set also as "interface" for monitoring events changes/hot-plug
  542. self.map:set(section, "interface", value)
  543. self.map:del(section, self.option) -- delete "ipv4_network" helper
  544. return self.map:set(section, "ip_network", value) -- and write "ip_network"
  545. end
  546. end
  547. -- IPv6 - ip_network (default "wan6") -- #######################################
  548. ipn6 = ns:taboption("advanced", ListValue, "ipv6_network",
  549. translate("Network") .. " [IPv6]" )
  550. ipn6:depends("ipv6_source", "network")
  551. ipn6.default = "wan6"
  552. WADM.cbi_add_networks(ipn6)
  553. if has_ipv6 then
  554. ipn6.description = translate("Defines the network to read systems IPv6-Address from")
  555. else
  556. ipn6.description = err_ipv6_other
  557. end
  558. function ipn6.cfgvalue(self, section)
  559. return DDNS.read_value(self, section, "ip_network")
  560. end
  561. function ipn6.validate(self, value)
  562. if usev6:formvalue(section) == "0"
  563. or src6:formvalue(section) ~= "network" then
  564. -- ignore if IPv4 selected OR
  565. -- ignore everything except "network"
  566. return ""
  567. elseif has_ipv6 then
  568. return value
  569. else
  570. return nil, err_tab_adv(self) .. err_ipv6_plain
  571. end
  572. end
  573. function ipn6.write(self, section, value)
  574. if usev6:formvalue(section) == "0"
  575. or src6:formvalue(section) ~= "network" then
  576. -- ignore if IPv4 selected OR
  577. -- ignore everything except "network"
  578. return true
  579. else
  580. -- set also as "interface" for monitoring events changes/hotplug
  581. self.map:set(section, "interface", value)
  582. self.map:del(section, self.option) -- delete "ipv6_network" helper
  583. return self.map:set(section, "ip_network", value) -- and write "ip_network"
  584. end
  585. end
  586. -- IPv4 - ip_url (default "checkip.dyndns.com") -- #############################
  587. iurl4 = ns:taboption("advanced", Value, "ipv4_url",
  588. translate("URL to detect") .. " [IPv4]",
  589. translate("Defines the Web page to read systems IPv4-Address from") )
  590. iurl4:depends("ipv4_source", "web")
  591. iurl4.default = "http://checkip.dyndns.com"
  592. function iurl4.cfgvalue(self, section)
  593. return DDNS.read_value(self, section, "ip_url")
  594. end
  595. function iurl4.validate(self, value)
  596. if usev6:formvalue(section) == "1"
  597. or src4:formvalue(section) ~= "web" then
  598. -- ignore if IPv6 selected OR
  599. -- ignore everything except "web"
  600. return ""
  601. elseif not value or #value == 0 then
  602. return nil, err_tab_adv(self) .. translate("missing / required")
  603. end
  604. local url = DDNS.parse_url(value)
  605. if not (url.scheme == "http" or url.scheme == "https") then
  606. return nil, err_tab_adv(self) .. translate("must start with 'http://'")
  607. elseif not url.host then
  608. return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
  609. elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
  610. return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
  611. else
  612. return value
  613. end
  614. end
  615. function iurl4.write(self, section, value)
  616. if usev6:formvalue(section) == "1"
  617. or src4:formvalue(section) ~= "web" then
  618. -- ignore if IPv6 selected OR
  619. -- ignore everything except "web"
  620. return true
  621. else
  622. self.map:del(section, self.option) -- delete "ipv4_url" helper
  623. return self.map:set(section, "ip_url", value) -- and write "ip_url"
  624. end
  625. end
  626. -- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ###########################
  627. iurl6 = ns:taboption("advanced", Value, "ipv6_url",
  628. translate("URL to detect") .. " [IPv6]" )
  629. iurl6:depends("ipv6_source", "web")
  630. iurl6.default = "http://checkipv6.dyndns.com"
  631. if has_ipv6 then
  632. iurl6.description = translate("Defines the Web page to read systems IPv6-Address from")
  633. else
  634. iurl6.description = err_ipv6_other
  635. end
  636. function iurl6.cfgvalue(self, section)
  637. return DDNS.read_value(self, section, "ip_url")
  638. end
  639. function iurl6.validate(self, value)
  640. if usev6:formvalue(section) == "0"
  641. or src6:formvalue(section) ~= "web" then
  642. -- ignore if IPv4 selected OR
  643. -- ignore everything except "web"
  644. return ""
  645. elseif not has_ipv6 then
  646. return nil, err_tab_adv(self) .. err_ipv6_plain
  647. elseif not value or #value == 0 then
  648. return nil, err_tab_adv(self) .. translate("missing / required")
  649. end
  650. local url = DDNS.parse_url(value)
  651. if not (url.scheme == "http" or url.scheme == "https") then
  652. return nil, err_tab_adv(self) .. translate("must start with 'http://'")
  653. elseif not url.host then
  654. return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required")
  655. elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then
  656. return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host
  657. else
  658. return value
  659. end
  660. end
  661. function iurl6.write(self, section, value)
  662. if usev6:formvalue(section) == "0"
  663. or src6:formvalue(section) ~= "web" then
  664. -- ignore if IPv4 selected OR
  665. -- ignore everything except "web"
  666. return true
  667. else
  668. self.map:del(section, self.option) -- delete "ipv6_url" helper
  669. return self.map:set(section, "ip_url", value) -- and write "ip_url"
  670. end
  671. end
  672. -- IPv4 + IPv6 - ip_interface -- ###############################################
  673. ipi = ns:taboption("advanced", ListValue, "ip_interface",
  674. translate("Interface"),
  675. translate("Defines the interface to read systems IP-Address from") )
  676. ipi:depends("ipv4_source", "interface") -- IPv4
  677. ipi:depends("ipv6_source", "interface") -- or IPv6
  678. for _, v in pairs(SYS.net.devices()) do
  679. -- show only interface set to a network
  680. -- and ignore loopback
  681. net = WADM.iface_get_network(v)
  682. if net and net ~= "loopback" then
  683. ipi:value(v)
  684. end
  685. end
  686. function ipi.validate(self, value)
  687. if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface")
  688. or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then
  689. return ""
  690. else
  691. return value
  692. end
  693. end
  694. function ipi.write(self, section, value)
  695. if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "interface")
  696. or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "interface") then
  697. return true
  698. else
  699. -- get network from device to
  700. -- set also as "interface" for monitoring events changes/hotplug
  701. local net = WADM.iface_get_network(value)
  702. self.map:set(section, "interface", net)
  703. return self.map:set(section, self.option, value)
  704. end
  705. end
  706. -- IPv4 + IPv6 - ip_script (NEW) -- ############################################
  707. ips = ns:taboption("advanced", Value, "ip_script",
  708. translate("Script"),
  709. translate("User defined script to read systems IP-Address") )
  710. ips:depends("ipv4_source", "script") -- IPv4
  711. ips:depends("ipv6_source", "script") -- or IPv6
  712. ips.rmempty = false
  713. ips.placeholder = "/path/to/script.sh"
  714. function ips.validate(self, value)
  715. local split
  716. if value then split = UTIL.split(value, " ") end
  717. if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script")
  718. or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then
  719. return ""
  720. elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then
  721. return nil, err_tab_adv(self) ..
  722. translate("not found or not executable - Sample: '/path/to/script.sh'")
  723. else
  724. return value
  725. end
  726. end
  727. function ips.write(self, section, value)
  728. if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script")
  729. or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then
  730. return true
  731. else
  732. return self.map:set(section, self.option, value)
  733. end
  734. end
  735. -- IPv4 - interface - default "wan" -- #########################################
  736. -- event network to monitor changes/hotplug/dynamic_dns_updater.sh
  737. -- only needs to be set if "ip_source"="web" or "script"
  738. -- if "ip_source"="network" or "interface" we use their network
  739. eif4 = ns:taboption("advanced", ListValue, "ipv4_interface",
  740. translate("Event Network") .. " [IPv4]",
  741. translate("Network on which the ddns-updater scripts will be started") )
  742. eif4:depends("ipv4_source", "web")
  743. eif4:depends("ipv4_source", "script")
  744. eif4.default = "wan"
  745. WADM.cbi_add_networks(eif4)
  746. function eif4.cfgvalue(self, section)
  747. return DDNS.read_value(self, section, "interface")
  748. end
  749. function eif4.validate(self, value)
  750. if usev6:formvalue(section) == "1"
  751. or src4:formvalue(section) == "network"
  752. or src4:formvalue(section) == "interface" then
  753. return "" -- ignore IPv6, network, interface
  754. else
  755. return value
  756. end
  757. end
  758. function eif4.write(self, section, value)
  759. if usev6:formvalue(section) == "1"
  760. or src4:formvalue(section) == "network"
  761. or src4:formvalue(section) == "interface" then
  762. return true -- ignore IPv6, network, interface
  763. else
  764. self.map:del(section, self.option) -- delete "ipv4_interface" helper
  765. return self.map:set(section, "interface", value) -- and write "interface"
  766. end
  767. end
  768. -- IPv6 - interface (NEW) - default "wan6" -- ##################################
  769. -- event network to monitor changes/hotplug (NEW)
  770. -- only needs to be set if "ip_source"="web" or "script"
  771. -- if "ip_source"="network" or "interface" we use their network
  772. eif6 = ns:taboption("advanced", ListValue, "ipv6_interface",
  773. translate("Event Network") .. " [IPv6]" )
  774. eif6:depends("ipv6_source", "web")
  775. eif6:depends("ipv6_source", "script")
  776. eif6.default = "wan6"
  777. WADM.cbi_add_networks(eif6)
  778. if not has_ipv6 then
  779. eif6.description = err_ipv6_other
  780. else
  781. eif6.description = translate("Network on which the ddns-updater scripts will be started")
  782. end
  783. function eif6.cfgvalue(self, section)
  784. return DDNS.read_value(self, section, "interface")
  785. end
  786. function eif6.validate(self, value)
  787. if usev6:formvalue(section) == "0"
  788. or src4:formvalue(section) == "network"
  789. or src4:formvalue(section) == "interface" then
  790. return "" -- ignore IPv4, network, interface
  791. elseif not has_ipv6 then
  792. return nil, err_tab_adv(self) .. err_ipv6_plain
  793. else
  794. return value
  795. end
  796. end
  797. function eif6.write(self, section, value)
  798. if usev6:formvalue(section) == "0"
  799. or src4:formvalue(section) == "network"
  800. or src4:formvalue(section) == "interface" then
  801. return true -- ignore IPv4, network, interface
  802. else
  803. self.map:del(section, self.option) -- delete "ipv6_interface" helper
  804. return self.map:set(section, "interface", value) -- and write "interface"
  805. end
  806. end
  807. -- IPv4/IPv6 - bind_network -- #################################################
  808. if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then
  809. bnet = ns:taboption("advanced", ListValue, "bind_network",
  810. translate("Bind Network") )
  811. bnet:depends("ipv4_source", "web")
  812. bnet:depends("ipv6_source", "web")
  813. bnet.rmempty = true
  814. bnet.default = ""
  815. bnet:value("", translate("-- default --"))
  816. WADM.cbi_add_networks(bnet)
  817. function bnet.cfgvalue(self, section)
  818. local value = AbstractValue.cfgvalue(self, section)
  819. if not has_ssl and value ~= "" then
  820. self.description = bold_on .. font_red ..
  821. translate("Binding to a specific network not supported") .. font_off .. "<br />" ..
  822. translate("please set to 'default'") .. " !" .. bold_off
  823. else
  824. self.description = translate("OPTIONAL: Network to use for communication") ..
  825. "<br />" .. translate("Casual users should not change this setting")
  826. end
  827. return value
  828. end
  829. function bnet.validate(self, value)
  830. if (value ~= "" and has_ssl ) or value == "" then return value end
  831. return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !"
  832. end
  833. end
  834. -- IPv4 + IPv6 - force_ipversion (NEW) -- ######################################
  835. -- optional to force wget/curl and host to use only selected IP version
  836. -- command parameter "-4" or "-6"
  837. if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then
  838. fipv = ns:taboption("advanced", Flag, "force_ipversion",
  839. translate("Force IP Version") )
  840. fipv.orientation = "horizontal"
  841. function fipv.cfgvalue(self, section)
  842. local value = AbstractValue.cfgvalue(self, section)
  843. if not has_force and value ~= "0" then
  844. self.description = bold_on .. font_red ..
  845. translate("Force IP Version not supported") .. font_off .. "<br />" ..
  846. translate("please disable") .. " !" .. bold_off
  847. else
  848. self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.")
  849. end
  850. return value
  851. end
  852. function fipv.validate(self, value)
  853. if (value == "1" and has_force) or value == "0" then return value end
  854. return nil, err_tab_adv(self) .. translate("Force IP Version not supported")
  855. end
  856. function fipv.parse(self, section)
  857. DDNS.flag_parse(self, section)
  858. end
  859. function fipv.write(self, section, value)
  860. if value == "1" then
  861. return self.map:set(section, self.option, value)
  862. else
  863. return self.map:del(section, self.option)
  864. end
  865. end
  866. end
  867. -- IPv4 + IPv6 - dns_server (NEW) -- ###########################################
  868. -- optional DNS Server to use resolving my IP if "ip_source"="web"
  869. dns = ns:taboption("advanced", Value, "dns_server",
  870. translate("DNS-Server"),
  871. translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" ..
  872. translate("Format: IP or FQDN"))
  873. dns.placeholder = "mydns.lan"
  874. function dns.validate(self, value)
  875. -- if .datatype is set, then it is checked before calling this function
  876. if not value then
  877. return "" -- ignore on empty
  878. elseif not DTYP.host(value) then
  879. return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address")
  880. else
  881. local ipv6 = usev6:formvalue(section)
  882. local force = (fipv) and fipv:formvalue(section) or "0"
  883. local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] ..
  884. value .. [[ ]] .. ipv6 .. [[ ]] .. force
  885. local ret = SYS.call(command)
  886. if ret == 0 then return value -- everything OK
  887. elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
  888. elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
  889. elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
  890. else return nil, err_tab_adv(self) .. translate("unspecific error")
  891. end
  892. end
  893. end
  894. -- IPv4 + IPv6 - force_dnstcp (NEW) -- #########################################
  895. if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then
  896. tcp = ns:taboption("advanced", Flag, "force_dnstcp",
  897. translate("Force TCP on DNS") )
  898. tcp.orientation = "horizontal"
  899. function tcp.cfgvalue(self, section)
  900. local value = AbstractValue.cfgvalue(self, section)
  901. if not has_dnstcp and value ~= "0" then
  902. self.description = bold_on .. font_red ..
  903. translate("DNS requests via TCP not supported") .. font_off .. "<br />" ..
  904. translate("please disable") .. " !" .. bold_off
  905. else
  906. self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.")
  907. end
  908. return value
  909. end
  910. function tcp.validate(self, value)
  911. if (value == "1" and has_dnstcp ) or value == "0" then
  912. return value
  913. end
  914. return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported")
  915. end
  916. function tcp.parse(self, section)
  917. DDNS.flag_parse(self, section)
  918. end
  919. end
  920. -- IPv4 + IPv6 - proxy (NEW) -- ################################################
  921. -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port]
  922. if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then
  923. pxy = ns:taboption("advanced", Value, "proxy",
  924. translate("PROXY-Server") )
  925. pxy.placeholder="user:password@myproxy.lan:8080"
  926. function pxy.cfgvalue(self, section)
  927. local value = AbstractValue.cfgvalue(self, section)
  928. if not has_proxy and value ~= "" then
  929. self.description = bold_on .. font_red ..
  930. translate("PROXY-Server not supported") .. font_off .. "<br />" ..
  931. translate("please remove entry") .. "!" .. bold_off
  932. else
  933. self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" ..
  934. translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" ..
  935. translate("IPv6 address must be given in square brackets") .. ": " ..
  936. bold_on .. " [2001:db8::1]:8080" .. bold_off
  937. end
  938. return value
  939. end
  940. function pxy.validate(self, value)
  941. -- if .datatype is set, then it is checked before calling this function
  942. if not value then
  943. return "" -- ignore on empty
  944. elseif has_proxy then
  945. local ipv6 = usev6:formvalue(section) or "0"
  946. local force = (fipv) and fipv:formvalue(section) or "0"
  947. local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] ..
  948. value .. [[ ]] .. ipv6 .. [[ ]] .. force
  949. local ret = SYS.call(command)
  950. if ret == 0 then return value
  951. elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host")
  952. elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect")
  953. elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched")
  954. elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing")
  955. else return nil, err_tab_adv(self) .. translate("unspecific error")
  956. end
  957. else
  958. return nil, err_tab_adv(self) .. translate("PROXY-Server not supported")
  959. end
  960. end
  961. end
  962. -- use_syslog -- ###############################################################
  963. slog = ns:taboption("advanced", ListValue, "use_syslog",
  964. translate("Log to syslog"),
  965. translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") )
  966. slog.default = "2"
  967. slog:value("0", translate("No logging"))
  968. slog:value("1", translate("Info"))
  969. slog:value("2", translate("Notice"))
  970. slog:value("3", translate("Warning"))
  971. slog:value("4", translate("Error"))
  972. -- use_logfile (NEW) -- ########################################################
  973. logf = ns:taboption("advanced", Flag, "use_logfile",
  974. translate("Log to file"),
  975. translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" ..
  976. translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] )
  977. logf.orientation = "horizontal"
  978. logf.rmempty = false -- we want to save in /etc/config/ddns file on "0" because
  979. logf.default = "1" -- if not defined write to log by default
  980. function logf.parse(self, section)
  981. DDNS.flag_parse(self, section)
  982. end
  983. -- TAB: Timer #####################################################################################
  984. -- check_interval -- ###########################################################
  985. ci = ns:taboption("timer", Value, "check_interval",
  986. translate("Check Interval") )
  987. ci.template = "ddns/detail_value"
  988. ci.default = 10
  989. ci.rmempty = false -- validate ourselves for translatable error messages
  990. function ci.validate(self, value)
  991. if not DTYP.uinteger(value)
  992. or tonumber(value) < 1 then
  993. return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
  994. end
  995. local secs = DDNS.calc_seconds(value, cu:formvalue(section))
  996. if secs >= 300 then
  997. return value
  998. else
  999. return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds")
  1000. end
  1001. end
  1002. function ci.write(self, section, value)
  1003. -- simulate rmempty=true remove default
  1004. local secs = DDNS.calc_seconds(value, cu:formvalue(section))
  1005. if secs ~= 600 then --default 10 minutes
  1006. return self.map:set(section, self.option, value)
  1007. else
  1008. self.map:del(section, "check_unit")
  1009. return self.map:del(section, self.option)
  1010. end
  1011. end
  1012. -- check_unit -- ###############################################################
  1013. cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error",
  1014. translate("Interval to check for changed IP" .. "<br />" ..
  1015. "Values below 5 minutes == 300 seconds are not supported") )
  1016. cu.template = "ddns/detail_lvalue"
  1017. cu.default = "minutes"
  1018. cu.rmempty = false -- want to control write process
  1019. cu:value("seconds", translate("seconds"))
  1020. cu:value("minutes", translate("minutes"))
  1021. cu:value("hours", translate("hours"))
  1022. --cu:value("days", translate("days"))
  1023. function cu.write(self, section, value)
  1024. -- simulate rmempty=true remove default
  1025. local secs = DDNS.calc_seconds(ci:formvalue(section), value)
  1026. if secs ~= 600 then --default 10 minutes
  1027. return self.map:set(section, self.option, value)
  1028. else
  1029. return true
  1030. end
  1031. end
  1032. -- force_interval (modified) -- ################################################
  1033. fi = ns:taboption("timer", Value, "force_interval",
  1034. translate("Force Interval") )
  1035. fi.template = "ddns/detail_value"
  1036. fi.default = 72 -- see dynamic_dns_updater.sh script
  1037. fi.rmempty = false -- validate ourselves for translatable error messages
  1038. function fi.validate(self, value)
  1039. if not DTYP.uinteger(value)
  1040. or tonumber(value) < 0 then
  1041. return nil, err_tab_timer(self) .. translate("minimum value '0'")
  1042. end
  1043. local force_s = DDNS.calc_seconds(value, fu:formvalue(section))
  1044. if force_s == 0 then
  1045. return value
  1046. end
  1047. local ci_value = ci:formvalue(section)
  1048. if not DTYP.uinteger(ci_value) then
  1049. return "" -- ignore because error in check_interval above
  1050. end
  1051. local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section))
  1052. if force_s >= check_s then
  1053. return value
  1054. end
  1055. return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'")
  1056. end
  1057. function fi.write(self, section, value)
  1058. -- simulate rmempty=true remove default
  1059. local secs = DDNS.calc_seconds(value, fu:formvalue(section))
  1060. if secs ~= 259200 then --default 72 hours == 3 days
  1061. return self.map:set(section, self.option, value)
  1062. else
  1063. self.map:del(section, "force_unit")
  1064. return self.map:del(section, self.option)
  1065. end
  1066. end
  1067. -- force_unit -- ###############################################################
  1068. fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error",
  1069. translate("Interval to force updates send to DDNS Provider" .. "<br />" ..
  1070. "Setting this parameter to 0 will force the script to only run once" .. "<br />" ..
  1071. "Values lower 'Check Interval' except '0' are not supported") )
  1072. fu.template = "ddns/detail_lvalue"
  1073. fu.default = "hours"
  1074. fu.rmempty = false -- want to control write process
  1075. --fu:value("seconds", translate("seconds"))
  1076. fu:value("minutes", translate("minutes"))
  1077. fu:value("hours", translate("hours"))
  1078. fu:value("days", translate("days"))
  1079. function fu.write(self, section, value)
  1080. -- simulate rmempty=true remove default
  1081. local secs = DDNS.calc_seconds(fi:formvalue(section), value)
  1082. if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days
  1083. return self.map:set(section, self.option, value)
  1084. else
  1085. return true
  1086. end
  1087. end
  1088. -- retry_count (NEW) -- ########################################################
  1089. rc = ns:taboption("timer", Value, "retry_count")
  1090. rc.title = translate("Error Retry Counter")
  1091. rc.description = translate("On Error the script will stop execution after given number of retrys")
  1092. .. "<br />"
  1093. .. translate("The default setting of '0' will retry infinite.")
  1094. rc.default = 0
  1095. rc.rmempty = false -- validate ourselves for translatable error messages
  1096. function rc.validate(self, value)
  1097. if not DTYP.uinteger(value) then
  1098. return nil, err_tab_timer(self) .. translate("minimum value '0'")
  1099. else
  1100. return value
  1101. end
  1102. end
  1103. function rc.write(self, section, value)
  1104. -- simulate rmempty=true remove default
  1105. if tonumber(value) ~= self.default then
  1106. return self.map:set(section, self.option, value)
  1107. else
  1108. return self.map:del(section, self.option)
  1109. end
  1110. end
  1111. -- retry_interval -- ###########################################################
  1112. ri = ns:taboption("timer", Value, "retry_interval",
  1113. translate("Error Retry Interval") )
  1114. ri.template = "ddns/detail_value"
  1115. ri.default = 60
  1116. ri.rmempty = false -- validate ourselves for translatable error messages
  1117. function ri.validate(self, value)
  1118. if not DTYP.uinteger(value)
  1119. or tonumber(value) < 1 then
  1120. return nil, err_tab_timer(self) .. translate("minimum value '1'")
  1121. else
  1122. return value
  1123. end
  1124. end
  1125. function ri.write(self, section, value)
  1126. -- simulate rmempty=true remove default
  1127. local secs = DDNS.calc_seconds(value, ru:formvalue(section))
  1128. if secs ~= 60 then --default 60seconds
  1129. return self.map:set(section, self.option, value)
  1130. else
  1131. self.map:del(section, "retry_unit")
  1132. return self.map:del(section, self.option)
  1133. end
  1134. end
  1135. -- retry_unit -- ###############################################################
  1136. ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error",
  1137. translate("On Error the script will retry the failed action after given time") )
  1138. ru.template = "ddns/detail_lvalue"
  1139. ru.default = "seconds"
  1140. ru.rmempty = false -- want to control write process
  1141. ru:value("seconds", translate("seconds"))
  1142. ru:value("minutes", translate("minutes"))
  1143. --ru:value("hours", translate("hours"))
  1144. --ru:value("days", translate("days"))
  1145. function ru.write(self, section, value)
  1146. -- simulate rmempty=true remove default
  1147. local secs = DDNS.calc_seconds(ri:formvalue(section), value)
  1148. if secs ~= 60 then --default 60seconds
  1149. return self.map:set(section, self.option, value)
  1150. else
  1151. return true -- will be deleted by retry_interval
  1152. end
  1153. end
  1154. -- TAB: LogView (NEW) #############################################################################
  1155. lv = ns:taboption("logview", DummyValue, "_logview")
  1156. lv.template = "ddns/detail_logview"
  1157. lv.inputtitle = translate("Read / Reread log file")
  1158. lv.rows = 50
  1159. function lv.cfgvalue(self, section)
  1160. local lfile=log_dir .. "/" .. section .. ".log"
  1161. if NXFS.access(lfile) then
  1162. return lfile .. "\n" .. translate("Please press [Read] button")
  1163. end
  1164. return lfile .. "\n" .. translate("File not found or empty")
  1165. end
  1166. return m