detail.lua 53 KB

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