diff options
Diffstat (limited to 'applications')
-rw-r--r-- | applications/luci-ddns/CHANGELOG | 25 | ||||
-rw-r--r-- | applications/luci-ddns/Makefile | 5 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/controller/ddns.lua | 113 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/model/cbi/ddns/detail.lua | 401 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/model/cbi/ddns/hints.lua | 92 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/model/cbi/ddns/overview.lua | 150 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/tools/ddns.lua | 141 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/view/ddns/overview_status.htm | 56 | ||||
-rw-r--r-- | applications/luci-ddns/luasrc/view/ddns/system_status.htm | 241 | ||||
-rwxr-xr-x | applications/luci-ddns/root/etc/uci-defaults/luci-ddns | 4 | ||||
-rwxr-xr-x | applications/luci-ddns/root/usr/lib/ddns/dynamic_dns_lucihelper.sh | 42 |
11 files changed, 755 insertions, 515 deletions
diff --git a/applications/luci-ddns/CHANGELOG b/applications/luci-ddns/CHANGELOG new file mode 100644 index 000000000..7848a14a0 --- /dev/null +++ b/applications/luci-ddns/CHANGELOG @@ -0,0 +1,25 @@ +Version: 2.1.0-1 +Date: 2014-11-09 +ddns-scripts: 2.1.0-1 or greater needed + +fix verify of entry for DNS server Issue #244 + https://github.com/openwrt/luci/issues/244 +add support for option 'update_script' +add display of version information when click on "Dynamic DNS" on overview page +add verify of installed ddns-scripts version and show as hint if not correct version +modified epoch to date conversation +cbi object Flag did not set section.changed state, fixed in tools.flag_parse function +ucitrack entry no longer needed and removed +minor fixes + +-------------------------------------------------------------------------------- +Version: 2.0.1-1 +Date: 2014-09-21 +ddns-scripts: 2.0.1-1 up to 2.0.1-9 + +New DDNS status in System->Status overview +New Overview page with option to start/stop a section +New Detail page with tabbed view incl. logfile viewer +Extended verify of all entries before save to config + incl. connect test to DNS- and Proxy-server +Support for all available options of ddns-scripts 1.x and 2.x diff --git a/applications/luci-ddns/Makefile b/applications/luci-ddns/Makefile index 0a723b61d..3f57d63c6 100644 --- a/applications/luci-ddns/Makefile +++ b/applications/luci-ddns/Makefile @@ -1,8 +1,3 @@ -# supports ddns-scripts 1.0.0-23 and ddns-scripts starting -# PKG_VERSION:=2.0.1 -# PKG_RELEASE:=8 -# PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> - PO = ddns include ../../build/config.mk diff --git a/applications/luci-ddns/luasrc/controller/ddns.lua b/applications/luci-ddns/luasrc/controller/ddns.lua index e59a2800c..952c9ffe3 100644 --- a/applications/luci-ddns/luasrc/controller/ddns.lua +++ b/applications/luci-ddns/luasrc/controller/ddns.lua @@ -16,15 +16,22 @@ $Id$ module("luci.controller.ddns", package.seeall) -require "nixio" -require "nixio.fs" -require "luci.sys" -require "luci.http" -require "luci.model.uci" -require "luci.dispatcher" -require "luci.tools.ddns" +local NX = require "nixio" +local NXFS = require "nixio.fs" +local DISP = require "luci.dispatcher" +local HTTP = require "luci.http" +local UCI = require "luci.model.uci" +local SYS = require "luci.sys" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions +local UTIL = require "luci.util" + +local luci_ddns_version = "2.1.0-1" -- luci-app-ddns / openwrt Makefile compatible version +local ddns_scripts_min = "2.1.0-1" -- minimum version of ddns-scripts required function index() + -- above 'require "mod"' definitions are not recognized + -- inside index() during initialisation + -- no configuration file, don't start if not nixio.fs.access("/etc/config/ddns") then return @@ -44,25 +51,27 @@ function index() entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), nil ).leaf = true entry( {"admin", "services", "ddns", "logview"}, call("logread") ).leaf = true - entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true entry( {"admin", "services", "ddns", "startstop"}, call("startstop") ).leaf = true + entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true end end -- function to read all sections status and return data array -function _get_status() - local uci = luci.model.uci.cursor() - local service = luci.sys.init.enabled("ddns") and 1 or 0 - local url_start = luci.dispatcher.build_url("admin", "system", "startup") - local data = {} -- Array to transfer data to javascript - - -- read application settings - local date_format = uci:get("ddns", "global", "date_format") or "%F %R" - local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns" +local function _get_status() + local uci = UCI.cursor() + local service = SYS.init.enabled("ddns") and 1 or 0 + local url_start = DISP.build_url("admin", "system", "startup") + local luci_build = DDNS.ipkg_version("luci-app-ddns").version + local ddns_act = DDNS.ipkg_version("ddns-scripts").version + local data = {} -- Array to transfer data to javascript data[#data+1] = { - enabled = service, -- service enabled - url_up = url_start -- link to enable DDS (System-Startup) + enabled = service, -- service enabled + url_up = url_start, -- link to enable DDS (System-Startup) + luci_ver = luci_ddns_version, -- luci-app-ddns / openwrt Makefile compatible version + luci_build = luci_build, -- installed luci build + script_min = ddns_scripts_min, -- minimum version of ddns-scripts needed + script_ver = ddns_act -- installed ddns-scripts } uci:foreach("ddns", "service", function (s) @@ -75,13 +84,13 @@ function _get_status() local datenext = "_empty_" -- formated date of next update -- get force seconds - local force_seconds = luci.tools.ddns.calc_seconds( + local force_seconds = DDNS.calc_seconds( tonumber(s["force_interval"]) or 72 , s["force_unit"] or "hours" ) -- get/validate pid and last update - local pid = luci.tools.ddns.get_pid(section, run_dir) - local uptime = luci.sys.uptime() - local lasttime = tonumber(nixio.fs.readfile("%s/%s.update" % { run_dir, section } ) or 0 ) + local pid = DDNS.get_pid(section) + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) if lasttime > uptime then -- /var might not be linked to /tmp lasttime = 0 -- and/or not cleared on reboot end @@ -96,10 +105,9 @@ function _get_status() -- sys.epoch - sys uptime + lastupdate(uptime) local epoch = os.time() - uptime + lasttime -- use linux date to convert epoch - datelast = luci.sys.exec([[/bin/date -d @]] .. epoch .. [[ +']] .. date_format .. [[']]) + datelast = DDNS.epoch2date(epoch) -- calc and fill next update - datenext = luci.sys.exec([[/bin/date -d @]] .. (epoch + force_seconds) .. - [[ +']] .. date_format .. [[']]) + datenext = DDNS.epoch2date(epoch + force_seconds) end -- process running but update needs to happen @@ -137,7 +145,7 @@ function _get_status() local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]] command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 .. [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver - local reg_ip = luci.sys.exec(command) + local reg_ip = SYS.exec(command) if reg_ip == "" then reg_ip = "_nodata_" end @@ -156,41 +164,38 @@ function _get_status() end) uci:unload("ddns") - return data end -- called by XHR.get from detail_logview.htm function logread(section) -- read application settings - local uci = luci.model.uci.cursor() + local uci = UCI.cursor() local log_dir = uci:get("ddns", "global", "log_dir") or "/var/log/ddns" local lfile=log_dir .. "/" .. section .. ".log" - local ldata=nixio.fs.readfile(lfile) + local ldata=NXFS.readfile(lfile) if not ldata or #ldata == 0 then ldata="_nodata_" end - luci.http.write(ldata) + uci:unload("ddns") + HTTP.write(ldata) end -- called by XHR.get from overview_status.htm function startstop(section, enabled) - -- Array to transfer data to javascript - local data = {} - -- read application settings - local uci = luci.model.uci.cursor() - local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns" + local uci = UCI.cursor() + local data = {} -- Array to transfer data to javascript -- if process running we want to stop and return - local pid = luci.tools.ddns.get_pid(section, run_dir) + local pid = DDNS.get_pid(section) if pid > 0 then - os.execute ([[kill -9 %s]] % pid) - nixio.nanosleep(2) -- 2 second "show time" + local tmp = NX.kill(pid, 15) -- terminate + NX.nanosleep(2) -- 2 second "show time" -- status changed so return full status data = _get_status() - luci.http.prepare_content("application/json") - luci.http.write_json(data) + HTTP.prepare_content("application/json") + HTTP.write_json(data) return end @@ -219,13 +224,12 @@ function startstop(section, enabled) end end end - end -- we can not execute because other -- uncommited changes pending, so exit here if not exec then - luci.http.write("_uncommited_") + HTTP.write("_uncommited_") return end @@ -237,17 +241,28 @@ function startstop(section, enabled) -- start dynamic_dns_updater.sh script os.execute ([[/usr/lib/ddns/dynamic_dns_updater.sh %s 0 > /dev/null 2>&1 &]] % section) - nixio.nanosleep(3) -- 3 seconds "show time" - + NX.nanosleep(3) -- 3 seconds "show time" + -- status changed so return full status data = _get_status() - luci.http.prepare_content("application/json") - luci.http.write_json(data) + HTTP.prepare_content("application/json") + HTTP.write_json(data) end -- called by XHR.poll from overview_status.htm function status() local data = _get_status() - luci.http.prepare_content("application/json") - luci.http.write_json(data) + HTTP.prepare_content("application/json") + HTTP.write_json(data) end + +-- check if installed ddns-scripts version < required version +function update_needed() + local sver = DDNS.ipkg_version("ddns-scripts") + local rver = UTIL.split(ddns_scripts_min, "[%.%-]", nil, true) + return (sver.major < (tonumber(rver[1]) or 0)) + or (sver.minor < (tonumber(rver[2]) or 0)) + or (sver.patch < (tonumber(rver[3]) or 0)) + or (sver.build < (tonumber(rver[4]) or 0)) +end + diff --git a/applications/luci-ddns/luasrc/model/cbi/ddns/detail.lua b/applications/luci-ddns/luasrc/model/cbi/ddns/detail.lua index c8d10f29b..8619d449d 100644 --- a/applications/luci-ddns/luasrc/model/cbi/ddns/detail.lua +++ b/applications/luci-ddns/luasrc/model/cbi/ddns/detail.lua @@ -21,50 +21,52 @@ You may obtain a copy of the License at $Id$ ]]-- -require "luci.dispatcher" -require "nixio.fs" -require "luci.sys" -require "luci.tools.webadmin" -require "luci.cbi.datatypes" -require "luci.tools.ddns" -- ddns multiused functions - --- takeover arguments +local NX = require "nixio" +local FS = require "nixio.fs" +local SYS = require "luci.sys" +local UTIL = require "luci.util" +local DISP = require "luci.dispatcher" +local WADM = require "luci.tools.webadmin" +local DTYP = require "luci.cbi.datatypes" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +-- takeover arguments -- ####################################################### section = arg[1] --- check supported options +-- check supported options -- ################################################## -- saved to local vars here because doing multiple os calls slow down the system -has_ipv6 = luci.tools.ddns.check_ipv6() -- IPv6 support -has_ssl = luci.tools.ddns.check_ssl() -- HTTPS support -has_proxy = luci.tools.ddns.check_proxy() -- Proxy support -has_dnstcp = luci.tools.ddns.check_bind_host() -- DNS TCP support +has_ipv6 = DDNS.check_ipv6() -- IPv6 support +has_ssl = DDNS.check_ssl() -- HTTPS support +has_proxy = DDNS.check_proxy() -- Proxy support +has_dnstcp = DDNS.check_bind_host() -- DNS TCP support has_force = has_ssl and has_dnstcp -- Force IP Protocoll --- html constants +-- html constants -- ########################################################### font_red = "<font color='red'>" font_off = "</font>" bold_on = "<strong>" bold_off = "</strong>" --- error text constants +-- error text constants -- ##################################################### err_ipv6_plain = translate("IPv6 not supported") .. " - " .. translate("please select 'IPv4' address version") -err_ipv6_basic = bold_on .. +err_ipv6_basic = bold_on .. font_red .. - translate("IPv6 not supported") .. - font_off .. - "<br />" .. translate("please select 'IPv4' address version") .. + translate("IPv6 not supported") .. + font_off .. + "<br />" .. translate("please select 'IPv4' address version") .. bold_off -err_ipv6_other = bold_on .. - font_red .. - translate("IPv6 not supported") .. - font_off .. - "<br />" .. translate("please select 'IPv4' address version in") .. " " .. - [[<a href="]] .. - luci.dispatcher.build_url("admin", "services", "ddns", "detail", section) .. +err_ipv6_other = bold_on .. + font_red .. + translate("IPv6 not supported") .. + font_off .. + "<br />" .. translate("please select 'IPv4' address version in") .. " " .. + [[<a href="]] .. + DISP.build_url("admin", "services", "ddns", "detail", section) .. "?tab.dns." .. section .. "=basic" .. - [[">]] .. - translate("Basic Settings") .. - [[</a>]] .. + [[">]] .. + translate("Basic Settings") .. + [[</a>]] .. bold_off function err_tab_basic(self) @@ -108,10 +110,10 @@ local function _verify_ip_source() _script = ips:formvalue(section) end - local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] .. - _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] .. + local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh get_local_ip ]] .. + _ipv6 .. [[ ]] .. _source .. [[ ]] .. _network .. [[ ]] .. _url .. [[ ]] .. _interface .. [[ ]] .. _script.. [[ ]] .. _proxy - local ret = luci.sys.call(command) + local ret = SYS.call(command) if ret == 0 then return true -- valid @@ -120,26 +122,36 @@ local function _verify_ip_source() end end --- cbi-map definition +-- cbi-map definition -- ####################################################### m = Map("ddns") -m.title = [[<a href="]] .. luci.dispatcher.build_url("admin", "services", "ddns") .. [[">]] .. - translate("Dynamic DNS") .. [[</a>]] +-- first need to close <a> from cbi map template our <a> closed by template +m.title = [[</a><a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] .. + translate("Dynamic DNS") m.description = translate("Dynamic DNS allows that your router can be reached with " .. "a fixed hostname while having a dynamically changing " .. "IP address.") -m.redirect = luci.dispatcher.build_url("admin", "services", "ddns") +m.redirect = DISP.build_url("admin", "services", "ddns") --- read application settings +m.on_after_commit = function(self) + if self.changed then -- changes ? + local pid = DDNS.get_pid(section) + if pid > 0 then -- running ? + local tmp = NX.kill(pid, 1) -- send SIGHUP + end + end +end + +-- read application settings -- ################################################ -- date format; if not set use ISO format date_format = m.uci:get(m.config, "global", "date_format") or "%F %R" -- log directory log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns" --- cbi-section definition -ns = m:section( NamedSection, section, "service", +-- cbi-section definition -- ################################################### +ns = m:section( NamedSection, section, "service", translate("Details for") .. ([[: <strong>%s</strong>]] % section), translate("Configure here the details for selected Dynamic DNS service") ) ns.instance = section -- arg [1] @@ -148,16 +160,19 @@ ns:tab("advanced", translate("Advanced Settings"), nil ) ns:tab("timer", translate("Timer Settings"), nil ) ns:tab("logview", translate("Log File Viewer"), nil ) --- TAB: Basic ################################################################## --- enabled -en = ns:taboption("basic", Flag, "enabled", +-- TAB: Basic ##################################################################################### +-- enabled -- ################################################################# +en = ns:taboption("basic", Flag, "enabled", translate("Enabled"), translate("If this service section is disabled it could not be started." .. "<br />" .. "Neither from LuCI interface nor from console") ) en.orientation = "horizontal" +function en.parse(self, section) + DDNS.flag_parse(self, section) +end --- use_ipv6 (NEW) -usev6 = ns:taboption("basic", ListValue, "use_ipv6", +-- use_ipv6 (NEW) -- ########################################################## +usev6 = ns:taboption("basic", ListValue, "use_ipv6", translate("IP address version"), translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") ) usev6.widget = "radio" @@ -187,8 +202,8 @@ function usev6.write(self, section, value) end end --- IPv4 - service_name -svc4 = ns:taboption("basic", ListValue, "ipv4_service_name", +-- IPv4 - service_name -- ###################################################### +svc4 = ns:taboption("basic", ListValue, "ipv4_service_name", translate("DDNS Service provider") .. " [IPv4]" ) svc4.default = "-" svc4:depends("use_ipv6", "0") -- only show on IPv4 @@ -206,11 +221,11 @@ if fd4 then fd4:close() end -for _, v in luci.util.vspairs(services4) do svc4:value(v) end +for _, v in UTIL.vspairs(services4) do svc4:value(v) end svc4:value("-", translate("-- custom --") ) function svc4.cfgvalue(self, section) - local v = luci.tools.ddns.read_value(self, section, "service_name") + local v = DDNS.read_value(self, section, "service_name") if not v or #v == 0 then return "-" else @@ -236,8 +251,8 @@ function svc4.write(self, section, value) end end --- IPv6 - service_name -svc6 = ns:taboption("basic", ListValue, "ipv6_service_name", +-- IPv6 - service_name -- ###################################################### +svc6 = ns:taboption("basic", ListValue, "ipv6_service_name", translate("DDNS Service provider") .. " [IPv6]" ) svc6.default = "-" svc6:depends("use_ipv6", "1") -- only show on IPv6 @@ -258,11 +273,11 @@ if fd6 then fd6:close() end -for _, v in luci.util.vspairs(services6) do svc6:value(v) end +for _, v in UTIL.vspairs(services6) do svc6:value(v) end svc6:value("-", translate("-- custom --") ) function svc6.cfgvalue(self, section) - local v = luci.tools.ddns.read_value(self, section, "service_name") + local v = DDNS.read_value(self, section, "service_name") if not v or #v == 0 then return "-" else @@ -289,8 +304,8 @@ function svc6.write(self, section, value) end end --- IPv4/IPv6 - update_url -uurl = ns:taboption("basic", Value, "update_url", +-- IPv4/IPv6 - update_url -- ################################################### +uurl = ns:taboption("basic", Value, "update_url", translate("Custom update-URL"), translate("Update URL to be used for updating your DDNS Provider." .. "<br />" .. "Follow instructions you will find on their WEB page.") ) @@ -312,22 +327,22 @@ function uurl.validate(self, value) return nil, err_tab_basic(self) .. translate("either url or script could be set") end - local url = luci.tools.ddns.parse_url(value) + local url = DDNS.parse_url(value) if not url.scheme == "http" then return nil, err_tab_basic(self) .. translate("must start with 'http://'") elseif not url.query then return nil, err_tab_basic(self) .. "<QUERY> " .. translate("missing / required") elseif not url.host then return nil, err_tab_basic(self) .. "<HOST> " .. translate("missing / required") - elseif luci.sys.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then + elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host end return value end --- IPv4/IPv6 - update_script -ush = ns:taboption("basic", Value, "update_script", +-- IPv4/IPv6 - update_script -- ################################################ +ush = ns:taboption("basic", Value, "update_script", translate("Custom update-script"), translate("Custom update script to be used for updating your DDNS Provider.") ) ush:depends("ipv4_service_name", "-") @@ -346,29 +361,29 @@ function ush.validate(self, value) end elseif (#url > 0) then return nil, err_tab_basic(self) .. translate("either url or script could be set") - elseif not nixio.fs.access(value) then + elseif not FS.access(value) then return nil, err_tab_basic(self) .. translate("File not found") end return value end --- IPv4/IPv6 - domain -dom = ns:taboption("basic", Value, "domain", +-- IPv4/IPv6 - domain -- ####################################################### +dom = ns:taboption("basic", Value, "domain", translate("Hostname/Domain"), translate("Replaces [DOMAIN] in Update-URL") ) dom.rmempty = false dom.placeholder = "mypersonaldomain.dyndns.org" function dom.validate(self, value) - if not value + if not value or not (#value > 0) - or not luci.cbi.datatypes.hostname(value) then + or not DTYP.hostname(value) then return nil, err_tab_basic(self) .. translate("invalid - Sample") .. ": 'mypersonaldomain.dyndns.org'" else return value end end --- IPv4/IPv6 - username +-- IPv4/IPv6 - username -- ##################################################### user = ns:taboption("basic", Value, "username", translate("Username"), translate("Replaces [USERNAME] in Update-URL") ) @@ -380,7 +395,7 @@ function user.validate(self, value) return value end --- IPv4/IPv6 - password +-- IPv4/IPv6 - password -- ##################################################### pw = ns:taboption("basic", Value, "password", translate("Password"), translate("Replaces [PASSWORD] in Update-URL") ) @@ -393,9 +408,9 @@ function pw.validate(self, value) return value end --- IPv4/IPv6 - use_https (NEW) +-- IPv4/IPv6 - use_https (NEW) -- ############################################## if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then - https = ns:taboption("basic", Flag, "use_https", + https = ns:taboption("basic", Flag, "use_https", translate("Use HTTP Secure") ) https.orientation = "horizontal" https.rmempty = false -- force validate function @@ -410,6 +425,9 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then end return value end + function https.parse(self, section) + DDNS.flag_parse(self, section) + end function https.validate(self, value) if (value == "1" and has_ssl ) or value == "0" then return value end return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !" @@ -424,10 +442,10 @@ if has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then end end --- IPv4/IPv6 - cacert (NEW) +-- IPv4/IPv6 - cacert (NEW) -- ################################################# if has_ssl then - cert = ns:taboption("basic", Value, "cacert", - translate("Path to CA-Certificate"), + cert = ns:taboption("basic", Value, "cacert", + translate("Path to CA-Certificate"), translate("directory or path/file") .. "<br />" .. translate("or") .. bold_on .. " IGNORE " .. bold_off .. translate("to run HTTPS without verification of server certificates (insecure)") ) @@ -439,8 +457,8 @@ if has_ssl then return "" -- supress validate error if NOT https end if value then -- otherwise errors in datatype check - if luci.cbi.datatypes.directory(value) - or luci.cbi.datatypes.file(value) + if DTYP.directory(value) + or DTYP.file(value) or value == "IGNORE" then return value end @@ -450,8 +468,8 @@ if has_ssl then end end --- use_syslog -slog = ns:taboption("basic", ListValue, "use_syslog", +-- use_syslog -- ############################################################### +slog = ns:taboption("basic", ListValue, "use_syslog", translate("Log to syslog"), translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") ) slog.default = "0" @@ -461,18 +479,21 @@ slog:value("2", translate("Notice")) slog:value("3", translate("Warning")) slog:value("4", translate("Error")) --- use_logfile (NEW) -logf = ns:taboption("basic", Flag, "use_logfile", +-- use_logfile (NEW) -- ######################################################## +logf = ns:taboption("basic", Flag, "use_logfile", translate("Log to file"), translate("Writes detailed messages to log file. File will be truncated automatically.") .. "<br />" .. translate("File") .. [[: "]] .. log_dir .. [[/]] .. section .. [[.log"]] ) logf.orientation = "horizontal" logf.rmempty = false -- we want to save in /etc/config/ddns file on "0" because logf.default = "1" -- if not defined write to log by default +function logf.parse(self, section) + DDNS.flag_parse(self, section) +end --- TAB: Advanced ############################################################## --- IPv4 - ip_source -src4 = ns:taboption("advanced", ListValue, "ipv4_source", +-- TAB: Advanced ################################################################################## +-- IPv4 - ip_source -- ######################################################### +src4 = ns:taboption("advanced", ListValue, "ipv4_source", translate("IP address source") .. " [IPv4]", translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") ) src4:depends("use_ipv6", "0") -- IPv4 selected @@ -481,11 +502,11 @@ src4:value("network", translate("Network")) src4:value("web", translate("URL")) src4:value("interface", translate("Interface")) src4:value("script", translate("Script")) -function src4.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_source") +function src4.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_source") end function src4.validate(self, value) - if usev6:formvalue(section) == "1" then + if usev6:formvalue(section) == "1" then return "" -- ignore on IPv6 selected elseif not _verify_ip_source() then return nil, err_tab_adv(self) .. @@ -495,7 +516,7 @@ function src4.validate(self, value) end end function src4.write(self, section, value) - if usev6:formvalue(section) == "1" then + if usev6:formvalue(section) == "1" then return true -- ignore on IPv6 selected elseif value == "network" then self.map:del(section, "ip_url") -- delete not need parameters @@ -514,12 +535,12 @@ function src4.write(self, section, value) self.map:del(section, "ip_url") -- delete not need parameters self.map:del(section, "ip_interface") end - self.map:del(section, self.option) -- delete "ipv4_source" helper + self.map:del(section, self.option) -- delete "ipv4_source" helper return self.map:set(section, "ip_source", value) -- and write "ip_source end --- IPv6 - ip_source -src6 = ns:taboption("advanced", ListValue, "ipv6_source", +-- IPv6 - ip_source -- ######################################################### +src6 = ns:taboption("advanced", ListValue, "ipv6_source", translate("IP address source") .. " [IPv6]", translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") ) src6:depends("use_ipv6", 1) -- IPv6 selected @@ -528,14 +549,14 @@ src6:value("network", translate("Network")) src6:value("web", translate("URL")) src6:value("interface", translate("Interface")) src6:value("script", translate("Script")) -if not has_ipv6 then - src6.description = err_ipv6_other +if not has_ipv6 then + src6.description = err_ipv6_other end function src6.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_source") + return DDNS.read_value(self, section, "ip_source") end function src6.validate(self, value) - if usev6:formvalue(section) == "0" then + if usev6:formvalue(section) == "0" then return "" -- ignore on IPv4 selected elseif not has_ipv6 then return nil, err_tab_adv(self) .. err_ipv6_plain @@ -547,7 +568,7 @@ function src6.validate(self, value) end end function src6.write(self, section, value) - if usev6:formvalue(section) == "0" then + if usev6:formvalue(section) == "0" then return true -- ignore on IPv4 selected elseif value == "network" then self.map:del(section, "ip_url") -- delete not need parameters @@ -566,19 +587,19 @@ function src6.write(self, section, value) self.map:del(section, "ip_url") -- delete not need parameters self.map:del(section, "ip_interface") end - self.map:del(section, self.option) -- delete "ipv4_source" helper + self.map:del(section, self.option) -- delete "ipv4_source" helper return self.map:set(section, "ip_source", value) -- and write "ip_source end --- IPv4 - ip_network (default "wan") -ipn4 = ns:taboption("advanced", ListValue, "ipv4_network", +-- IPv4 - ip_network (default "wan") -- ######################################## +ipn4 = ns:taboption("advanced", ListValue, "ipv4_network", translate("Network") .. " [IPv4]", translate("Defines the network to read systems IPv4-Address from") ) ipn4:depends("ipv4_source", "network") ipn4.default = "wan" -luci.tools.webadmin.cbi_add_networks(ipn4) -function ipn4.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_network") +WADM.cbi_add_networks(ipn4) +function ipn4.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_network") end function ipn4.validate(self, value) if usev6:formvalue(section) == "1" @@ -599,24 +620,24 @@ function ipn4.write(self, section, value) else -- set also as "interface" for monitoring events changes/hot-plug self.map:set(section, "interface", value) - self.map:del(section, self.option) -- delete "ipv4_network" helper + self.map:del(section, self.option) -- delete "ipv4_network" helper return self.map:set(section, "ip_network", value) -- and write "ip_network" end end --- IPv6 - ip_network (default "wan6") -ipn6 = ns:taboption("advanced", ListValue, "ipv6_network", +-- IPv6 - ip_network (default "wan6") -- ####################################### +ipn6 = ns:taboption("advanced", ListValue, "ipv6_network", translate("Network") .. " [IPv6]" ) ipn6:depends("ipv6_source", "network") ipn6.default = "wan6" -luci.tools.webadmin.cbi_add_networks(ipn6) +WADM.cbi_add_networks(ipn6) if has_ipv6 then ipn6.description = translate("Defines the network to read systems IPv6-Address from") else ipn6.description = err_ipv6_other end function ipn6.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_network") + return DDNS.read_value(self, section, "ip_network") end function ipn6.validate(self, value) if usev6:formvalue(section) == "0" @@ -639,19 +660,19 @@ function ipn6.write(self, section, value) else -- set also as "interface" for monitoring events changes/hotplug self.map:set(section, "interface", value) - self.map:del(section, self.option) -- delete "ipv6_network" helper + self.map:del(section, self.option) -- delete "ipv6_network" helper return self.map:set(section, "ip_network", value) -- and write "ip_network" end end --- IPv4 - ip_url (default "checkip.dyndns.com") -iurl4 = ns:taboption("advanced", Value, "ipv4_url", +-- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################# +iurl4 = ns:taboption("advanced", Value, "ipv4_url", translate("URL to detect") .. " [IPv4]", translate("Defines the Web page to read systems IPv4-Address from") ) iurl4:depends("ipv4_source", "web") iurl4.default = "http://checkip.dyndns.com" function iurl4.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_url") + return DDNS.read_value(self, section, "ip_url") end function iurl4.validate(self, value) if usev6:formvalue(section) == "1" @@ -663,14 +684,12 @@ function iurl4.validate(self, value) return nil, err_tab_adv(self) .. translate("missing / required") end - local url = luci.tools.ddns.parse_url(value) + local url = DDNS.parse_url(value) if not (url.scheme == "http" or url.scheme == "https") then return nil, err_tab_adv(self) .. translate("must start with 'http://'") elseif not url.host then return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required") - elseif luci.sys.call([[nslookup ]] .. - url.host .. - [[>/dev/null 2>&1]]) ~= 0 then + elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host else return value @@ -688,8 +707,8 @@ function iurl4.write(self, section, value) end end --- IPv6 - ip_url (default "checkipv6.dyndns.com") -iurl6 = ns:taboption("advanced", Value, "ipv6_url", +-- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ########################### +iurl6 = ns:taboption("advanced", Value, "ipv6_url", translate("URL to detect") .. " [IPv6]" ) iurl6:depends("ipv6_source", "web") iurl6.default = "http://checkipv6.dyndns.com" @@ -699,7 +718,7 @@ else iurl6.description = err_ipv6_other end function iurl6.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "ip_url") + return DDNS.read_value(self, section, "ip_url") end function iurl6.validate(self, value) if usev6:formvalue(section) == "0" @@ -713,14 +732,12 @@ function iurl6.validate(self, value) return nil, err_tab_adv(self) .. translate("missing / required") end - local url = luci.tools.ddns.parse_url(value) + local url = DDNS.parse_url(value) if not (url.scheme == "http" or url.scheme == "https") then return nil, err_tab_adv(self) .. translate("must start with 'http://'") elseif not url.host then return nil, err_tab_adv(self) .. "<HOST> " .. translate("missing / required") - elseif luci.sys.call([[nslookup ]] .. - url.host .. - [[>/dev/null 2>&1]]) ~= 0 then + elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host else return value @@ -738,17 +755,17 @@ function iurl6.write(self, section, value) end end --- IPv4 + IPv6 - ip_interface -ipi = ns:taboption("advanced", ListValue, "ip_interface", +-- IPv4 + IPv6 - ip_interface -- ############################################### +ipi = ns:taboption("advanced", ListValue, "ip_interface", translate("Interface"), translate("Defines the interface to read systems IP-Address from") ) ipi:depends("ipv4_source", "interface") -- IPv4 ipi:depends("ipv6_source", "interface") -- or IPv6 -for _, v in pairs(luci.sys.net.devices()) do +for _, v in pairs(SYS.net.devices()) do -- show only interface set to a network -- and ignore loopback - net = luci.tools.webadmin.iface_get_network(v) - if net and net ~= "loopback" then + net = WADM.iface_get_network(v) + if net and net ~= "loopback" then ipi:value(v) end end @@ -767,14 +784,14 @@ function ipi.write(self, section, value) else -- get network from device to -- set also as "interface" for monitoring events changes/hotplug - local net = luci.tools.webadmin.iface_get_network(value) + local net = WADM.iface_get_network(value) self.map:set(section, "interface", net) return self.map:set(section, self.option, value) end end --- IPv4 + IPv6 - ip_script (NEW) -ips = ns:taboption("advanced", Value, "ip_script", +-- IPv4 + IPv6 - ip_script (NEW) -- ############################################ +ips = ns:taboption("advanced", Value, "ip_script", translate("Script"), translate("User defined script to read systems IP-Address") ) ips:depends("ipv4_source", "script") -- IPv4 @@ -784,8 +801,8 @@ function ips.validate(self, value) if (usev6:formvalue(section) == "0" and src4:formvalue(section) ~= "script") or (usev6:formvalue(section) == "1" and src6:formvalue(section) ~= "script") then return "" - elseif not value or not nixio.fs.access(value, "x") then - return nil, err_tab_adv(self) .. + elseif not value or not FS.access(value, "x") then + return nil, err_tab_adv(self) .. translate("not found or not executable - Sample: '/path/to/script.sh'") else return value @@ -800,19 +817,19 @@ function ips.write(self, section, value) end end --- IPv4 - interface - default "wan" +-- IPv4 - interface - default "wan" -- ######################################### -- event network to monitor changes/hotplug/dynamic_dns_updater.sh -- only needs to be set if "ip_source"="web" or "script" --- if "ip_source"="network" or "interface" we use their network -eif4 = ns:taboption("advanced", ListValue, "ipv4_interface", - translate("Event Network") .. " [IPv4]", +-- if "ip_source"="network" or "interface" we use their network +eif4 = ns:taboption("advanced", ListValue, "ipv4_interface", + translate("Event Network") .. " [IPv4]", translate("Network on which the ddns-updater scripts will be started") ) eif4:depends("ipv4_source", "web") eif4:depends("ipv4_source", "script") eif4.default = "wan" -luci.tools.webadmin.cbi_add_networks(eif4) +WADM.cbi_add_networks(eif4) function eif4.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "interface") + return DDNS.read_value(self, section, "interface") end function eif4.validate(self, value) if usev6:formvalue(section) == "1" @@ -829,35 +846,35 @@ function eif4.write(self, section, value) or src4:formvalue(section) == "interface" then return true -- ignore IPv6, network, interface else - self.map:del(section, self.option) -- delete "ipv4_interface" helper + self.map:del(section, self.option) -- delete "ipv4_interface" helper return self.map:set(section, "interface", value) -- and write "interface" end end --- IPv6 - interface (NEW) - default "wan6" +-- IPv6 - interface (NEW) - default "wan6" -- ################################## -- event network to monitor changes/hotplug (NEW) -- only needs to be set if "ip_source"="web" or "script" --- if "ip_source"="network" or "interface" we use their network -eif6 = ns:taboption("advanced", ListValue, "ipv6_interface", +-- if "ip_source"="network" or "interface" we use their network +eif6 = ns:taboption("advanced", ListValue, "ipv6_interface", translate("Event Network") .. " [IPv6]" ) eif6:depends("ipv6_source", "web") eif6:depends("ipv6_source", "script") eif6.default = "wan6" -luci.tools.webadmin.cbi_add_networks(eif6) -if not has_ipv6 then +WADM.cbi_add_networks(eif6) +if not has_ipv6 then eif6.description = err_ipv6_other else eif6.description = translate("Network on which the ddns-updater scripts will be started") end function eif6.cfgvalue(self, section) - return luci.tools.ddns.read_value(self, section, "interface") + return DDNS.read_value(self, section, "interface") end function eif6.validate(self, value) if usev6:formvalue(section) == "0" or src4:formvalue(section) == "network" or src4:formvalue(section) == "interface" then return "" -- ignore IPv4, network, interface - elseif not has_ipv6 then + elseif not has_ipv6 then return nil, err_tab_adv(self) .. err_ipv6_plain else return value @@ -869,12 +886,12 @@ function eif6.write(self, section, value) or src4:formvalue(section) == "interface" then return true -- ignore IPv4, network, interface else - self.map:del(section, self.option) -- delete "ipv6_interface" helper + self.map:del(section, self.option) -- delete "ipv6_interface" helper return self.map:set(section, "interface", value) -- and write "interface" end end --- IPv4 + IPv6 - force_ipversion (NEW) +-- IPv4 + IPv6 - force_ipversion (NEW) -- ###################################### -- optional to force wget/curl and host to use only selected IP version -- command parameter "-4" or "-6" if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then @@ -896,6 +913,9 @@ if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then if (value == "1" and has_force) or value == "0" then return value end return nil, err_tab_adv(self) .. translate("Force IP Version not supported") end + function fipv.parse(self, section) + DDNS.flag_parse(self, section) + end function fipv.write(self, section, value) if value == "1" then return self.map:set(section, self.option, value) @@ -905,9 +925,9 @@ if has_force or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then end end --- IPv4 + IPv6 - dns_server (NEW) +-- IPv4 + IPv6 - dns_server (NEW) -- ########################################### -- optional DNS Server to use resolving my IP if "ip_source"="web" -dns = ns:taboption("advanced", Value, "dns_server", +dns = ns:taboption("advanced", Value, "dns_server", translate("DNS-Server"), translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "<br />" .. translate("Format: IP or FQDN")) @@ -916,14 +936,14 @@ function dns.validate(self, value) -- if .datatype is set, then it is checked before calling this function if not value then return "" -- ignore on empty - elseif not luci.cbi.datatypes.hostname(value) then - return nil, err .. translate("use hostname, FQDN, IPv4- or IPv6-Address") + elseif not DTYP.host(value) then + return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address") else local ipv6 = usev6:formvalue(section) local force = (fipv) and fipv:formvalue(section) or "0" - local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] .. + local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_dns ]] .. value .. [[ ]] .. ipv6 .. [[ ]] .. force - local ret = luci.sys.call(command) + local ret = SYS.call(command) if ret == 0 then return value -- everything OK elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host") elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect") @@ -933,7 +953,7 @@ function dns.validate(self, value) end end --- IPv4 + IPv6 - force_dnstcp (NEW) +-- IPv4 + IPv6 - force_dnstcp (NEW) -- ######################################### if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then tcp = ns:taboption("advanced", Flag, "force_dnstcp", translate("Force TCP on DNS") ) @@ -955,12 +975,15 @@ if has_dnstcp or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then end return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported") end + function tcp.parse(self, section) + DDNS.flag_parse(self, section) + end end --- IPv4 + IPv6 - proxy (NEW) +-- IPv4 + IPv6 - proxy (NEW) -- ################################################ -- optional Proxy to use for http/https requests [user:password@]proxyhost[:port] if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then - pxy = ns:taboption("advanced", Value, "proxy", + pxy = ns:taboption("advanced", Value, "proxy", translate("PROXY-Server") ) pxy.placeholder="user:password@myproxy.lan:8080" function pxy.cfgvalue(self, section) @@ -972,8 +995,8 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then else self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "<br />" .. translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "<br />" .. - translate("IPv6 address must be given in square brackets") .. ": " .. - bold_on .. " [2001:db8::1]:8080" .. bold_off + translate("IPv6 address must be given in square brackets") .. ": " .. + bold_on .. " [2001:db8::1]:8080" .. bold_off end return value end @@ -981,12 +1004,12 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then -- if .datatype is set, then it is checked before calling this function if not value then return "" -- ignore on empty - elseif has_proxy then + elseif has_proxy then local ipv6 = usev6:formvalue(section) or "0" local force = (fipv) and fipv:formvalue(section) or "0" - local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] .. + local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh verify_proxy ]] .. value .. [[ ]] .. ipv6 .. [[ ]] .. force - local ret = luci.sys.call(command) + local ret = SYS.call(command) if ret == 0 then return value elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host") elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect") @@ -1000,21 +1023,21 @@ if has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then end end --- TAB: Timer ################################################################# --- check_interval -ci = ns:taboption("timer", Value, "check_interval", +-- TAB: Timer ##################################################################################### +-- check_interval -- ########################################################### +ci = ns:taboption("timer", Value, "check_interval", translate("Check Interval") ) ci.template = "ddns/detail_value" -ci.default = 10 +ci.default = 10 ci.rmempty = false -- validate ourselves for translatable error messages function ci.validate(self, value) - if not luci.cbi.datatypes.uinteger(value) + if not DTYP.uinteger(value) or tonumber(value) < 1 then return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds") end - local secs = luci.tools.ddns.calc_seconds(value, cu:formvalue(section)) - if secs >= 300 then + local secs = DDNS.calc_seconds(value, cu:formvalue(section)) + if secs >= 300 then return value else return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds") @@ -1022,7 +1045,7 @@ function ci.validate(self, value) end function ci.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(value, cu:formvalue(section)) + local secs = DDNS.calc_seconds(value, cu:formvalue(section)) if secs ~= 600 then --default 10 minutes return self.map:set(section, self.option, value) else @@ -1031,9 +1054,9 @@ function ci.write(self, section, value) end end --- check_unit +-- check_unit -- ############################################################### cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error", - translate("Interval to check for changed IP" .. "<br />" .. + translate("Interval to check for changed IP" .. "<br />" .. "Values below 5 minutes == 300 seconds are not supported") ) cu.template = "ddns/detail_lvalue" cu.default = "minutes" @@ -1044,7 +1067,7 @@ cu:value("hours", translate("hours")) --cu:value("days", translate("days")) function cu.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(ci:formvalue(section), value) + local secs = DDNS.calc_seconds(ci:formvalue(section), value) if secs ~= 600 then --default 10 minutes return self.map:set(section, self.option, value) else @@ -1052,29 +1075,29 @@ function cu.write(self, section, value) end end --- force_interval (modified) -fi = ns:taboption("timer", Value, "force_interval", +-- force_interval (modified) -- ################################################ +fi = ns:taboption("timer", Value, "force_interval", translate("Force Interval") ) fi.template = "ddns/detail_value" fi.default = 72 -- see dynamic_dns_updater.sh script fi.rmempty = false -- validate ourselves for translatable error messages function fi.validate(self, value) - if not luci.cbi.datatypes.uinteger(value) + if not DTYP.uinteger(value) or tonumber(value) < 0 then return nil, err_tab_timer(self) .. translate("minimum value '0'") end - local force_s = luci.tools.ddns.calc_seconds(value, fu:formvalue(section)) - if force_s == 0 then + local force_s = DDNS.calc_seconds(value, fu:formvalue(section)) + if force_s == 0 then return value end local ci_value = ci:formvalue(section) - if not luci.cbi.datatypes.uinteger(ci_value) then + if not DTYP.uinteger(ci_value) then return "" -- ignore because error in check_interval above end - local check_s = luci.tools.ddns.calc_seconds(ci_value, cu:formvalue(section)) + local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section)) if force_s >= check_s then return value end @@ -1083,7 +1106,7 @@ function fi.validate(self, value) end function fi.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(value, fu:formvalue(section)) + local secs = DDNS.calc_seconds(value, fu:formvalue(section)) if secs ~= 259200 then --default 72 hours == 3 days return self.map:set(section, self.option, value) else @@ -1092,7 +1115,7 @@ function fi.write(self, section, value) end end --- force_unit +-- force_unit -- ############################################################### fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error", translate("Interval to force updates send to DDNS Provider" .. "<br />" .. "Setting this parameter to 0 will force the script to only run once" .. "<br />" .. @@ -1106,7 +1129,7 @@ fu:value("hours", translate("hours")) fu:value("days", translate("days")) function fu.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(fi:formvalue(section), value) + local secs = DDNS.calc_seconds(fi:formvalue(section), value) if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days return self.map:set(section, self.option, value) else @@ -1114,14 +1137,14 @@ function fu.write(self, section, value) end end --- retry_count (NEW) -rc = ns:taboption("timer", Value, "retry_count", +-- retry_count (NEW) -- ######################################################## +rc = ns:taboption("timer", Value, "retry_count", translate("Error Retry Counter"), translate("On Error the script will stop execution after given number of retrys") ) rc.default = 5 rc.rmempty = false -- validate ourselves for translatable error messages function rc.validate(self, value) - if not luci.cbi.datatypes.uinteger(value) + if not DTYP.uinteger(value) or tonumber(value) < 1 then return nil, err_tab_timer(self) .. translate("minimum value '1'") else @@ -1137,14 +1160,14 @@ function rc.write(self, section, value) end end --- retry_interval -ri = ns:taboption("timer", Value, "retry_interval", - translate("Error Retry Interval") ) +-- retry_interval -- ########################################################### +ri = ns:taboption("timer", Value, "retry_interval", + translate("Error Retry Interval") ) ri.template = "ddns/detail_value" ri.default = 60 ri.rmempty = false -- validate ourselves for translatable error messages function ri.validate(self, value) - if not luci.cbi.datatypes.uinteger(value) + if not DTYP.uinteger(value) or tonumber(value) < 1 then return nil, err_tab_timer(self) .. translate("minimum value '1'") else @@ -1153,7 +1176,7 @@ function ri.validate(self, value) end function ri.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(value, ru:formvalue(section)) + local secs = DDNS.calc_seconds(value, ru:formvalue(section)) if secs ~= 60 then --default 60seconds return self.map:set(section, self.option, value) else @@ -1162,7 +1185,7 @@ function ri.write(self, section, value) end end --- retry_unit +-- retry_unit -- ############################################################### ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error", translate("On Error the script will retry the failed action after given time") ) ru.template = "ddns/detail_lvalue" @@ -1174,7 +1197,7 @@ ru:value("minutes", translate("minutes")) --ru:value("days", translate("days")) function ru.write(self, section, value) -- simulate rmempty=true remove default - local secs = luci.tools.ddns.calc_seconds(ri:formvalue(section), value) + local secs = DDNS.calc_seconds(ri:formvalue(section), value) if secs ~= 60 then --default 60seconds return self.map:set(section, self.option, value) else @@ -1189,7 +1212,7 @@ lv.inputtitle = translate("Read / Reread log file") lv.rows = 50 function lv.cfgvalue(self, section) local lfile=log_dir .. "/" .. section .. ".log" - if nixio.fs.access(lfile) then + if FS.access(lfile) then return lfile .. "\n" .. translate("Please press [Read] button") end return lfile .. "\n" .. translate("File not found or empty") diff --git a/applications/luci-ddns/luasrc/model/cbi/ddns/hints.lua b/applications/luci-ddns/luasrc/model/cbi/ddns/hints.lua index d0d323c03..dfbc7468c 100644 --- a/applications/luci-ddns/luasrc/model/cbi/ddns/hints.lua +++ b/applications/luci-ddns/luasrc/model/cbi/ddns/hints.lua @@ -12,56 +12,74 @@ You may obtain a copy of the License at $Id$ ]]-- -require "luci.sys" -require "luci.dispatcher" -require "luci.tools.ddns" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DISP = require "luci.dispatcher" +local SYS = require "luci.sys" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions --- check supported options +-- check supported options -- ################################################## -- saved to local vars here because doing multiple os calls slow down the system -has_ssl = luci.tools.ddns.check_ssl() -- HTTPS support -has_proxy = luci.tools.ddns.check_proxy() -- Proxy support -has_dnstcp = luci.tools.ddns.check_bind_host() -- DNS TCP support +has_ssl = DDNS.check_ssl() -- HTTPS support +has_proxy = DDNS.check_proxy() -- Proxy support +has_dnstcp = DDNS.check_bind_host() -- DNS TCP support +need_update = CTRL.update_needed() -- correct ddns-scripts version -- html constants +font_red = [[<font color="red">]] +font_off = [[</font>]] bold_on = [[<strong>]] bold_off = [[</strong>]] --- cbi-map definition +-- cbi-map definition -- ####################################################### m = Map("ddns") -m.title = [[<a href="]] .. luci.dispatcher.build_url("admin", "services", "ddns") .. [[">]] .. - translate("Dynamic DNS") .. [[</a>]] +-- first need to close <a> from cbi map template our <a> closed by template +m.title = [[</a><a href="]] .. DISP.build_url("admin", "services", "ddns") .. [[">]] .. + translate("Dynamic DNS") m.description = translate("Dynamic DNS allows that your router can be reached with " .. "a fixed hostname while having a dynamically changing " .. "IP address.") -m.redirect = luci.dispatcher.build_url("admin", "services", "ddns") +m.redirect = DISP.build_url("admin", "services", "ddns") --- SimpleSection definition +-- SimpleSection definition -- ################################################# -- show Hints to optimize installation and script usage -s = m:section( SimpleSection, - translate("Hints"), +s = m:section( SimpleSection, + translate("Hints"), translate("Below a list of configuration tips for your system to run Dynamic DNS updates without limitations") ) + +-- ddns_scripts needs to be updated for full functionality +if need_update then + local dv = s:option(DummyValue, "_update_needed") + dv.titleref = DISP.build_url("admin", "system", "packages") + dv.rawhtml = true + dv.title = font_red .. bold_on .. + translate("Software update required") .. bold_off .. font_off + dv.value = translate("The currently installed 'ddns-scripts' package did not support all available settings.") .. + "<br />" .. + translate("Please update to the current version!") +end + -- DDNS Service disabled -if not luci.sys.init.enabled("ddns") then +if not SYS.init.enabled("ddns") then local dv = s:option(DummyValue, "_not_enabled") - dv.titleref = luci.dispatcher.build_url("admin", "system", "startup") + dv.titleref = DISP.build_url("admin", "system", "startup") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("DDNS Autostart disabled") .. bold_off - dv.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "<br />" .. + dv.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "<br />" .. "This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')" ) end -- No IPv6 support -if not luci.tools.ddns.check_ipv6() then +if not DDNS.check_ipv6() then local dv = s:option(DummyValue, "_no_ipv6") dv.titleref = 'http://www.openwrt.org" target="_blank' dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("IPv6 not supported") .. bold_off - dv.value = translate("IPv6 is currently not (fully) supported by this system" .. "<br />" .. + dv.value = translate("IPv6 is currently not (fully) supported by this system" .. "<br />" .. "Please follow the instructions on OpenWrt's homepage to enable IPv6 support" .. "<br />" .. "or update your system to the latest OpenWrt Release") end @@ -69,13 +87,13 @@ end -- No HTTPS support if not has_ssl then local dv = s:option(DummyValue, "_no_https") - dv.titleref = luci.dispatcher.build_url("admin", "system", "packages") + dv.titleref = DISP.build_url("admin", "system", "packages") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("HTTPS not supported") .. bold_off - dv.value = translate("Neither GNU Wget with SSL nor cURL installed to support updates via HTTPS protocol.") .. - "<br />- " .. - translate("You should install GNU Wget with SSL (prefered) or cURL package.") .. + dv.value = translate("Neither GNU Wget with SSL nor cURL installed to support updates via HTTPS protocol.") .. + "<br />- " .. + translate("You should install GNU Wget with SSL (prefered) or cURL package.") .. "<br />- " .. translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") end @@ -83,13 +101,13 @@ end -- cURL without proxy support if has_ssl and not has_proxy then local dv = s:option(DummyValue, "_no_proxy") - dv.titleref = luci.dispatcher.build_url("admin", "system", "packages") + dv.titleref = DISP.build_url("admin", "system", "packages") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("cURL without Proxy Support") .. bold_off - dv.value = translate("cURL is installed, but libcurl was compiled without proxy support.") .. - "<br />- " .. - translate("You should install GNU Wget with SSL or replace libcurl.") .. + dv.value = translate("cURL is installed, but libcurl was compiled without proxy support.") .. + "<br />- " .. + translate("You should install GNU Wget with SSL or replace libcurl.") .. "<br />- " .. translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") end @@ -97,12 +115,12 @@ end -- "Force IP Version not supported" if not (has_ssl and has_dnstcp) then local dv = s:option(DummyValue, "_no_force_ip") - dv.titleref = luci.dispatcher.build_url("admin", "system", "packages") + dv.titleref = DISP.build_url("admin", "system", "packages") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("Force IP Version not supported") .. bold_off local value = translate("BusyBox's nslookup and Wget do not support to specify " .. - "the IP version to use for communication with DDNS Provider.") + "the IP version to use for communication with DDNS Provider.") if not has_ssl then value = value .. "<br />- " .. translate("You should install GNU Wget with SSL (prefered) or cURL package.") @@ -117,11 +135,11 @@ end -- "DNS requests via TCP not supported" if not has_dnstcp then local dv = s:option(DummyValue, "_no_dnstcp") - dv.titleref = luci.dispatcher.build_url("admin", "system", "packages") + dv.titleref = DISP.build_url("admin", "system", "packages") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("DNS requests via TCP not supported") .. bold_off - dv.value = translate("BusyBox's nslookup does not support to specify to use TCP instead of default UDP when requesting DNS server") .. + dv.value = translate("BusyBox's nslookup does not support to specify to use TCP instead of default UDP when requesting DNS server") .. "<br />- " .. translate("You should install BIND host package for DNS requests.") end diff --git a/applications/luci-ddns/luasrc/model/cbi/ddns/overview.lua b/applications/luci-ddns/luasrc/model/cbi/ddns/overview.lua index a1ce9c3ae..5d24ee5a0 100644 --- a/applications/luci-ddns/luasrc/model/cbi/ddns/overview.lua +++ b/applications/luci-ddns/luasrc/model/cbi/ddns/overview.lua @@ -12,17 +12,20 @@ You may obtain a copy of the License at $Id$ ]]-- -require "nixio.fs" -require "luci.sys" -require "luci.dispatcher" -require "luci.tools.ddns" +local NXFS = require "nixio.fs" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DISP = require "luci.dispatcher" +local HTTP = require "luci.http" +local SYS = require "luci.sys" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- show hints ? -show_hints = not (luci.tools.ddns.check_ipv6() -- IPv6 support - and luci.tools.ddns.check_ssl() -- HTTPS support - and luci.tools.ddns.check_proxy() -- Proxy support - and luci.tools.ddns.check_bind_host() -- DNS TCP support +show_hints = not (DDNS.check_ipv6() -- IPv6 support + and DDNS.check_ssl() -- HTTPS support + and DDNS.check_proxy() -- Proxy support + and DDNS.check_bind_host() -- DNS TCP support ) +need_update = CTRL.update_needed() -- correct ddns-scripts version -- html constants font_red = [[<font color="red">]] @@ -30,70 +33,96 @@ font_off = [[</font>]] bold_on = [[<strong>]] bold_off = [[</strong>]] --- cbi-map definition -m = Map("ddns", - translate("Dynamic DNS"), - translate("Dynamic DNS allows that your router can be reached with " .. - "a fixed hostname while having a dynamically changing " .. - "IP address.")) +-- cbi-map definition -- ####################################################### +m = Map("ddns") --- read application settings -date_format = m.uci:get(m.config, "global", "date_format") or "%F %R" -run_dir = m.uci:get(m.config, "global", "run_dir") or "/var/run/ddns" +-- first need to close <a> from cbi map template our <a> closed by template +--m.title = [[</a><a href="javascript:alert(']] .. CTRL.show_versions() ..[[')">]] .. +-- translate("Dynamic DNS") +m.title = [[</a><a href="#" onclick="onclick_maptitle();">]] .. + translate("Dynamic DNS") --- SimpleSection definition +m.description = translate("Dynamic DNS allows that your router can be reached with " .. + "a fixed hostname while having a dynamically changing " .. + "IP address.") + +m.on_after_commit = function(self) + if self.changed then -- changes ? + if SYS.init.enabled("ddns") then -- ddns service enabled, restart all + os.execute("/etc/init.d/ddns restart") + else -- ddns service disabled, send SIGHUP to running + os.execute("killall -1 dynamic_dns_updater.sh") + end + end +end + +-- SimpleSection definiton -- ################################################## +-- with all the JavaScripts we need for "a good Show" +a = m:section( SimpleSection ) +a.template = "ddns/overview_status" + +-- SimpleSection definition -- ################################################# -- show Hints to optimize installation and script usage -- only show if service not enabled -- or no IPv6 support -- or not GNU Wget and not cURL (for https support) -- or not GNU Wget but cURL without proxy support -- or not BIND's host -if show_hints or not luci.sys.init.enabled("ddns") then +-- or ddns-scripts package need update +if show_hints or need_update or not SYS.init.enabled("ddns") then s = m:section( SimpleSection, translate("Hints") ) + + -- ddns_scripts needs to be updated for full functionality + if need_update then + local dv = s:option(DummyValue, "_update_needed") + dv.titleref = DISP.build_url("admin", "system", "packages") + dv.rawhtml = true + dv.title = font_red .. bold_on .. + translate("Software update required") .. bold_off .. font_off + dv.value = translate("The currently installed 'ddns-scripts' package did not support all available settings.") .. + "<br />" .. + translate("Please update to the current version!") + end + -- DDNS Service disabled - if not luci.sys.init.enabled("ddns") then + if not SYS.init.enabled("ddns") then local dv = s:option(DummyValue, "_not_enabled") - dv.titleref = luci.dispatcher.build_url("admin", "system", "startup") + dv.titleref = DISP.build_url("admin", "system", "startup") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("DDNS Autostart disabled") .. bold_off - dv.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "<br />" .. + dv.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "<br />" .. "You can start/stop each configuration here. It will run until next reboot.") end -- Show more hints on a separate page if show_hints then local dv = s:option(DummyValue, "_separate") - dv.titleref = luci.dispatcher.build_url("admin", "services", "ddns", "hints") + dv.titleref = DISP.build_url("admin", "services", "ddns", "hints") dv.rawhtml = true - dv.title = bold_on .. + dv.title = bold_on .. translate("Show more") .. bold_off dv.value = translate("Follow this link" .. "<br />" .. "You will find more hints to optimize your system to run DDNS scripts with all options") end end --- SimpleSection definiton --- with all the JavaScripts we need for "a good Show" -a = m:section( SimpleSection ) -a.template = "ddns/overview_status" - --- TableSection definition -ts = m:section( TypedSection, "service", - translate("Overview"), +-- TableSection definition -- ################################################## +ts = m:section( TypedSection, "service", + translate("Overview"), translate("Below is a list of configured DDNS configurations and their current state." .. "<br />" .. "If you want to send updates for IPv4 and IPv6 you need to define two separate Configurations " .. "i.e. 'myddns_ipv4' and 'myddns_ipv6'") ) ts.sectionhead = translate("Configuration") ts.template = "cbi/tblsection" ts.addremove = true -ts.extedit = luci.dispatcher.build_url("admin", "services", "ddns", "detail", "%s") +ts.extedit = DISP.build_url("admin", "services", "ddns", "detail", "%s") function ts.create(self, name) AbstractSection.create(self, name) - luci.http.redirect( self.extedit:format(name) ) + HTTP.redirect( self.extedit:format(name) ) end --- Domain and registered IP +-- Domain and registered IP -- ################################################# dom = ts:option(DummyValue, "_domainIP", translate("Hostname/Domain") .. "<br />" .. translate("Registered IP") ) dom.template = "ddns/overview_doubleline" @@ -113,32 +142,35 @@ function dom.set_two(self, section) local force_ipversion = tonumber(self.map:get(section, "force_ipversion") or 0) local force_dnstcp = tonumber(self.map:get(section, "force_dnstcp") or 0) local command = [[/usr/lib/ddns/dynamic_dns_lucihelper.sh]] - if not nixio.fs.access(command, "rwx", "rx", "rx") then - nixio.fs.chmod(command, 755) + if not NXFS.access(command, "rwx", "rx", "rx") then + NXFS.chmod(command, 755) end - command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 .. + command = command .. [[ get_registered_ip ]] .. domain .. [[ ]] .. use_ipv6 .. [[ ]] .. force_ipversion .. [[ ]] .. force_dnstcp .. [[ ]] .. dnsserver - local ip = luci.sys.exec(command) + local ip = SYS.exec(command) if ip == "" then ip = translate("no data") end return ip end --- enabled -ena = ts:option( Flag, "enabled", +-- enabled +ena = ts:option( Flag, "enabled", translate("Enabled")) ena.template = "ddns/overview_enabled" ena.rmempty = false +function ena.parse(self, section) + DDNS.flag_parse(self, section) +end -- show PID and next update -upd = ts:option( DummyValue, "_update", +upd = ts:option( DummyValue, "_update", translate("Last Update") .. "<br />" .. translate("Next Update")) upd.template = "ddns/overview_doubleline" function upd.set_one(self, section) -- fill Last Update -- get/validate last update - local uptime = luci.sys.uptime() - local lasttime = tonumber(nixio.fs.readfile("%s/%s.update" % { run_dir, section } ) or 0 ) + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) if lasttime > uptime then -- /var might not be linked to /tmp and cleared on reboot - lasttime = 0 + lasttime = 0 end -- no last update happen @@ -151,7 +183,7 @@ function upd.set_one(self, section) -- fill Last Update -- os.epoch - sys.uptime + lastupdate(uptime) local epoch = os.time() - uptime + lasttime -- use linux date to convert epoch - return luci.sys.exec([[/bin/date -d @]] .. epoch .. [[ +']] .. date_format .. [[']]) + return DDNS.epoch2date(epoch) end end function upd.set_two(self, section) -- fill Next Update @@ -162,28 +194,28 @@ function upd.set_two(self, section) -- fill Next Update -- get force seconds local force_interval = tonumber(self.map:get(section, "force_interval") or 72) local force_unit = self.map:get(section, "force_unit") or "hours" - local force_seconds = luci.tools.ddns.calc_seconds(force_interval, force_unit) + local force_seconds = DDNS.calc_seconds(force_interval, force_unit) -- get last update and get/validate PID - local uptime = luci.sys.uptime() - local lasttime = tonumber(nixio.fs.readfile("%s/%s.update" % { run_dir, section } ) or 0 ) + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) if lasttime > uptime then -- /var might not be linked to /tmp and cleared on reboot - lasttime = 0 + lasttime = 0 end - local pid = luci.tools.ddns.get_pid(section, run_dir) + local pid = DDNS.get_pid(section) -- calc next update if lasttime > 0 then local epoch = os.time() - uptime + lasttime + force_seconds -- use linux date to convert epoch - datelast = luci.sys.exec([[/bin/date -d @]] .. epoch .. [[ +']] .. date_format .. [[']]) - end + datelast = DDNS.epoch2date(epoch) + end - -- process running but update needs to happen + -- process running but update needs to happen if pid > 0 and ( lasttime + force_seconds - uptime ) < 0 then datenext = translate("Verify") - -- run once + -- run once elseif force_seconds == 0 then datenext = translate("Run once") @@ -191,7 +223,7 @@ function upd.set_two(self, section) -- fill Next Update elseif pid == 0 and enabled == 0 then datenext = translate("Disabled") - -- no process running and NOT + -- no process running and NOT elseif pid == 0 and enabled ~= 0 then datenext = translate("Stopped") end @@ -200,11 +232,11 @@ function upd.set_two(self, section) -- fill Next Update end -- start/stop button -btn = ts:option( Button, "_startstop", +btn = ts:option( Button, "_startstop", translate("Process ID") .. "<br />" .. translate("Start / Stop") ) btn.template = "ddns/overview_startstop" function btn.cfgvalue(self, section) - local pid = luci.tools.ddns.get_pid(section, run_dir) + local pid = DDNS.get_pid(section) if pid > 0 then btn.inputtitle = "PID: " .. pid btn.inputstyle = "reset" diff --git a/applications/luci-ddns/luasrc/tools/ddns.lua b/applications/luci-ddns/luasrc/tools/ddns.lua index 4172d84ae..8d56c92a1 100644 --- a/applications/luci-ddns/luasrc/tools/ddns.lua +++ b/applications/luci-ddns/luasrc/tools/ddns.lua @@ -1,7 +1,7 @@ --[[ LuCI - Lua Configuration Interface -shared module for luci-app-ddns-v2 +shared module for luci-app-ddns Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> function parse_url copied from https://svn.nmap.org/nmap/nselib/url.lua @@ -18,67 +18,128 @@ You may obtain a copy of the License at module("luci.tools.ddns", package.seeall) -require "luci.sys" -require "nixio.fs" +local NX = require "nixio" +local NXFS = require "nixio.fs" +local OPKG = require "luci.model.ipkg" +local UCI = require "luci.model.uci" +local SYS = require "luci.sys" +local UTIL = require "luci.util" +-- function to calculate seconds from given interval and unit +function calc_seconds(interval, unit) + if not tonumber(interval) then + return nil + elseif unit == "days" then + return (tonumber(interval) * 86400) -- 60 sec * 60 min * 24 h + elseif unit == "hours" then + return (tonumber(interval) * 3600) -- 60 sec * 60 min + elseif unit == "minutes" then + return (tonumber(interval) * 60) -- 60 sec + elseif unit == "seconds" then + return tonumber(interval) + else + return nil + end +end + +-- check if IPv6 supported by OpenWrt function check_ipv6() - return nixio.fs.access("/proc/net/ipv6_route") - and nixio.fs.access("/usr/sbin/ip6tables") + return NXFS.access("/proc/net/ipv6_route") + and NXFS.access("/usr/sbin/ip6tables") end +-- check if Wget with SSL support or cURL installed function check_ssl() - if (luci.sys.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then + if (SYS.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then return true else - return nixio.fs.access("/usr/bin/curl") + return NXFS.access("/usr/bin/curl") end end +-- check if Wget with SSL or cURL with proxy support installed function check_proxy() -- we prefere GNU Wget for communication - if (luci.sys.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then + if (SYS.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then return true -- if not installed cURL must support proxy - elseif nixio.fs.access("/usr/bin/curl") then - return (luci.sys.call([[ grep -iq all_proxy /usr/lib/libcurl.so* 2>/dev/null ]]) == 0) + elseif NXFS.access("/usr/bin/curl") then + return (SYS.call([[ grep -iq all_proxy /usr/lib/libcurl.so* 2>/dev/null ]]) == 0) -- only BusyBox Wget is installed else - return nixio.fs.access("/usr/bin/wget") + return NXFS.access("/usr/bin/wget") end end +-- check if BIND host installed function check_bind_host() - return nixio.fs.access("/usr/bin/host") + return NXFS.access("/usr/bin/host") end --- function to calculate seconds from given interval and unit -function calc_seconds(interval, unit) - if not tonumber(interval) then - return nil - elseif unit == "days" then - return (tonumber(interval) * 86400) -- 60 sec * 60 min * 24 h - elseif unit == "hours" then - return (tonumber(interval) * 3600) -- 60 sec * 60 min - elseif unit == "minutes" then - return (tonumber(interval) * 60) -- 60 sec - elseif unit == "seconds" then - return tonumber(interval) - else - return nil +-- convert epoch date to given format +function epoch2date(epoch, format) + if not format or #format < 2 then + local uci = UCI.cursor() + format = uci:get("ddns", "global", "date_format") or "%F %R" + uci:unload("ddns") end + format = format:gsub("%%n", "<br />") -- replace newline + format = format:gsub("%%t", " ") -- replace tab + return os.date(format, epoch) +end + +-- read lastupdate from [section].update file +function get_lastupd(section) + local uci = UCI.cursor() + local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns" + local etime = tonumber(NXFS.readfile("%s/%s.update" % { run_dir, section } ) or 0 ) + uci:unload("ddns") + return etime end -- read PID from run file and verify if still running -function get_pid(section, run_dir) - local pid = tonumber(nixio.fs.readfile("%s/%s.pid" % { run_dir, section } ) or 0 ) - if pid > 0 and not luci.sys.process.signal(pid, 0) then +function get_pid(section) + local uci = UCI.cursor() + local run_dir = uci:get("ddns", "global", "run_dir") or "/var/run/ddns" + local pid = tonumber(NXFS.readfile("%s/%s.pid" % { run_dir, section } ) or 0 ) + if pid > 0 and not NX.kill(pid, 0) then pid = 0 end + uci:unload("ddns") return pid end +-- read version information for given package if installed +function ipkg_version(package) + if not package then + return nil + end + local info = OPKG.info(package) + local data = {} + local version = "" + local i = 0 + for k, v in pairs(info) do + if v.Package == package and v.Status.installed then + version = v.Version + i = i + 1 + end + end + if i > 1 then -- more then one valid record + return data + end + local sver = UTIL.split(version, "[%.%-]", nil, true) + data = { + version = version, + major = tonumber(sver[1]) or 0, + minor = tonumber(sver[2]) or 0, + patch = tonumber(sver[3]) or 0, + build = tonumber(sver[4]) or 0 + } + return data +end + -- replacement of build-in read of UCI option -- modified AbstractValue.cfgvalue(self, section) from cbi.lua -- needed to read from other option then current value definition @@ -103,6 +164,28 @@ function read_value(self, section, option) end end +-- replacement of build-in Flag.parse of cbi.lua +-- modified to mark section as changed if value changes +-- current parse did not do this, but it is done AbstaractValue.parse() +function flag_parse(self, section) + local fexists = self.map:formvalue( + luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option) + + if fexists then + local fvalue = self:formvalue(section) and self.enabled or self.disabled + local cvalue = self:cfgvalue(section) + if fvalue ~= self.default or (not self.optional and not self.rmempty) then + self:write(section, fvalue) + else + self:remove(section) + end + if (fvalue ~= cvalue) then self.section.changed = true end + else + self:remove(section) + self.section.changed = true + end +end + ----------------------------------------------------------------------------- -- copied from https://svn.nmap.org/nmap/nselib/url.lua -- @author Diego Nehab diff --git a/applications/luci-ddns/luasrc/view/ddns/overview_status.htm b/applications/luci-ddns/luasrc/view/ddns/overview_status.htm index b0cc2fac3..e23cc796e 100644 --- a/applications/luci-ddns/luasrc/view/ddns/overview_status.htm +++ b/applications/luci-ddns/luasrc/view/ddns/overview_status.htm @@ -2,6 +2,12 @@ <!-- ++ BEGIN ++ Dynamic DNS ++ overview_status.htm ++ --> <script type="text/javascript">//<![CDATA[ + // variables to store version information + var luci_version + var luci_build + var ddns_version + var ddns_required + // helper to extract section from objects id // cbi.ddns.SECTION._xyz function _id2section(id) { @@ -14,7 +20,11 @@ // called by XHR.poll and onclick_startstop function _data2elements(data) { // DDNS Service - // data[0] ignored here + // fill Version informations + luci_version = data[0].luci_ver + luci_build = data[0].luci_build + ddns_version = data[0].script_ver + ddns_required = data[0].script_min // Service sections for( i = 1; i < data.length; i++ ) @@ -38,7 +48,7 @@ btn.className = "cbi-button cbi-input-apply"; } btn.disabled = false; // button enabled - + // last update switch (data[i].datelast) { case "_empty_": @@ -71,7 +81,7 @@ nup.innerHTML = '<em><%:Disabled%></em>'; btn.value = '----------'; btn.className = "cbi-button cbi-input-button"; // no image - btn.disabled = true; // disabled + btn.disabled = true; // disabled } break; default: @@ -84,13 +94,13 @@ // registered IP // rip.innerHTML = "Registered IP"; - if (data[i].domain == "_nodomain_") + if (data[i].domain == "_nodomain_") rip.innerHTML = ''; else if (data[i].reg_ip == "_nodata_") rip.innerHTML = '<em><%:No data%></em>'; else rip.innerHTML = data[i].reg_ip; - + // monitored interfacce // data[i].iface ignored here } @@ -121,13 +131,27 @@ } else { btn.value = '----------'; btn.className = "cbi-button cbi-input-button"; // no image - btn.disabled = true; // disabled - } + btn.disabled = true; // disabled + } + } + + // event handler for map.title link + function onclick_maptitle() { + var str = "<%:Version Information%>"; + str += "\n\nluci-app-ddns:"; + str += "\n\t<%:Version%>:\t" + luci_version; + str += "\n\t<%:Build%>:\t" + luci_build; + str += "\n\nddns-scripts <%:installed%>:"; + str += "\n\t<%:Version%>:\t" + ddns_version; + str += "\n\nddns-scripts <%:required%>:"; + str += "\n\t<%:Version%>:\t" + ddns_required + " <%:or greater%>"; + str += "\n\n" + alert(str); } // event handler for start/stop button function onclick_startstop(id) { - // extract section + // extract section var section = _id2section(id); // get elements var cbx = document.getElementById("cbid.ddns." + section + ".enabled"); // Enabled @@ -158,14 +182,20 @@ ); } + // force to immediate show status on page load (not waiting for XHR.poll) + XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + function(x, data) { + _data2elements(data); + } + ); + // define only ONE XHR.poll in a page because if one is running it blocks the other one // optimum is to define on Map or Section Level from here you can reach all elements - // we need update every 30 seconds only - XHR.poll(30, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, - function(x, data) - { + // we need update every 15 seconds only + XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + function(x, data) { _data2elements(data); - } + } ); //]]></script> diff --git a/applications/luci-ddns/luasrc/view/ddns/system_status.htm b/applications/luci-ddns/luasrc/view/ddns/system_status.htm index d2c2b7e62..dcf06cc0b 100644 --- a/applications/luci-ddns/luasrc/view/ddns/system_status.htm +++ b/applications/luci-ddns/luasrc/view/ddns/system_status.htm @@ -1,127 +1,140 @@ <!-- ++ BEGIN ++ Dynamic DNS ++ system_status.htm ++ --> <script type="text/javascript">//<![CDATA[ - XHR.poll(10, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, - function(x, data) + // helper to move status data to the relevant + // screen objects + // called by XHR.poll and XHR.get + function _data2elements(x, data) { + var tbl = document.getElementById('ddns_status_table'); + // security check + if ( !(tbl) ) { return; } + + // clear all rows + while (tbl.rows.length > 1) + tbl.deleteRow(1); + + // variable for Modulo-Division use to set cbi-rowstyle-? (0 or 1) + var x = -1; + var i = 1; + + // no data => no ddns-scripts Version 2 installed + if ( !data ) { + var txt = '<br /><strong><font color="red"><%:Old version of ddns-scripts installed%></font>' ; + var url = '<a href="' ; + url += '<%=luci.dispatcher.build_url("admin", "system", "packages")%>' ; + url += '"><%:install update here%></a></strong>' ; + var tr = tbl.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1); + var td = tr.insertCell(-1); + td.colSpan = 2 ; + td.innerHTML = txt + " - " + url + tr.insertCell(-1).colSpan = 3 ; + return; + } + + // DDNS Service disabled + if (data[0].enabled == 0) { + var txt = '<strong><font color="red"><%:DDNS Autostart disabled%></font>' ; + var url = '<a href="' + data[0].url_up + '"><%:enable here%></a></strong>' ; + var tr = tbl.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1); + var td = tr.insertCell(-1); + td.colSpan = 2 ; + td.innerHTML = txt + " - " + url + tr.insertCell(-1).colSpan = 3 ; + x++ ; + } + + for( i = 1; i < data.length; i++ ) { - var tbl = document.getElementById('ddns_status_table'); - // security check - if ( !(tbl) ) { return; } - - // clear all rows - while (tbl.rows.length > 1) - tbl.deleteRow(1); - - // variable for Modulo-Division use to set cbi-rowstyle-? (0 or 1) - var x = -1; - var i = 1; - - // no data => no ddns-scripts Version 2 installed - if ( !data ) { - var txt = '<br /><strong><font color="red"><%:Old version of ddns-scripts installed%></font>' ; - var url = '<a href="' ; - url += '<%=luci.dispatcher.build_url("admin", "system", "packages")%>' ; - url += '"><%:install update here%></a></strong>' ; - var tr = tbl.insertRow(-1); - tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1); - var td = tr.insertCell(-1); - td.colSpan = 2 ; - td.innerHTML = txt + " - " + url - tr.insertCell(-1).colSpan = 3 ; - return; + var tr = tbl.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1) ; + + // configuration + tr.insertCell(-1).innerHTML = '<strong>' + data[i].section + '</strong>' ; + + // pid + // data[i].pid ignored here + + // last update + // data[i].datelast ignored here + + // next update + switch (data[i].datenext) { + case "_empty_": + tr.insertCell(-1).innerHTML = '<em><%:Unknown error%></em>' ; + break; + case "_stopped_": + tr.insertCell(-1).innerHTML = '<em><%:Stopped%></em>' ; + break; + case "_disabled_": + tr.insertCell(-1).innerHTML = '<em><%:Disabled%></em>' ; + break; + case "_noupdate_": + tr.insertCell(-1).innerHTML = '<em><%:Update error%></em>' ; + break; + case "_runonce_": + tr.insertCell(-1).innerHTML = '<em><%:Run once%></em>' ; + break; + case "_verify_": + tr.insertCell(-1).innerHTML = '<em><%:Verify%></em>'; + break; + default: + tr.insertCell(-1).innerHTML = data[i].datenext ; + break; } - // DDNS Service disabled - if (data[0].enabled == 0) { - var txt = '<strong><font color="red"><%:DDNS Autostart disabled%></font>' ; - var url = '<a href="' + data[0].url_up + '"><%:enable here%></a></strong>' ; - var tr = tbl.insertRow(-1); - tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1); - var td = tr.insertCell(-1); - td.colSpan = 2 ; - td.innerHTML = txt + " - " + url - tr.insertCell(-1).colSpan = 3 ; - x++ ; - } + // domain + if (data[i].domain == "_nodomain_") + tr.insertCell(-1).innerHTML = '<em><%:config error%></em>'; + else + tr.insertCell(-1).innerHTML = data[i].domain; - for( i = 1; i < data.length; i++ ) - { - var tr = tbl.insertRow(-1); - tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1) ; - - // configuration - tr.insertCell(-1).innerHTML = '<strong>' + data[i].section + '</strong>' ; - - // pid - // data[i].pid ignored here - - // last update - // data[i].datelast ignored here - - // next update - switch (data[i].datenext) { - case "_empty_": - tr.insertCell(-1).innerHTML = '<em><%:Unknown error%></em>' ; - break; - case "_stopped_": - tr.insertCell(-1).innerHTML = '<em><%:Stopped%></em>' ; - break; - case "_disabled_": - tr.insertCell(-1).innerHTML = '<em><%:Disabled%></em>' ; - break; - case "_noupdate_": - tr.insertCell(-1).innerHTML = '<em><%:Update error%></em>' ; - break; - case "_runonce_": - tr.insertCell(-1).innerHTML = '<em><%:Run once%></em>' ; - break; - case "_verify_": - tr.insertCell(-1).innerHTML = '<em><%:Verify%></em>'; - break; - default: - tr.insertCell(-1).innerHTML = data[i].datenext ; - break; - } - - // domain - if (data[i].domain == "_nodomain_") - tr.insertCell(-1).innerHTML = '<em><%:config error%></em>'; - else - tr.insertCell(-1).innerHTML = data[i].domain; - - // registered IP - switch (data[i].reg_ip) { - case "_nodomain_": - tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; - break; - case "_nodata_": - tr.insertCell(-1).innerHTML = '<em><%:No data%></em>'; - break; - case "_noipv6_": - tr.insertCell(-1).innerHTML = '<em><%:IPv6 not supported%></em>'; - break; - default: - tr.insertCell(-1).innerHTML = data[i].reg_ip; - break; - } - - // monitored interfacce - if (data[i].iface == "_nonet_") + // registered IP + switch (data[i].reg_ip) { + case "_nodomain_": tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; - else - tr.insertCell(-1).innerHTML = data[i].iface; + break; + case "_nodata_": + tr.insertCell(-1).innerHTML = '<em><%:No data%></em>'; + break; + case "_noipv6_": + tr.insertCell(-1).innerHTML = '<em><%:IPv6 not supported%></em>'; + break; + default: + tr.insertCell(-1).innerHTML = data[i].reg_ip; + break; } - if (tbl.rows.length == 1 || (data[0].enabled == 0 && tbl.rows.length == 2) ) { - var br = '<br />'; - if (tbl.rows.length > 1) - br = ''; - var tr = tbl.insertRow(-1); - tr.className = "cbi-section-table-row"; - var td = tr.insertCell(-1); - td.colSpan = 5; - td.innerHTML = '<em>' + br + '<%:There is no service configured.%></em>' ; - } + // monitored interfacce + if (data[i].iface == "_nonet_") + tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; + else + tr.insertCell(-1).innerHTML = data[i].iface; + } + + if (tbl.rows.length == 1 || (data[0].enabled == 0 && tbl.rows.length == 2) ) { + var br = '<br />'; + if (tbl.rows.length > 1) + br = ''; + var tr = tbl.insertRow(-1); + tr.className = "cbi-section-table-row"; + var td = tr.insertCell(-1); + td.colSpan = 5; + td.innerHTML = '<em>' + br + '<%:There is no service configured.%></em>' ; + } + } + + // force to immediate show status (not waiting for XHR.poll) + XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + function(x, data) { + _data2elements(x, data); + } + ); + + XHR.poll(10, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + function(x, data) { + _data2elements(x, data); } ); //]]></script> diff --git a/applications/luci-ddns/root/etc/uci-defaults/luci-ddns b/applications/luci-ddns/root/etc/uci-defaults/luci-ddns index f3bad5807..4849e59b7 100755 --- a/applications/luci-ddns/root/etc/uci-defaults/luci-ddns +++ b/applications/luci-ddns/root/etc/uci-defaults/luci-ddns @@ -1,10 +1,8 @@ #!/bin/sh -# needed for "Save and Apply" to restart ddns +# no longer needed for "Save and Apply" to restart ddns uci -q batch <<-EOF >/dev/null delete ucitrack.@ddns[-1] - add ucitrack ddns - set ucitrack.@ddns[-1].init="ddns" commit ucitrack EOF diff --git a/applications/luci-ddns/root/usr/lib/ddns/dynamic_dns_lucihelper.sh b/applications/luci-ddns/root/usr/lib/ddns/dynamic_dns_lucihelper.sh index 1782d1f03..3fc84748e 100755 --- a/applications/luci-ddns/root/usr/lib/ddns/dynamic_dns_lucihelper.sh +++ b/applications/luci-ddns/root/usr/lib/ddns/dynamic_dns_lucihelper.sh @@ -1,13 +1,14 @@ #!/bin/sh # /usr/lib/ddns/luci_dns_helper.sh # -# Written by Christian Schoenebeck in August 2014 to support: -# this script is used by luci-app-ddns +# Written in August 2014 +# by Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +# This script is used by luci-app-ddns # - getting registered IP # - check if possible to get local IP # - verifing given DNS- or Proxy-Server # -# variables in small chars are read from /etc/config/ddns +# variables in small chars are read from /etc/config/ddns as parameter given here # variables in big chars are defined inside these scripts as gloval vars # variables in big chars beginning with "__" are local defined inside functions only # set -vx #script debugger @@ -18,13 +19,13 @@ # set -vx #script debugger -# preset some variables wrong or not set in dynamic_dns_functions.sh -SECTION_ID="dynamic_dns_lucihelper" +# preset some variables, wrong or not set in dynamic_dns_functions.sh +SECTION_ID="lucihelper" LOGFILE="$LOGDIR/$SECTION_ID.log" -LUCI_HELPER="ACTIV" # supress verbose and critical logging +VERBOSE_MODE=0 # no console logging # global variables normally set by reading DDNS UCI configuration -use_logfile=0 -use_syslog=0 +use_syslog=0 # no syslog +use_logfile=0 # by default no logfile, can be changed here case "$1" in get_registered_ip) @@ -34,21 +35,24 @@ case "$1" in force_ipversion=${4:-"0"} # Force IP Version - default 0 - No force_dnstcp=${5:-"0"} # Force TCP on DNS - default 0 - No dns_server=${6:-""} # DNS server - default No DNS + write_log 7 "-----> get_registered_ip IP" get_registered_ip IP [ $? -ne 0 ] && IP="" echo -n "$IP" # suppress LF ;; verify_dns) - # $2 == dns-server to verify # no need for force_dnstcp because + # $2 : dns-server to verify # no need for force_dnstcp because # verify with nc (netcat) uses tcp anyway use_ipv6=${3:-"0"} # Use IPv6 - default IPv4 force_ipversion=${4:-"0"} # Force IP Version - default 0 - No + write_log 7 "-----> verify_dns '$2'" verify_dns "$2" ;; verify_proxy) - # $2 == proxy string to verify + # $2 : proxy string to verify use_ipv6=${3:-"0"} # Use IPv6 - default IPv4 force_ipversion=${4:-"0"} # Force IP Version - default 0 - No + write_log 7 "-----> verify_proxy '$2'" verify_proxy "$2" ;; get_local_ip) @@ -62,7 +66,7 @@ case "$1" in proxy="$8" # proxy if set force_ipversion="0" # not needed but must be set use_https="0" # not needed but must be set - [ -n "$proxy" -a "$ip_source" == "web" ] && { + [ -n "$proxy" -a "$ip_source" = "web" ] && { # proxy defined, used for ip_source=web export HTTP_PROXY="http://$proxy" export HTTPS_PROXY="http://$proxy" @@ -70,13 +74,17 @@ case "$1" in export https_proxy="http://$proxy" } # don't need IP only the return code - [ "$ip_source" == "web" -o "$ip_source" == "script"] && { - # we wait only 3 seconds for an + [ "$ip_source" = "web" -o "$ip_source" = "script" ] && { + # we wait only 3 seconds for an # answer from "web" or "script" - __timeout 3 -- get_local_ip IP - } || get_local_ip IP + write_log 7 "-----> timeout 3 -- get_local_ip IP" + timeout 3 -- get_local_ip IP + } || { + write_log 7 "-----> get_local_ip IP" + get_local_ip IP + } ;; - *) - return 1 + *) + return 255 ;; esac |