diff options
138 files changed, 8769 insertions, 2368 deletions
diff --git a/applications/luci-app-asterisk/Makefile b/applications/luci-app-asterisk/Makefile index bb4d4158d..f2f3cd1e7 100644 --- a/applications/luci-app-asterisk/Makefile +++ b/applications/luci-app-asterisk/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI Support for AHCPd +LUCI_TITLE:=LuCI Support for Asterisk LUCI_DEPENDS:=+ahcpd include ../../luci.mk diff --git a/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua b/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua index e1a084a98..025bc1795 100644 --- a/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua +++ b/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua @@ -2,8 +2,8 @@ -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. -require("luci.sys") -require("luci.ip") +local sys = require"luci.sys" +local ip = require "luci.ip" m = Map("coovachilli") @@ -16,8 +16,8 @@ s1:option( Value, "tundev" ).optional = true s1:option( Value, "txqlen" ).optional = true net = s1:option( Value, "net" ) -for _, route in ipairs(luci.sys.net.routes()) do - if route.device ~= "lo" and route.dest:prefix() < 32 then +for _, route in ipairs(ip.routes({ family = 4, type = 1 })) do + if route.dest:prefix() > 0 and route.dest:prefix() < 32 then net:value( route.dest:string() ) end end @@ -41,7 +41,7 @@ s2 = m:section(TypedSection, "dhcp") s2.anonymous = true dif = s2:option( Value, "dhcpif" ) -for _, nif in ipairs(luci.sys.net.devices()) do +for _, nif in ipairs(sys.net.devices()) do if nif ~= "lo" then dif:value(nif) end end diff --git a/applications/luci-app-ddns/Makefile b/applications/luci-app-ddns/Makefile index d116f06a4..f20b49e90 100644 --- a/applications/luci-app-ddns/Makefile +++ b/applications/luci-app-ddns/Makefile @@ -6,17 +6,33 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI Support for Dynamic DNS (ddns-scripts) -LUCI_DEPENDS:=+ddns-scripts +luci-mod-admin-full - PKG_NAME:=luci-app-ddns -PKG_VERSION:=2.1.0 -PKG_RELEASE:=4 -PKG_LICENSE:=Apache-2.0 -PKGARCH:=all +# Version == major.minor.patch +# increase on new functionality (minor) or patches (patch) +PKG_VERSION:=2.2.1 + +# Release == build +# increase on changes of translation files +PKG_RELEASE:=1 + +PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> -include ../../luci.mk +# LuCI specific settings +LUCI_TITLE:=LuCI Support for Dynamic DNS Client (ddns-scripts) +LUCI_DEPENDS:=+luci-mod-admin-full +ddns-scripts +LUCI_PKGARCH:=all + +define Package/$(PKG_NAME)/config +# shown in make menuconfig <Help> +help + $(LUCI_TITLE) + . + Version: $(PKG_VERSION)-$(PKG_RELEASE) + $(PKG_MAINTAINER) +endef + +include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-ddns/luasrc/controller/ddns.lua b/applications/luci-app-ddns/luasrc/controller/ddns.lua index 3cd52e5e3..0b82021e2 100644 --- a/applications/luci-app-ddns/luasrc/controller/ddns.lua +++ b/applications/luci-app-ddns/luasrc/controller/ddns.lua @@ -15,25 +15,28 @@ 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-4" -- luci-app-ddns / openwrt Makefile compatible version -local ddns_scripts_min = "2.1.0-3" -- minimum version of ddns-scripts required +DDNS_MIN = "2.2.0-1" -- minimum version of service required function index() - -- no services_ipv6 file or no dynamic_dns_lucihelper.sh - -- do NOT start - if not nixio.fs.access("/usr/lib/ddns/services_ipv6") - or not nixio.fs.access("/usr/lib/ddns/dynamic_dns_lucihelper.sh") then + local nxfs = require "nixio.fs" -- global definitions not available + local sys = require "luci.sys" -- in function index() + local ddns = require "luci.tools.ddns" -- ddns multiused functions + local verinst = ddns.ipkg_ver_installed("ddns-scripts") + local verok = ddns.ipkg_ver_compare(verinst, ">=", "2.0.0-0") + -- do NOT start it not ddns-scripts version 2.x + if not verok then return end -- no config create an empty one - if not nixio.fs.access("/etc/config/ddns") then - nixio.fs.writefile("/etc/config/ddns", "") + if not nxfs.access("/etc/config/ddns") then + nxfs.writefile("/etc/config/ddns", "") end entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59) entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), nil ).leaf = true + entry( {"admin", "services", "ddns", "global"}, cbi("ddns/global"), nil ).leaf = true entry( {"admin", "services", "ddns", "logview"}, call("logread") ).leaf = true entry( {"admin", "services", "ddns", "startstop"}, call("startstop") ).leaf = true entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true @@ -44,17 +47,11 @@ 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) - 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) @@ -63,8 +60,8 @@ local function _get_status() -- and enabled state local section = s[".name"] local enabled = tonumber(s["enabled"]) or 0 - local datelast = "_empty_" -- formated date of last update - local datenext = "_empty_" -- formated date of next update + local datelast = "_empty_" -- formatted date of last update + local datenext = "_empty_" -- formatted date of next update -- get force seconds local force_seconds = DDNS.calc_seconds( @@ -94,7 +91,7 @@ local function _get_status() end -- process running but update needs to happen - -- problems it force_seconds > uptime + -- problems if force_seconds > uptime force_seconds = (force_seconds > uptime) and uptime or force_seconds if pid > 0 and ( lasttime + force_seconds - uptime ) <= 0 then datenext = "_verify_" @@ -107,7 +104,7 @@ local function _get_status() elseif pid == 0 and enabled == 0 then datenext = "_disabled_" - -- no process running and NOT + -- no process running and enabled elseif pid == 0 and enabled ~= 0 then datenext = "_stopped_" end @@ -153,11 +150,11 @@ end -- called by XHR.get from detail_logview.htm function logread(section) -- read application settings - local uci = UCI.cursor() - local log_dir = uci:get("ddns", "global", "log_dir") or "/var/log/ddns" - local lfile=log_dir .. "/" .. section .. ".log" + 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 = NXFS.readfile(lfile) - local ldata=NXFS.readfile(lfile) if not ldata or #ldata == 0 then ldata="_nodata_" end @@ -168,10 +165,10 @@ end -- called by XHR.get from overview_status.htm function startstop(section, enabled) local uci = UCI.cursor() + local pid = DDNS.get_pid(section) local data = {} -- Array to transfer data to javascript -- if process running we want to stop and return - local pid = DDNS.get_pid(section) if pid > 0 then local tmp = NX.kill(pid, 15) -- terminate NX.nanosleep(2) -- 2 second "show time" @@ -182,7 +179,7 @@ function startstop(section, enabled) return end - -- read uncommited changes + -- read uncommitted changes -- we don't save and commit data from other section or other options -- only enabled will be done local exec = true @@ -210,9 +207,9 @@ function startstop(section, enabled) end -- we can not execute because other - -- uncommited changes pending, so exit here + -- uncommitted changes pending, so exit here if not exec then - HTTP.write("_uncommited_") + HTTP.write("_uncommitted_") return end @@ -238,14 +235,3 @@ function status() 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-app-ddns/luasrc/model/cbi/ddns/detail.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua index 057b8c8aa..40a66ef15 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua @@ -1,11 +1,11 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> -- Copyright 2013 Manuel Munz <freifunk at somakoma dot de> --- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +-- Copyright 2014-2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> -- Licensed to the public under the Apache License 2.0. local NX = require "nixio" -local FS = require "nixio.fs" +local NXFS = require "nixio.fs" local SYS = require "luci.sys" local UTIL = require "luci.util" local DISP = require "luci.dispatcher" @@ -14,21 +14,21 @@ local DTYP = require "luci.cbi.datatypes" local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- takeover arguments -- ####################################################### -section = arg[1] +local section = arg[1] -- check supported options -- ################################################## -- saved to local vars here because doing multiple os calls slow down the system -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 +local has_ipv6 = DDNS.check_ipv6() -- IPv6 support +local has_ssl = DDNS.check_ssl() -- HTTPS support +local has_proxy = DDNS.check_proxy() -- Proxy support +local has_dnstcp = DDNS.check_bind_host() -- DNS TCP support +local has_force = has_ssl and has_dnstcp -- Force IP Protocoll -- html constants -- ########################################################### -font_red = "<font color='red'>" -font_off = "</font>" -bold_on = "<strong>" -bold_off = "</strong>" +local font_red = "<font color='red'>" +local font_off = "</font>" +local bold_on = "<strong>" +local bold_off = "</strong>" -- error text constants -- ##################################################### err_ipv6_plain = translate("IPv6 not supported") .. " - " .. @@ -136,7 +136,10 @@ log_dir = m.uci:get(m.config, "global", "log_dir") or "/var/log/ddns" -- 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") ) + translate("Configure here the details for selected Dynamic DNS service.") + .. [[<br /><a href="http://wiki.openwrt.org/doc/uci/ddns#version_1x" target="_blank">]] + .. translate("For detailed information about parameter settings look here.") + .. [[</a>]] ) ns.instance = section -- arg [1] ns:tab("basic", translate("Basic Settings"), nil ) ns:tab("advanced", translate("Advanced Settings"), nil ) @@ -344,7 +347,7 @@ 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 FS.access(value) then + elseif not NXFS.access(value) then return nil, err_tab_basic(self) .. translate("File not found") end return value @@ -765,7 +768,7 @@ 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 (#value > 0) or not FS.access(split[1], "x") then + elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then return nil, err_tab_adv(self) .. translate("not found or not executable - Sample: '/path/to/script.sh'") else @@ -855,6 +858,34 @@ function eif6.write(self, section, value) end end +-- IPv4/IPv6 - bind_network -- ################################################# +if has_ssl or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then + bnet = ns:taboption("advanced", ListValue, "bind_network", + translate("Bind Network") ) + bnet:depends("ipv4_source", "web") + bnet:depends("ipv6_source", "web") + bnet.rmempty = true + bnet.default = "" + bnet:value("", translate("-- default --")) + WADM.cbi_add_networks(bnet) + function bnet.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not has_ssl and value ~= "" then + self.description = bold_on .. font_red .. + translate("Binding to a specific network not supported") .. font_off .. "<br />" .. + translate("please set to 'default'") .. " !" .. bold_off + else + self.description = translate("OPTIONAL: Network to use for communication") .. + "<br />" .. translate("Casual users should not change this setting") + end + return value + end + function bnet.validate(self, value) + if (value ~= "" and has_ssl ) or value == "" then return value end + return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !" + end +end + -- IPv4 + IPv6 - force_ipversion (NEW) -- ###################################### -- optional to force wget/curl and host to use only selected IP version -- command parameter "-4" or "-6" @@ -1198,7 +1229,7 @@ lv.inputtitle = translate("Read / Reread log file") lv.rows = 50 function lv.cfgvalue(self, section) local lfile=log_dir .. "/" .. section .. ".log" - if FS.access(lfile) then + if NXFS.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-app-ddns/luasrc/model/cbi/ddns/global.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua new file mode 100644 index 000000000..e1718739f --- /dev/null +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/global.lua @@ -0,0 +1,156 @@ +-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +-- Licensed to the public under the Apache License 2.0. + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local DISP = require "luci.dispatcher" +local SYS = require "luci.sys" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +-- cbi-map definition -- ####################################################### +local m = Map("ddns") + +-- 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 = DISP.build_url("admin", "services", "ddns") + +function m.commit_handler(self) + if self.changed then -- changes ? + os.execute("/etc/init.d/ddns reload &") -- reload configuration + end +end + +-- cbi-section definition -- ################################################### +local ns = m:section( NamedSection, "global", "ddns", + translate("Global Settings"), + translate("Configure here the details for all Dynamic DNS services including this LuCI application.") + .. [[<br /><strong>]] + .. translate("It is NOT recommended for casual users to change settings on this page.") + .. [[</strong><br />]] + .. [[<a href="http://wiki.openwrt.org/doc/uci/ddns#version_2x1" target="_blank">]] + .. translate("For detailed information about parameter settings look here.") + .. [[</a>]] + +-- section might not exist +function ns.cfgvalue(self, section) + if not self.map:get(section) then + self.map:set(section, nil, self.sectiontype) + end + return self.map:get(section) +end + +-- allow_local_ip -- ########################################################## +local ali = ns:option(Flag, "allow_local_ip") +ali.title = translate("Allow non-public IP's") +ali.description = translate("Non-public and by default blocked IP's") .. ":" + .. [[<br /><strong>IPv4: </strong>]] + .. "0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16" + .. [[<br /><strong>IPv6: </strong>]] + .. "::/32, f000::/4" +ali.reempty = true +ali.default = "0" +function ali.parse(self, section) + DDNS.flag_parse(self, section) +end +function ali.validate(self, value) + if value == self.default then + return "" -- default = empty + end + return value +end + +-- date_format -- ############################################################# +local df = ns:option(Value, "date_format") +df.title = translate("Date format") +df.description = [[<a href="http://www.cplusplus.com/reference/ctime/strftime/" target="_blank">]] + .. translate("For supported codes look here") + .. [[</a>]] +df.template = "ddns/global_value" +df.rmempty = true +df.default = "%F %R" +df.date_string = "" +function df.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) or self.default + local epoch = os.time() + self.date_string = DDNS.epoch2date(epoch, value) + return value +end +function df.validate(self, value) + if value == self.default then + return "" -- default = empty + end + return value +end + +-- run_dir -- ################################################################# +local rd = ns:option(Value, "run_dir") +rd.title = translate("Status directory") +rd.description = translate("Directory contains PID and other status information for each running section") +rd.rmempty = true +rd.default = "/var/run/ddns" +function rd.validate(self, value) + if value == self.default then + return "" -- default = empty + end + return value +end + +-- log_dir -- ################################################################# +local ld = ns:option(Value, "log_dir") +ld.title = translate("Log directory") +ld.description = translate("Directory contains Log files for each running section") +ld.rmempty = true +ld.default = "/var/log/ddns" +function ld.validate(self, value) + if value == self.default then + return "" -- default = empty + end + return value +end + +-- log_lines -- ############################################################### +local ll = ns:option(Value, "log_lines") +ll.title = translate("Log length") +ll.description = translate("Number of last lines stored in log files") +ll.rmempty = true +ll.default = "250" +ll.datatype = "and(uinteger,min(1))" +function ll.validate(self, value) + local n = tonumber(value) + if not n or math.floor(n) ~= n or n < 1 then + return nil, self.title .. ": " .. translate("minimum value '1'") + end + if value == self.default then + return "" -- default = empty + end + return value +end + +-- use_curl -- ################################################################ +if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) +and NXFS.access("/usr/bin/curl") then + local pc = ns:option(Flag, "use_curl") + pc.title = translate("Use cURL") + pc.description = translate("If both cURL and GNU Wget are installed, Wget is used by default.") + .. [[<br />]] + .. translate("To use cURL activate this option.") + pc.orientation = "horizontal" + pc.rmempty = true + pc.default = "0" + function pc.parse(self, section) + DDNS.flag_parse(self, section) + end + function pc.validate(self, value) + if value == self.default then + return "" -- default = empty + end + return value + end +end + +return m diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua index 15da0289f..ff7aa7a41 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua @@ -8,10 +8,11 @@ local DDNS = require "luci.tools.ddns" -- ddns multiused functions -- check supported options -- ################################################## -- saved to local vars here because doing multiple os calls slow down the system -has_ssl = DDNS.check_ssl() -- HTTPS support +has_ssl = DDNS.check_ssl() -- HTTPS support and --bind-network / --interface 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 +-- correct ddns-scripts version +need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN) -- html constants font_red = [[<font color="red">]] @@ -87,6 +88,22 @@ if not has_ssl then translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") end +-- No bind_network +if not has_ssl then + local dv = s:option(DummyValue, "_no_bind_network") + dv.titleref = DISP.build_url("admin", "system", "packages") + dv.rawhtml = true + dv.title = bold_on .. + translate("Binding to a specific network not supported") .. bold_off + dv.value = translate("Neither GNU Wget with SSL nor cURL installed to select a network to use for communication.") .. + "<br />- " .. + translate("You should install GNU Wget with SSL or cURL package.") .. + "<br />- " .. + translate("GNU Wget will use the IP of given network, cURL will use the physical interface.") .. + "<br />- " .. + translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") +end + -- cURL without proxy support if has_ssl and not has_proxy then local dv = s:option(DummyValue, "_no_proxy") diff --git a/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua b/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua index 2944ec52f..9e8df2d08 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/overview.lua @@ -14,7 +14,8 @@ show_hints = not (DDNS.check_ipv6() -- IPv6 support and DDNS.check_proxy() -- Proxy support and DDNS.check_bind_host() -- DNS TCP support ) -need_update = CTRL.update_needed() -- correct ddns-scripts version +-- correct ddns-scripts version +need_update = DDNS.ipkg_ver_compare(DDNS.ipkg_ver_installed("ddns-scripts"), "<<", CTRL.DDNS_MIN) -- html constants font_red = [[<font color="red">]] @@ -26,10 +27,17 @@ bold_off = [[</strong>]] m = Map("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") +m.title = [[</a><a href="javascript:alert(']] + .. translate("Version Information") + .. [[\n\nluci-app-ddns]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("luci-app-ddns") + .. [[\n\nddns-scripts ]] .. translate("required") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.DDNS_MIN .. [[ ]] .. translate("or higher") + .. [[\n\nddns-scripts ]] .. translate("installed") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. DDNS.ipkg_ver_installed("ddns-scripts") + .. [[\n\n]] + .. [[')">]] + .. translate("Dynamic DNS") m.description = translate("Dynamic DNS allows that your router can be reached with " .. "a fixed hostname while having a dynamically changing " .. @@ -99,9 +107,13 @@ end -- 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'") ) + translate("Below is a list of configured DDNS configurations and their current state.") + .. "<br />" + .. translate("If you want to send updates for IPv4 and IPv6 you need to define two separate Configurations " + .. "i.e. 'myddns_ipv4' and 'myddns_ipv6'") + .. "<br />" + .. [[<a href="]] .. DISP.build_url("admin", "services", "ddns", "global") .. [[">]] + .. translate("To change global settings click here") .. [[</a>]] ) ts.sectionhead = translate("Configuration") ts.template = "cbi/tblsection" ts.addremove = true diff --git a/applications/luci-app-ddns/luasrc/tools/ddns.lua b/applications/luci-app-ddns/luasrc/tools/ddns.lua index 5c8ced50b..6d5393146 100644 --- a/applications/luci-app-ddns/luasrc/tools/ddns.lua +++ b/applications/luci-app-ddns/luasrc/tools/ddns.lua @@ -35,7 +35,7 @@ end -- check if Wget with SSL support or cURL installed function check_ssl() - if (SYS.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then + if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) then return true else return NXFS.access("/usr/bin/curl") @@ -45,12 +45,12 @@ end -- check if Wget with SSL or cURL with proxy support installed function check_proxy() -- we prefere GNU Wget for communication - if (SYS.call([[ grep -iq "\+ssl" /usr/bin/wget 2>/dev/null ]]) == 0) then + if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) then return true -- if not installed cURL must support proxy elseif NXFS.access("/usr/bin/curl") then - return (SYS.call([[ grep -iq all_proxy /usr/lib/libcurl.so* 2>/dev/null ]]) == 0) + return (SYS.call([[ grep -i all_proxy /usr/lib/libcurl.so* >/dev/null 2>&1 ]]) == 0) -- only BusyBox Wget is installed else @@ -96,33 +96,71 @@ function get_pid(section) 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 +-- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>" +function ipkg_ver_compare(ver1, comp, ver2) + if not ver1 or not (#ver1 > 0) + or not ver2 or not (#ver2 > 0) + or not comp or not (#comp > 0) then return nil end + -- correct compare string + if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~=" + elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<=" + elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">=" + elseif comp == "=" or comp == "==" then comp = "==" + elseif comp == "<<" then comp = "<" + elseif comp == ">>" then comp = ">" + else return nil end + + local av1 = UTIL.split(ver1, "[%.%-]", nil, true) + local av2 = UTIL.split(ver2, "[%.%-]", nil, true) + + for i = 1, math.max(table.getn(av1),table.getn(av2)), 1 do + local s1 = av1[i] or "" + local s2 = av2[i] or "" + local n1 = tonumber(s1) + local n2 = tonumber(s2) + + -- one numeric and other empty string then set other to 0 + if n1 and not n2 and (not s2 or #s2 == 0) then n2 = 0 end + if n2 and not n1 and (not s1 or #s1 == 0) then n1 = 0 end + + local nc = (n1 and n2) -- numeric compare + + if nc then + -- first "not equal" found return true + if comp == "~=" and (n1 ~= n2) then return true end + -- first "lower" found return true + if (comp == "<" or comp == "<=") and (n1 < n2) then return true end + -- first "greater" found return true + if (comp == ">" or comp == ">=") and (n1 > n2) then return true end + -- not equal then return false + if (n1 ~= n2) then return false end + else + if comp == "~=" and (s1 ~= s2) then return true end + if (comp == "<" or comp == "<=") and (s1 < s2) then return true end + if (comp == ">" or comp == ">=") and (s1 > s2) then return true end + if (s1 ~= s2) then return false end end end - if i > 1 then -- more then one valid record - return data + -- all equal then true + return true +end + +-- read version information for given package if installed +function ipkg_ver_installed(pkg) + local version = nil + local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r") + if control then + local ln + repeat + ln = control:read("*l") + if ln and ln:match("^Version: ") then + version = ln:gsub("^Version: ", "") + break + end + until not ln + control:close() 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 + return version end -- replacement of build-in read of UCI option diff --git a/applications/luci-app-ddns/luasrc/view/ddns/detail_lvalue.htm b/applications/luci-app-ddns/luasrc/view/ddns/detail_lvalue.htm index d516837b2..0bcfd4428 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/detail_lvalue.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/detail_lvalue.htm @@ -1,6 +1,6 @@ <!-- ++ BEGIN ++ Dynamic DNS ++ detail_lvalue.htm ++ --> -<!-- no value header to supress next line --> +<!-- no value header to suppress next line -->   <% if self.widget == "select" then %> <select class="cbi-input-select" onchange="cbi_d_update(this.id)"<%= attr("id", cbid) .. attr("name", cbid) .. ifattr(self.size, "size") %>> diff --git a/applications/luci-app-ddns/luasrc/view/ddns/detail_value.htm b/applications/luci-app-ddns/luasrc/view/ddns/detail_value.htm index 7cb28e282..cbe76abc6 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/detail_value.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/detail_value.htm @@ -5,5 +5,5 @@ attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) .. ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") %> /> -<!-- no value footer to supress next line --> +<!-- no value footer to suppress next line --> <!-- ++ END ++ Dynamic DNS ++ detail_value.htm ++ --> diff --git a/applications/luci-app-ddns/luasrc/view/ddns/global_value.htm b/applications/luci-app-ddns/luasrc/view/ddns/global_value.htm new file mode 100644 index 000000000..159cb6014 --- /dev/null +++ b/applications/luci-app-ddns/luasrc/view/ddns/global_value.htm @@ -0,0 +1,34 @@ + +<!-- ++ BEGIN ++ Dynamic DNS ++ global_value.htm ++ --> +<%+cbi/valueheader%> +<script type="text/javascript">//<![CDATA[ + // event handler on changed date + function onkeyup_date(value) { + var obj = document.getElementById("cbid.ddns.global.date_format.help"); + if ( !obj ) { return; } // security check + + if ( value == "" || value.length == 0 ) { value = "%F %R"; } + var now = new Date(); + var txt = now.toLocaleFormat(value); + // handle newline(%n) and tab(%t) needs to be converted to HTML + txt = txt.replace(new RegExp('\r?\n','g'), '<br />'); + txt = txt.replace(new RegExp('\t','g'), ' '); + obj.innerHTML = "<%:Current setting%>: <strong>" + txt + "<\/strong>"; + } +//]]></script> + +<input type="text" class="cbi-input-text" onchange="cbi_d_update(this.id)" onkeyup="onkeyup_date(this.value)" + <%= + attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) .. + ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") + %> +/> +<br /> +<div class="cbi-value-description"> + <span class="cbi-value-helpicon"><img src="<%=resource%>/cbi/help.gif" alt="<%:help%>" /><%=self.description%></span> + <br /> + <span id="<%=cbid%>.help" class="cbi-value-helpicon"><%:Current setting%>: <strong><%=self.date_string%></strong></span> +</div> <!-- div class="cbi-value-description" --> +</div> <!-- div class="cbi-value-field" --> +</div> <!-- div class="cbi-value cbi-value-last" --> +<!-- ++ END ++ Dynamic DNS ++ global_value.htm ++ --> diff --git a/applications/luci-app-ddns/luasrc/view/ddns/overview_startstop.htm b/applications/luci-app-ddns/luasrc/view/ddns/overview_startstop.htm index 8255aa63f..327028cbe 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/overview_startstop.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/overview_startstop.htm @@ -6,7 +6,7 @@ -- We need to garantie that function cfgvalue run first to set missing parameters %> <!-- style="font-size: 100%;" needed for openwrt theme to fix font size --> - <!-- type="button" onclick="..." enable standard onclick functionalty --> + <!-- type="button" onclick="..." enable standard onclick functionality --> <input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" style="font-size: 100%;" type="button" onclick="onclick_startstop(this.id)" <%= attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle) .. ifattr(self.disabled, "disabled") diff --git a/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm b/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm index 37c54be25..ea8e4a1e3 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/overview_status.htm @@ -2,12 +2,6 @@ <!-- ++ 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) { @@ -19,13 +13,6 @@ // screen objects // called by XHR.poll and onclick_startstop function _data2elements(data) { - // DDNS Service - // 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++ ) { @@ -135,27 +122,13 @@ } } - // 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 <%:required%>:"; - str += "\n\t<%:Version%>:\t" + ddns_required + " <%:or greater%>"; - str += "\n\nddns-scripts <%:installed%>:"; - str += "\n\t<%:Version%>:\t" + ddns_version; - str += "\n\n" - alert(str); - } - // event handler for start/stop button function onclick_startstop(id) { // extract section var section = _id2section(id); // get elements var cbx = document.getElementById("cbid.ddns." + section + ".enabled"); // Enabled - var obj = document.getElementById("cbi-ddns-overview-status-legend"); // objext defined below to make in-/visible + var obj = document.getElementById("cbi-ddns-overview-status-legend"); // object defined below to make in-/visible if ( !(obj && cbx) ) { return; } // security check // make me visible @@ -165,7 +138,7 @@ var btnXHR = new XHR(); btnXHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "startstop")%>/' + section + '/' + cbx.checked, null, function(x, data) { - if (x.responseText == "_uncommited_") { + if (x.responseText == "_uncommitted_") { // we need a trick to display Ampersand "&" in stead of "&" or "&" // after translation txt="<%:Please [Save & Apply] your changes first%>"; @@ -173,8 +146,7 @@ } else { // should have data because status changed // so update screen - if (data) - _data2elements(data); + if (data) { _data2elements(data); } } // make me invisible obj.parentNode.style.display = "none"; @@ -185,7 +157,7 @@ // 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); + if (data) { _data2elements(data); } } ); @@ -194,7 +166,7 @@ // we need update every 15 seconds only XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, data) { - _data2elements(data); + if (data) { _data2elements(data); } } ); diff --git a/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm b/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm index db9d1d1a0..4ca0abb0e 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm @@ -14,53 +14,38 @@ 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 i = -1; + var j = 1; // 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); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((j + i) % 2) + 1); var td = tr.insertCell(-1); td.colSpan = 2 ; td.innerHTML = txt + " - " + url tr.insertCell(-1).colSpan = 3 ; - x++ ; + i++ ; } - for( i = 1; i < data.length; i++ ) + for( j = 1; j < data.length; j++ ) { var tr = tbl.insertRow(-1); - tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((i + x) % 2) + 1) ; + tr.className = 'cbi-section-table-row cbi-rowstyle-' + (((j + i) % 2) + 1) ; // configuration - tr.insertCell(-1).innerHTML = '<strong>' + data[i].section + '</strong>' ; + tr.insertCell(-1).innerHTML = '<strong>' + data[j].section + '</strong>' ; // pid - // data[i].pid ignored here + // data[j].pid ignored here // last update - // data[i].datelast ignored here + // data[j].datelast ignored here // next update - switch (data[i].datenext) { + switch (data[j].datenext) { case "_empty_": tr.insertCell(-1).innerHTML = '<em><%:Unknown error%></em>' ; break; @@ -80,18 +65,18 @@ tr.insertCell(-1).innerHTML = '<em><%:Verify%></em>'; break; default: - tr.insertCell(-1).innerHTML = data[i].datenext ; + tr.insertCell(-1).innerHTML = data[j].datenext ; break; } // domain - if (data[i].domain == "_nodomain_") + if (data[j].domain == "_nodomain_") tr.insertCell(-1).innerHTML = '<em><%:config error%></em>'; else - tr.insertCell(-1).innerHTML = data[i].domain; + tr.insertCell(-1).innerHTML = data[j].domain; // registered IP - switch (data[i].reg_ip) { + switch (data[j].reg_ip) { case "_nodomain_": tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; break; @@ -102,15 +87,15 @@ tr.insertCell(-1).innerHTML = '<em><%:IPv6 not supported%></em>'; break; default: - tr.insertCell(-1).innerHTML = data[i].reg_ip; + tr.insertCell(-1).innerHTML = data[j].reg_ip; break; } - // monitored interfacce - if (data[i].iface == "_nonet_") + // monitored interface + if (data[j].iface == "_nonet_") tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; else - tr.insertCell(-1).innerHTML = data[i].iface; + tr.insertCell(-1).innerHTML = data[j].iface; } if (tbl.rows.length == 1 || (data[0].enabled == 0 && tbl.rows.length == 2) ) { @@ -128,15 +113,16 @@ // 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); + if (data) { _data2elements(x, data); } } ); - XHR.poll(10, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, data) { - _data2elements(x, data); + if (data) { _data2elements(x, data); } } ); + //]]></script> <fieldset class="cbi-section" id="ddns_status_section"> diff --git a/applications/luci-app-ddns/po/de/ddns.po b/applications/luci-app-ddns/po/de/ddns.po index 9706dbd10..f6e0d5e39 100644 --- a/applications/luci-app-ddns/po/de/ddns.po +++ b/applications/luci-app-ddns/po/de/ddns.po @@ -1,10 +1,10 @@ msgid "" msgstr "" "Project-Id-Version: luci-app-ddns\n" -"POT-Creation-Date: 2014-11-09 13:41+0100\n" -"PO-Revision-Date: 2014-11-09 14:29+0100\n" +"POT-Creation-Date: 2015-02-08 18:30+0100\n" +"PO-Revision-Date: 2015-02-08 18:36+0100\n" "Last-Translator: Christian Schoenebeck <christian.schoenebeck@gmail.com>\n" -"Language-Team: LANGUAGE <LL@li.org>\n" +"Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,6 +17,21 @@ msgstr "" msgid "&" msgstr "&" +msgid "-- custom --" +msgstr "-- benutzerdefiniert --" + +msgid "-- default --" +msgstr "-- Standard --" + +msgid "Advanced Settings" +msgstr "Erweiterte Einstellungen" + +msgid "Allow non-public IP's" +msgstr "Erlaube Nicht-öffentliche IPs" + +msgid "Applying changes" +msgstr "Änderungen anwenden" + msgid "Basic Settings" msgstr "Grundlegende Einstellungen" @@ -29,15 +44,14 @@ msgstr "" msgid "" "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'" msgstr "" -"Liste der definierten DDNS Konfigurationen und ihr aktueller Status.<br /" -">Wenn Sie Aktualisierungen für IPv4 und IPv6 senden möchten benötigen Sie " -"zwei Konfigurationen z.B. 'myddns_ipv4' und 'myddns_ipv6'" +"Liste der konfigurierten DDNS Konfigurationen und ihr momentaner Status." + +msgid "Bind Network" +msgstr "Bind-Netzwerk" -msgid "Build" -msgstr "Build" +msgid "Binding to a specific network not supported" +msgstr "'Bind' an ein bestimmtes Netzwerk wird nicht unterstützt" msgid "" "BusyBox's nslookup and Wget do not support to specify the IP version to use " @@ -53,20 +67,33 @@ msgstr "" "BusyBox's nslookup unterstützt es nicht das TCP-Protokoll für DNS Anfragen " "anstelle des standardmäßigen UDP-Protokolls." +msgid "Casual users should not change this setting" +msgstr "Standard Benutzer sollten diese Einstellung nicht ändern." + msgid "Check Interval" msgstr "Prüfinterval" -msgid "Check for changed IP every" -msgstr "Teste auf neue IP alle" - -msgid "Check-time unit" -msgstr "Zeiteinheit" +msgid "Collecting data..." +msgstr "Sammle Daten..." msgid "Config error" msgstr "Konfigurationsfehler" -msgid "Configure here the details for selected Dynamic DNS service" -msgstr "Konfiguriere hier die Details für den gewählten Dynamik DNS Dienst" +msgid "Configuration" +msgstr "Einstellungen" + +msgid "" +"Configure here the details for all Dynamic DNS services including this LuCI " +"application." +msgstr "" +"Konfiguriere hier die Details für alle Dynamik DNS Dienste einschließlich " +"dieser LuCI Anwendung." + +msgid "Configure here the details for selected Dynamic DNS service." +msgstr "Konfiguriere hier die Details für den gewählten Dynamik DNS Dienst." + +msgid "Current setting" +msgstr "Aktuelle Einstellung" msgid "" "Currently DDNS updates are not started at boot or on interface events.<br /" @@ -107,6 +134,9 @@ msgstr "DNS Anfragen über TCP nicht unterstützt" msgid "DNS-Server" msgstr "DNS-Server" +msgid "Date format" +msgstr "Datumsformat " + msgid "Defines the Web page to read systems IPv4-Address from" msgstr "" "Definiert die Web-Seite von der die aktuelle IPv4-Adresse des System gelesen " @@ -153,6 +183,19 @@ msgstr "" msgid "Details for" msgstr "Details für" +msgid "Directory contains Log files for each running section" +msgstr "" +"Das Verzeichnis enthält die Protokolldateien aller laufenden Konfigurationen." + +msgid "" +"Directory contains PID and other status information for each running section" +msgstr "" +"Das Verzeichnis enthält die PID und andere Statusinformationen aller " +"laufenden Konfigurationen." + +msgid "Disabled" +msgstr "Deaktiviert" + msgid "Dynamic DNS" msgstr "Dynamisches DNS" @@ -166,6 +209,12 @@ msgstr "" msgid "Enable secure communication with DDNS provider" msgstr "Aktiviert sichere Kommunikation mit dem DDNS Anbieter" +msgid "Enabled" +msgstr "Aktiviert" + +msgid "Error" +msgstr "Fehler" + msgid "Error Retry Counter" msgstr "Wiederholungszähler bei Fehler" @@ -175,8 +224,8 @@ msgstr "Wiederholungsintervall bei Fehler" msgid "Event Network" msgstr "Ereignis Netzwerk" -msgid "Event interface" -msgstr "Ereignis Netzwerk" +msgid "File" +msgstr "Datei" msgid "File not found" msgstr "Datei nicht gefunden" @@ -191,6 +240,13 @@ msgstr "" "Folgen Sie dem Link<br />Hier finden Sie weitere Hinweise um Ihr System für " "die Nutzung aller Optionen der DDNS Skripte zu optimieren." +msgid "For detailed information about parameter settings look here." +msgstr "" +"Detaillierte Informationen zu den Parametereinstellungen finden Sie hier." + +msgid "For supported codes look here" +msgstr "Unterstützte Kodierungen finden Sie hier." + msgid "Force IP Version" msgstr "Erzwinge IP-Version" @@ -203,12 +259,6 @@ msgstr "Erzwungene Aktualisierung" msgid "Force TCP on DNS" msgstr "Erzwinge TCP bei DNS-Anfragen" -msgid "Force update every" -msgstr "Erzwinge Aktualisierung alle" - -msgid "Force-time unit" -msgstr "Zeiteinheit" - msgid "Forced IP Version don't matched" msgstr "Erzwungene IP Version stimmt nicht überein" @@ -218,6 +268,16 @@ msgstr "Format" msgid "Format: IP or FQDN" msgstr "Format: IP-Adresse oder FQDN" +msgid "" +"GNU Wget will use the IP of given network, cURL will use the physical " +"interface." +msgstr "" +"GNU Wget verwendet die IP des gewählten Netzwerkes; cURL verwendet die " +"physikalische Schnittstelle." + +msgid "Global Settings" +msgstr "Globale Einstellungen" + msgid "HTTPS not supported" msgstr "HTTPS nicht unterstützt" @@ -233,6 +293,9 @@ msgstr "IP-Adressquelle" msgid "IP address version" msgstr "IP-Adressversion" +msgid "IPv4-Address" +msgstr "IPv4-Adresse" + msgid "IPv6 address must be given in square brackets" msgstr "Eine IPv6 Adresse muss in eckigen Klammern angegeben werden" @@ -248,6 +311,12 @@ msgstr "" msgid "IPv6 not supported" msgstr "IPv6 nicht unterstützt" +msgid "IPv6-Address" +msgstr "IPv6-Adresse" + +msgid "If both cURL and GNU Wget are installed, Wget is used by default." +msgstr "Wenn cURL und GNU Wget installiert sind, wird Wget verwendet." + msgid "" "If this service section is disabled it could not be started.<br />Neither " "from LuCI interface nor from console" @@ -256,11 +325,24 @@ msgstr "" "über das LuCI Web Interface noch von der Geräte-Konsole" msgid "" +"If you want to send updates for IPv4 and IPv6 you need to define two " +"separate Configurations i.e. 'myddns_ipv4' and 'myddns_ipv6'" +msgstr "" +"Wenn Sie Aktualisierungen für IPv4 und IPv6 senden möchten benötigen Sie " +"zwei Konfigurationen z.B. 'myddns_ipv4' und 'myddns_ipv6'" + +msgid "" "In some versions cURL/libcurl in OpenWrt is compiled without proxy support." msgstr "" "In einigen Versionen von OpenWrt wurde cURL/libcurl ohne Proxy Unterstützung " "compiliert." +msgid "Info" +msgstr "Informationen" + +msgid "Interface" +msgstr "Schnittstelle" + msgid "" "Interval to check for changed IP<br />Values below 5 minutes == 300 seconds " "are not supported" @@ -277,12 +359,26 @@ msgstr "" "werden.<br />Ein Wert von '0' führt das Skript nur einmalig aus. <br />Der " "Wert muss größer als das Prüfintervall sein oder '0'." +msgid "It is NOT recommended for casual users to change settings on this page." +msgstr "" +"Es wird nicht empfohlen, dass Standard Benutzer die Einstellungen auf dieser " +"Seite ändern." + msgid "Last Update" msgstr "Letztes Aktualisierung" +msgid "Loading" +msgstr "Lade" + msgid "Log File Viewer" msgstr "Protokolldatei" +msgid "Log directory" +msgstr "Protokoll-Verzeichnis" + +msgid "Log length" +msgstr "Protokolllänge" + msgid "Log to file" msgstr "Protokoll in Datei schreiben" @@ -290,12 +386,22 @@ msgid "Log to syslog" msgstr "Systemprotokoll verwenden" msgid "" +"Neither GNU Wget with SSL nor cURL installed to select a network to use for " +"communication." +msgstr "" +"Weder GNU Wget mit SSL noch cURL sind installiert um ein Netzwerk zur " +"Kommunikation festzulegen." + +msgid "" "Neither GNU Wget with SSL nor cURL installed to support updates via HTTPS " "protocol." msgstr "" "Weder GNU Wget mit SSL noch cURL sind installiert um Aktualisierungen über " "HTTPS Protokoll zu unterstützen." +msgid "Network" +msgstr "Netzwerk" + msgid "Network on which the ddns-updater scripts will be started" msgstr "Netzwerk auf dem Ereignisse die ddns-updater Skripte starten" @@ -311,6 +417,16 @@ msgstr "Keine Daten" msgid "No logging" msgstr "Keine Protokollierung" +msgid "Non-public and by default blocked IP's" +msgstr "Nicht-öffentliche und standardmäßig blockierte IPs." + +msgid "Notice" +msgstr "Notiz" + +msgid "Number of last lines stored in log files" +msgstr "" +"Anzahl der letzten Zeilen die in der Protokolldatei gespeichert werden." + msgid "OPTIONAL: Force the usage of pure IPv4/IPv6 only communication." msgstr "" "OPTIONAL: Erzwingt die Verwendung einer reinen IPv4/IPv6 Kommunikation." @@ -319,6 +435,9 @@ msgid "OPTIONAL: Force the use of TCP instead of default UDP on DNS requests." msgstr "" "OPTIONAL: Erzwingt die Verwendung von TCP anstelle von UDP bei DNS Anfragen." +msgid "OPTIONAL: Network to use for communication" +msgstr "OPTIONAL: Netzwerk das zur Kommunikation verwendet werden soll." + msgid "OPTIONAL: Proxy-Server for detection and updates." msgstr "OPTIONAL: Proxy-Server für Adresserkennung und Aktualisierungen" @@ -327,9 +446,6 @@ msgstr "" "OPTIONAL: Ersetzt den voreingestellten DNS-Server um die 'Registrierte IP' " "zu ermitteln." -msgid "Old version of ddns-scripts installed" -msgstr "Alte Version von ddns-scripts installiert" - msgid "On Error the script will retry the failed action after given time" msgstr "" "Bei Fehlern wird das Skript die fehlerhafte Aktion nach der gegebenen Zeit " @@ -338,12 +454,21 @@ msgstr "" msgid "On Error the script will stop execution after given number of retrys" msgstr "Das Skript wird nach der gegebener Anzahlt von Fehlversuchen beendet" +msgid "Overview" +msgstr "Ãœbersicht" + msgid "PROXY-Server" msgstr "Proxy-Server" msgid "PROXY-Server not supported" msgstr "Proxy-Server nicht unterstützt" +msgid "Password" +msgstr "Passwort" + +msgid "Path to CA-Certificate" +msgstr "Pfad zum CA-Zertifikat" + msgid "Please [Save & Apply] your changes first" msgstr "Bitte [Speichern & Anwenden] Sie Änderungen zunächst" @@ -377,21 +502,21 @@ msgstr "Einmalig ausführen" msgid "Script" msgstr "Skript" -msgid "Service" -msgstr "Dienst" - msgid "Show more" msgstr "Zeige mehr" msgid "Software update required" msgstr "Softwareaktualisierung nötig" -msgid "Source of IP address" -msgstr "Quelle der IP-Adresse" +msgid "Start" +msgstr "Start" msgid "Start / Stop" msgstr "Start / Stopp" +msgid "Status directory" +msgstr "Status-Verzeichnis" + msgid "Stopped" msgstr "Angehalten" @@ -408,6 +533,12 @@ msgstr "Kein Dienst konfiguriert" msgid "Timer Settings" msgstr "Zeitgeber Einstellungen" +msgid "To change global settings click here" +msgstr "Globale Einstellungen können sie hier ändern." + +msgid "To use cURL activate this option." +msgstr "Um cURL zu verwenden aktivieren sie diese Einstellung." + msgid "URL" msgstr "URL" @@ -430,14 +561,32 @@ msgstr "Aktualisierungsfehler" msgid "Use HTTP Secure" msgstr "Verwende sicheres HTTP" +msgid "Use cURL" +msgstr "Verwende cURL" + msgid "User defined script to read systems IP-Address" msgstr "" "Definiert das Skript mit dem die aktuelle IP-Adresse des System gelesen " "wird." +msgid "Username" +msgstr "Benutzername" + +msgid "Verify" +msgstr "überprüfen" + +msgid "Version" +msgstr "Version" + msgid "Version Information" msgstr "Versionsinformationen" +msgid "Waiting for changes to be applied..." +msgstr "Änderungen werden angewandt..." + +msgid "Warning" +msgstr "Warnung" + msgid "" "Writes detailed messages to log file. File will be truncated automatically." msgstr "" @@ -460,6 +609,9 @@ msgstr "" "Sie sollten das Programmpaket GNU Wget mit SSL (bevorzugt) oder cURL " "installieren." +msgid "You should install GNU Wget with SSL or cURL package." +msgstr "Sie sollten das Programmpaket GNU Wget mit SSL oder cURL installieren." + msgid "You should install GNU Wget with SSL or replace libcurl." msgstr "" "Sie sollten das Programmpaket GNU Wget mit SSL installieren oder libcurl " @@ -482,9 +634,6 @@ msgstr "Konnte Server nicht finden:" msgid "config error" msgstr "Konfigurationsfehler" -msgid "custom" -msgstr "benutzerdefiniert" - msgid "days" msgstr "Tage" @@ -500,27 +649,18 @@ msgstr "hier aktivieren" msgid "file or directory not found or not 'IGNORE'" msgstr "Datei oder Verzeichnis nicht gefunden oder nicht 'IGNORE'" -msgid "h" -msgstr "Stunden" +msgid "help" +msgstr "Hilfe" msgid "hours" msgstr "Stunden" -msgid "install update here" -msgstr "Aktualisierung hier installieren" - msgid "installed" msgstr "installiert" -msgid "interface" -msgstr "Schnittstelle" - msgid "invalid - Sample" msgstr "ungültig - Beispiel" -msgid "min" -msgstr "Minuten" - msgid "minimum value '0'" msgstr "Minimum Wert '0'" @@ -545,9 +685,6 @@ msgstr "muss mit 'http://' beginnen" msgid "nc (netcat) can not connect" msgstr "nc (netcat) kann keine Verbindung herstellen" -msgid "network" -msgstr "Netzwerk" - msgid "never" msgstr "nie" @@ -564,8 +701,8 @@ msgstr "nslookup kann den Namen nicht auflösen" msgid "or" msgstr "oder" -msgid "or greater" -msgstr "oder größer" +msgid "or higher" +msgstr "oder höher" msgid "please disable" msgstr "Bitte deaktivieren" @@ -579,6 +716,9 @@ msgstr "Bitte 'IPv4' Adressversion auswählen" msgid "please select 'IPv4' address version in" msgstr "Bitte 'IPv4' Adressversion auswählen in den" +msgid "please set to 'default'" +msgstr "Bitte auf 'Standard' setzen" + msgid "proxy port missing" msgstr "Proxy-Port fehlt" diff --git a/applications/luci-app-ddns/po/templates/ddns.pot b/applications/luci-app-ddns/po/templates/ddns.pot index c3b8c9367..6e4f2fe08 100644 --- a/applications/luci-app-ddns/po/templates/ddns.pot +++ b/applications/luci-app-ddns/po/templates/ddns.pot @@ -4,6 +4,21 @@ msgstr "Content-Type: text/plain; charset=UTF-8" msgid "&" msgstr "" +msgid "-- custom --" +msgstr "" + +msgid "-- default --" +msgstr "" + +msgid "Advanced Settings" +msgstr "" + +msgid "Allow non-public IP's" +msgstr "" + +msgid "Applying changes" +msgstr "" + msgid "Basic Settings" msgstr "" @@ -14,11 +29,12 @@ msgstr "" msgid "" "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'" msgstr "" -msgid "Build" +msgid "Bind Network" +msgstr "" + +msgid "Binding to a specific network not supported" msgstr "" msgid "" @@ -31,19 +47,30 @@ msgid "" "UDP when requesting DNS server" msgstr "" -msgid "Check Interval" +msgid "Casual users should not change this setting" msgstr "" -msgid "Check for changed IP every" +msgid "Check Interval" msgstr "" -msgid "Check-time unit" +msgid "Collecting data..." msgstr "" msgid "Config error" msgstr "" -msgid "Configure here the details for selected Dynamic DNS service" +msgid "Configuration" +msgstr "" + +msgid "" +"Configure here the details for all Dynamic DNS services including this LuCI " +"application." +msgstr "" + +msgid "Configure here the details for selected Dynamic DNS service." +msgstr "" + +msgid "Current setting" msgstr "" msgid "" @@ -78,6 +105,9 @@ msgstr "" msgid "DNS-Server" msgstr "" +msgid "Date format" +msgstr "" + msgid "Defines the Web page to read systems IPv4-Address from" msgstr "" @@ -109,6 +139,16 @@ msgstr "" msgid "Details for" msgstr "" +msgid "Directory contains Log files for each running section" +msgstr "" + +msgid "" +"Directory contains PID and other status information for each running section" +msgstr "" + +msgid "Disabled" +msgstr "" + msgid "Dynamic DNS" msgstr "" @@ -120,6 +160,12 @@ msgstr "" msgid "Enable secure communication with DDNS provider" msgstr "" +msgid "Enabled" +msgstr "" + +msgid "Error" +msgstr "" + msgid "Error Retry Counter" msgstr "" @@ -129,7 +175,7 @@ msgstr "" msgid "Event Network" msgstr "" -msgid "Event interface" +msgid "File" msgstr "" msgid "File not found" @@ -143,6 +189,12 @@ msgid "" "run DDNS scripts with all options" msgstr "" +msgid "For detailed information about parameter settings look here." +msgstr "" + +msgid "For supported codes look here" +msgstr "" + msgid "Force IP Version" msgstr "" @@ -155,19 +207,21 @@ msgstr "" msgid "Force TCP on DNS" msgstr "" -msgid "Force update every" +msgid "Forced IP Version don't matched" msgstr "" -msgid "Force-time unit" +msgid "Format" msgstr "" -msgid "Forced IP Version don't matched" +msgid "Format: IP or FQDN" msgstr "" -msgid "Format" +msgid "" +"GNU Wget will use the IP of given network, cURL will use the physical " +"interface." msgstr "" -msgid "Format: IP or FQDN" +msgid "Global Settings" msgstr "" msgid "HTTPS not supported" @@ -185,6 +239,9 @@ msgstr "" msgid "IP address version" msgstr "" +msgid "IPv4-Address" +msgstr "" + msgid "IPv6 address must be given in square brackets" msgstr "" @@ -197,15 +254,32 @@ msgstr "" msgid "IPv6 not supported" msgstr "" +msgid "IPv6-Address" +msgstr "" + +msgid "If both cURL and GNU Wget are installed, Wget is used by default." +msgstr "" + msgid "" "If this service section is disabled it could not be started.<br />Neither " "from LuCI interface nor from console" msgstr "" msgid "" +"If you want to send updates for IPv4 and IPv6 you need to define two " +"separate Configurations i.e. 'myddns_ipv4' and 'myddns_ipv6'" +msgstr "" + +msgid "" "In some versions cURL/libcurl in OpenWrt is compiled without proxy support." msgstr "" +msgid "Info" +msgstr "" + +msgid "Interface" +msgstr "" + msgid "" "Interval to check for changed IP<br />Values below 5 minutes == 300 seconds " "are not supported" @@ -217,12 +291,24 @@ msgid "" "Interval' except '0' are not supported" msgstr "" +msgid "It is NOT recommended for casual users to change settings on this page." +msgstr "" + msgid "Last Update" msgstr "" +msgid "Loading" +msgstr "" + msgid "Log File Viewer" msgstr "" +msgid "Log directory" +msgstr "" + +msgid "Log length" +msgstr "" + msgid "Log to file" msgstr "" @@ -230,10 +316,18 @@ msgid "Log to syslog" msgstr "" msgid "" +"Neither GNU Wget with SSL nor cURL installed to select a network to use for " +"communication." +msgstr "" + +msgid "" "Neither GNU Wget with SSL nor cURL installed to support updates via HTTPS " "protocol." msgstr "" +msgid "Network" +msgstr "" + msgid "Network on which the ddns-updater scripts will be started" msgstr "" @@ -249,19 +343,28 @@ msgstr "" msgid "No logging" msgstr "" +msgid "Non-public and by default blocked IP's" +msgstr "" + +msgid "Notice" +msgstr "" + +msgid "Number of last lines stored in log files" +msgstr "" + msgid "OPTIONAL: Force the usage of pure IPv4/IPv6 only communication." msgstr "" msgid "OPTIONAL: Force the use of TCP instead of default UDP on DNS requests." msgstr "" -msgid "OPTIONAL: Proxy-Server for detection and updates." +msgid "OPTIONAL: Network to use for communication" msgstr "" -msgid "OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'." +msgid "OPTIONAL: Proxy-Server for detection and updates." msgstr "" -msgid "Old version of ddns-scripts installed" +msgid "OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'." msgstr "" msgid "On Error the script will retry the failed action after given time" @@ -270,12 +373,21 @@ msgstr "" msgid "On Error the script will stop execution after given number of retrys" msgstr "" +msgid "Overview" +msgstr "" + msgid "PROXY-Server" msgstr "" msgid "PROXY-Server not supported" msgstr "" +msgid "Password" +msgstr "" + +msgid "Path to CA-Certificate" +msgstr "" + msgid "Please [Save & Apply] your changes first" msgstr "" @@ -309,21 +421,21 @@ msgstr "" msgid "Script" msgstr "" -msgid "Service" -msgstr "" - msgid "Show more" msgstr "" msgid "Software update required" msgstr "" -msgid "Source of IP address" +msgid "Start" msgstr "" msgid "Start / Stop" msgstr "" +msgid "Status directory" +msgstr "" + msgid "Stopped" msgstr "" @@ -338,6 +450,12 @@ msgstr "" msgid "Timer Settings" msgstr "" +msgid "To change global settings click here" +msgstr "" + +msgid "To use cURL activate this option." +msgstr "" + msgid "URL" msgstr "" @@ -358,12 +476,30 @@ msgstr "" msgid "Use HTTP Secure" msgstr "" +msgid "Use cURL" +msgstr "" + msgid "User defined script to read systems IP-Address" msgstr "" +msgid "Username" +msgstr "" + +msgid "Verify" +msgstr "" + +msgid "Version" +msgstr "" + msgid "Version Information" msgstr "" +msgid "Waiting for changes to be applied..." +msgstr "" + +msgid "Warning" +msgstr "" + msgid "" "Writes detailed messages to log file. File will be truncated automatically." msgstr "" @@ -379,6 +515,9 @@ msgstr "" msgid "You should install GNU Wget with SSL (prefered) or cURL package." msgstr "" +msgid "You should install GNU Wget with SSL or cURL package." +msgstr "" + msgid "You should install GNU Wget with SSL or replace libcurl." msgstr "" @@ -397,9 +536,6 @@ msgstr "" msgid "config error" msgstr "" -msgid "custom" -msgstr "" - msgid "days" msgstr "" @@ -415,27 +551,18 @@ msgstr "" msgid "file or directory not found or not 'IGNORE'" msgstr "" -msgid "h" +msgid "help" msgstr "" msgid "hours" msgstr "" -msgid "install update here" -msgstr "" - msgid "installed" msgstr "" -msgid "interface" -msgstr "" - msgid "invalid - Sample" msgstr "" -msgid "min" -msgstr "" - msgid "minimum value '0'" msgstr "" @@ -460,9 +587,6 @@ msgstr "" msgid "nc (netcat) can not connect" msgstr "" -msgid "network" -msgstr "" - msgid "never" msgstr "" @@ -478,7 +602,7 @@ msgstr "" msgid "or" msgstr "" -msgid "or greater" +msgid "or higher" msgstr "" msgid "please disable" @@ -493,6 +617,9 @@ msgstr "" msgid "please select 'IPv4' address version in" msgstr "" +msgid "please set to 'default'" +msgstr "" + msgid "proxy port missing" msgstr "" diff --git a/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm b/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm index 3726f643d..3c46e228f 100644 --- a/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm +++ b/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm @@ -6,7 +6,8 @@ for _, z in ipairs(fw:get_zones()) do if z:name() ~= "wan" then izl[#izl+1] = z - elseif z:name() ~= "lan" then + end + if z:name() ~= "lan" then ezl[#ezl+1] = z end end diff --git a/applications/luci-app-ocserv/luasrc/controller/ocserv.lua b/applications/luci-app-ocserv/luasrc/controller/ocserv.lua index bb7fe3f2e..91bddc73d 100644 --- a/applications/luci-app-ocserv/luasrc/controller/ocserv.lua +++ b/applications/luci-app-ocserv/luasrc/controller/ocserv.lua @@ -44,7 +44,7 @@ function ocserv_status() if not ln then break end local id, user, group, vpn_ip, ip, device, time, cipher, status = - ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+).*") + ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%(%)%:%.-_%w]+)%s+([%:%.-_%w]+).*") if id then fwd[#fwd+1] = { id = id, diff --git a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua index d354cf644..d1cc155fa 100644 --- a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua +++ b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua @@ -42,7 +42,7 @@ if fd then local ln found_pki = true elseif found_pki then local hash = ln:match("([a-f0-9]+)") - o_pki.default = hash and hash:upper() + o_pki.default = hash and "sha1:" .. hash:upper() complete = complete + 1 found_pki = false end @@ -90,6 +90,10 @@ local pip = s:taboption("general", Flag, "predictable_ips", translate("Predictab translate("The assigned IPs will be selected deterministically")) pip.default = "1" +local compr = s:taboption("general", Flag, "compression", translate("Enable compression"), + translate("Enable compression")) +compr.default = "1" + local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"), translate("Enable UDP channel support; this must be enabled unless you know what you are doing")) udp.default = "1" diff --git a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua index 2e530c084..c4be1818f 100644 --- a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua +++ b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua @@ -47,7 +47,7 @@ if fd then local ln if not ln then break end local id, user, group, vpn_ip, ip, device, time, cipher, status = - ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+).*") + ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%(%)%:%.-_%w]+)%s+([%:%.-_%w]+).*") if id then table.insert(lusers, {id, user, group, vpn_ip, ip, device, time, cipher, status}) end diff --git a/applications/luci-app-olsr-services/luasrc/view/freifunk-services/services.htm b/applications/luci-app-olsr-services/luasrc/view/freifunk-services/services.htm index 7142c77b3..0e59c6125 100644 --- a/applications/luci-app-olsr-services/luasrc/view/freifunk-services/services.htm +++ b/applications/luci-app-olsr-services/luasrc/view/freifunk-services/services.htm @@ -93,9 +93,9 @@ if luci.http.formvalue("status") == "1" then local rv = {} for k, line in ipairs(services) do local field = utl.split(line, "[#|]", split, true) - local origin_lnk = ip.IPv6(pcdata(field[4])) or "" + local origin_lnk = ip.IPv6(pcdata(field[4])) local origin_link = "" - if #origin_lnk ~= 0 and origin_lnk:is6() then + if origin_lnk and origin_lnk:is6() then origin_link = "["..origin_lnk:string().."]" else origin_link = pcdata(field[4]) @@ -168,9 +168,9 @@ end local field = {} -- split line at # and |, 1=url, 2=proto, 3=description, 4=source local field = utl.split(line, "[#|]", split, true) - local origin_lnk = ip.IPv6(pcdata(field[4])) or "" + local origin_lnk = ip.IPv6(pcdata(field[4])) local origin_link - if #origin_lnk ~= 0 and origin_lnk:is6() then + if origin_lnk and origin_lnk:is6() then origin_link = "["..origin_lnk:string().."]" else origin_link = pcdata(field[4]) diff --git a/applications/luci-app-olsr/luasrc/controller/olsr.lua b/applications/luci-app-olsr/luasrc/controller/olsr.lua index 361537290..74deb716c 100644 --- a/applications/luci-app-olsr/luasrc/controller/olsr.lua +++ b/applications/luci-app-olsr/luasrc/controller/olsr.lua @@ -1,5 +1,8 @@ module("luci.controller.olsr", package.seeall) +local neigh_table = nil +local ifaddr_table = nil + function index() local ipv4,ipv6 if nixio.fs.access("/etc/config/olsrd") then @@ -93,6 +96,46 @@ function action_json() http.write('{"v4":' .. jsonreq4 .. ', "v6":' .. jsonreq6 .. '}') end + +local function local_mac_lookup(ipaddr) + local _, ifa, dev + + ipaddr = tostring(ipaddr) + + if not ifaddr_table then + ifaddr_table = nixio.getifaddrs() + end + + -- ipaddr -> ifname + for _, ifa in ipairs(ifaddr_table) do + if ifa.addr == ipaddr then + dev = ifa.name + break + end + end + + -- ifname -> macaddr + for _, ifa in ipairs(ifaddr_table) do + if ifa.name == dev and ifa.family == "packet" then + return ifa.addr + end + end +end + +local function remote_mac_lookup(ipaddr) + local _, n + + if not neigh_table then + neigh_table = luci.ip.neighbors() + end + + for _, n in ipairs(neigh_table) do + if n.mac and n.dest and n.dest:equal(ipaddr) then + return n.mac + end + end +end + function action_neigh(json) local data, has_v4, has_v6, error = fetch_jsoninfo('links') @@ -107,17 +150,13 @@ function action_neigh(json) local sys = require "luci.sys" local assoclist = {} --local neightbl = require "neightbl" + local ntm = require "luci.model.network" local ipc = require "luci.ip" + local nxo = require "nixio" + local defaultgw - luci.sys.net.routes(function(r) - if r.dest:prefix() == 0 then - defaultgw = r.gateway:string() - end - end) - - if not defaultgw then - defaultgw = luci.util.exec("ip route list exact 0.0.0.0/0 table all"):match(" via (%S+)") - end + ipc.routes({ family = 4, type = 1, dest_exact = "0.0.0.0/0" }, + function(rt) defaultgw = rt.gw end) local function compare(a,b) if a.proto == b.proto then @@ -138,14 +177,10 @@ function action_neigh(json) end for k, v in ipairs(data) do - local interface local snr = 0 local signal = 0 local noise = 0 - local arptable = sys.net.arptable() local mac = "" - local rmac = "" - local lmac = "" local ip local neihgt = {} @@ -155,45 +190,11 @@ function action_neigh(json) v.hostname = hostname end end - if v.proto == '4' then - uci:foreach("network", "interface",function(vif) - if vif.ipaddr and vif.ipaddr == v.localIP then - interface = vif['.name'] or vif.interface - lmac = string.lower(vif.macaddr or "") - return - end - end) - for _, arpt in ipairs(arptable) do - ip = arpt['IP address'] - if ip == v.remoteIP then - rmac = string.lower(arpt['HW address'] or "") - end - end - elseif v.proto == '6' then - uci:foreach("network", "interface",function(vif) - local name = vif['.name'] - local net = ntm:get_network(name) - local device = net and net:get_interface() - local locip = ipc.IPv6(v.localIP) - if device and device:ip6addrs() and locip then - for _, a in ipairs(device:ip6addrs()) do - if not a:is6linklocal() then - if a:host() == locip:host() then - interface = name - --neihgt = neightbl.get(device.ifname) or {} - end - end - end - end - end) - --[[ - for ip,mac in pairs(neihgt) do - if ip == v.remoteIP then - rmac = mac - end - end - ]]-- - end + + local interface = ntm:get_status_by_address(v.localIP) + local lmac = local_mac_lookup(v.localIP) + local rmac = remote_mac_lookup(v.remoteIP) + for _, val in ipairs(assoclist) do if val.network == interface and val.list then for assocmac, assot in pairs(val.list) do diff --git a/applications/luci-app-privoxy/Makefile b/applications/luci-app-privoxy/Makefile new file mode 100644 index 000000000..bc2c57ee9 --- /dev/null +++ b/applications/luci-app-privoxy/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org> +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-privoxy + +# Version == major.minor.patch +# increase "minor" on new functionality and "patch" on patches/optimization +PKG_VERSION:=1.0.3 + +# Release == build +# increase on changes of translation files +PKG_RELEASE:=1 + +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> + +# LuCI specific settings +LUCI_TITLE:=LuCI Support for Privoxy WEB proxy +LUCI_DEPENDS:=+luci-mod-admin-full +privoxy +LUCI_PKGARCH:=all + +define Package/$(PKG_NAME)/config +# shown in make menuconfig <Help> +help + $(LUCI_TITLE) + . + Version: $(PKG_VERSION)-$(PKG_RELEASE) + $(PKG_MAINTAINER) +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-privoxy/luasrc/controller/privoxy.lua b/applications/luci-app-privoxy/luasrc/controller/privoxy.lua new file mode 100755 index 000000000..9ffc404ce --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/controller/privoxy.lua @@ -0,0 +1,160 @@ +-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +-- Licensed under the Apache License, Version 2.0 + +module("luci.controller.privoxy", package.seeall) + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local HTTP = require "luci.http" +local UCI = require "luci.model.uci" +local UTIL = require "luci.util" +local SYS = require "luci.sys" + +PRIVOXY_MIN = "3.0.22-0" -- minimum version of service required + +function index() + entry( {"admin", "services", "privoxy"}, cbi("privoxy"), _("Privoxy WEB proxy"), 59) + entry( {"admin", "services", "privoxy", "logview"}, call("logread") ).leaf = true + entry( {"admin", "services", "privoxy", "startstop"}, call("startstop") ).leaf = true + entry( {"admin", "services", "privoxy", "status"}, call("get_pid") ).leaf = true +end + +-- called by XHR.get from detail_logview.htm +function logread() + -- read application settings + local uci = UCI.cursor() + local logdir = uci:get("privoxy", "privoxy", "logdir") or "/var/log" + local logfile = uci:get("privoxy", "privoxy", "logfile") or "privoxy.log" + uci:unload("privoxy") + + local lfile=logdir .. "/" .. logfile + local ldata=NXFS.readfile(lfile) + if not ldata or #ldata == 0 then + ldata="_nodata_" + end + HTTP.write(ldata) +end + +-- called by XHR.get from detail_startstop.htm +function startstop() + local pid = get_pid(true) + if pid > 0 then + SYS.call("/etc/init.d/privoxy stop") + NX.nanosleep(1) -- sleep a second + if NX.kill(pid, 0) then -- still running + NX.kill(pid, 9) -- send SIGKILL + end + pid = 0 + else + SYS.call("/etc/init.d/privoxy start") + NX.nanosleep(1) -- sleep a second + pid = tonumber(NXFS.readfile("/var/run/privoxy.pid") or 0 ) + if pid > 0 and not NX.kill(pid, 0) then + pid = 0 -- process did not start + end + end + HTTP.write(tostring(pid)) -- HTTP needs string not number +end + +-- called by XHR.poll from detail_startstop.htm +-- and from lua (with parameter "true") +function get_pid(from_lua) + local pid = tonumber(NXFS.readfile("/var/run/privoxy.pid") or 0 ) + if pid > 0 and not NX.kill(pid, 0) then + pid = 0 + end + if from_lua then + return pid + else + HTTP.write(tostring(pid)) -- HTTP needs string not number + end +end + +-- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>" +function ipkg_ver_compare(ver1, comp, ver2) + if not ver1 or not (#ver1 > 0) + or not ver2 or not (#ver2 > 0) + or not comp or not (#comp > 0) then return nil end + -- correct compare string + if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~=" + elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<=" + elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">=" + elseif comp == "=" or comp == "==" then comp = "==" + elseif comp == "<<" then comp = "<" + elseif comp == ">>" then comp = ">" + else return nil end + + local av1 = UTIL.split(ver1, "[%.%-]", nil, true) + local av2 = UTIL.split(ver2, "[%.%-]", nil, true) + + for i = 1, math.max(table.getn(av1),table.getn(av2)), 1 do + local s1 = av1[i] or "" + local s2 = av2[i] or "" + local n1 = tonumber(s1) + local n2 = tonumber(s2) + + -- one numeric and other empty string then set other to 0 + if n1 and not n2 and (not s2 or #s2 == 0) then n2 = 0 end + if n2 and not n1 and (not s1 or #s1 == 0) then n1 = 0 end + + local nc = (n1 and n2) -- numeric compare + + if nc then + -- first "not equal" found return true + if comp == "~=" and (n1 ~= n2) then return true end + -- first "lower" found return true + if (comp == "<" or comp == "<=") and (n1 < n2) then return true end + -- first "greater" found return true + if (comp == ">" or comp == ">=") and (n1 > n2) then return true end + -- not equal then return false + if (n1 ~= n2) then return false end + else + if comp == "~=" and (s1 ~= s2) then return true end + if (comp == "<" or comp == "<=") and (s1 < s2) then return true end + if (comp == ">" or comp == ">=") and (s1 > s2) then return true end + if (s1 ~= s2) then return false end + end + end + -- all equal then true + return true +end + +-- read version information for given package if installed +function ipkg_ver_installed(pkg) + local version = nil + local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r") + if control then + local ln + repeat + ln = control:read("*l") + if ln and ln:match("^Version: ") then + version = ln:gsub("^Version: ", "") + break + end + until not ln + control:close() + end + return version +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 diff --git a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua new file mode 100755 index 000000000..c009313e3 --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy.lua @@ -0,0 +1,977 @@ +-- Copyright 2014-2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +-- Licensed under the Apache License, Version 2.0 + +local NXFS = require "nixio.fs" +local SYS = require "luci.sys" +local UTIL = require "luci.util" +local DISP = require "luci.dispatcher" +local DTYP = require "luci.cbi.datatypes" +local CTRL = require "luci.controller.privoxy" -- this application's controller + +-- Bootstrap theme needs 2 or 3 additional linefeeds for tab description for better optic +local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]] + +local VERINST = CTRL.ipkg_ver_installed("privoxy") +local VEROK = CTRL.ipkg_ver_compare(VERINST,">=",CTRL.PRIVOXY_MIN) + +local TITLE = [[</a><a href="javascript:alert(']] + .. translate("Version Information") + .. [[\n\nluci-app-privoxy]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] + .. CTRL.ipkg_ver_installed("luci-app-privoxy") + .. [[\n\nprivoxy ]] .. translate("required") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher") + .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. VERINST + .. [[\n\n]] + .. [[')">]] + .. translate("Privoxy WEB proxy") + +local DESC = translate("Privoxy is a non-caching web proxy with advanced filtering " + .. "capabilities for enhancing privacy, modifying web page data and HTTP headers, " + .. "controlling access, and removing ads and other obnoxious Internet junk.") + .. [[<br /><strong>]] + .. translate("For help use link at the relevant option") + .. [[</strong>]] + +-- Error handling if wrong privoxy version installed -- ######################## +if not nixio.fs.access("/etc/config/privoxy") or not VEROK then + local f = SimpleForm("_no_config") + f.title = TITLE + f.description = DESC + f.submit = false + f.reset = false + + local s = f:section(SimpleSection) + s.title = [[<font color="red">]] .. [[<strong>]] + .. translate("Software update required") + .. [[</strong>]] .. [[</font>]] + + local v = s:option(DummyValue, "_update_needed") + v.titleref = DISP.build_url("admin", "system", "packages") + v.rawhtml = true + v.value = [[<h3><strong><br /><br /> ]] + .. translate("The currently installed 'privoxy' package is not supported by LuCI application.") + .. [[<br /><br /> ]] + .. translate("required") .. ": " .. CTRL.PRIVOXY_MIN .. " *** ".. translate("installed") .. ": " .. VERINST + .. [[<br /><br /> ]] + .. translate("Please update to the current version!") + .. [[<br /><br /></strong></h3>]] + return f +end + +-- cbi-map -- ################################################################## +local m = Map("privoxy") +m.title = TITLE +m.description = DESC +function m.commit_handler(self) + if self.changed then -- changes ? + os.execute("/etc/init.d/privoxy reload &") -- reload configuration + end +end + +-- cbi-section -- ############################################################## +local ns = m:section( NamedSection, "privoxy", "privoxy") + +ns:tab("local", + translate("Local Set-up"), + translate("If you intend to operate Privoxy for more users than just yourself, " + .. "it might be a good idea to let them know how to reach you, what you block " + .. "and why you do that, your policies, etc.") ) +local function err_tab_local(title, msg) + return string.format(translate("Local Set-up") .. " - %s: %s", title, msg ) +end + +ns:tab("filter", + translate("Files and Directories"), + translate("Privoxy can (and normally does) use a number of other files " + .. "for additional configuration, help and logging. This section of " + .. "the configuration file tells Privoxy where to find those other files.") ) +local function err_tab_filter(title, msg) + return string.format(translate("Files and Directories") .. " - %s: %s", title, msg ) +end + +ns:tab("access", + translate("Access Control"), + translate("This tab controls the security-relevant aspects of Privoxy's configuration.") ) +local function err_tab_access(title, msg) + return string.format(translate("Access Control") .. " - %s: %s", title, msg ) +end + +ns:tab("forward", + translate("Forwarding"), + translate("Configure here the routing of HTTP requests through a chain of multiple proxies. " + .. "Note that parent proxies can severely decrease your privacy level. " + .. "Also specified here are SOCKS proxies.") ) + +ns:tab("misc", + translate("Miscellaneous"), + nil) +local function err_tab_misc(self, msg) + return string.format(translate("Miscellaneous") .. " - %s: %s", self.title_base, msg ) +end + +ns:tab("debug", + translate("Logging"), + nil ) + +ns:tab("logview", + translate("Log File Viewer"), + nil ) + +-- tab: local -- ############################################################### + +-- start/stop button ----------------------------------------------------------- +local btn = ns:taboption("local", Button, "_startstop") +btn.title = translate("Start / Stop") +btn.description = translate("Start/Stop Privoxy WEB Proxy") +btn.template = "privoxy/detail_startstop" +function btn.cfgvalue(self, section) + local pid = CTRL.get_pid(true) + if pid > 0 then + btn.inputtitle = "PID: " .. pid + btn.inputstyle = "reset" + btn.disabled = false + else + btn.inputtitle = translate("Start") + btn.inputstyle = "apply" + btn.disabled = false + end + return true +end + +-- enabled --------------------------------------------------------------------- +local ena = ns:taboption("local", Flag, "_enabled") +ena.title = translate("Enabled") +ena.description = translate("Enable/Disable autostart of Privoxy on system startup and interface events") +ena.orientation = "horizontal" -- put description under the checkbox +ena.rmempty = false +function ena.cfgvalue(self, section) + return (SYS.init.enabled("privoxy")) and "1" or "0" +end +function ena.validate(self, value) + error("Validate " .. value) +end +function ena.write(self, section, value) + --error("Write " .. value) + if value == "1" then + return SYS.init.enable("privoxy") + else + return SYS.init.disable("privoxy") + end +end + +-- hostname -------------------------------------------------------------------- +local hn = ns:taboption("local", Value, "hostname") +hn.title = string.format(HELP, "HOSTNAME", "Hostname" ) +hn.description = translate("The hostname shown on the CGI pages.") +hn.placeholder = SYS.hostname() +hn.optional = true +hn.rmempty = true + +-- user-manual ----------------------------------------------------------------- +local um = ns:taboption("local", Value, "user_manual") +um.title = string.format(HELP, "USER-MANUAL", "User Manual" ) +um.description = translate("Location of the Privoxy User Manual.") +um.placeholder = "http://www.privoxy.org/user-manual/" +um.optional = true +um.rmempty = true + +-- admin-address --------------------------------------------------------------- +local aa = ns:taboption("local", Value, "admin_address") +aa.title_base = "Admin Email" +aa.title = string.format(HELP, "ADMIN-ADDRESS", aa.title_base ) +aa.description = translate("An email address to reach the Privoxy administrator.") +aa.placeholder = "privoxy.admin@example.com" +aa.optional = true +aa.rmempty = true +function aa.validate(self, value) + if not value or #value == 0 then + return "" + end + if not (value:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")) then + return nil, err_tab_local(self.title_base, translate("Invalid email address") ) + end + return value +end + +-- proxy-info-url -------------------------------------------------------------- +local piu = ns:taboption("local", Value, "proxy_info_url") +piu.title = string.format(HELP, "PROXY-INFO-URL", "Proxy Info URL" ) +piu.description = translate("A URL to documentation about the local Privoxy setup, configuration or policies.") +piu.optional = true +piu.rmempty = true + +-- trust-info-url -------------------------------------------------------------- +local tiu = ns:taboption("local", Value, "trust_info_url") +tiu.title = string.format(HELP, "TRUST-INFO-URL", "Trust Info URLs" ) +tiu.description = translate("A URL to be displayed in the error page that users will see if access to an untrusted page is denied.") + .. [[<br /><strong>]] + .. translate("The value of this option only matters if the experimental trust mechanism has been activated.") + .. [[</strong>]] +tiu.optional = true +tiu.rmepty = true + +-- tab: filter -- ############################################################## + +-- logdir ---------------------------------------------------------------------- +local ld = ns:taboption("filter", Value, "logdir") +ld.title_base = "Log Directory" +ld.title = string.format(HELP, "LOGDIR", ld.title_base ) +ld.description = translate("The directory where all logging takes place (i.e. where the logfile is located).") + .. [[<br />]] + .. translate("No trailing '/', please.") +ld.default = "/var/log" +ld.rmempty = false +function ld.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") ) + elseif not NXFS.access(value) then + return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") ) + else + return value + end +end + +-- logfile --------------------------------------------------------------------- +local lf = ns:taboption("filter", Value, "logfile") +lf.title_base = "Log File" +lf.title = string.format(HELP, "LOGFILE", lf.title_base ) +lf.description = translate("The log file to use. File name, relative to log directory.") +lf.default = "privoxy.log" +lf.rmempty = false +function lf.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No File given!") ) + else + return value + end +end + +-- confdir --------------------------------------------------------------------- +local cd = ns:taboption("filter", Value, "confdir") +cd.title_base = "Configuration Directory" +cd.title = string.format(HELP, "CONFDIR", cd.title_base ) +cd.description = translate("The directory where the other configuration files are located.") + .. [[<br />]] + .. translate("No trailing '/', please.") +cd.default = "/etc/privoxy" +cd.rmempty = false +function cd.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") ) + elseif not NXFS.access(value) then + return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") ) + else + return value + end +end + +-- templdir -------------------------------------------------------------------- +local tld = ns:taboption("filter", Value, "templdir") +tld.title_base = "Template Directory" +tld.title = string.format(HELP, "TEMPLDIR", tld.title_base ) +tld.description = translate("An alternative directory where the templates are loaded from.") + .. [[<br />]] + .. translate("No trailing '/', please.") +tld.placeholder = "/etc/privoxy/templates" +tld.rmempty = true +function tld.validate(self, value) + if not NXFS.access(value) then + return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") ) + else + return value + end +end + +-- temporary-directory --------------------------------------------------------- +local td = ns:taboption("filter", Value, "temporary_directory") +td.title_base = "Temporary Directory" +td.title = string.format(HELP, "TEMPORARY-DIRECTORY", td.title_base ) +td.description = translate("A directory where Privoxy can create temporary files.") + .. [[<br /><strong>]] + .. translate("Only when using 'external filters', Privoxy has to create temporary files.") + .. [[</strong>]] +td.rmempty = true + +-- actionsfile ----------------------------------------------------------------- +local af = ns:taboption("filter", DynamicList, "actionsfile") +af.title_base = "Action Files" +af.title = string.format(HELP, "ACTIONSFILE", af.title_base) +af.description = translate("The actions file(s) to use. Multiple actionsfile lines are permitted, and are in fact recommended!") + .. [[<br /><strong>match-all.action := </strong>]] + .. translate("Actions that are applied to all sites and maybe overruled later on.") + .. [[<br /><strong>default.action := </strong>]] + .. translate("Main actions file") + .. [[<br /><strong>user.action := </strong>]] + .. translate("User customizations") +af.rmempty = false +function af.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") ) + end + local confdir = cd:formvalue(ns.section) + local err = false + local file = "" + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + if not NXFS.access(confdir .."/".. x) then + err = true + file = x + break -- break/leave for on error + end + end + end + else + if not NXFS.access(confdir .."/".. value) then + err = true + file = value + end + end + if err then + return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file) + end + return value +end + +-- filterfile ------------------------------------------------------------------ +local ff = ns:taboption("filter", DynamicList, "filterfile") +ff.title_base = "Filter files" +ff.title = string.format(HELP, "FILTERFILE", ff.title_base ) +ff.description = translate("The filter files contain content modification rules that use regular expressions.") +ff.rmempty = false +function ff.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") ) + end + local confdir = cd:formvalue(ns.section) + local err = false + local file = "" + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + if not NXFS.access(confdir .."/".. x) then + err = true + file = x + break -- break/leave for on error + end + end + end + else + if not NXFS.access(confdir .."/".. value) then + err = true + file = value + end + end + if err then + return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file ) + end + return value +end + +-- trustfile ------------------------------------------------------------------- +local tf = ns:taboption("filter", Value, "trustfile") +tf.title_base = "Trust file" +tf.title = string.format(HELP, "TRUSTFILE", tf.title_base ) +tf.description = translate("The trust mechanism is an experimental feature for building white-lists " + .."and should be used with care.") + .. [[<br /><strong>]] + .. translate("It is NOT recommended for the casual user.") + .. [[</strong>]] +tf.placeholder = "user.trust" +tf.rmempty = true +function tf.validate(self, value) + local confdir = cd:formvalue(ns.section) + local err = false + local file = "" + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + if not NCFS.access(confdir .."/".. x) then + err = true + file = x + break -- break/leave for on error + end + end + end + else + if not NXFS.access(confdir .."/".. value) then + err = true + file = value + end + end + if err then + return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file ) + end + return value +end + +-- tab: access -- ############################################################## + +-- listen-address -------------------------------------------------------------- +local la = ns:taboption("access", DynamicList, "listen_address") +la.title_base = "Listen addresses" +la.title = string.format(HELP, "LISTEN-ADDRESS", la.title_base ) +la.description = translate("The address and TCP port on which Privoxy will listen for client requests.") + .. [[<br />]] + .. translate("Syntax: ") + .. "IPv4:Port / [IPv6]:Port / Host:Port" +la.default = "127.0.0.1:8118" +la.rmempty = false +function la.validate(self, value) + if not value or #value == 0 then + return nil, err_tab_access(self.title_base, translate("Mandatory Input: No Data given!") ) + end + + local function check_value(v) + local _ret = UTIL.split(v, "]:") + local _ip + if _ret[2] then -- ip6 with port + _ip = string.gsub(_ret[1], "%[", "") -- remove "[" at beginning + if not DTYP.ip6addr(_ip) then + return translate("Mandatory Input: No valid IPv6 address given!") + elseif not DTYP.port(_ret[2]) then + return translate("Mandatory Input: No valid Port given!") + else + return nil + end + end + _ret = UTIL.split(v, ":") + if not _ret[2] then + return translate("Mandatory Input: No Port given!") + end + if #_ret[1] > 0 and not DTYP.host(_ret[1]) then -- :8118 is valid address + return translate("Mandatory Input: No valid IPv4 address or host given!") + elseif not DTYP.port(_ret[2]) then + return translate("Mandatory Input: No valid Port given!") + else + return nil + end + end + + local err = "" + local entry = "" + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + err = check_value(x) + if err then + entry = x + break + end + end + end + else + err = check_value(value) + entry = value + end + if err then + return nil, string.format(err_tab_access(self.title_base, err .. " - %s"), entry ) + end + return value +end + +-- permit-access --------------------------------------------------------------- +local pa = ns:taboption("access", DynamicList, "permit_access") +pa.title = string.format(HELP, "ACLS", "Permit access" ) +pa.description = translate("Who can access what.") + .. [[<br /><strong>]] + .. translate("Please read Privoxy manual for details!") + .. [[</strong>]] +pa.rmempty = true + +-- deny-access ----------------------------------------------------------------- +local da = ns:taboption("access", DynamicList, "deny_access") +da.title = string.format(HELP, "ACLS", "Deny Access" ) +da.description = translate("Who can access what.") + .. [[<br /><strong>]] + .. translate("Please read Privoxy manual for details!") + .. [[</strong>]] +da.rmempty = true + +-- buffer-limit ---------------------------------------------------------------- +local bl = ns:taboption("access", Value, "buffer_limit") +bl.title_base = "Buffer Limit" +bl.title = string.format(HELP, "BUFFER-LIMIT", bl.title_base ) +bl.description = translate("Maximum size (in KB) of the buffer for content filtering.") + .. [[<br />]] + .. translate("Value range 1 to 4096, no entry defaults to 4096") +bl.default = 4096 +bl.rmempty = true +function bl.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_access(self.title_base, translate("Value is not a number") ) + elseif v < 1 or v > 4096 then + return nil, err_tab_access(self.title_base, translate("Value not between 1 and 4096") ) + elseif v == self.default then + return "" -- dont need to save default + end + return value +end + +-- toggle ---------------------------------------------------------------------- +local tgl = ns:taboption("access", Flag, "toggle") +tgl.title = string.format(HELP, "TOGGLE", "Toggle Status" ) +tgl.description = translate("Enable/Disable filtering when Privoxy starts.") + .. [[<br />]] + .. translate("Disabled == Transparent Proxy Mode") +tgl.orientation = "horizontal" +tgl.default = "1" +tgl.rmempty = false +function tgl.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- enable-remote-toggle -------------------------------------------------------- +local ert = ns:taboption("access", Flag, "enable_remote_toggle") +ert.title = string.format(HELP, "ENABLE-REMOTE-TOGGLE", "Enable remote toggle" ) +ert.description = translate("Whether or not the web-based toggle feature may be used.") +ert.orientation = "horizontal" +ert.rmempty = true +function ert.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- enable-remote-http-toggle --------------------------------------------------- +local eht = ns:taboption("access", Flag, "enable_remote_http_toggle") +eht.title = string.format(HELP, "ENABLE-REMOTE-HTTP-TOGGLE", "Enable remote toggle via HTTP" ) +eht.description = translate("Whether or not Privoxy recognizes special HTTP headers to change toggle state.") + .. [[<br /><strong>]] + .. translate("This option will be removed in future releases as it has been obsoleted by the more general header taggers.") + .. [[</strong>]] +eht.orientation = "horizontal" +eht.rmempty = true +function eht.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- enable-edit-actions --------------------------------------------------------- +local eea = ns:taboption("access", Flag, "enable_edit_actions") +eea.title = string.format(HELP, "ENABLE-EDIT-ACTIONS", "Enable action file editor" ) +eea.description = translate("Whether or not the web-based actions file editor may be used.") +eea.orientation = "horizontal" +eea.rmempty = true +function eea.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- enforce-blocks -------------------------------------------------------------- +local eb = ns:taboption("access", Flag, "enforce_blocks") +eb.title = string.format(HELP, "ENFORCE-BLOCKS", "Enforce page blocking" ) +eb.description = translate("If enabled, Privoxy hides the 'go there anyway' link. " + .. "The user obviously should not be able to bypass any blocks.") +eb.orientation = "horizontal" +eb.rmempty = true +function eb.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- tab: forward -- ############################################################# + +-- enable-proxy-authentication-forwarding -------------------------------------- +local paf = ns:taboption("forward", Flag, "enable_proxy_authentication_forwarding") +paf.title = string.format(HELP, "ENABLE-PROXY-AUTHENTICATION-FORWARDING", + translate("Enable proxy authentication forwarding") ) +paf.description = translate("Whether or not proxy authentication through Privoxy should work.") + .. [[<br /><strong>]] + .. translate("Enabling this option is NOT recommended if there is no parent proxy that requires authentication!") + .. [[</strong>]] +--paf.orientation = "horizontal" +paf.rmempty = true +function paf.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- forward --------------------------------------------------------------------- +local fwd = ns:taboption("forward", DynamicList, "forward") +fwd.title = string.format(HELP, "FORWARD", "Forward HTTP" ) +fwd.description = translate("To which parent HTTP proxy specific requests should be routed.") + .. [[<br />]] + .. translate("Syntax: target_pattern http_parent[:port]") +fwd.rmempty = true + +-- forward-socks4 -------------------------------------------------------------- +local fs4 = ns:taboption("forward", DynamicList, "forward_socks4") +fs4.title = string.format(HELP, "SOCKS", "Forward SOCKS 4" ) +fs4.description = translate("Through which SOCKS proxy (and optionally to which parent HTTP proxy) specific requests should be routed.") + .. [[<br />]] + .. translate("Syntax: target_pattern socks_proxy[:port] http_parent[:port]") +fs4.rmempty = true + +-- forward-socks4a ------------------------------------------------------------- +local f4a = ns:taboption("forward", DynamicList, "forward_socks4a") +f4a.title = string.format(HELP, "SOCKS", "Forward SOCKS 4A" ) +f4a.description = fs4.description +f4a.rmempty = true + +-- forward-socks5 -------------------------------------------------------------- +local fs5 = ns:taboption("forward", DynamicList, "forward_socks5") +fs5.title = string.format(HELP, "SOCKS", "Forward SOCKS 5" ) +fs5.description = fs4.description +fs5.rmempty = true + +-- forward-socks5t ------------------------------------------------------------- +local f5t = ns:taboption("forward", DynamicList, "forward_socks5t") +f5t.title = string.format(HELP, "SOCKS", "Forward SOCKS 5t" ) +f5t.description = fs4.description +f5t.rmempty = true + +-- tab: misc -- ################################################################ + +-- accept-intercepted-requests ------------------------------------------------- +local air = ns:taboption("misc", Flag, "accept_intercepted_requests") +air.title = string.format(HELP, "ACCEPT-INTERCEPTED-REQUESTS", "Accept intercepted requests" ) +air.description = translate("Whether intercepted requests should be treated as valid.") +air.orientation = "horizontal" +air.rmempty = true +function air.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- allow-cgi-request-crunching ------------------------------------------------- +local crc = ns:taboption("misc", Flag, "allow_cgi_request_crunching") +crc.title = string.format(HELP, "ALLOW-CGI-REQUEST-CRUNCHING", "Allow CGI request crunching" ) +crc.description = translate("Whether requests to Privoxy's CGI pages can be blocked or redirected.") +crc.orientation = "horizontal" +crc.rmempty = true +function crc.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- split-large-forms ----------------------------------------------------------- +local slf = ns:taboption("misc", Flag, "split_large_forms") +slf.title = string.format(HELP, "SPLIT-LARGE-FORMS", "Split large forms" ) +slf.description = translate("Whether the CGI interface should stay compatible with broken HTTP clients.") +slf.orientation = "horizontal" +slf.rmempty = true +function slf.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- keep-alive-timeout ---------------------------------------------------------- +local kat = ns:taboption("misc", Value, "keep_alive_timeout") +kat.title_base = "Keep-alive timeout" +kat.title = string.format(HELP, "KEEP-ALIVE-TIMEOUT", kat.title_base) +kat.description = translate("Number of seconds after which an open connection will no longer be reused.") +kat.rmempty = true +function kat.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_misc(self.title_base, translate("Value is not a number") ) + elseif v < 1 then + return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") ) + end + return value +end + +-- tolerate-pipelining --------------------------------------------------------- +local tp = ns:taboption("misc", Flag, "tolerate_pipelining") +tp.title = string.format(HELP, "TOLERATE-PIPELINING", "Tolerate pipelining" ) +tp.description = translate("Whether or not pipelined requests should be served.") +tp.orientation = "horizontal" +tp.rmempty = true +function tp.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- default-server-timeout ------------------------------------------------------ +local dst = ns:taboption("misc", Value, "default_server_timeout") +dst.title_base = "Default server timeout" +dst.title = string.format(HELP, "DEFAULT-SERVER-TIMEOUT", dst.title_base) +dst.description = translate("Assumed server-side keep-alive timeout (in seconds) if not specified by the server.") +dst.rmempty = true +function dst.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_misc(self.title_base, translate("Value is not a number") ) + elseif v < 1 then + return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") ) + end + return value +end + +-- connection-sharing ---------------------------------------------------------- +local cs = ns:taboption("misc", Flag, "connection_sharing") +cs.title = string.format(HELP, "CONNECTION-SHARING", "Connection sharing" ) +cs.description = translate("Whether or not outgoing connections that have been kept alive should be shared between different incoming connections.") +cs.orientation = "horizontal" +cs.rmempty = true +function cs.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- socket-timeout -------------------------------------------------------------- +local st = ns:taboption("misc", Value, "socket_timeout") +st.title_base = "Socket timeout" +st.title = string.format(HELP, "SOCKET-TIMEOUT", st.title_base ) +st.description = translate("Number of seconds after which a socket times out if no data is received.") +st.default = 300 +st.rmempty = true +function st.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_misc(self.title_base, translate("Value is not a number") ) + elseif v < 1 then + return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") ) + elseif v == self.default then + return "" -- dont need to save default + end + return value +end + +-- max-client-connections ------------------------------------------------------ +local mcc = ns:taboption("misc", Value, "max_client_connections") +mcc.title_base = "Max. client connections" +mcc.title = string.format(HELP, "MAX-CLIENT-CONNECTIONS", mcc.title_base ) +mcc.description = translate("Maximum number of client connections that will be served.") +mcc.default = 128 +mcc.rmempty = true +function mcc.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_misc(self.title_base, translate("Value is not a number") ) + elseif v < 1 then + return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") ) + elseif v == self.default then + return "" -- dont need to save default + end + return value +end + +-- handle-as-empty-doc-returns-ok ---------------------------------------------- +local her = ns:taboption("misc", Flag, "handle_as_empty_doc_returns_ok") +her.title = string.format(HELP, "HANDLE-AS-EMPTY-DOC-RETURNS-OK", "Handle as empty doc returns ok" ) +her.description = translate("The status code Privoxy returns for pages blocked with +handle-as-empty-document.") +her.orientation = "horizontal" +her.rmempty = true +function her.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- enable-compression ---------------------------------------------------------- +local ec = ns:taboption("misc", Flag, "enable_compression") +ec.title = string.format(HELP, "ENABLE-COMPRESSION", "Enable compression" ) +ec.description = translate("Whether or not buffered content is compressed before delivery.") +ec.orientation = "horizontal" +ec.rmempty = true +function ec.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- compression-level ----------------------------------------------------------- +local cl = ns:taboption("misc", Value, "compression_level") +cl.title_base = "Compression level" +cl.title = string.format(HELP, "COMPRESSION-LEVEL", cl.title_base ) +cl.description = translate("The compression level that is passed to the zlib library when compressing buffered content.") +cl.default = 1 +cl.rmempty = true +function cl.validate(self, value) + local v = tonumber(value) + if not v then + return nil, err_tab_misc(self.title_base, translate("Value is not a number") ) + elseif v < 0 or v > 9 then + return nil, err_tab_misc(self.title_base, translate("Value not between 0 and 9") ) + elseif v == self.default then + return "" -- don't need to save default + end + return value +end + +-- client-header-order --------------------------------------------------------- +local cho = ns:taboption("misc", Value, "client_header_order") +cho.title = string.format(HELP, "CLIENT-HEADER-ORDER", "Client header order" ) +cho.description = translate("The order in which client headers are sorted before forwarding them.") + .. [[<br />]] + .. translate("Syntax: Client header names delimited by spaces.") +cho.rmempty = true + +-- "debug"-tab definition -- ################################################### + +-- single-threaded ------------------------------------------------------------- +local st = ns:taboption("debug", Flag, "single_threaded") +st.title = string.format(HELP, "SINGLE-THREADED", "Single Threaded" ) +st.description = translate("Whether to run only one server thread.") + .. [[<br /><strong>]] + .. translate("This option is only there for debugging purposes. It will drastically reduce performance.") + .. [[</strong>]] +st.rmempty = true +function st.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 1 --------------------------------------------------------------------- +local d0 = ns:taboption("debug", Flag, "debug_1") +d0.title = string.format(HELP, "DEBUG", "Debug 1" ) +d0.description = translate("Log the destination for each request Privoxy let through. See also 'Debug 1024'.") +d0.rmempty = true +function d0.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 2 --------------------------------------------------------------------- +local d1 = ns:taboption("debug", Flag, "debug_2") +d1.title = string.format(HELP, "DEBUG", "Debug 2" ) +d1.description = translate("Show each connection status") +d1.rmempty = true +function d1.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 4 --------------------------------------------------------------------- +local d2 = ns:taboption("debug", Flag, "debug_4") +d2.title = string.format(HELP, "DEBUG", "Debug 4" ) +d2.description = translate("Show I/O status") +d2.rmempty = true +function d2.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 8 --------------------------------------------------------------------- +local d3 = ns:taboption("debug", Flag, "debug_8") +d3.title = string.format(HELP, "DEBUG", "Debug 8" ) +d3.description = translate("Show header parsing") +d3.rmempty = true +function d3.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 16 -------------------------------------------------------------------- +local d4 = ns:taboption("debug", Flag, "debug_16") +d4.title = string.format(HELP, "DEBUG", "Debug 16" ) +d4.description = translate("Log all data written to the network") +d4.rmempty = true +function d4.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 32 -------------------------------------------------------------------- +local d5 = ns:taboption("debug", Flag, "debug_32") +d5.title = string.format(HELP, "DEBUG", "Debug 32" ) +d5.description = translate("Debug force feature") +d5.rmempty = true +function d5.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 64 -------------------------------------------------------------------- +local d6 = ns:taboption("debug", Flag, "debug_64") +d6.title = string.format(HELP, "DEBUG", "Debug 64" ) +d6.description = translate("Debug regular expression filters") +d6.rmempty = true +function d6.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 128 ------------------------------------------------------------------- +local d7 = ns:taboption("debug", Flag, "debug_128") +d7.title = string.format(HELP, "DEBUG", "Debug 128" ) +d7.description = translate("Debug redirects") +d7.rmempty = true +function d7.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 256 ------------------------------------------------------------------- +local d8 = ns:taboption("debug", Flag, "debug_256") +d8.title = string.format(HELP, "DEBUG", "Debug 256" ) +d8.description = translate("Debug GIF de-animation") +d8.rmempty = true +function d8.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 512 ------------------------------------------------------------------- +local d9 = ns:taboption("debug", Flag, "debug_512") +d9.title = string.format(HELP, "DEBUG", "Debug 512" ) +d9.description = translate("Common Log Format") +d9.rmempty = true +function d9.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 1024 ------------------------------------------------------------------ +local d10 = ns:taboption("debug", Flag, "debug_1024") +d10.title = string.format(HELP, "DEBUG", "Debug 1024" ) +d10.description = translate("Log the destination for requests Privoxy didn't let through, and the reason why.") +d10.rmempty = true +function d10.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 2048 ------------------------------------------------------------------ +local d11 = ns:taboption("debug", Flag, "debug_2048") +d11.title = string.format(HELP, "DEBUG", "Debug 2048" ) +d11.description = translate("CGI user interface") +d11.rmempty = true +function d11.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 4096 ------------------------------------------------------------------ +local d12 = ns:taboption("debug", Flag, "debug_4096") +d12.title = string.format(HELP, "DEBUG", "Debug 4096" ) +d12.description = translate("Startup banner and warnings.") +d12.rmempty = true +function d12.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 8192 ------------------------------------------------------------------ +local d13 = ns:taboption("debug", Flag, "debug_8192") +d13.title = string.format(HELP, "DEBUG", "Debug 8192" ) +d13.description = translate("Non-fatal errors - *we highly recommended enabling this*") +d13.rmempty = true +function d13.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 16384 ----------------------------------------------------------------- +--[[ TODO ??? +local d14 = ns:taboption("debug", Flag, "debug_16384") +d14.title = string.format(HELP, "DEBUG", "Debug 16384" ) +d14.description = translate("") +d14.rmempty = true +function d14.parse(self, section) + CTRL.flag_parse(self, section) +end +]]-- + +-- debug 32768 ----------------------------------------------------------------- +local d15 = ns:taboption("debug", Flag, "debug_32768") +d15.title = string.format(HELP, "DEBUG", "Debug 32768" ) +d15.description = translate("Log all data read from the network") +d15.rmempty = true +function d15.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug 65536 ----------------------------------------------------------------- +local d16 = ns:taboption("debug", Flag, "debug_65536") +d16.title = string.format(HELP, "DEBUG", "Debug 65536" ) +d16.description = translate("Log the applying actions") +d16.rmempty = true +function d16.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- tab: logview -- ############################################################# + +local lv = ns:taboption("logview", DummyValue, "_logview") +lv.template = "privoxy/detail_logview" +lv.inputtitle = translate("Read / Reread log file") +lv.rows = 50 +function lv.cfgvalue(self, section) + local lfile=self.map:get(ns.section, "logdir") .. "/" .. self.map:get(ns.section, "logfile") + if NXFS.access(lfile) then + return lfile .. "\n" .. translate("Please press [Read] button") + end + return lfile .. "\n" .. translate("File not found or empty") +end + +return m diff --git a/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm new file mode 100755 index 000000000..3e190709f --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm @@ -0,0 +1,56 @@ + +<!-- ++ BEGIN ++ Privoxy ++ detail_logview.htm ++ --> +<script type="text/javascript">//<![CDATA[ + function onclick_logview(section, bottom) { + // get elements + var txt = document.getElementById("cbid.privoxy.privoxy._logview.txt"); // TextArea + if ( !txt ) { return; } // security check + var lvXHR = new XHR(); + lvXHR.get('<%=luci.dispatcher.build_url("admin", "services", "privoxy", "logview")%>', null, + function(x) { + if (x.responseText == "_nodata_") + txt.value = "<%:File not found or empty%>"; + else + txt.value = x.responseText; + if (bottom) + txt.scrollTop = txt.scrollHeight; + else + txt.scrollTop = 0; } + ); + } +//]]></script> + +<%+cbi/valueheader%> + +<br /> + +<% +-- one button on top, one at the buttom +%> +<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick="onclick_logview(this.name, false)" +<%= +attr("name", section) .. attr("id", cbid .. ".btn1") .. attr("value", self.inputtitle) +%> /> + +<br /><br /> + +<% +-- set a readable style taken from openwrt theme for textarea#syslog +-- in openwrt theme there are problems with a width of 100 so we check for theme and set to lower value +%> +<textarea style="width: <%if media == "/luci-static/openwrt.org" then%>98.7%<%else%>100%<%end%> ; min-height: 500px; border: 3px solid #cccccc; padding: 5px; font-family: monospace; resize: none;" wrap="off" readonly="readonly" +<%= +attr("name", cbid .. ".txt") .. attr("id", cbid .. ".txt") .. ifattr(self.rows, "rows") +%> > +<%-=pcdata(self:cfgvalue(section))-%> +</textarea> +<br /><br /> + +<% +-- one button on top, one at the buttom +%> +<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick="onclick_logview(this.name, true)" +<%= attr("name", section) .. attr("id", cbid .. ".btn2") .. attr("value", self.inputtitle) %> /> + +<%+cbi/valuefooter%> +<!-- ++ END ++ Privoxy ++ detail_logview.htm ++ --> diff --git a/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm new file mode 100644 index 000000000..b9de8864e --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm @@ -0,0 +1,49 @@ + +<!-- ++ BEGIN ++ Privoxy ++ detail_startstop.htm ++ --> +<script type="text/javascript">//<![CDATA[ + + // show XHR.poll/XHR.get response on button + function _data2elements(x) { + var btn = document.getElementById("cbid.privoxy.privoxy._startstop"); + if ( ! btn ) { return; } // security check + if (x.responseText == "0") { + btn.value = "<%:Start%>"; + btn.className = "cbi-button cbi-button-apply"; + btn.disabled = false; + } else { + btn.value = "PID: " + x.responseText; + btn.className = "cbi-button cbi-button-reset"; + btn.disabled = false; + } + } + + // event handler for start/stop button + function onclick_startstop(id) { + // do start/stop + var btnXHR = new XHR(); + btnXHR.get('<%=luci.dispatcher.build_url("admin", "services", "privoxy", "startstop")%>', null, + function(x) { _data2elements(x); } + ); + } + + XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "privoxy", "status")%>', null, + function(x, data) { _data2elements(x); } + ); + +//]]></script> + +<%+cbi/valueheader%> + +<% if self:cfgvalue(section) ~= false then +-- We need to garantie that function cfgvalue run first to set missing parameters +%> + <!-- style="font-size: 100%;" needed for openwrt theme to fix font size --> + <!-- type="button" onclick="..." enable standard onclick functionalty --> + <input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" style="font-size: 100%;" type="button" onclick="onclick_startstop(this.id)" + <%= + attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle) .. ifattr(self.disabled, "disabled") + %> /> +<% end %> + +<%+cbi/valuefooter%> +<!-- ++ END ++ Privoxy ++ detail_startstop.htm ++ --> diff --git a/applications/luci-app-privoxy/po/de/privoxy.po b/applications/luci-app-privoxy/po/de/privoxy.po new file mode 100644 index 000000000..6ee3af47b --- /dev/null +++ b/applications/luci-app-privoxy/po/de/privoxy.po @@ -0,0 +1,496 @@ +msgid "" +msgstr "" +"Project-Id-Version: luci-app-privoxy\n" +"POT-Creation-Date: 2015-01-18 21:48+0100\n" +"PO-Revision-Date: 2015-01-18 21:51+0100\n" +"Last-Translator: Christian Schoenebeck <christian.schoenebeck@gmail.com>\n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgid "" +"A URL to be displayed in the error page that users will see if access to an " +"untrusted page is denied." +msgstr "" +"Ein Link auf der Fehlerseite, der Benutzern angezeigt wird, wenn der Zugang " +"zu einer nicht vertrauenswürdigen Seite verweigert wird." + +msgid "" +"A URL to documentation about the local Privoxy setup, configuration or " +"policies." +msgstr "" +"Ein Link zur Dokumentation über die lokale Privoxy Konfiguration und die " +"Sicherheitseinstellungen." + +msgid "Access Control" +msgstr "Zugriffskontrolle" + +msgid "Actions that are applied to all sites and maybe overruled later on." +msgstr "" +"Aktionen, die für alle Websites angewendet werden, und vielleicht später " +"überschrieben werden." + +msgid "An alternative directory where the templates are loaded from." +msgstr "Eine alternatives Verzeichnis, aus dem die Vorlagen geladen werden." + +msgid "An email address to reach the Privoxy administrator." +msgstr "Eine E-Mail-Adresse, um die Privoxy-Administrator zu erreichen." + +msgid "" +"Assumed server-side keep-alive timeout (in seconds) if not specified by the " +"server." +msgstr "" +"Angenommenes serverseitiges Keep-Alive-Timeout (in Sekunden), falls nicht " +"vom Server festgelegt." + +msgid "CGI user interface" +msgstr "Protokolliert die CGI Benutzer Schnittstelle" + +msgid "Common Log Format" +msgstr "Gemeinsames Protokollformat" + +msgid "" +"Configure here the routing of HTTP requests through a chain of multiple " +"proxies. Note that parent proxies can severely decrease your privacy level. " +"Also specified here are SOCKS proxies." +msgstr "" +"Konfigurieren Sie hier das Weiterleiten von HTTP-Anforderungen durch eine " +"Kette von mehreren Proxies. Beachten Sie, dass übergeordnete Proxies Ihre " +"Privatsphäre stark verringern können. Auch hier angegeben werden SOCKS-" +"Proxies." + +msgid "Debug GIF de-animation" +msgstr "Protokolliert die GIF de-animation" + +msgid "Debug force feature" +msgstr "Protokolliert die 'Force' Eigenschaft" + +msgid "Debug redirects" +msgstr "Protokolliert Weiterleitungen" + +msgid "Debug regular expression filters" +msgstr "Protokolliert Filter für reguläre Ausdrücke" + +msgid "Directory does not exist!" +msgstr "Verzeichnis existiert nicht!" + +msgid "Disabled == Transparent Proxy Mode" +msgstr "Deaktiviert == Transparent Proxy Betrieb" + +msgid "Enable proxy authentication forwarding" +msgstr "Aktivieren die Weiterleitung von Proxy-Authentifizierungen" + +msgid "" +"Enable/Disable autostart of Privoxy on system startup and interface events" +msgstr "" +"Aktivieren / Deaktivieren des Autostart von Privoxy beim Systemstart und " +"Schnittstellenereignissen." + +msgid "Enable/Disable filtering when Privoxy starts." +msgstr "Aktivieren / Deaktivieren der Filterung, wenn Privoxy startet." + +msgid "Enabled" +msgstr "Aktiviert" + +msgid "" +"Enabling this option is NOT recommended if there is no parent proxy that " +"requires authentication!" +msgstr "" +"Die Aktivierung dieser Option wird NICHT empfohlen, wenn es keinen " +"übergeordneten Proxy gibt, der eine Authentifizierung erfordert!" + +msgid "File '%s' not found inside Configuration Directory" +msgstr "Datei '%s' nicht im Konfigurationsverzeichnis gefunden!" + +msgid "File not found or empty" +msgstr "Datei nicht gefunden oder leer" + +msgid "Files and Directories" +msgstr "Dateien und Verzeichnisse" + +msgid "For help use link at the relevant option" +msgstr "" +"Für Hilfe zur Verwendung, benutzen Sie die Verknüpfung der betreffenden " +"Option." + +msgid "Forwarding" +msgstr "Weiterleitung" + +msgid "" +"If enabled, Privoxy hides the 'go there anyway' link. The user obviously " +"should not be able to bypass any blocks." +msgstr "" +"Wenn aktiviert, verbirgt Privoxy den Link 'go there anyway'. Normalerweise " +"sollten Benutzer nicht in der Lage sein, Blockierungen zu umgehen." + +msgid "" +"If you intend to operate Privoxy for more users than just yourself, it might " +"be a good idea to let them know how to reach you, what you block and why you " +"do that, your policies, etc." +msgstr "" +"Wenn Sie beabsichtigen, Privoxy für mehr Nutzer als nur sich selbst zu " +"betreiben, ist es eine gute Idee, sie wissen zu lassen, wie sie Sie " +"erreichen können, was Sie blockieren und warum Sie das tun, etc." + +msgid "Invalid email address" +msgstr "Ungültige Email Adresse" + +msgid "It is NOT recommended for the casual user." +msgstr "Es wird NICHT für den gelegentlichen Anwender empfohlen." + +msgid "Local Set-up" +msgstr "Lokale Einstellungen" + +msgid "Location of the Privoxy User Manual." +msgstr "Ort des Privoxy Benutzer Handbuches" + +msgid "Log File Viewer" +msgstr "Protokolldatei" + +msgid "Log all data read from the network" +msgstr "Protokolliert alle Daten, die vom Netzwerk gelesen werden." + +msgid "Log all data written to the network" +msgstr "Protokolliert alle Daten, die auf das Netzwerk geschrieben werden." + +msgid "Log the applying actions" +msgstr "Protokiolliert angewendete Aktionen" + +msgid "" +"Log the destination for each request Privoxy let through. See also 'Debug " +"1024'." +msgstr "" +"Protokolliert das Ziel für jede Anforderung die Privoxy durchlässt. Siehe " +"auch 'Debug 1024'." + +msgid "" +"Log the destination for requests Privoxy didn't let through, and the reason " +"why." +msgstr "" +"Protokolliert das Ziel für Anfragen die Privoxy nicht durchgelassen hat, und " +"den Grund dafür." + +msgid "Logging" +msgstr "Protokollierung" + +msgid "Main actions file" +msgstr "Wichtige Aktionen-Datei" + +msgid "Mandatory Input: No Data given!" +msgstr "Pflichtfeld: Keine Daten angegeben!" + +msgid "Mandatory Input: No Directory given!" +msgstr "Pflichtfeld: Kein Verzeichnis angegeben!" + +msgid "Mandatory Input: No File given!" +msgstr "Pflichtfeld: Keine Datei angegeben!" + +msgid "Mandatory Input: No Port given!" +msgstr "Pflichtfeld: Kein Port angegeben!" + +msgid "Mandatory Input: No files given!" +msgstr "Pflichtfeld: Keine Dateien angegeben!" + +msgid "Mandatory Input: No valid IPv4 address or host given!" +msgstr "" +"Pflichtfeld: Keine gültige IPv4 Adresse oder gültiger Hostname angegeben!" + +msgid "Mandatory Input: No valid IPv6 address given!" +msgstr "Pflichtfeld: Keine gültige IPv6 Adresse angegeben!" + +msgid "Mandatory Input: No valid Port given!" +msgstr "Pflichtfeld: Keine gültige Port Nummer angegeben!" + +msgid "Maximum number of client connections that will be served." +msgstr "Maximale Anzahl von Client-Verbindungen." + +msgid "Maximum size (in KB) of the buffer for content filtering." +msgstr "Maximale Größe (in KB) des Puffers für die Inhaltsfilterung." + +msgid "Miscellaneous" +msgstr "Verschiedenes" + +msgid "No trailing '/', please." +msgstr "Bitte kein '/' am Ende." + +msgid "Non-fatal errors - *we highly recommended enabling this*" +msgstr "" +"Protokolliert nicht schwerwiegende Fehler - * Es wird dringend empfohlen, " +"dieses zu aktivieren *" + +msgid "" +"Number of seconds after which a socket times out if no data is received." +msgstr "" +"Anzahl der Sekunden, nach der eine Socket Timeout erfolgt, wenn keine Daten " +"empfangen werden." + +msgid "" +"Number of seconds after which an open connection will no longer be reused." +msgstr "" +"Anzahl von Sekunden, nach der eine offene Verbindung nicht mehr " +"wiederverwendet wird." + +msgid "Please press [Read] button" +msgstr "Bitte Protokolldatei einlesen" + +msgid "Please read Privoxy manual for details!" +msgstr "Bitte lesen Sie das Privoxy Handbuch für Details!" + +msgid "Please update to the current version!" +msgstr "Aktualisieren Sie bitte auf die aktuelle Version!" + +msgid "Privoxy WEB proxy" +msgstr "Privoxy WEB proxy" + +msgid "" +"Privoxy can (and normally does) use a number of other files for additional " +"configuration, help and logging. This section of the configuration file " +"tells Privoxy where to find those other files." +msgstr "" +"Privoxy verwendet (was in der Regel der Fall ist), eine Reihe von anderen " +"Dateien für eine zusätzliche Konfiguration, Hilfe und Protokollierung. " +"Dieser Abschnitt der Konfigurationsdatei definiert, wo diese Dateien zu " +"finden sind." + +msgid "" +"Privoxy is a non-caching web proxy with advanced filtering capabilities for " +"enhancing privacy, modifying web page data and HTTP headers, controlling " +"access, and removing ads and other obnoxious Internet junk." +msgstr "" +"Privoxy ist ein non-caching Web-Proxy mit erweiterten Filterfunktion zur " +"Verbesserung der Privatsphäre. Er modifiziert Webseitendaten und HTTP-" +"Header, kontrolliert den Zugang und das Entfernen von Anzeigen und anderem " +"abscheulichen Internet Schrott." + +msgid "Read / Reread log file" +msgstr "Protokolldatei (neu) lesen" + +msgid "Show I/O status" +msgstr "Protokolliert den I/O Status" + +msgid "Show each connection status" +msgstr "Protokolliert jeden Verbindungsstatus" + +msgid "Show header parsing" +msgstr "Protokolliert das 'Header parsing'" + +msgid "Software update required" +msgstr "Softwareaktualisierung nötig" + +msgid "Start" +msgstr "Start" + +msgid "Start / Stop" +msgstr "Start / Stopp" + +msgid "Start/Stop Privoxy WEB Proxy" +msgstr "Start/Stopp Privoxy WEB Proxy" + +msgid "Startup banner and warnings." +msgstr "Protokolliert Start-Meldungen und Warnungen" + +msgid "Syntax:" +msgstr "Syntax:" + +msgid "Syntax: Client header names delimited by spaces." +msgstr "Syntax: Client header Namen getrennt durch Leerzeichen." + +msgid "Syntax: target_pattern http_parent[:port]" +msgstr "Syntax: target_pattern http_parent[:port]" + +msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" +msgstr "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" + +msgid "" +"The actions file(s) to use. Multiple actionsfile lines are permitted, and " +"are in fact recommended!" +msgstr "" +"Die zu verwendenden Aktion-Datei(en). Mehrere Dateien sind gestattet und " +"empfohlen!" + +msgid "" +"The address and TCP port on which Privoxy will listen for client requests." +msgstr "" +"Die Adresse und das TCP-Port, auf dem Privoxy auf Client-Anforderungen " +"wartet." + +msgid "" +"The compression level that is passed to the zlib library when compressing " +"buffered content." +msgstr "" +"Die Komprimierungsstufe (0-9), die der zlib-Bibliothek beim Komprimieren " +"gepufferten Inhaltes übergeben wird." + +msgid "" +"The currently installed 'privoxy' package is not supported by LuCI " +"application." +msgstr "" +"Das aktuell installierte 'privoxy' Paket wird von dieser LuCI Anwendung " +"NICHT unterstützt." + +msgid "" +"The directory where all logging takes place (i.e. where the logfile is " +"located)." +msgstr "Das Verzeichnis in dem die Protokolldatei gespeichert wird." + +msgid "The directory where the other configuration files are located." +msgstr "Das Verzeichnis in dem weitere Konfigurationsdateien gespeichert sind." + +msgid "" +"The filter files contain content modification rules that use regular " +"expressions." +msgstr "" +"Die Filterdateien enthalten Änderung des Inhalts, die reguläre Ausdrücke " +"als Regeln verwenden." + +msgid "The hostname shown on the CGI pages." +msgstr "Der Hostname der auf CGI-Seiten angezeigt wird." + +msgid "The log file to use. File name, relative to log directory." +msgstr "" +"Zu verwendende Protokolldatei. Dateiname relativ zum Protokoll-Verzeichnis." + +msgid "The order in which client headers are sorted before forwarding them." +msgstr "" +"Die Reihenfolge, in der Client-Header sortiert werden, bevor sie " +"weitergeleitet werden." + +msgid "" +"The status code Privoxy returns for pages blocked with +handle-as-empty-" +"document." +msgstr "" +"Ob Statuscode 200(OK) oder 403(forbidden) für Seiten gemeldet wird, die " +"durch den Filter 'handle-as-empty-document' blockiert werden." + +msgid "" +"The trust mechanism is an experimental feature for building white-lists and " +"should be used with care." +msgstr "" +"Der Trust-Mechanismus ist eine experimentelle Funktion für den Aufbau von " +"White-Listen und sollte mit Vorsicht verwendet werden." + +msgid "" +"The value of this option only matters if the experimental trust mechanism " +"has been activated." +msgstr "" +"Der Wert dieser Option ist nur wirksam, wenn der experimentelle Trust-" +"Mechanismus aktiviert wurde." + +msgid "" +"This option is only there for debugging purposes. It will drastically reduce " +"performance." +msgstr "" +"Diese Option ist ausschließlich zur Fehlersuche. Es wird drastisch die " +"Leistung beeinträchtigt." + +msgid "" +"This option will be removed in future releases as it has been obsoleted by " +"the more general header taggers." +msgstr "Diese Option wird in zukünftigen Versionen entfernt werden." + +msgid "" +"This tab controls the security-relevant aspects of Privoxy's configuration." +msgstr "" +"Diese Registerkarte steuert die sicherheitsrelevanten Aspekte der Privoxy " +"Konfiguration." + +msgid "" +"Through which SOCKS proxy (and optionally to which parent HTTP proxy) " +"specific requests should be routed." +msgstr "" +"An welchen SOCKS-Proxy (und gegebenenfalls an welchen übergeordneten HTTP-" +"Proxy) spezifischen Anforderungen weitergeleitet werden." + +msgid "To which parent HTTP proxy specific requests should be routed." +msgstr "" +"An welchen übergeordneten HTTP-Proxy spezifischen Anforderungen " +"weitergeleitet werden." + +msgid "User customizations" +msgstr "Benutzerdefinierte Anpassungen" + +msgid "Value is not a number" +msgstr "Eingabe ist keine Zahl" + +msgid "Value not between 0 and 9" +msgstr "Wert nicht zwischen 0 und 9" + +msgid "Value not between 1 and 4096" +msgstr "Wert nicht zwischen 1 und 4096" + +msgid "Value not greater 0 or empty" +msgstr "Wert nicht größer 0 oder leer" + +msgid "Value range 1 to 4096, no entry defaults to 4096" +msgstr "Wertebereich: 1 bis 4096; Keine Angabe setzt 4096." + +msgid "Version" +msgstr "Version" + +msgid "Version Information" +msgstr "Versionsinformation" + +msgid "Whether intercepted requests should be treated as valid." +msgstr "Ob abgefangen Anfragen als gültig behandelt werden." + +msgid "" +"Whether or not Privoxy recognizes special HTTP headers to change toggle " +"state." +msgstr "" +"Ob Privoxy erkannte spezielle HTTP-Header zur Änderung des Toggle-Status " +"verwendet.." + +msgid "Whether or not buffered content is compressed before delivery." +msgstr "" +"Ob gepufferte Inhalte vor der Weiterleitung komprimiert werden oder nicht." + +msgid "" +"Whether or not outgoing connections that have been kept alive should be " +"shared between different incoming connections." +msgstr "" +"Ob ausgehende Verbindungen, die am Leben gehalten werden, für verschiedenen " +"eingehenden Verbindungen gemeinsam genutzt werden oder nicht." + +msgid "Whether or not pipelined requests should be served." +msgstr "Ob Pipeline-Anfragen bedient werden oder nicht." + +msgid "Whether or not proxy authentication through Privoxy should work." +msgstr "" +"Ob Proxy-Authentifizierungen durch Privoxy weitergeleitet werden oder nicht." + +msgid "Whether or not the web-based actions file editor may be used." +msgstr "De-/Aktiviert den webbasierte Action-Datei Editor." + +msgid "Whether or not the web-based toggle feature may be used." +msgstr "De-Aktiviert die webbasierte Umschaltfunktion." + +msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected." +msgstr "" +"Ob Anfragen an Privoxy CGI-Seiten gesperrt oder umgeleitet werden können " +"oder nicht." + +msgid "" +"Whether the CGI interface should stay compatible with broken HTTP clients." +msgstr "" +"Ob die CGI-Schnittstelle mit broken HTTP-Clients kompatibel bleibt oder " +"nicht." + +msgid "Whether to run only one server thread." +msgstr "Ob nur ein Server-Thread ausgeführt wird." + +msgid "Who can access what." +msgstr "Wer kann auf Was zugreifen." + +msgid "installed" +msgstr "installiert" + +msgid "or higher" +msgstr "oder höher" + +msgid "required" +msgstr "benötigt" diff --git a/applications/luci-app-privoxy/po/templates/privoxy.pot b/applications/luci-app-privoxy/po/templates/privoxy.pot new file mode 100644 index 000000000..8f836bef0 --- /dev/null +++ b/applications/luci-app-privoxy/po/templates/privoxy.pot @@ -0,0 +1,405 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "" +"A URL to be displayed in the error page that users will see if access to an " +"untrusted page is denied." +msgstr "" + +msgid "" +"A URL to documentation about the local Privoxy setup, configuration or " +"policies." +msgstr "" + +msgid "Access Control" +msgstr "" + +msgid "Actions that are applied to all sites and maybe overruled later on." +msgstr "" + +msgid "An alternative directory where the templates are loaded from." +msgstr "" + +msgid "An email address to reach the Privoxy administrator." +msgstr "" + +msgid "" +"Assumed server-side keep-alive timeout (in seconds) if not specified by the " +"server." +msgstr "" + +msgid "CGI user interface" +msgstr "" + +msgid "Common Log Format" +msgstr "" + +msgid "" +"Configure here the routing of HTTP requests through a chain of multiple " +"proxies. Note that parent proxies can severely decrease your privacy level. " +"Also specified here are SOCKS proxies." +msgstr "" + +msgid "Debug GIF de-animation" +msgstr "" + +msgid "Debug force feature" +msgstr "" + +msgid "Debug redirects" +msgstr "" + +msgid "Debug regular expression filters" +msgstr "" + +msgid "Directory does not exist!" +msgstr "" + +msgid "Disabled == Transparent Proxy Mode" +msgstr "" + +msgid "Enable proxy authentication forwarding" +msgstr "" + +msgid "" +"Enable/Disable autostart of Privoxy on system startup and interface events" +msgstr "" + +msgid "Enable/Disable filtering when Privoxy starts." +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "" +"Enabling this option is NOT recommended if there is no parent proxy that " +"requires authentication!" +msgstr "" + +msgid "File '%s' not found inside Configuration Directory" +msgstr "" + +msgid "File not found or empty" +msgstr "" + +msgid "Files and Directories" +msgstr "" + +msgid "For help use link at the relevant option" +msgstr "" + +msgid "Forwarding" +msgstr "" + +msgid "" +"If enabled, Privoxy hides the 'go there anyway' link. The user obviously " +"should not be able to bypass any blocks." +msgstr "" + +msgid "" +"If you intend to operate Privoxy for more users than just yourself, it might " +"be a good idea to let them know how to reach you, what you block and why you " +"do that, your policies, etc." +msgstr "" + +msgid "Invalid email address" +msgstr "" + +msgid "It is NOT recommended for the casual user." +msgstr "" + +msgid "Local Set-up" +msgstr "" + +msgid "Location of the Privoxy User Manual." +msgstr "" + +msgid "Log File Viewer" +msgstr "" + +msgid "Log all data read from the network" +msgstr "" + +msgid "Log all data written to the network" +msgstr "" + +msgid "Log the applying actions" +msgstr "" + +msgid "" +"Log the destination for each request Privoxy let through. See also 'Debug " +"1024'." +msgstr "" + +msgid "" +"Log the destination for requests Privoxy didn't let through, and the reason " +"why." +msgstr "" + +msgid "Logging" +msgstr "" + +msgid "Main actions file" +msgstr "" + +msgid "Mandatory Input: No Data given!" +msgstr "" + +msgid "Mandatory Input: No Directory given!" +msgstr "" + +msgid "Mandatory Input: No File given!" +msgstr "" + +msgid "Mandatory Input: No Port given!" +msgstr "" + +msgid "Mandatory Input: No files given!" +msgstr "" + +msgid "Mandatory Input: No valid IPv4 address or host given!" +msgstr "" + +msgid "Mandatory Input: No valid IPv6 address given!" +msgstr "" + +msgid "Mandatory Input: No valid Port given!" +msgstr "" + +msgid "Maximum number of client connections that will be served." +msgstr "" + +msgid "Maximum size (in KB) of the buffer for content filtering." +msgstr "" + +msgid "Miscellaneous" +msgstr "" + +msgid "No trailing '/', please." +msgstr "" + +msgid "Non-fatal errors - *we highly recommended enabling this*" +msgstr "" + +msgid "" +"Number of seconds after which a socket times out if no data is received." +msgstr "" + +msgid "" +"Number of seconds after which an open connection will no longer be reused." +msgstr "" + +msgid "Please press [Read] button" +msgstr "" + +msgid "Please read Privoxy manual for details!" +msgstr "" + +msgid "Please update to the current version!" +msgstr "" + +msgid "Privoxy WEB proxy" +msgstr "" + +msgid "" +"Privoxy can (and normally does) use a number of other files for additional " +"configuration, help and logging. This section of the configuration file " +"tells Privoxy where to find those other files." +msgstr "" + +msgid "" +"Privoxy is a non-caching web proxy with advanced filtering capabilities for " +"enhancing privacy, modifying web page data and HTTP headers, controlling " +"access, and removing ads and other obnoxious Internet junk." +msgstr "" + +msgid "Read / Reread log file" +msgstr "" + +msgid "Show I/O status" +msgstr "" + +msgid "Show each connection status" +msgstr "" + +msgid "Show header parsing" +msgstr "" + +msgid "Software update required" +msgstr "" + +msgid "Start" +msgstr "" + +msgid "Start / Stop" +msgstr "" + +msgid "Start/Stop Privoxy WEB Proxy" +msgstr "" + +msgid "Startup banner and warnings." +msgstr "" + +msgid "Syntax:" +msgstr "" + +msgid "Syntax: Client header names delimited by spaces." +msgstr "" + +msgid "Syntax: target_pattern http_parent[:port]" +msgstr "" + +msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" +msgstr "" + +msgid "" +"The actions file(s) to use. Multiple actionsfile lines are permitted, and " +"are in fact recommended!" +msgstr "" + +msgid "" +"The address and TCP port on which Privoxy will listen for client requests." +msgstr "" + +msgid "" +"The compression level that is passed to the zlib library when compressing " +"buffered content." +msgstr "" + +msgid "" +"The currently installed 'privoxy' package is not supported by LuCI " +"application." +msgstr "" + +msgid "" +"The directory where all logging takes place (i.e. where the logfile is " +"located)." +msgstr "" + +msgid "The directory where the other configuration files are located." +msgstr "" + +msgid "" +"The filter files contain content modification rules that use regular " +"expressions." +msgstr "" + +msgid "The hostname shown on the CGI pages." +msgstr "" + +msgid "The log file to use. File name, relative to log directory." +msgstr "" + +msgid "The order in which client headers are sorted before forwarding them." +msgstr "" + +msgid "" +"The status code Privoxy returns for pages blocked with +handle-as-empty-" +"document." +msgstr "" + +msgid "" +"The trust mechanism is an experimental feature for building white-lists and " +"should be used with care." +msgstr "" + +msgid "" +"The value of this option only matters if the experimental trust mechanism " +"has been activated." +msgstr "" + +msgid "" +"This option is only there for debugging purposes. It will drastically reduce " +"performance." +msgstr "" + +msgid "" +"This option will be removed in future releases as it has been obsoleted by " +"the more general header taggers." +msgstr "" + +msgid "" +"This tab controls the security-relevant aspects of Privoxy's configuration." +msgstr "" + +msgid "" +"Through which SOCKS proxy (and optionally to which parent HTTP proxy) " +"specific requests should be routed." +msgstr "" + +msgid "To which parent HTTP proxy specific requests should be routed." +msgstr "" + +msgid "User customizations" +msgstr "" + +msgid "Value is not a number" +msgstr "" + +msgid "Value not between 0 and 9" +msgstr "" + +msgid "Value not between 1 and 4096" +msgstr "" + +msgid "Value not greater 0 or empty" +msgstr "" + +msgid "Value range 1 to 4096, no entry defaults to 4096" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Version Information" +msgstr "" + +msgid "Whether intercepted requests should be treated as valid." +msgstr "" + +msgid "" +"Whether or not Privoxy recognizes special HTTP headers to change toggle " +"state." +msgstr "" + +msgid "Whether or not buffered content is compressed before delivery." +msgstr "" + +msgid "" +"Whether or not outgoing connections that have been kept alive should be " +"shared between different incoming connections." +msgstr "" + +msgid "Whether or not pipelined requests should be served." +msgstr "" + +msgid "Whether or not proxy authentication through Privoxy should work." +msgstr "" + +msgid "Whether or not the web-based actions file editor may be used." +msgstr "" + +msgid "Whether or not the web-based toggle feature may be used." +msgstr "" + +msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected." +msgstr "" + +msgid "" +"Whether the CGI interface should stay compatible with broken HTTP clients." +msgstr "" + +msgid "Whether to run only one server thread." +msgstr "" + +msgid "Who can access what." +msgstr "" + +msgid "installed" +msgstr "" + +msgid "or higher" +msgstr "" + +msgid "required" +msgstr "" diff --git a/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy b/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy new file mode 100755 index 000000000..3405479b5 --- /dev/null +++ b/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy @@ -0,0 +1,12 @@ +#!/bin/sh + +# no longer needed for "Save and Apply" to restart privoxy +# luci-app-privoxy calls /etc/init.d/privoxy reload +uci -q batch <<-EOF >/dev/null + delete ucitrack.@privoxy[-1] + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache + +exit 0 diff --git a/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua b/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua index 4068cdbf5..0d738326a 100644 --- a/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua +++ b/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua @@ -1,23 +1,24 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> --- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> +-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. local uci = require "luci.model.uci".cursor() local bit = require "nixio".bit +local ip = require "luci.ip" -------------------- Init -------------------- -- -- Find link-local address -- -LL_PREFIX = luci.ip.IPv6("fe80::/64") function find_ll() - for _, r in ipairs(luci.sys.net.routes6()) do - if LL_PREFIX:contains(r.dest) and r.dest:higher(LL_PREFIX) then - return r.dest:sub(LL_PREFIX) + local _, r + for _, r in ipairs(ip.routes({ family = 6, dest = "fe80::/64" })) do + if r.dest:higher("fe80:0:0:0:ff:fe00:0:0") then + return (r.dest - "fe80::") end end - return luci.ip.IPv6("::") + return ip.IPv6("::") end -- @@ -33,15 +34,15 @@ local ipv4_netsz = uci:get("siit", "ipv4", "netsize") or "24" -- -- Find IPv4 allocation pool -- -local gv4_net = luci.ip.IPv4(ipv4_pool) +local gv4_net = ip.IPv4(ipv4_pool) -- -- Generate ULA -- -local ula = luci.ip.IPv6("::/64") +local ula = ip.IPv6("::/64") for _, prefix in ipairs({ ula_prefix, ula_global, ula_subnet }) do - ula = ula:add(luci.ip.IPv6(prefix)) + ula = ula:add(ip.IPv6(prefix)) end ula = ula:add(find_ll()) @@ -72,7 +73,7 @@ uci:foreach("wireless", "wifi-device", lanip = f:field(Value, "ipaddr", "LAN IPv4 subnet") function lanip.formvalue(self, section) local val = self.map:formvalue(self:cbid(section)) - local net = luci.ip.IPv4("%s/%i" %{ val, ipv4_netsz }) + local net = ip.IPv4("%s/%i" %{ val, ipv4_netsz }) if net then if gv4_net:contains(net) then @@ -110,7 +111,7 @@ function mode.write(self, section, value) -- -- Find LAN IPv4 range -- - local lan_net = luci.ip.IPv4( + local lan_net = ip.IPv4( ( lanip:formvalue(section) or "172.16.0.1" ) .. "/" .. ipv4_netsz ) @@ -182,7 +183,7 @@ function mode.write(self, section, value) }) -- use full siit subnet - siit_route = luci.ip.IPv6(siit_prefix .. "/96") + siit_route = ip.IPv6(siit_prefix .. "/96") -- v4 <-> siit route uci:delete_all("network", "route", @@ -212,7 +213,7 @@ function mode.write(self, section, value) }) -- derive siit subnet from lan config - siit_route = luci.ip.IPv6( + siit_route = ip.IPv6( siit_prefix .. "/" .. (96 + lan_net:prefix()) ):add(lan_net[2]) @@ -301,7 +302,7 @@ function mode.write(self, section, value) -- siit0 route uci:delete_all("network", "route6", - function(s) return siit_route:contains(luci.ip.IPv6(s.target)) end) + function(s) return siit_route:contains(ip.IPv6(s.target)) end) uci:section("network", "route6", nil, { interface = "siit0", diff --git a/applications/luci-app-splash/luasrc/controller/splash/splash.lua b/applications/luci-app-splash/luasrc/controller/splash/splash.lua index 97d040082..4add43559 100644 --- a/applications/luci-app-splash/luasrc/controller/splash/splash.lua +++ b/applications/luci-app-splash/luasrc/controller/splash/splash.lua @@ -23,14 +23,26 @@ function index() page.leaf = true end +function ip_to_mac(ip) + local ipc = require "luci.ip" + local i, n + + for i, n in ipairs(ipc.neighbors()) do + if n.mac and n.dest and n.dest:equal(ip) then + return n.mac + end + end +end + function action_dispatch() local uci = luci.model.uci.cursor_state() - local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR")) or "" + local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR")) or "" local access = false uci:foreach("luci_splash", "lease", function(s) if s.mac and s.mac:lower() == mac then access = true end end) + uci:foreach("luci_splash", "whitelist", function(s) if s.mac and s.mac:lower() == mac then access = true end end) @@ -51,13 +63,13 @@ function blacklist() end function action_activate() - local ip = luci.http.getenv("REMOTE_ADDR") or "127.0.0.1" - local mac = luci.sys.net.ip4mac(ip:match("^[\[::ffff:]*(%d+.%d+%.%d+%.%d+)\]*$")) + local ipc = require "luci.ip" + local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR") or "127.0.0.1") or "" local uci_state = require "luci.model.uci".cursor_state() local blacklisted = false if mac and luci.http.formvalue("accept") then uci:foreach("luci_splash", "blacklist", - function(s) if s.mac:lower() == mac or s.mac == mac then blacklisted = true end + function(s) if s.mac and s.mac:lower() == mac then blacklisted = true end end) if blacklisted then luci.http.redirect(luci.dispatcher.build_url("splash" ,"blocked")) diff --git a/applications/luci-app-splash/luasrc/view/admin_status/splash.htm b/applications/luci-app-splash/luasrc/view/admin_status/splash.htm index 67bb2fc49..831fa75f6 100644 --- a/applications/luci-app-splash/luasrc/view/admin_status/splash.htm +++ b/applications/luci-app-splash/luasrc/view/admin_status/splash.htm @@ -9,6 +9,7 @@ local utl = require "luci.util" local ipt = require "luci.sys.iptparser".IptParser() local uci = require "luci.model.uci".cursor_state() local wat = require "luci.tools.webadmin" +local ipc = require "luci.ip" local fs = require "nixio.fs" local clients = { } @@ -100,10 +101,12 @@ if fs.access(leasefile) then end end -for i, a in ipairs(luci.sys.net.arptable()) do - local c = clients[a["HW address"]:lower()] - if c and not c.ip then - c.ip = a["IP address"] +for i, n in ipairs(ipc.neighbors({ family = 4 })) do + if n.mac and n.dest then + local c = clients[n.mac] + if c and not c.ip then + c.ip = n.dest:string() + end end end diff --git a/applications/luci-app-splash/root/usr/sbin/luci-splash b/applications/luci-app-splash/root/usr/sbin/luci-splash index 0f8bdc2c4..2870dbe6a 100755 --- a/applications/luci-app-splash/root/usr/sbin/luci-splash +++ b/applications/luci-app-splash/root/usr/sbin/luci-splash @@ -2,14 +2,12 @@ utl = require "luci.util" sys = require "luci.sys" +ipc = require "luci.ip" -require("luci.model.uci") -require("luci.sys.iptparser") -- Init state session -local uci = luci.model.uci.cursor_state() -local ipt = luci.sys.iptparser.IptParser() -local net = sys.net +local uci = require "luci.model.uci".cursor_state() +local ipt = require "luci.sys.iptparser".IptParser() local fs = require "nixio.fs" local ip = require "luci.ip" @@ -139,6 +137,36 @@ function ipvalid(ipaddr) return false end +function mac_to_ip(mac) + local ipaddr = nil + ipc.neighbors({ family = 4 }, function(n) + if n.mac == mac and n.dest then + ipaddr = n.dest:string() + end + end) + return ipaddr +end + +function mac_to_dev(mac) + local dev = nil + ipc.neighbors({ family = 4 }, function(n) + if n.mac == mac and n.dev then + dev = n.dev + end + end) + return dev +end + +function ip_to_mac(ip) + local mac = nil + ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest and n.dest:equal(ip) then + mac = n.mac + end + end) + return mac +end + function main(argv) local cmd = table.remove(argv, 1) local arg = argv[1] @@ -157,7 +185,6 @@ function main(argv) lock() - local arp_cache = net.arptable() local leased_macs = get_known_macs("lease") local blacklist_macs = get_known_macs("blacklist") local whitelist_macs = get_known_macs("whitelist") @@ -167,17 +194,12 @@ function main(argv) if adr:find(":") then mac = adr:lower() else - for _, e in ipairs(arp_cache) do - if e["IP address"] == adr then - mac = e["HW address"]:lower() - break - end - end + mac = ip_to_mac(adr) end if mac and cmd == "add-rules" then if leased_macs[mac] then - add_lease(mac, arp_cache, true) + add_lease(mac, true) elseif blacklist_macs[mac] then add_blacklist_rule(mac) elseif whitelist_macs[mac] then @@ -277,15 +299,6 @@ function main(argv) end end --- Get current arp cache -function get_arpcache() - local arpcache = { } - for _, entry in ipairs(net.arptable()) do - arpcache[entry["HW address"]:lower()] = { entry["Device"]:lower(), entry["IP address"]:lower() } - end - return arpcache -end - -- Get a list of known mac addresses function get_known_macs(list) local leased_macs = { } @@ -355,17 +368,11 @@ function convert_mac_to_secname(mac) end -- Add a lease to state and invoke add_rule -function add_lease(mac, arp, no_uci) +function add_lease(mac, no_uci) mac = mac:lower() -- Get current ip address - local ipaddr - for _, entry in ipairs(arp or net.arptable()) do - if entry["HW address"]:lower() == mac then - ipaddr = entry["IP address"] - break - end - end + local ipaddr = mac_to_ip(mac) -- Add lease if there is an ip addr if ipaddr then @@ -598,8 +605,6 @@ function sync() uci:revert("luci_splash_leases") - local arpcache = get_arpcache() - local blackwhitelist = uci:get_all("luci_splash") local whitelist_total = 0 local whitelist_online = 0 @@ -618,7 +623,7 @@ function sync() leasecount = leasecount + 1 -- only count leases_online for connected clients - if arpcache[v.mac] then + if mac_to_ip(v.mac) then leases_online = leases_online + 1 end @@ -643,7 +648,7 @@ function sync() whitelist_total = whitelist_total + 1 if s.mac then local mac = s.mac:lower() - if arpcache[mac] then + if mac_to_ip(mac) then whitelist_online = whitelist_online + 1 end end @@ -652,7 +657,7 @@ function sync() blacklist_total = blacklist_total + 1 if s.mac then local mac = s.mac:lower() - if arpcache[mac] then + if mac_to_ip(mac) then blacklist_online = blacklist_online + 1 end end @@ -693,7 +698,6 @@ end -- Show client info function list() - local arpcache = get_arpcache() -- Find traffic usage local function traffic(lease) local traffic_in = 0 @@ -722,12 +726,11 @@ function list() if s[".type"] == "lease" and s.mac then local ti, to = traffic(s) local mac = s.mac:lower() - local arp = arpcache[mac] print(string.format( "%-17s %-15s %-9s %3dm %-7s %7dKB %7dKB", mac, s.ipaddr, "leased", math.floor(( os.time() - tonumber(s.start) ) / 60), - arp and arp[1] or "?", ti, to + mac_to_dev(mac) or "?", ti, to )) end end @@ -738,11 +741,10 @@ function list() ) do if (s[".type"] == "whitelist" or s[".type"] == "blacklist") and s.mac then local mac = s.mac:lower() - local arp = arpcache[mac] print(string.format( "%-17s %-15s %-9s %4s %-7s %9s %9s", - mac, arp and arp[2] or "?", s[".type"], - "- ", arp and arp[1] or "?", "-", "-" + mac, mac_to_ip(mac) or "?", s[".type"], + "- ", mac_to_dev(mac) or "?", "-", "-" )) end end diff --git a/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua b/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua index 329341ddc..c532b4a6d 100644 --- a/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua +++ b/applications/luci-app-statistics/luasrc/controller/luci_statistics/luci_statistics.lua @@ -150,7 +150,6 @@ function statistics_render() if png then luci.http.prepare_content("image/png") l12.pump.all(l12.source.file(png), luci.http.write) - png:close() end return end diff --git a/applications/luci-app-upnp/luasrc/controller/upnp.lua b/applications/luci-app-upnp/luasrc/controller/upnp.lua index 8fc697fd6..790bf29d8 100644 --- a/applications/luci-app-upnp/luasrc/controller/upnp.lua +++ b/applications/luci-app-upnp/luasrc/controller/upnp.lua @@ -19,7 +19,7 @@ function index() end function act_status() - local ipt = io.popen("iptables --line-numbers -t nat -xnvL MINIUPNPD") + local ipt = io.popen("iptables --line-numbers -t nat -xnvL MINIUPNPD 2>/dev/null") if ipt then local fwd = { } while true do diff --git a/contrib/luadoc/hostfiles/bin/luadoc b/build/luadoc/doc.lua index ba99a3775..383dde29c 100755 --- a/contrib/luadoc/hostfiles/bin/luadoc +++ b/build/luadoc/doc.lua @@ -4,7 +4,12 @@ -- @release $Id: luadoc.lua.in,v 1.1 2008/02/17 06:42:51 jasonsantos Exp $ ------------------------------------------------------------------------------- -require "luadoc" +--local source = debug.getinfo(1).source or "" +--local mypath = source:match("@(.+)/[^/]+") + +--package.path = package.path .. ";" .. mypath .. "/?.lua;" .. mypath .. "/?/init.lua" + +require "luadoc.init" ------------------------------------------------------------------------------- -- Print version number. diff --git a/contrib/luadoc/lua/luadoc/config.lua b/build/luadoc/luadoc/config.lua index 9e4b9de3c..9e4b9de3c 100644 --- a/contrib/luadoc/lua/luadoc/config.lua +++ b/build/luadoc/luadoc/config.lua diff --git a/contrib/luadoc/lua/luadoc/doclet/debug.lua b/build/luadoc/luadoc/doclet/debug.lua index 0b75f84cb..0b75f84cb 100644 --- a/contrib/luadoc/lua/luadoc/doclet/debug.lua +++ b/build/luadoc/luadoc/doclet/debug.lua diff --git a/contrib/luadoc/lua/luadoc/doclet/formatter.lua b/build/luadoc/luadoc/doclet/formatter.lua index 2d725389c..2d725389c 100644 --- a/contrib/luadoc/lua/luadoc/doclet/formatter.lua +++ b/build/luadoc/luadoc/doclet/formatter.lua diff --git a/contrib/luadoc/lua/luadoc/doclet/html.lua b/build/luadoc/luadoc/doclet/html.lua index e77fb7441..e77fb7441 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html.lua +++ b/build/luadoc/luadoc/doclet/html.lua diff --git a/contrib/luadoc/lua/luadoc/doclet/html/constant.lp b/build/luadoc/luadoc/doclet/html/constant.lp index 2e35392ad..2e35392ad 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/constant.lp +++ b/build/luadoc/luadoc/doclet/html/constant.lp diff --git a/contrib/luadoc/lua/luadoc/doclet/html/file.lp b/build/luadoc/luadoc/doclet/html/file.lp index 67926b4a7..68f486404 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/file.lp +++ b/build/luadoc/luadoc/doclet/html/file.lp @@ -52,7 +52,7 @@ <%for _, func_name in ipairs(file_doc.functions) do local func_data = file_doc.functions[func_name]%> <tr> - <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a> (<%=table.concat(func_data.param, ", ")%>)</td> + <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a> (<%=table.concat(func_data.param or {}, ", ")%>)</td> <td class="summary"><%=func_data.summary%></td> </tr> <%end%> diff --git a/contrib/luadoc/lua/luadoc/doclet/html/function.lp b/build/luadoc/luadoc/doclet/html/function.lp index a870ff8bb..29d403e00 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/function.lp +++ b/build/luadoc/luadoc/doclet/html/function.lp @@ -8,7 +8,7 @@ else end %> -<dt><%=func.private and "local " or ""%><a name="<%=func.name%>"></a><strong><%=(oop and func.name:gsub("%.",":") or func.name:gsub(".+%.",""))%></strong> (<%=table.concat(func.param or {}, ", ")%>)</dt> +<dt><%=func.private and "local " or ""%><a name="<%=func.name%>"></a><strong><%=func.printname%></strong> (<%=table.concat(func.param or {}, ", ")%>)</dt> <dd> <%=func.description or ""%> @@ -59,6 +59,6 @@ end <%=(oop and func.see[i]:gsub("%.",":") or func.see[i]:gsub(".+%.",""))%> </a> <%end%> -</ul +</ul> <%end%> </dd> diff --git a/contrib/luadoc/lua/luadoc/doclet/html/index.lp b/build/luadoc/luadoc/doclet/html/index.lp index b4b9f5c3b..b4b9f5c3b 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/index.lp +++ b/build/luadoc/luadoc/doclet/html/index.lp diff --git a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css b/build/luadoc/luadoc/doclet/html/luadoc.css index bc0f98a5a..f9f974951 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css +++ b/build/luadoc/luadoc/doclet/html/luadoc.css @@ -32,7 +32,7 @@ hr { color:#cccccc } img { border-width: 0px; } -h3 { padding-top: 1em; } +h3 { padding: 1em 0 0.5em; } p { margin-left: 1em; } @@ -81,9 +81,8 @@ table.index ul { padding-top: 0em; margin-top: 0em; } table { border: 1px solid black; - border-collapse: collapse; - margin-left: auto; - margin-right: auto; + border-collapse: collapse; + margin: 1em auto; } th { border: 1px solid black; @@ -275,8 +274,8 @@ table.table_list td.name { background-color: #f0f0f0; } table.table_list td.summary { width: 100%; } dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} -dl.function dd {padding-bottom: 1em;} -dl.function h3 {padding: 0; margin: 0; font-size: medium;} +dl.function dd {padding: 0.5em 0;} +dl.function h3 {margin: 0; font-size: medium;} dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;} dl.table dd {padding-bottom: 1em;} diff --git a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp b/build/luadoc/luadoc/doclet/html/menu.lp index 0fe365202..0fe365202 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp +++ b/build/luadoc/luadoc/doclet/html/menu.lp diff --git a/contrib/luadoc/lua/luadoc/doclet/html/module.lp b/build/luadoc/luadoc/doclet/html/module.lp index daa708628..0798c1be0 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/module.lp +++ b/build/luadoc/luadoc/doclet/html/module.lp @@ -20,7 +20,7 @@ <div id="navigation"> <%=luadoc.doclet.html.include("menu.lp", { doc=doc, module_doc=module_doc })%> -<% oop = ( module_doc.doc[1].cstyle == "instance" ) and true or false %> +<% oop = not not ( module_doc.doc[1] and module_doc.doc[1].cstyle == "instance" ) %> </div><!-- id="navigation" --> @@ -58,13 +58,34 @@ </table> <%end%> -<%if #module_doc.functions > 0 then %> +<% local funcs = { }; if #module_doc.functions > 0 then %> <h2>Functions</h2> <table class="function_list"> -<%for _, func_name in ipairs(module_doc.functions) do - local func_data = module_doc.functions[func_name]%> +<% +for _, func_name in ipairs(module_doc.functions) do + funcs[#funcs+1] = func_name +end + +table.sort(funcs, function(a, b) + local func_data_a = module_doc.functions[a] + local func_data_b = module_doc.functions[b] + local x = func_data_a.sort or a + local y = func_data_b.sort or b + return x < y +end) + +for _, func_name in ipairs(funcs) do + local func_data = module_doc.functions[func_name] + + func_data.printname = func_name:gsub("^%d+#", "") + if oop then + func_data.printname = func_data.printname:gsub("%.", ":") + else + func_data.printname = func_data.printname:gsub("^.+%.", "") + end +%> <tr> - <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=(oop and func_name:gsub("%.",":") or func_name:gsub(".+%.",""))%></a> (<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)</td> + <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_data.printname%></a> (<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)</td> <td class="summary"><%=module_doc.functions[func_name].summary%></td> </tr> <%end%> @@ -72,10 +93,24 @@ <%end%> -<%if #module_doc.tables > 0 then%> +<% local tabs = { }; if #module_doc.tables > 0 then%> <h2>Tables</h2> <table class="table_list"> -<%for _, tab_name in ipairs(module_doc.tables) do%> +<% +for _, tab_name in ipairs(module_doc.tables) do + tabs[#tabs+1] = tab_name +end + +table.sort(tabs, function(a, b) + local tab_data_a = module_doc.tables[a] + local tab_data_b = module_doc.tables[b] + local x = tab_data_a.sort or a + local y = tab_data_b.sort or b + return x < y +end) + +for _, tab_name in ipairs(tabs) do +%> <tr> <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td> <td class="summary"><%=module_doc.tables[tab_name].summary%></td> @@ -91,7 +126,7 @@ <%if #module_doc.functions > 0 then%> <h2><a name="functions"></a>Functions</h2> <dl class="function"> -<%for _, func_name in ipairs(module_doc.functions) do%> +<%for _, func_name in ipairs(funcs) do%> <%=luadoc.doclet.html.include("function.lp", { doc=doc, module_doc=module_doc, func=module_doc.functions[func_name], oop=oop })%> <%end%> </dl> @@ -100,7 +135,7 @@ <%if #module_doc.tables > 0 then%> <h2><a name="tables"></a>Tables</h2> <dl class="table"> -<%for _, tab_name in ipairs(module_doc.tables) do%> +<%for _, tab_name in ipairs(tabs) do%> <%=luadoc.doclet.html.include("table.lp", { doc=doc, module_doc=module_doc, tab=module_doc.tables[tab_name] })%> <%end%> </dl> diff --git a/contrib/luadoc/lua/luadoc/doclet/html/table.lp b/build/luadoc/luadoc/doclet/html/table.lp index 5cd023953..5cd023953 100644 --- a/contrib/luadoc/lua/luadoc/doclet/html/table.lp +++ b/build/luadoc/luadoc/doclet/html/table.lp diff --git a/contrib/luadoc/lua/luadoc/doclet/raw.lua b/build/luadoc/luadoc/doclet/raw.lua index 1e880b883..1e880b883 100644 --- a/contrib/luadoc/lua/luadoc/doclet/raw.lua +++ b/build/luadoc/luadoc/doclet/raw.lua diff --git a/contrib/luadoc/lua/luadoc/init.lua b/build/luadoc/luadoc/init.lua index 649515de6..649515de6 100644 --- a/contrib/luadoc/lua/luadoc/init.lua +++ b/build/luadoc/luadoc/init.lua diff --git a/contrib/luadoc/lua/luadoc/lp.lua b/build/luadoc/luadoc/lp.lua index adf84f9f0..adf84f9f0 100644 --- a/contrib/luadoc/lua/luadoc/lp.lua +++ b/build/luadoc/luadoc/lp.lua diff --git a/contrib/luadoc/lua/luadoc/taglet/standard.lua b/build/luadoc/luadoc/taglet/standard.lua index 17a305889..ef925f8c7 100644 --- a/contrib/luadoc/lua/luadoc/taglet/standard.lua +++ b/build/luadoc/luadoc/taglet/standard.lua @@ -242,7 +242,7 @@ local function parse_comment (block, first_line, modulename) currenttag = tag currenttext = text else - currenttext = util.concat(currenttext, line) + currenttext = util.concat(currenttext, "\n" .. line) assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line)) end end) @@ -270,13 +270,15 @@ end -- @return modulename if found local function parse_block (f, line, modulename, first) + local multiline = not not (line and line:match("%[%[")) local block = { comment = {}, code = {}, } while line ~= nil do - if string.find(line, "^[\t ]*%-%-") == nil then + if (multiline == true and string.find(line, "%]%]") ~= nil) or + (multiline == false and string.find(line, "^[\t ]*%-%-") == nil) then -- reached end of comment, read the code below it -- TODO: allow empty lines line, block.code, modulename = parse_code(f, line, modulename) diff --git a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua b/build/luadoc/luadoc/taglet/standard/tags.lua index d03df82df..e9d035483 100644 --- a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua +++ b/build/luadoc/luadoc/taglet/standard/tags.lua @@ -7,7 +7,7 @@ local luadoc = require "luadoc" local util = require "luadoc.util" local string = require "string" local table = require "table" -local assert, type, tostring = assert, type, tostring +local assert, type, tostring, tonumber = assert, type, tostring, tonumber module "luadoc.taglet.standard.tags" @@ -38,6 +38,12 @@ end ------------------------------------------------------------------------------- +local function sort (tag, block, text) + block[tag] = tonumber(text) or 0 +end + +------------------------------------------------------------------------------- + local function copyright (tag, block, text) block[tag] = text end @@ -164,6 +170,7 @@ handlers["param"] = param handlers["release"] = release handlers["return"] = ret handlers["see"] = see +handlers["sort"] = sort handlers["usage"] = usage ------------------------------------------------------------------------------- @@ -173,6 +180,12 @@ function handle (tag, block, text) luadoc.logger:error(string.format("undefined handler for tag `%s'", tag)) return end + + if text then + text = text:gsub("`([^\n]-)`", "<code>%1</code>") + text = text:gsub("`(.-)`", "<pre>%1</pre>") + end + -- assert(handlers[tag], string.format("undefined handler for tag `%s'", tag)) return handlers[tag](tag, block, text) end diff --git a/contrib/luadoc/lua/luadoc/util.lua b/build/luadoc/luadoc/util.lua index acaaac828..e1058d590 100644 --- a/contrib/luadoc/lua/luadoc/util.lua +++ b/build/luadoc/luadoc/util.lua @@ -29,8 +29,9 @@ end -- @see string.gsub function trim_comment (s) - s = string.gsub(s, "%-%-+(.*)$", "%1") - return trim(s) + s = string.gsub(s, "^%s*%-%-+%[%[(.*)$", "%1") + s = string.gsub(s, "^%s*%-%-+(.*)$", "%1") + return s end ------------------------------------------------------------------------------- @@ -58,8 +59,8 @@ end ------------------------------------------------------------------------------- -- Split text into a list consisting of the strings in text, --- separated by strings matching delim (which may be a pattern). --- @param delim if delim is "" then action is the same as %s+ except that +-- separated by strings matching delim (which may be a pattern). +-- @param delim if delim is "" then action is the same as %s+ except that -- field 1 may be preceeded by leading whitespace -- @usage split(",%s*", "Anna, Bob, Charlie,Dolores") -- @usage split(""," x y") gives {"x","y"} @@ -73,12 +74,12 @@ function split(delim, text) delim = delim or "" local pos = 1 -- if delim matches empty string then it would give an endless loop - if string.find("", delim, 1) and delim ~= "" then + if string.find("", delim, 1) and delim ~= "" then error("delim matches empty string!") end local first, last while 1 do - if delim ~= "" then + if delim ~= "" then first, last = string.find(text, delim, pos) else first, last = string.find(text, "%s+", pos) @@ -169,33 +170,32 @@ function loadlogengine(options) require "logging" require "logging.console" end) - + local logging = logenabled and logging - + if logenabled then if options.filelog then logger = logging.file("luadoc.log") -- use this to get a file log else logger = logging.console("[%level] %message\n") end - + if options.verbose then logger:setLevel(logging.INFO) else logger:setLevel(logging.WARN) end - + else noop = {__index=function(...) return function(...) -- noop end end} - - logger = {} + + logger = {} setmetatable(logger, noop) end - + return logger end - diff --git a/build/makedocs.sh b/build/makedocs.sh index 2f7f57345..a9fc760e7 100755 --- a/build/makedocs.sh +++ b/build/makedocs.sh @@ -1,2 +1,14 @@ -luadoc -d $2 --no-files $(for f in $(find $1 -name '*.lua' -type f); do if grep -q -- "@return" $f; then echo $f; fi; done) -echo API-Documentation was created in $2. +#!/bin/bash + +topdir=$(pwd) + +[ -f "$topdir/build/makedocs.sh" -a -n "$1" ] || { + echo "Please execute as ./build/makedocs.sh [output directory]" >&2 + exit 1 +} + +( + cd "$topdir/build/luadoc/" + find "$topdir/libs/" "$topdir/modules/" -type f -name '*.lua' -or -name '*.luadoc' | \ + xargs grep -l '@return' | xargs ./doc.lua --no-files -d "$1" +) diff --git a/build/zoneinfo2lua.pl b/build/zoneinfo2lua.pl index 4a90dae35..d3f040326 100755 --- a/build/zoneinfo2lua.pl +++ b/build/zoneinfo2lua.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl # zoneinfo2lua.pl - Make Lua module from /usr/share/zoneinfo -# Execute from within /usr/share/zoneinfo +# Execute from within root of Luci feed, usually feeds/luci # $Id$ use strict; @@ -51,16 +51,7 @@ open(O, "> $tzdout/tzdata.lua") || die "open($tzdout/tzdata.lua): $!\n"; print STDERR "Writing time zones to $tzdout/tzdata.lua ... "; print O <<HEAD; ---[[ -LuCI - Autogenerated Zoneinfo Module - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -]]-- +-- Licensed to the public under the Apache License 2.0. module "luci.sys.zoneinfo.tzdata" @@ -81,16 +72,7 @@ open (O, "> $tzdout/tzoffset.lua") || die "open($tzdout/tzoffset.lua): $!\n"; print STDERR "Writing time offsets to $tzdout/tzoffset.lua ... "; print O <<HEAD; ---[[ -LuCI - Autogenerated Zoneinfo Module - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -]]-- +-- Licensed to the public under the Apache License 2.0. module "luci.sys.zoneinfo.tzoffset" diff --git a/contrib/luadoc/Makefile b/contrib/luadoc/Makefile deleted file mode 100644 index 81a96f6a8..000000000 --- a/contrib/luadoc/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -include ../../build/config.mk -include ../../build/module.mk
\ No newline at end of file diff --git a/contrib/package/community-profiles/files/etc/config/profile_aachen b/contrib/package/community-profiles/files/etc/config/profile_aachen deleted file mode 100644 index cc643e131..000000000 --- a/contrib/package/community-profiles/files/etc/config/profile_aachen +++ /dev/null @@ -1,10 +0,0 @@ -config 'community' 'profile' - option 'name' 'Aachen' - option 'homepage' 'http://aachen.freifunk.net' - option 'ssid' 'aachen.freifunk.net' - option 'suffix' 'ffac' - option 'latitude' '50.77900' - option 'longitude' '6.05399' - option 'mesh_network' '10.90.0.0/16' - option 'splash_network' '10.104.0.0/16' - option 'splash_prefix' '28' diff --git a/contrib/package/community-profiles/files/etc/config/profile_augsburg b/contrib/package/community-profiles/files/etc/config/profile_augsburg index 0bbafcaea..4434b0fba 100644 --- a/contrib/package/community-profiles/files/etc/config/profile_augsburg +++ b/contrib/package/community-profiles/files/etc/config/profile_augsburg @@ -13,10 +13,7 @@ config 'community' 'profile' option 'ipv6' '1' option 'ipv6_config' 'auto-ipv6-fromv4' option 'ipv6_prefix' 'fdca:ffee:ffa::/48' - option 'extrapackages' 'luci-app-owm luci-app-owm-ant luci-app-owm-cmd luci-app-owm-gui' - list 'owm_api' 'http://api.openwifimap.net' - list 'owm_api' 'http://owmapi.pberg.freifunk.net' - option 'mapserver' 'http://openwifimap.net/' + #option 'extrapackages' '' config 'defaults' 'interface' option 'netmask' '255.255.192.0' diff --git a/contrib/package/community-profiles/files/etc/config/profile_demo b/contrib/package/community-profiles/files/etc/config/profile_demo index d32234961..c11a74f26 100644 --- a/contrib/package/community-profiles/files/etc/config/profile_demo +++ b/contrib/package/community-profiles/files/etc/config/profile_demo @@ -11,10 +11,7 @@ config 'community' 'profile' option 'ipv6' '1' option 'ipv6_config' 'auto-ipv6-fromv4' option 'ipv6_prefix' 'fdca:ffff:ffff::/48' - option 'extrapackages' 'luci-app-owm luci-app-owm-ant luci-app-owm-cmd luci-app-owm-gui' - list 'owm_api' 'http://api.openwifimap.net' - list 'owm_api' 'http://owmapi.pberg.freifunk.net' - option 'mapserver' 'http://openwifimap.net/' + #option 'extrapackages' '' config 'defaults' 'interface' option 'netmask' '255.255.255.0' diff --git a/contrib/package/community-profiles/files/etc/config/profile_neuss b/contrib/package/community-profiles/files/etc/config/profile_neuss deleted file mode 100644 index 8e3061730..000000000 --- a/contrib/package/community-profiles/files/etc/config/profile_neuss +++ /dev/null @@ -1,15 +0,0 @@ -config 'community' 'profile' - option 'name' 'Freifunk Neuss' - option 'homepage' 'http://neuss.freifunk.net' - option 'ssid' 'neuss.freifunk.net' - option 'mesh_network' '172.28.0.0/16' - option 'splash_network' '10.104.0.0/16' - option 'splash_prefix' '27' - option 'latitude' '51.19045' - option 'longitude' '6.69471' - -config 'defaults' 'wifi_device' - option 'channel' '11' - -config 'defaults' 'wifi_iface' - option 'bssid' 'DE:AD:BE:EF:CA:FE' diff --git a/contrib/package/community-profiles/files/etc/config/profile_potsdam b/contrib/package/community-profiles/files/etc/config/profile_potsdam index bde824f35..c15624a9f 100644 --- a/contrib/package/community-profiles/files/etc/config/profile_potsdam +++ b/contrib/package/community-profiles/files/etc/config/profile_potsdam @@ -13,3 +13,7 @@ config 'defaults' 'wifi_device' config 'defaults' 'bssidscheme' option '5' '02:CA:FF:EE:BA:BE' + +config 'defaults' 'interface' + option 'dns' '85.214.20.141 213.73.91.35 194.150.168.168' + diff --git a/contrib/package/freifunk-common/files/etc/config/freifunk b/contrib/package/freifunk-common/files/etc/config/freifunk index c55fe0aff..9a46f056a 100644 --- a/contrib/package/freifunk-common/files/etc/config/freifunk +++ b/contrib/package/freifunk-common/files/etc/config/freifunk @@ -96,17 +96,19 @@ config 'defaults' 'wifi_device' option 'diversity' '1' option 'disabled' '0' option 'country' 'DE' - option 'hwmode' '11g' option 'distance' '1000' config 'defaults' 'wifi_iface' option 'mode' 'adhoc' option 'encryption' 'none' - option 'bgscan' '0' option 'bssid' '12:CA:FF:EE:BA:BE' + option 'mcast_rate' '6000' + +config 'defaults' 'madwifi_wifi_iface' + option 'bgscan' '0' option 'sw_merge' '1' - option 'mcast_rate' '5500' - option 'probereq' '1' + option 'probereq' '1' + option 'mcast_rate' '5500' config 'defaults' 'interface' option 'netmask' '255.255.0.0' diff --git a/contrib/package/meshwizard/Makefile b/contrib/package/meshwizard/Makefile index 45d5f2162..9d74fe547 100644 --- a/contrib/package/meshwizard/Makefile +++ b/contrib/package/meshwizard/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=meshwizard -PKG_RELEASE:=0.1.0 +PKG_RELEASE:=0.1.1 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) diff --git a/contrib/package/meshwizard/files/usr/bin/meshwizard/helpers/setup_wifi.sh b/contrib/package/meshwizard/files/usr/bin/meshwizard/helpers/setup_wifi.sh index 707b7b72c..41feb8665 100755 --- a/contrib/package/meshwizard/files/usr/bin/meshwizard/helpers/setup_wifi.sh +++ b/contrib/package/meshwizard/files/usr/bin/meshwizard/helpers/setup_wifi.sh @@ -59,6 +59,11 @@ uci set wireless.$net\_iface=wifi-iface # create new wifi-iface for $net from defaults set_defaults "wifi_iface_" wireless.$net\_iface +# overwrite some settings for type atheros (madwifi) +if [ "$type" = "atheros" ]; then + set_defaults "madwifi_wifi_iface_" wireless.${net} +fi + # overwrite defaults bssid="$($dir/helpers/gen_bssid.sh $channel $community)" diff --git a/libs/luci-lib-httpclient/luasrc/httpclient.lua b/libs/luci-lib-httpclient/luasrc/httpclient.lua index 94c2e9eca..c76cc542e 100644 --- a/libs/luci-lib-httpclient/luasrc/httpclient.lua +++ b/libs/luci-lib-httpclient/luasrc/httpclient.lua @@ -97,7 +97,11 @@ end function request_raw(uri, options) options = options or {} local pr, auth, host, port, path - + + if options.params then + uri = uri .. '?' .. http.urlencode_params(options.params) + end + if uri:find("%[") then if uri:find("@") then pr, auth, host, port, path = uri:match("(%w+)://(.+)@(%b[]):?([0-9]*)(.*)") @@ -176,20 +180,8 @@ function request_raw(uri, options) options.method = options.method or "POST" end - -- Assemble message - local message = {(options.method or "GET") .. " " .. path .. " " .. protocol} - - for k, v in pairs(headers) do - if type(v) == "string" or type(v) == "number" then - message[#message+1] = k .. ": " .. v - elseif type(v) == "table" then - for i, j in ipairs(v) do - message[#message+1] = k .. ": " .. j - end - end - end - if options.cookies then + local cookiedata = {} for _, c in ipairs(options.cookies) do local cdo = c.flags.domain local cpa = c.flags.path @@ -197,11 +189,29 @@ function request_raw(uri, options) and (cpa == path or cpa == "/" or cpa .. "/" == path:sub(#cpa+1)) and (not c.flags.secure or pr == "https") then - message[#message+1] = "Cookie: " .. c.key .. "=" .. c.value + cookiedata[#cookiedata+1] = c.key .. "=" .. c.value end end + if headers["Cookie"] then + headers["Cookie"] = headers["Cookie"] .. "; " .. table.concat(cookiedata, "; ") + else + headers["Cookie"] = table.concat(cookiedata, "; ") + end end + + -- Assemble message + local message = {(options.method or "GET") .. " " .. path .. " " .. protocol} + for k, v in pairs(headers) do + if type(v) == "string" or type(v) == "number" then + message[#message+1] = k .. ": " .. v + elseif type(v) == "table" then + for i, j in ipairs(v) do + message[#message+1] = k .. ": " .. j + end + end + end + message[#message+1] = "" message[#message+1] = "" @@ -323,7 +333,7 @@ function request_raw(uri, options) end end - return response.code, response, linesrc(true), sock + return response.code, response, linesrc(true)..sock:readall(), sock end function cookie_parse(cookiestr) diff --git a/libs/luci-lib-ip/Makefile b/libs/luci-lib-ip/Makefile new file mode 100644 index 000000000..eb80dcb25 --- /dev/null +++ b/libs/luci-lib-ip/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2015 LuCI Team <luci@lists.subsignal.org> +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Lua library for IP calculation and routing information +LUCI_DEPENDS:=+liblua +libnl-tiny + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-ip/src/Makefile b/libs/luci-lib-ip/src/Makefile new file mode 100644 index 000000000..76abd27d2 --- /dev/null +++ b/libs/luci-lib-ip/src/Makefile @@ -0,0 +1,17 @@ +IP_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/libnl-tiny/ +IP_LDFLAGS = -llua -lm -lnl-tiny +IP_OBJ = ip.o +IP_LIB = ip.so + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(IP_CFLAGS) $(FPIC) -c -o $@ $< + +compile: $(IP_OBJ) + $(CC) $(LDFLAGS) -shared -o $(IP_LIB) $(IP_OBJ) $(IP_LDFLAGS) + +install: compile + mkdir -p $(DESTDIR)/usr/lib/lua/luci + cp $(IP_LIB) $(DESTDIR)/usr/lib/lua/luci/$(IP_LIB) + +clean: + rm -f *.o *.so diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c new file mode 100644 index 000000000..b91966c53 --- /dev/null +++ b/libs/luci-lib-ip/src/ip.c @@ -0,0 +1,1392 @@ +/* +Copyright 2015 Jo-Philipp Wich <jow@openwrt.org> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <limits.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include <net/if.h> +#include <netinet/ether.h> +#include <arpa/inet.h> +#include <netlink/msg.h> +#include <netlink/attr.h> +#include <netlink/socket.h> +#include <linux/rtnetlink.h> + +#define LUCI_IP "luci.ip" +#define LUCI_IP_CIDR "luci.ip.cidr" + +#define RTA_INT(x) (*(int *)RTA_DATA(x)) +#define RTA_U32(x) (*(uint32_t *)RTA_DATA(x)) + +static int hz = 0; +static struct nl_sock *sock = NULL; + +typedef struct { + union { + struct in_addr v4; + struct in6_addr v6; + } addr; + int len; + int bits; + int family; + bool exact; +} cidr_t; + +struct dump_filter { + bool get; + int family; + int iif; + int oif; + int type; + int scope; + int proto; + int table; + cidr_t gw; + cidr_t from; + cidr_t src; + cidr_t dst; + struct ether_addr mac; +}; + +struct dump_state { + int index; + int pending; + int callback; + struct lua_State *L; + struct dump_filter *filter; +}; + + +static int _cidr_new(lua_State *L, int index, int family, bool mask); + +static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p) +{ + if (lua_type(L, index) == LUA_TUSERDATA) + return luaL_checkudata(L, index, LUCI_IP_CIDR); + + if (_cidr_new(L, index, p ? p->family : 0, false)) + return lua_touserdata(L, -1); + + luaL_error(L, "Invalid operand"); + return NULL; +} + +static bool parse_mask(int family, const char *mask, int *bits) +{ + char *e; + struct in_addr m; + struct in6_addr m6; + + if (family == AF_INET && inet_pton(AF_INET, mask, &m)) + { + for (*bits = 0, m.s_addr = ntohl(m.s_addr); + *bits < 32 && (m.s_addr << *bits) & 0x80000000; + ++*bits); + } + else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6)) + { + for (*bits = 0; + *bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128; + ++*bits); + } + else + { + *bits = strtoul(mask, &e, 10); + + if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128)) + return false; + } + + return true; +} + +static bool parse_cidr(const char *dest, cidr_t *pp) +{ + char *p, buf[INET6_ADDRSTRLEN * 2 + 2]; + uint8_t bitlen = 0; + + strncpy(buf, dest, sizeof(buf) - 1); + + p = strchr(buf, '/'); + + if (p) + *p++ = 0; + + if (inet_pton(AF_INET, buf, &pp->addr.v4)) + { + bitlen = 32; + pp->family = AF_INET; + pp->len = sizeof(struct in_addr); + } + else if (inet_pton(AF_INET6, buf, &pp->addr.v6)) + { + bitlen = 128; + pp->family = AF_INET6; + pp->len = sizeof(struct in6_addr); + } + else + return false; + + if (p) + { + if (!parse_mask(pp->family, p, &pp->bits)) + return false; + } + else + { + pp->bits = bitlen; + } + + return true; +} + +static int L_getint(lua_State *L, int index, const char *name) +{ + int rv = 0; + + lua_getfield(L, index, name); + + if (lua_type(L, -1) == LUA_TNUMBER) + rv = lua_tonumber(L, -1); + + lua_pop(L, 1); + + return rv; +} + +static const char * L_getstr(lua_State *L, int index, const char *name) +{ + const char *rv = NULL; + + lua_getfield(L, index, name); + + if (lua_type(L, -1) == LUA_TSTRING) + rv = lua_tostring(L, -1); + + lua_pop(L, 1); + + return rv; +} + +static void L_setint(struct lua_State *L, const char *name, uint32_t n) +{ + lua_pushinteger(L, n); + lua_setfield(L, -2, name); +} + +static void L_setbool(struct lua_State *L, const char *name, bool val) +{ + lua_pushboolean(L, val); + lua_setfield(L, -2, name); +} + +static void L_setaddr(struct lua_State *L, const char *name, + int family, void *addr, int bits) +{ + cidr_t *p; + + if (!addr) + return; + + p = lua_newuserdata(L, sizeof(*p)); + + if (!p) + return; + + if (family == AF_INET) + { + p->family = AF_INET; + p->bits = (bits < 0) ? 32 : bits; + p->len = sizeof(p->addr.v4); + p->addr.v4 = *(struct in_addr *)addr; + } + else + { + p->family = AF_INET6; + p->bits = (bits < 0) ? 128 : bits; + p->len = sizeof(p->addr.v6); + p->addr.v6 = *(struct in6_addr *)addr; + } + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + lua_setfield(L, -2, name); +} + +static void L_setstr(struct lua_State *L, const char *name, const char *val) +{ + lua_pushstring(L, val); + lua_setfield(L, -2, name); +} + +static void L_setdev(struct lua_State *L, const char *name, + struct nlattr *attr) +{ + char buf[32]; + + if (if_indextoname(RTA_INT(attr), buf)) + L_setstr(L, name, buf); +} + +static int L_checkbits(lua_State *L, int index, cidr_t *p) +{ + int bits; + + if (lua_gettop(L) < index || lua_isnil(L, index)) + { + bits = p->bits; + } + else if (lua_type(L, index) == LUA_TNUMBER) + { + bits = lua_tointeger(L, index); + + if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128)) + return luaL_error(L, "Invalid prefix size"); + } + else if (lua_type(L, index) == LUA_TSTRING) + { + if (!parse_mask(p->family, lua_tostring(L, index), &bits)) + return luaL_error(L, "Invalid netmask format"); + } + else + { + return luaL_error(L, "Invalid data type"); + } + + return bits; +} + +static int _cidr_new(lua_State *L, int index, int family, bool mask) +{ + uint32_t n; + const char *addr; + cidr_t cidr = { }, *cidrp; + + if (lua_type(L, index) == LUA_TNUMBER) + { + n = htonl(lua_tointeger(L, index)); + + if (family == AF_INET6) + { + cidr.family = AF_INET6; + cidr.bits = 128; + cidr.len = sizeof(cidr.addr.v6); + cidr.addr.v6.s6_addr[12] = n; + cidr.addr.v6.s6_addr[13] = (n >> 8); + cidr.addr.v6.s6_addr[14] = (n >> 16); + cidr.addr.v6.s6_addr[15] = (n >> 24); + } + else + { + cidr.family = AF_INET; + cidr.bits = 32; + cidr.len = sizeof(cidr.addr.v4); + cidr.addr.v4.s_addr = n; + } + } + else + { + addr = luaL_checkstring(L, index); + + if (!parse_cidr(addr, &cidr)) + return 0; + + if (family && cidr.family != family) + return 0; + + if (mask) + cidr.bits = L_checkbits(L, index + 1, &cidr); + } + + if (!(cidrp = lua_newuserdata(L, sizeof(*cidrp)))) + return 0; + + *cidrp = cidr; + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_new(lua_State *L) +{ + return _cidr_new(L, 1, 0, true); +} + +static int cidr_ipv4(lua_State *L) +{ + return _cidr_new(L, 1, AF_INET, true); +} + +static int cidr_ipv6(lua_State *L) +{ + return _cidr_new(L, 1, AF_INET6, true); +} + +static int cidr_is4(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, p->family == AF_INET); + return 1; +} + +static int cidr_is4rfc1918(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + uint32_t a = htonl(p->addr.v4.s_addr); + + lua_pushboolean(L, (p->family == AF_INET && + ((a >= 0x0A000000 && a <= 0x0AFFFFFF) || + (a >= 0xAC100000 && a <= 0xAC1FFFFF) || + (a >= 0xC0A80000 && a <= 0xC0A8FFFF)))); + + return 1; +} + +static int cidr_is4linklocal(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + uint32_t a = htonl(p->addr.v4.s_addr); + + lua_pushboolean(L, (p->family == AF_INET && + a >= 0xA9FE0000 && + a <= 0xA9FEFFFF)); + + return 1; +} + +static bool _is_mapped4(cidr_t *p) +{ + return (p->family == AF_INET6 && + p->addr.v6.s6_addr[0] == 0 && + p->addr.v6.s6_addr[1] == 0 && + p->addr.v6.s6_addr[2] == 0 && + p->addr.v6.s6_addr[3] == 0 && + p->addr.v6.s6_addr[4] == 0 && + p->addr.v6.s6_addr[5] == 0 && + p->addr.v6.s6_addr[6] == 0 && + p->addr.v6.s6_addr[7] == 0 && + p->addr.v6.s6_addr[8] == 0 && + p->addr.v6.s6_addr[9] == 0 && + p->addr.v6.s6_addr[10] == 0xFF && + p->addr.v6.s6_addr[11] == 0xFF); +} + +static int cidr_is6mapped4(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, _is_mapped4(p)); + return 1; +} + +static int cidr_is6(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, p->family == AF_INET6); + return 1; +} + +static int cidr_is6linklocal(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, (p->family == AF_INET6 && + p->addr.v6.s6_addr[0] == 0xFE && + p->addr.v6.s6_addr[1] >= 0x80 && + p->addr.v6.s6_addr[1] <= 0xBF)); + + return 1; +} + +static int _cidr_cmp(lua_State *L) +{ + cidr_t *a = L_checkcidr(L, 1, NULL); + cidr_t *b = L_checkcidr(L, 2, NULL); + + if (a->family != b->family) + return (a->family - b->family); + + return memcmp(&a->addr.v6, &b->addr.v6, a->len); +} + +static int cidr_lower(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) < 0); + return 1; +} + +static int cidr_higher(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) > 0); + return 1; +} + +static int cidr_equal(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) == 0); + return 1; +} + +static int cidr_lower_equal(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) <= 0); + return 1; +} + +static int cidr_prefix(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + int bits = L_checkbits(L, 2, p); + + p->bits = bits; + lua_pushinteger(L, p->bits); + return 1; +} + +static void _apply_mask(cidr_t *p, int bits, bool inv) +{ + uint8_t b, i; + + if (bits <= 0) + { + memset(&p->addr.v6, inv * 0xFF, p->len); + } + else if (p->family == AF_INET && bits <= 32) + { + if (inv) + p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1); + else + p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1)); + } + else if (p->family == AF_INET6 && bits <= 128) + { + for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++) + { + b = (bits > 8) ? 8 : bits; + if (inv) + p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b))); + else + p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b)); + bits -= b; + } + } +} + +static int cidr_network(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2; + int bits = L_checkbits(L, 2, p1); + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + _apply_mask(p2, bits, false); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_host(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = lua_newuserdata(L, sizeof(*p2)); + + if (!p2) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_mask(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2; + int bits = L_checkbits(L, 2, p1); + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->family = p1->family; + + memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr)); + _apply_mask(p2, bits, false); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_broadcast(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + int bits = L_checkbits(L, 2, p1); + + if (p1->family == AF_INET6) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + _apply_mask(p2, bits, true); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_mapped4(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + + if (!_is_mapped4(p1)) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->family = AF_INET; + p2->bits = (p1->bits > 32) ? 32 : p1->bits; + memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4)); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_contains(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = L_checkcidr(L, 2, NULL); + cidr_t a = *p1, b = *p2; + bool rv = false; + + if (p1->family == p2->family && p1->bits <= p2->bits) + { + _apply_mask(&a, p1->bits, false); + _apply_mask(&b, p1->bits, false); + + rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len); + } + + lua_pushboolean(L, rv); + return 1; +} + +#define S6_BYTE(a, i) \ + (a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1] + +static int _cidr_add_sub(lua_State *L, bool add) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = L_checkcidr(L, 2, p1); + cidr_t r = *p1; + bool inplace = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : false; + bool ok = true; + uint8_t i, carry; + uint32_t a, b; + + if (p1->family == p2->family) + { + if (p1->family == AF_INET6) + { + for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++) + { + if (add) + { + S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry; + carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256; + } + else + { + S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry); + carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry)); + } + } + + /* would over/underflow */ + if (carry) + { + memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6)); + ok = false; + } + } + else + { + a = ntohl(p1->addr.v4.s_addr); + b = ntohl(p2->addr.v4.s_addr); + + /* would over/underflow */ + if ((add && (UINT_MAX - a) < b) || (!add && a < b)) + { + r.addr.v4.s_addr = add * 0xFFFFFFFF; + ok = false; + } + else + { + r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); + } + } + } + else + { + ok = false; + } + + if (inplace) + { + *p1 = r; + lua_pushboolean(L, ok); + return 1; + } + + if (!(p1 = lua_newuserdata(L, sizeof(*p1)))) + return 0; + + *p1 = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_add(lua_State *L) +{ + return _cidr_add_sub(L, true); +} + +static int cidr_sub(lua_State *L) +{ + return _cidr_add_sub(L, false); +} + +static int cidr_minhost(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + cidr_t r = *p; + uint8_t i, rest, carry; + + _apply_mask(&r, r.bits, false); + + if (r.family == AF_INET6 && r.bits < 128) + { + r.bits = 128; + + for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++) + { + rest = (S6_BYTE(&r, i) + carry) > 255; + S6_BYTE(&r, i) += carry; + carry = rest; + } + } + else if (r.family == AF_INET && r.bits < 32) + { + r.bits = 32; + r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1); + } + + if (!(p = lua_newuserdata(L, sizeof(*p)))) + return 0; + + *p = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_maxhost(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + cidr_t r = *p; + + _apply_mask(&r, r.bits, true); + + if (r.family == AF_INET && r.bits < 32) + { + r.bits = 32; + r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1); + } + else if (r.family == AF_INET6) + { + r.bits = 128; + } + + if (!(p = lua_newuserdata(L, sizeof(*p)))) + return 0; + + *p = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_gc (lua_State *L) +{ + return 0; +} + +static int cidr_tostring (lua_State *L) +{ + char buf[INET6_ADDRSTRLEN]; + cidr_t *p = L_checkcidr(L, 1, NULL); + + if ((p->family == AF_INET && p->bits < 32) || + (p->family == AF_INET6 && p->bits < 128)) + { + lua_pushfstring(L, "%s/%d", + inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)), + p->bits); + } + else + { + lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf))); + } + + return 1; +} + +/* + * route functions + */ + +static bool diff_prefix(int family, void *addr, int bits, cidr_t *p) +{ + uint8_t i, b, r; + uint32_t m; + + if (!p->family) + return false; + + if (!addr || p->family != family || p->bits > bits) + return true; + + if (family == AF_INET6) + { + for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++) + { + b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0; + + if ((((struct in6_addr *)addr)->s6_addr[i] & b) != + (p->addr.v6.s6_addr[i] & b)) + return true; + + r -= ((r > 8) ? 8 : r); + } + } + else + { + m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0; + + if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m)) + return true; + } + + return (p->exact && p->bits != bits); +} + +static int cb_dump_route(struct nl_msg *msg, void *arg) +{ + struct dump_state *s = arg; + struct dump_filter *f = s->filter; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct rtmsg *rt = NLMSG_DATA(hdr); + struct nlattr *tb[RTA_MAX+1]; + struct in6_addr *src, *dst, *gw, *from, def = { }; + int iif, oif, bitlen; + uint32_t table; + + if (hdr->nlmsg_type != RTM_NEWROUTE || + (rt->rtm_family != AF_INET && rt->rtm_family != AF_INET6)) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*rt), tb, RTA_MAX, NULL); + + iif = tb[RTA_IIF] ? RTA_INT(tb[RTA_IIF]) : 0; + oif = tb[RTA_OIF] ? RTA_INT(tb[RTA_OIF]) : 0; + table = tb[RTA_TABLE] ? RTA_U32(tb[RTA_TABLE]) : rt->rtm_table; + from = tb[RTA_SRC] ? RTA_DATA(tb[RTA_SRC]) : NULL; + src = tb[RTA_PREFSRC] ? RTA_DATA(tb[RTA_PREFSRC]) : NULL; + dst = tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &def; + gw = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL; + + bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32; + + if ((f->type && rt->rtm_type != f->type) || + (f->family && rt->rtm_family != f->family) || + (f->proto && rt->rtm_protocol != f->proto) || + (f->scope && rt->rtm_scope != f->scope) || + (f->iif && iif != f->iif) || + (f->oif && oif != f->oif) || + (f->table && table != f->table) || + diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) || + diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, &f->dst) || + diff_prefix(rt->rtm_family, gw, bitlen, &f->gw) || + diff_prefix(rt->rtm_family, src, bitlen, &f->src)) + goto out; + + if (s->callback) + lua_pushvalue(s->L, 2); + + lua_newtable(s->L); + + L_setint(s->L, "type", rt->rtm_type); + L_setint(s->L, "family", (rt->rtm_family == AF_INET) ? 4 : 6); + + L_setaddr(s->L, "dest", rt->rtm_family, dst, rt->rtm_dst_len); + + if (gw) + L_setaddr(s->L, "gw", rt->rtm_family, gw, -1); + + if (from) + L_setaddr(s->L, "from", rt->rtm_family, from, rt->rtm_src_len); + + if (iif) + L_setdev(s->L, "iif", tb[RTA_IIF]); + + if (oif) + L_setdev(s->L, "dev", tb[RTA_OIF]); + + L_setint(s->L, "table", table); + L_setint(s->L, "proto", rt->rtm_protocol); + L_setint(s->L, "scope", rt->rtm_scope); + + if (src) + L_setaddr(s->L, "src", rt->rtm_family, src, -1); + + if (tb[RTA_PRIORITY]) + L_setint(s->L, "metric", RTA_U32(tb[RTA_PRIORITY])); + + if (rt->rtm_family == AF_INET6 && tb[RTA_CACHEINFO]) + { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + + if (ci->rta_expires) + { + if (ci->rta_expires) + L_setint(s->L, "expires", ci->rta_expires / hz); + + if (ci->rta_error != 0) + L_setint(s->L, "error", ci->rta_error); + } + } + + s->index++; + + if (s->callback) + lua_call(s->L, 1, 0); + else if (hdr->nlmsg_flags & NLM_F_MULTI) + lua_rawseti(s->L, -2, s->index); + +out: + s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI); + return NL_SKIP; +} + +static int +cb_done(struct nl_msg *msg, void *arg) +{ + struct dump_state *s = arg; + s->pending = 0; + return NL_STOP; +} + +static int +cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +{ + struct dump_state *s = arg; + s->pending = 0; + return NL_STOP; +} + +static int _error(lua_State *L, int code, const char *msg) +{ + lua_pushnil(L); + lua_pushnumber(L, code ? code : errno); + lua_pushstring(L, msg ? msg : strerror(errno)); + + return 3; +} + +static int _route_dump(lua_State *L, struct dump_filter *filter) +{ + int flags = NLM_F_REQUEST; + struct dump_state s = { + .L = L, + .pending = 1, + .index = 0, + .callback = lua_isfunction(L, 2), + .filter = filter + }; + + if (!hz) + hz = sysconf(_SC_CLK_TCK); + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg; + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct rtmsg rtm = { + .rtm_family = filter->family, + .rtm_dst_len = filter->dst.bits, + .rtm_src_len = filter->src.bits + }; + + if (!filter->get) + flags |= NLM_F_DUMP; + + msg = nlmsg_alloc_simple(RTM_GETROUTE, flags); + if (!msg) + goto out; + + nlmsg_append(msg, &rtm, sizeof(rtm), 0); + + if (filter->get) + nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &s); + + nl_send_auto_complete(sock, msg); + + if (!filter->get && !s.callback) + lua_newtable(L); + + while (s.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + +out: + nl_cb_put(cb); + return (s.callback == 0); +} + +static int route_get(lua_State *L) +{ + struct dump_filter filter = { .get = true }; + const char *dest = luaL_checkstring(L, 1); + + if (!parse_cidr(dest, &filter.dst)) + return _error(L, -1, "Invalid destination"); + + filter.family = filter.dst.family; + + return _route_dump(L, &filter); +} + +static int route_dump(lua_State *L) +{ + const char *s; + cidr_t p = { }; + struct dump_filter filter = { }; + + if (lua_type(L, 1) == LUA_TTABLE) + { + filter.family = L_getint(L, 1, "family"); + + if (filter.family == 4) + filter.family = AF_INET; + else if (filter.family == 6) + filter.family = AF_INET6; + else + filter.family = 0; + + if ((s = L_getstr(L, 1, "iif")) != NULL) + filter.iif = if_nametoindex(s); + + if ((s = L_getstr(L, 1, "oif")) != NULL) + filter.oif = if_nametoindex(s); + + filter.type = L_getint(L, 1, "type"); + filter.scope = L_getint(L, 1, "scope"); + filter.proto = L_getint(L, 1, "proto"); + filter.table = L_getint(L, 1, "table"); + + if ((s = L_getstr(L, 1, "gw")) != NULL && parse_cidr(s, &p)) + filter.gw = p; + + if ((s = L_getstr(L, 1, "from")) != NULL && parse_cidr(s, &p)) + filter.from = p; + + if ((s = L_getstr(L, 1, "src")) != NULL && parse_cidr(s, &p)) + filter.src = p; + + if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p)) + filter.dst = p; + + if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p)) + filter.from = p, filter.from.exact = true; + + if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p)) + filter.dst = p, filter.dst.exact = true; + } + + return _route_dump(L, &filter); +} + + +static bool diff_macaddr(struct ether_addr *mac1, struct ether_addr *mac2) +{ + struct ether_addr empty = { }; + + if (!memcmp(mac2, &empty, sizeof(empty))) + return false; + + if (!mac1 || memcmp(mac1, mac2, sizeof(empty))) + return true; + + return false; +} + +static int cb_dump_neigh(struct nl_msg *msg, void *arg) +{ + char buf[32]; + struct ether_addr *mac; + struct in6_addr *dst; + struct dump_state *s = arg; + struct dump_filter *f = s->filter; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ndmsg *nd = NLMSG_DATA(hdr); + struct nlattr *tb[NDA_MAX+1]; + int bitlen; + + if (hdr->nlmsg_type != RTM_NEWNEIGH || + (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6)) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL); + + mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL; + dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL; + + bitlen = (nd->ndm_family == AF_INET) ? 32 : 128; + + if ((f->family && nd->ndm_family != f->family) || + (f->iif && nd->ndm_ifindex != f->iif) || + (f->type && !(f->type & nd->ndm_state)) || + diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) || + diff_macaddr(mac, &f->mac)) + goto out; + + if (s->callback) + lua_pushvalue(s->L, 2); + + lua_newtable(s->L); + + L_setint(s->L, "family", (nd->ndm_family == AF_INET) ? 4 : 6); + L_setstr(s->L, "dev", if_indextoname(nd->ndm_ifindex, buf)); + + L_setbool(s->L, "router", (nd->ndm_flags & NTF_ROUTER)); + L_setbool(s->L, "proxy", (nd->ndm_flags & NTF_PROXY)); + + L_setbool(s->L, "incomplete", (nd->ndm_state & NUD_INCOMPLETE)); + L_setbool(s->L, "reachable", (nd->ndm_state & NUD_REACHABLE)); + L_setbool(s->L, "stale", (nd->ndm_state & NUD_STALE)); + L_setbool(s->L, "delay", (nd->ndm_state & NUD_DELAY)); + L_setbool(s->L, "probe", (nd->ndm_state & NUD_PROBE)); + L_setbool(s->L, "failed", (nd->ndm_state & NUD_FAILED)); + L_setbool(s->L, "noarp", (nd->ndm_state & NUD_NOARP)); + L_setbool(s->L, "permanent", (nd->ndm_state & NUD_PERMANENT)); + + if (dst) + L_setaddr(s->L, "dest", nd->ndm_family, dst, -1); + + if (mac) + { + snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac->ether_addr_octet[0], mac->ether_addr_octet[1], + mac->ether_addr_octet[2], mac->ether_addr_octet[3], + mac->ether_addr_octet[4], mac->ether_addr_octet[5]); + + lua_pushstring(s->L, buf); + lua_setfield(s->L, -2, "mac"); + } + + s->index++; + + if (s->callback) + lua_call(s->L, 1, 0); + else if (hdr->nlmsg_flags & NLM_F_MULTI) + lua_rawseti(s->L, -2, s->index); + +out: + s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI); + return NL_SKIP; +} + +static int neighbor_dump(lua_State *L) +{ + cidr_t p = { }; + const char *s; + struct ether_addr *mac; + struct dump_filter filter = { .type = 0xFF & ~NUD_NOARP }; + struct dump_state st = { + .callback = lua_isfunction(L, 2), + .pending = 1, + .filter = &filter, + .L = L + }; + + if (lua_type(L, 1) == LUA_TTABLE) + { + filter.family = L_getint(L, 1, "family"); + + if (filter.family == 4) + filter.family = AF_INET; + else if (filter.family == 6) + filter.family = AF_INET6; + else + filter.family = 0; + + if ((s = L_getstr(L, 1, "dev")) != NULL) + filter.iif = if_nametoindex(s); + + if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p)) + filter.dst = p; + + if ((s = L_getstr(L, 1, "mac")) != NULL && + (mac = ether_aton(s)) != NULL) + filter.mac = *mac; + } + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg; + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct ndmsg ndm = { + .ndm_family = filter.family + }; + + msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) + goto out; + + nlmsg_append(msg, &ndm, sizeof(ndm), 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_neigh, &st); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st); + + nl_send_auto_complete(sock, msg); + + if (!st.callback) + lua_newtable(L); + + while (st.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + +out: + nl_cb_put(cb); + return (st.callback == 0); +} + + +static int cb_dump_link(struct nl_msg *msg, void *arg) +{ + char *p, *addr, buf[48]; + struct dump_state *s = arg; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ifinfomsg *ifm = NLMSG_DATA(hdr); + struct nlattr *tb[IFLA_MAX+1]; + int i, len; + + if (hdr->nlmsg_type != RTM_NEWLINK) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*ifm), tb, IFLA_MAX, NULL); + + L_setbool(s->L, "up", (ifm->ifi_flags & IFF_RUNNING)); + L_setint(s->L, "type", ifm->ifi_type); + L_setstr(s->L, "name", if_indextoname(ifm->ifi_index, buf)); + + if (tb[IFLA_MTU]) + L_setint(s->L, "mtu", RTA_U32(tb[IFLA_MTU])); + + if (tb[IFLA_TXQLEN]) + L_setint(s->L, "qlen", RTA_U32(tb[IFLA_TXQLEN])); + + if (tb[IFLA_MASTER]) + L_setdev(s->L, "master", tb[IFLA_MASTER]); + + if (tb[IFLA_ADDRESS]) + { + len = nla_len(tb[IFLA_ADDRESS]); + addr = nla_get_string(tb[IFLA_ADDRESS]); + + if ((len * 3) <= sizeof(buf)) + { + for (p = buf, i = 0; i < len; i++) + p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++); + + L_setstr(s->L, "mac", buf); + } + } + + s->pending = 0; + return NL_SKIP; +} + +static int link_get(lua_State *L) +{ + const char *dev = luaL_checkstring(L, 1); + struct dump_state st = { + .pending = 1, + .L = L + }; + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST); + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct ifinfomsg ifm = { .ifi_index = if_nametoindex(dev) }; + + if (!msg || !cb) + return 0; + + nlmsg_append(msg, &ifm, sizeof(ifm), 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_link, &st); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st); + + lua_newtable(L); + + nl_send_auto_complete(sock, msg); + + while (st.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + nl_cb_put(cb); + + return 1; +} + + +static const luaL_reg ip_methods[] = { + { "new", cidr_new }, + { "IPv4", cidr_ipv4 }, + { "IPv6", cidr_ipv6 }, + + { "route", route_get }, + { "routes", route_dump }, + + { "neighbors", neighbor_dump }, + + { "link", link_get }, + + { } +}; + +static const luaL_reg ip_cidr_methods[] = { + { "is4", cidr_is4 }, + { "is4rfc1918", cidr_is4rfc1918 }, + { "is4linklocal", cidr_is4linklocal }, + { "is6", cidr_is6 }, + { "is6linklocal", cidr_is6linklocal }, + { "is6mapped4", cidr_is6mapped4 }, + { "lower", cidr_lower }, + { "higher", cidr_higher }, + { "equal", cidr_equal }, + { "prefix", cidr_prefix }, + { "network", cidr_network }, + { "host", cidr_host }, + { "mask", cidr_mask }, + { "broadcast", cidr_broadcast }, + { "mapped4", cidr_mapped4 }, + { "contains", cidr_contains }, + { "add", cidr_add }, + { "sub", cidr_sub }, + { "minhost", cidr_minhost }, + { "maxhost", cidr_maxhost }, + { "string", cidr_tostring }, + + { "__lt", cidr_lower }, + { "__le", cidr_lower_equal }, + { "__eq", cidr_equal }, + { "__add", cidr_add }, + { "__sub", cidr_sub }, + { "__gc", cidr_gc }, + { "__tostring", cidr_tostring }, + + { } +}; + +int luaopen_luci_ip(lua_State *L) +{ + luaL_register(L, LUCI_IP, ip_methods); + + luaL_newmetatable(L, LUCI_IP_CIDR); + luaL_register(L, NULL, ip_cidr_methods); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + return 1; +} diff --git a/libs/luci-lib-ip/src/ip.luadoc b/libs/luci-lib-ip/src/ip.luadoc new file mode 100644 index 000000000..00738832c --- /dev/null +++ b/libs/luci-lib-ip/src/ip.luadoc @@ -0,0 +1,831 @@ +--- LuCI IP calculation and netlink access library. +module "luci.ip" + +---[[ +Construct a new luci.ip.cidr instance and autodetect the address family. +Throws an error if the given strings do not represent a valid address or +if the given optional netmask is of a different family. +@class function +@sort 1 +@name new +@param address String containing a valid IPv4 or IPv6 address, optionally +with prefix size (CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 or IPv6 netmask or number +containing a prefix size in bits (`0..32` for IPv4, +`0..128` for IPv6). Overrides mask embedded in the first argument +if specified. (optional) +@return A `luci.ip.cidr` object representing the given +address/mask range. +@usage `addr = luci.ip.new("10.24.0.1/24") +addr = luci.ip.new("10.24.0.1/255.255.255.0") +addr = luci.ip.new("10.24.0.1", "255.255.255.0") -- separate netmask +addr = luci.ip.new("10.24.0.1/24", 16) -- override netmask + +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` +@see IPv4 +@see IPv6 +]] + +---[[ +Construct a new IPv4 luci.ip.cidr instance. +Throws an error if the given string does not represent a valid IPv4 address or +if the given optional netmask is of a different family. +@class function +@sort 2 +@name IPv4 +@param address String containing a valid IPv4, optionally with prefix size +(CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 netmask or number +containing a prefix size between `0` and `32` bit. +Overrides mask embedded in the first argument if specified. (optional) +@return A `luci.ip.cidr` object representing the given IPv4 range. +@usage `addr = luci.ip.new("10.24.0.1/24") +addr = luci.ip.new("10.24.0.1/255.255.255.0") +addr = luci.ip.new("10.24.0.1", "255.255.255.0") -- separate netmask +addr = luci.ip.new("10.24.0.1/24", 16) -- override netmask` +@see IPv6 +]] + +---[[ +Construct a new IPv6 luci.ip.cidr instance. +Throws an error if the given string does not represent a valid IPv6 address or +if the given optional netmask is of a different family. +@class function +@sort 3 +@name IPv6 +@param address String containing a valid IPv6, optionally with prefix size +(CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 netmask or number +containing a prefix size between `0` and `128` bit. +Overrides mask embedded in the first argument if specified. (optional) +@return A `luci.ip.cidr` object representing the given IPv6 range. +@usage `addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` +@see IPv4 +]] + +---[[ +Determine the route leading to the given destination. +@class function +@sort 4 +@name route +@param address A `luci.ip.cidr` instance or a string containing +a valid IPv4 or IPv6 range as specified by `luci.ip.new()`. +@return <p>Table containing the fields described below.</p> +<table id="routetable"> +<tr><th>Field</th><th>Description</th></tr> +<tr><td>`type`<td> + <p>Route type with one of the following numeric values:</p> + <table> + <tr> + <td>`1`</td> + <td>`RTN_UNICAST` - Gateway or direct route</td> + </tr> + <tr> + <td>`2`</td> + <td>`RTN_LOCAL` - Accept locally</td> + </tr> + <tr> + <td>`3`</td> + <td>`RTN_BROADCAST` - + Accept locally as broadcast send as broadcast</td> + </tr> + <tr> + <td>`4`</td> + <td>`RTN_ANYCAST` - + Accept locally as broadcast but send as unicast</td> + </tr> + <tr> + <td>`5`</td> + <td>`RTN_MULTICAST` - Multicast route</td> + </tr> + </table> +</td></tr> +<tr> + <td>`family`</td> + <td>Number containing the route family, `4` for IPv4 or + `6` for IPv6</td> +</tr> +<tr> + <td>`dest`</td> + <td>Destination `luci.ip.cidr` instance</td> +</tr> +<tr> + <td>`gw`</td> + <td>Gateway `luci.ip.cidr` instance (optional)</td> +</tr> +<tr> + <td>`from`</td> + <td>Source address `luci.ip.cidr` instance (optional)</td> +</tr> +<tr> + <td>`src`</td> + <td>Preferred source `luci.ip.cidr` instance (optional)</td> +</tr> +<tr> + <td>`dev`</td> + <td>String containing the name of the outgoing interface</td> +</tr> +<tr> + <td>`iif`</td> + <td>String containing the name of the incoming interface (optional)</td> +</tr> +<tr> + <td>`table`</td> + <td>Number of the associated routing table (`0..65535`)</td> +</tr> +<tr> + <td>`proto`</td> + <td>Number of the associated routing protocol</td> +</tr> +<tr> + <td>`scope`</td> + <td>Number describing the scope of the route, most commonly + `0` for global or `253` for on-link</td> +</tr> +<tr> + <td>`metric`</td> + <td>Number describing the route metric (optional)</td> +</tr> +<tr> + <td>`expires`</td> + <td>Number of seconds the prefix is valid (IPv6 only, optional)</td> +</tr> +<tr> + <td>`error`</td> + <td>Route destination error code (optional)</td> +</tr> +</table> +@usage <ul> +<li>Find default gateway by getting route to Google's public NS server +`rt = luci.ip.route("8.8.8.8") +if rt ~= nil then + print("gateway is", rt.gw) +end`</li> +<li>Determine IPv6 upstream interface `rt = luci.ip.route("2001::/7") +if rt ~= nil then + print("ipv6 upstream device is", rt.dev) +end`</li> +</ul> +@see routes +]] + +---[[ +Fetch all routes, optionally matching the given criteria. +@class function +@sort 5 +@name routes +@param filter <p>Table containing one or more of the possible filter +critera described below (optional)</p><table> +<tr><th>Field</th><th>Description</th></tr> +<tr><td>`family`</td><td> + Number describing the address family to return - `4` selects + IPv4 routes, `6` IPv6 ones. Any other value selects both. +</td></tr> +<tr><td>`iif`</td><td> + String containing the incoming route interface to match. +</td></tr> +<tr><td>`oif`</td><td> + String containing the outgoing route interface to match. +</td></tr> +<tr><td>`type`</td><td> + Numeric type to match, e.g. `1` for unicast. +</td></tr> +<tr><td>`scope`</td><td> + Numeric scope to match, e.g. `253` for onlink. +</td></tr> +<tr><td>`proto`</td><td> + Numeric protocol to match, e.g. `2` for boot. +</td></tr> +<tr><td>`table`</td><td> + Numeric routing table to match (`0..65535`). +</td></tr> +<tr><td>`gw`</td><td> + String containing the gateway address to match. Can be in any notation + specified by `luci.ip.new()`. Prefix matching is performed when + comparing the routes, e.g. "192.168.1.0/24" would select routes with gateway + addresses `192.168.1.1 .. 192.168.1.255`. +</td></tr> +<tr><td>`dest`</td><td> + String containing the destination to match. Prefix matching is performed. +</td></tr> +<tr><td>`from`</td><td> + String containing the source address to match. Prefix matching is performed. +</td></tr> +<tr><td>`src`</td><td> + String containing the preferred source address to match. + Prefix matching is performed. +</td></tr> +<tr><td>`dest_exact`</td><td> + String containing the destination to match. Exact matching is performed, + e.g. `dest = "0.0.0.0/0"` would match <em>any</em> IPv4 route + while `dest_exact = "0.0.0.0/0"` will <em>only</em> match the + default route. +</td></tr> +<tr><td>`from_exact`</td><td> + String containing the source address to match. Exact matching is performed. +</td></tr> +</table> +@param callback <p>Callback function to invoke for each found route +instead of returning one table of route objects (optional)</p> +@return If no callback function is provided, a table of routes +<a href="#routetable">as specified by `luci.ip.route()`</a> +is returned. If a callback function is given, it is invoked for each route +and nothing is returned. +@see route +@usage <ul> +<li>Find all IPv4 default routes: +`luci.ip.routes({ dest_exact = "0.0.0.0/0" }, function(rt) + print(rt.type, rt.gw, rt.dev) +end)`</li> +<li>Find all global IPv6 prefixes on the current system: +`luci.ip.routes({ from = "2001::/7" }, function(rt) + print(rt.from) +end)`</li> +<li>Fetch all IPv4 routes: +`routes = luci.ip.routes({ family = 4 }) +for _, rt in ipairs(routes) do + print(rt.dest, rt.gw, rt.dev) +end`</li> +</ul> +]] + +---[[ +Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table +@class function +@sort 6 +@name neighbors +@param filter <p>Table containing one or more of the possible filter +critera described below (optional)</p><table> +<tr><th>Field</th><th>Description</th></tr> +<tr><td>`family`</td><td> + Number describing the address family to return - `4` selects + IPv4 ARP, `6` select IPv6 neighbour entries. Any other value + selects both. +</td></tr> +<tr><td>`dev`</td><td> + String containing the associated interface to match. +</td></tr> +<tr><td>`dest`</td><td> + String containing the associated address to match. Can be in any notation + specified by `luci.ip.new()`. Prefix matching is performed when + comparing the addresses, e.g. "192.168.1.0/24" would select ARP entries + for `192.168.1.1 .. 192.168.1.255`. +</td></tr> +<tr><td>`mac`</td><td> + String containing MAC address to match. +</td></tr> +</table> +@param callback <p>Callback function to invoke for each found neighbour +entry instead of returning one table of neighbour entries (optional)</p> +@return If no callback function is provided, a table of neighbour entries +is returned. If a callback function is given, it is invoked for each entry +and nothing is returned. + +A neighbour entry is a table containing the following fields: + +<table> +<tr><th>Field</th><th>Description</th></tr> +<tr> + <td>`family`</td> + <td>Number containing the neighbour entry family, `4` for IPv4 + ARP or `6` for IPv6 NDP</td> +</tr> +<tr> + <td>`dev`</td> + <td>String containing the associated device of the neighbour entry</td> +</tr> +<tr> + <td>`dest`</td> + <td>IP address `luci.ip.cidr` instance</td> +</tr> +<tr> + <td>`mac`</td> + <td>String containing the associated MAC address</td> +</tr> +<tr> + <td>`router`</td> + <td>Boolean "true" if the neighbour entry is a router (IPv6, optional)</td> +</tr> +<tr> + <td>`proxy`</td> + <td>Boolean "true" if this is a proxy entry (optional)</td> +</tr> +<tr> + <td>`incomplete`</td> + <td>Boolean "true" if the entry is in incomplete state (optional)</td> +</tr> +<tr> + <td>`reachable`</td> + <td>Boolean "true" if the entry is in reachable state (optional)</td> +</tr> +<tr> + <td>`stale`</td> + <td>Boolean "true" if the entry is stale (optional)</td> +</tr> +<tr> + <td>`delay`</td> + <td>Boolean "true" if the entry is delayed (optional)</td> +</tr> +<tr> + <td>`probe`</td> + <td>Boolean "true" if the entry is in probe state (optional)</td> +</tr> +<tr> + <td>`failed`</td> + <td>Boolean "true" if the entry is in failed state (optional)</td> +</tr> +<tr> + <td>`noarp`</td> + <td>Boolean "true" if the entry is not caused by NDP or + ARP (optional)</td> +</tr> +<tr> + <td>`permanent`</td> + <td>Boolean "true" if the entry was statically configured from + userspace (optional)</td> +</tr> +</table> +@usage <ul> +<li>Find all ARP neighbours in the LAN: +`luci.ip.neighbors({ dest = "192.168.0.0/16" }, function(n) + print(n.dest, n.mac) +end)`</li> +<li>Find all active IPv6 addresses of host with given MAC: +`luci.ip.neighbors({ family = 6, mac = "00:21:63:75:aa:17" }, + function(n) + print(n.dest) + end)`</li> +</ul> +]] + +---[[ +Fetch basic device information +@class function +@sort 7 +@name link +@param device String containing the network device to query +@return If the given interface is found, a table containing the fields +described below is returned, else an empty table. + +<table> +<tr><th>Field</th><th>Description</th></tr> +<tr> + <td>`up`</td> + <td>Boolean indicating whether the device is in IFF_RUNNING state</td> +</tr> +<tr> + <td>`type`</td> + <td>Numeric value indicating the type of the device, e.g. `1` + for ethernet.</td> +</tr> +<tr> + <td>`name`</td> + <td>String containing the name of the device</td> +</tr> +<tr> + <td>`master`</td> + <td>If queried device is a bridge port, string containing the name of + parent bridge device (optional)</td> +</tr> +<tr> + <td>`mtu`</td> + <td>Number containing the current MTU of the device</td> +</tr> +<tr> + <td>`qlen`</td> + <td>Number containing the TX queue length of the device</td> +</tr> +<tr> + <td>`mac`</td> + <td>String containing the link local address of the device in + dotted hex notation</td> +</tr> +</table> +@usage <ul> +<li>Test whether device br-lan exists: +`print(luci.ip.link("br-lan").name ~= nil) +`</li> +<li>Query MAC address of eth0: +`print(luci.ip.link("eth0").mac) +`</li> +</ul> +]] + + +--- IP CIDR Object. +-- Represents an IPv4 or IPv6 address range. +-- @cstyle instance +module "luci.ip.cidr" + +---[[ +Checks whether the CIDR instance is an IPv4 address range + +@class function +@sort 1 +@name cidr.is4 +@see cidr.is6 +@return `true` if the CIDR is an IPv4 range, else `false` +]] + +---[[ +Checks whether the CIDR instance is within the private RFC1918 address space + +@class function +@sort 2 +@name cidr.is4rfc1918 +@return `true` if the entire range of this CIDR lies within one of + the ranges `10.0.0.0-10.255.255.255`, + `172.16.0.0-172.31.0.0` or + `192.168.0.0-192.168.255.255`, else `false`. +@usage `local addr = luci.ip.new("192.168.45.2/24") +if addr:is4rfc1918() then + print("Is a private address") +end` +]] + +---[[ +Checks whether the CIDR instance is an IPv4 link local (Zeroconf) address + +@class function +@sort 3 +@name cidr.is4linklocal +@return `true` if the entire range of this CIDR lies within the range + the range `169.254.0.0-169.254.255.255`, else `false`. +@usage `local addr = luci.ip.new("169.254.34.125") +if addr:is4linklocal() then + print("Is a zeroconf address") +end` +]] + +---[[ +Checks whether the CIDR instance is an IPv6 address range + +@class function +@sort 4 +@name cidr.is6 +@see cidr.is4 +@return `true` if the CIDR is an IPv6 range, else `false` +]] + +---[[ +Checks whether the CIDR instance is an IPv6 link local address + +@class function +@sort 5 +@name cidr.is6linklocal +@return `true` if the entire range of this CIDR lies within the range + the `fe80::/10` range, else `false`. +@usage `local addr = luci.ip.new("fe92:53a:3216:af01:221:63ff:fe75:aa17/64") +if addr:is6linklocal() then + print("Is a linklocal address") +end` +]] + +---[[ +Checks whether the CIDR instance is an IPv6 mapped IPv4 address + +@class function +@sort 6 +@name cidr.is6mapped4 +@return `true` if the address is an IPv6 mapped IPv4 address in the + form `::ffff:1.2.3.4`. +@usage `local addr = luci.ip.new("::ffff:192.168.1.1") +if addr:is6mapped4() then + print("Is a mapped IPv4 address") +end` +]] + +---[[ +Checks whether this CIDR instance is lower than the given argument. +The comparisation follows these rules: +<ul><li>An IPv4 address is always lower than an IPv6 address</li> +<li>Prefix sizes are ignored</li></ul> + +@class function +@sort 7 +@name cidr.lower +@param addr A `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()` to compare against. +@return `true` if this CIDR is lower than the given address, + else `false`. +@usage `local addr = luci.ip.new("192.168.1.1") +print(addr:lower(addr)) -- false +print(addr:lower("10.10.10.10/24")) -- false +print(addr:lower(luci.ip.new("::1"))) -- true +print(addr:lower(luci.ip.new("192.168.200.1"))) -- true` +@see cidr.higher +@see cidr.equal +]] + +---[[ +Checks whether this CIDR instance is higher than the given argument. +The comparisation follows these rules: +<ul><li>An IPv4 address is always lower than an IPv6 address</li> +<li>Prefix sizes are ignored</li></ul> + +@class function +@sort 8 +@name cidr.higher +@param addr A `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()` to compare against. +@return `true` if this CIDR is higher than the given address, + else `false`. +@usage `local addr = luci.ip.new("192.168.1.1") +print(addr:higher(addr)) -- false +print(addr:higher("10.10.10.10/24")) -- true +print(addr:higher(luci.ip.new("::1"))) -- false +print(addr:higher(luci.ip.new("192.168.200.1"))) -- false` +@see cidr.lower +@see cidr.equal +]] + +---[[ +Checks whether this CIDR instance is equal to the given argument. + +@class function +@sort 9 +@name cidr.equal +@param addr A `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()` to compare against. +@return `true` if this CIDR is equal to the given address, + else `false`. +@usage `local addr = luci.ip.new("192.168.1.1") +print(addr:equal(addr)) -- true +print(addr:equal("192.168.1.1")) -- true +print(addr:equal(luci.ip.new("::1"))) -- false + +local addr6 = luci.ip.new("::1") +print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true +print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false` +@see cidr.lower +@see cidr.higher +]] + +---[[ +Get or set prefix size of CIDR instance. +If the optional mask parameter is given, the prefix size of this CIDR is altered +else the current prefix size is returned. + +@class function +@sort 10 +@name cidr.prefix +@param mask Either a number containing the number of bits (`0..32` + for IPv4, `0..128` for IPv6) or a string containing a valid + netmask (optional) +@return Bit count of the current prefix size +@usage `local range = luci.ip.new("192.168.1.1/255.255.255.0") +print(range:prefix()) -- 24 + +range:prefix(16) +print(range:prefix()) -- 16 + +range:prefix("255.255.255.255") +print(range:prefix()) -- 32` +]] + +---[[ +Derive network address of CIDR instance. + +Returns a new CIDR instance representing the network address of this instance +with all host parts masked out. The used prefix size can be overridden by the +optional mask parameter. + +@class function +@sort 11 +@name cidr.network +@param mask Either a number containing the number of bits (`0..32` + for IPv4, `0..128` for IPv6) or a string containing a valid + netmask (optional) +@return CIDR instance representing the network address +@usage `local range = luci.ip.new("192.168.62.243/255.255.0.0") +print(range:network()) -- "192.168.0.0" +print(range:network(24)) -- "192.168.62.0" +print(range:network("255.255.255.0")) -- "192.168.62.0" + +local range6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") +print(range6:network()) -- "fd9b:62b3:9cc5::"` +]] + +---[[ +Derive host address of CIDR instance. + +This function essentially constructs a copy of this CIDR with the prefix size +set to `32` for IPv4 and `128` for IPv6. + +@class function +@sort 12 +@name cidr.host +@return CIDR instance representing the host address +@usage `local range = luci.ip.new("172.19.37.45/16") +print(range) -- "172.19.37.45/16" +print(range:host()) -- "172.19.37.45"` +]] + +---[[ +Derive netmask of CIDR instance. + +Constructs a CIDR instance representing the netmask of this instance. The used +prefix size can be overridden by the optional mask parameter. + +@class function +@sort 13 +@name cidr.mask +@param mask Either a number containing the number of bits (`0..32` + for IPv4, `0..128` for IPv6) or a string containing a valid + netmask (optional) +@return CIDR instance representing the netmask +@usage `local range = luci.ip.new("172.19.37.45/16") +print(range:mask()) -- "255.255.0.0" +print(range:mask(24)) -- "255.255.255.0" +print(range:mask("255.0.0.0")) -- "255.0.0.0"` +]] + +---[[ +Derive broadcast address of CIDR instance. + +Constructs a CIDR instance representing the broadcast address of this instance. +The used prefix size can be overridden by the optional mask parameter. + +This function has no effect on IPv6 instances, it will return nothing in this +case. + +@class function +@sort 14 +@name cidr.broadcast +@param mask Either a number containing the number of bits (`0..32` + for IPv4, `0..128` for IPv6) or a string containing a valid + netmask (optional) +@return Return a new CIDR instance representing the broadcast address if this + instance is an IPv4 range, else return nothing. +@usage `local range = luci.ip.new("172.19.37.45/16") +print(range:broadcast()) -- "172.19.255.255" +print(range:broadcast(24)) -- "172.19.37.255" +print(range:broadcast("255.0.0.0")) -- "172.255.255.255"` +]] + +---[[ +Derive mapped IPv4 address of CIDR instance. + +Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped +IPv4 address in this instance. + +This function has no effect on IPv4 instances or IPv6 instances which are not a +mapped address, it will return nothing in this case. + +@class function +@sort 15 +@name cidr.mapped4 +@return Return a new CIDR instance representing the IPv4 address if this + instance is an IPv6 mapped IPv4 address, else return nothing. +@usage `local addr = luci.ip.new("::ffff:172.16.19.1") +print(addr:mapped4()) -- "172.16.19.1"` +]] + +---[[ +Test whether CIDR contains given range. + +@class function +@sort 16 +@name cidr.contains +@param addr A `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()` to test. +@return `true` if this instance fully contains the given address else + `false`. +@usage `local range = luci.ip.new("10.24.0.0/255.255.0.0") +print(range:contains("10.24.5.1")) -- true +print(range:contains("::1")) -- false +print(range:contains("10.0.0.0/8")) -- false + +local range6 = luci.ip.new("fe80::/10") +print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true +print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false` +]] + +---[[ +Add given amount to CIDR instance. If the result would overflow the maximum +address space, the result is set to the highest possible address. + +@class function +@sort 17 +@name cidr.add +@param amount A numeric value between 0 and 0xFFFFFFFF, a + `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()`. +@param inplace If `true`, modify this instance instead of returning + a new derived CIDR instance. +@return <ul> + <li>When adding inplace: Return `true` if the addition succeded + or `false` when the addition overflowed.</li> + <li>When deriving new CIDR: Return new instance representing the value of + this instance plus the added amount or the highest possible address if + the addition overflowed the available address space.</li></ul> +@usage `local addr = luci.ip.new("192.168.1.1/24") +print(addr:add(250)) -- "192.168.1.251/24" +print(addr:add("0.0.99.0")) -- "192.168.100.1/24" + +addr:add(256, true) -- true +print(addr) -- "192.168.2.1/24 + +addr:add("255.0.0.0", true) -- false (overflow) +print(addr) -- "255.255.255.255/24 + +local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64") +print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64" +print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64" + +addr:add(256, true) -- true +print(addr) -- "fe80::221:63f:fe75:ab17/64 + +addr:add("ffff::", true) -- false (overflow) +print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"` +]] + +---[[ +Substract given amount from CIDR instance. If the result would under, the lowest +possible address is returned. + +@class function +@sort 18 +@name cidr.sub +@param amount A numeric value between 0 and 0xFFFFFFFF, a + `luci.ip.cidr` instance or a string convertable by + `luci.ip.new()`. +@param inplace If `true`, modify this instance instead of returning + a new derived CIDR instance. +@return <ul> + <li>When substracting inplace: Return `true` if the substraction + succeded or `false` when the substraction underflowed.</li> + <li>When deriving new CIDR: Return new instance representing the value of + this instance minus the substracted amount or the lowest address if + the substraction underflowed.</li></ul> +@usage `local addr = luci.ip.new("192.168.1.1/24") +print(addr:sub(256)) -- "192.168.0.1/24" +print(addr:sub("0.168.0.0")) -- "192.0.1.1/24" + +addr:sub(256, true) -- true +print(addr) -- "192.168.0.1/24 + +addr:sub("255.0.0.0", true) -- false (underflow) +print(addr) -- "0.0.0.0/24 + +local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64") +print(addr6:sub(256)) -- "fe80::221:63f:fe75:a917/64" +print(addr6:sub("::ffff:0")) -- "fe80::221:63e:fe76:aa17/64" + +addr:sub(256, true) -- true +print(addr) -- "fe80::221:63f:fe75:a917/64" + +addr:sub("ffff::", true) -- false (underflow) +print(addr) -- "::/64"` +]] + +---[[ +Calculate the lowest possible host address within this CIDR instance. + +@class function +@sort 19 +@name cidr.minhost +@return Returns a new CIDR instance representing the lowest host address + within this range. +@usage `local addr = luci.ip.new("192.168.123.56/24") +print(addr:minhost()) -- "192.168.123.1" + +local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") +print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"` +]] + +---[[ +Calculate the highest possible host address within this CIDR instance. + +@class function +@sort 20 +@name cidr.maxhost +@return Returns a new CIDR instance representing the highest host address + within this range. +@usage `local addr = luci.ip.new("192.168.123.56/24") +print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast) + +local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") +print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"` +]] + +---[[ +Convert CIDR instance into string representation. + +If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the +address is returned in the form "address/prefix" otherwise just "address". + +It is usually not required to call this function directly as CIDR objects +define it as __tostring function in the associated metatable. + +@class function +@sort 21 +@name cidr.string +@return Returns a string representing the range or address of this CIDR instance +]] diff --git a/libs/luci-lib-json/luasrc/json.lua b/libs/luci-lib-json/luasrc/json.lua index 57b12c940..416b25faa 100644 --- a/libs/luci-lib-json/luasrc/json.lua +++ b/libs/luci-lib-json/luasrc/json.lua @@ -25,14 +25,9 @@ local char = string.char local getmetatable = getmetatable ---- LuCI JSON-Library --- @cstyle instance module "luci.json" ---- Directly decode a JSON string --- @param json JSON-String --- @return Lua object function decode(json, ...) local a = ActiveDecoder(function() return nil end, ...) a.chunk = json @@ -41,9 +36,6 @@ function decode(json, ...) end ---- Direcly encode a Lua object into a JSON string. --- @param obj Lua Object --- @return JSON string function encode(obj, ...) local out = {} local e = Encoder(obj, 1, ...):source() @@ -56,19 +48,10 @@ function encode(obj, ...) end ---- Null replacement function --- @return null function null() return null end ---- Create a new JSON-Encoder. --- @class function --- @name Encoder --- @param data Lua-Object to be encoded. --- @param buffersize Blocksize of returned data source. --- @param fastescape Use non-standard escaping (don't escape control chars) --- @return JSON-Encoder Encoder = util.class() function Encoder.__init__(self, data, buffersize, fastescape) @@ -80,8 +63,6 @@ function Encoder.__init__(self, data, buffersize, fastescape) getmetatable(self).__call = Encoder.source end ---- Create an LTN12 source providing the encoded JSON-Data. --- @return LTN12 source function Encoder.source(self) local source = coroutine.create(self.dispatch) return function() @@ -208,11 +189,6 @@ Encoder.parsers = { } ---- Create a new JSON-Decoder. --- @class function --- @name Decoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return JSON-Decoder Decoder = util.class() function Decoder.__init__(self, customnull) @@ -220,8 +196,6 @@ function Decoder.__init__(self, customnull) getmetatable(self).__call = Decoder.sink end ---- Create an LTN12 sink from the decoder object which accepts the JSON-Data. --- @return LTN12 sink function Decoder.sink(self) local sink = coroutine.create(self.dispatch) return function(...) @@ -230,8 +204,6 @@ function Decoder.sink(self) end ---- Get the decoded data packets after the rawdata has been sent to the sink. --- @return Decoded data function Decoder.get(self) return self.data end @@ -526,11 +498,6 @@ Decoder.parsers = { } ---- Create a new Active JSON-Decoder. --- @class function --- @name ActiveDecoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return Active JSON-Decoder ActiveDecoder = util.class(Decoder) function ActiveDecoder.__init__(self, source, customnull) @@ -541,8 +508,6 @@ function ActiveDecoder.__init__(self, source, customnull) end ---- Fetches one JSON-object from given source --- @return Decoded object function ActiveDecoder.get(self) local chunk, src_err, object if not self.chunk then diff --git a/libs/luci-lib-json/luasrc/json.luadoc b/libs/luci-lib-json/luasrc/json.luadoc new file mode 100644 index 000000000..d48dba13b --- /dev/null +++ b/libs/luci-lib-json/luasrc/json.luadoc @@ -0,0 +1,94 @@ +---[[ +LuCI JSON-Library + +@cstyle instance +module "luci.json" +]] + +---[[ +Directly decode a JSON string + +@class function +@name decode +@param json JSON-String +@return Lua object +]] + +---[[ +Direcly encode a Lua object into a JSON string. + +@class function +@name encode +@param obj Lua Object +@return JSON string +]] + +---[[ +Null replacement function + +@class function +@name null +@return null +]] + +---[[ +Create a new JSON-Encoder. + +@class function +@name Encoder +@param data Lua-Object to be encoded. +@param buffersize Blocksize of returned data source. +@param fastescape Use non-standard escaping (don't escape control chars) +@return JSON-Encoder +]] + +---[[ +Create an LTN12 source providing the encoded JSON-Data. + +@class function +@name Encoder.source +@return LTN12 source +]] + +---[[ +Create a new JSON-Decoder. + +@class function +@name Decoder +@param customnull Use luci.json.null instead of nil for decoding null +@return JSON-Decoder +]] + +---[[ +Create an LTN12 sink from the decoder object which accepts the JSON-Data. + +@class function +@name Decoder.sink +@return LTN12 sink +]] + +---[[ +Get the decoded data packets after the rawdata has been sent to the sink. + +@class function +@name Decoder.get +@return Decoded data +]] + +---[[ +Create a new Active JSON-Decoder. + +@class function +@name ActiveDecoder +@param customnull Use luci.json.null instead of nil for decoding null +@return Active JSON-Decoder +]] + +---[[ +Fetches one JSON-object from given source + +@class function +@name ActiveDecoder.get +@return Decoded object +]] + diff --git a/libs/luci-lib-jsonc/Makefile b/libs/luci-lib-jsonc/Makefile new file mode 100644 index 000000000..6a63dab5e --- /dev/null +++ b/libs/luci-lib-jsonc/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2015 LuCI Team <luci@lists.subsignal.org> +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Lua binding for JSON-C +LUCI_DEPENDS:=+liblua +libjson-c + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-jsonc/src/Makefile b/libs/luci-lib-jsonc/src/Makefile new file mode 100644 index 000000000..e15fbac38 --- /dev/null +++ b/libs/luci-lib-jsonc/src/Makefile @@ -0,0 +1,17 @@ +JSONC_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/json-c/ +JSONC_LDFLAGS = -llua -lm -ljson-c +JSONC_OBJ = jsonc.o +JSONC_LIB = jsonc.so + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(JSONC_CFLAGS) $(FPIC) -c -o $@ $< + +compile: $(JSONC_OBJ) + $(CC) $(LDFLAGS) -shared -o $(JSONC_LIB) $(JSONC_OBJ) $(JSONC_LDFLAGS) + +install: compile + mkdir -p $(DESTDIR)/usr/lib/lua/luci + cp $(JSONC_LIB) $(DESTDIR)/usr/lib/lua/luci/$(JSONC_LIB) + +clean: + rm -f *.o *.so diff --git a/libs/luci-lib-jsonc/src/jsonc.c b/libs/luci-lib-jsonc/src/jsonc.c new file mode 100644 index 000000000..49cb21f5b --- /dev/null +++ b/libs/luci-lib-jsonc/src/jsonc.c @@ -0,0 +1,388 @@ +/* +Copyright 2015 Jo-Philipp Wich <jow@openwrt.org> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#define _GNU_SOURCE + +#include <math.h> +#include <stdbool.h> +#include <json-c/json.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#define LUCI_JSONC "luci.jsonc" +#define LUCI_JSONC_PARSER "luci.jsonc.parser" + +struct json_state { + struct json_object *obj; + struct json_tokener *tok; + enum json_tokener_error err; +}; + +static void _json_to_lua(lua_State *L, struct json_object *obj); +static struct json_object * _lua_to_json(lua_State *L, int index); + +static int json_new(lua_State *L) +{ + struct json_state *s; + struct json_tokener *tok = json_tokener_new(); + + if (!tok) + return 0; + + s = lua_newuserdata(L, sizeof(*s)); + + if (!s) + { + json_tokener_free(tok); + return 0; + } + + s->tok = tok; + s->obj = NULL; + s->err = json_tokener_continue; + + luaL_getmetatable(L, LUCI_JSONC_PARSER); + lua_setmetatable(L, -2); + + return 1; +} + +static int json_parse(lua_State *L) +{ + size_t len; + const char *json = luaL_checklstring(L, 1, &len); + struct json_state s = { + .tok = json_tokener_new() + }; + + if (!s.tok) + return 0; + + s.obj = json_tokener_parse_ex(s.tok, json, len); + s.err = json_tokener_get_error(s.tok); + + if (s.obj) + { + _json_to_lua(L, s.obj); + json_object_put(s.obj); + } + else + { + lua_pushnil(L); + } + + if (s.err == json_tokener_continue) + s.err = json_tokener_error_parse_eof; + + if (s.err) + lua_pushstring(L, json_tokener_error_desc(s.err)); + + json_tokener_free(s.tok); + return (1 + !!s.err); +} + +static int json_stringify(lua_State *L) +{ + struct json_object *obj = _lua_to_json(L, 1); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(obj, flags)); + return 1; +} + + +static int json_parse_chunk(lua_State *L) +{ + size_t len; + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + const char *chunk = luaL_checklstring(L, 2, &len); + + s->obj = json_tokener_parse_ex(s->tok, chunk, len); + s->err = json_tokener_get_error(s->tok); + + if (!s->err) + { + lua_pushboolean(L, true); + return 1; + } + else if (s->err == json_tokener_continue) + { + lua_pushboolean(L, false); + return 1; + } + + lua_pushnil(L); + lua_pushstring(L, json_tokener_error_desc(s->err)); + return 2; +} + +static void _json_to_lua(lua_State *L, struct json_object *obj) +{ + int n; + + switch (json_object_get_type(obj)) + { + case json_type_object: + lua_newtable(L); + json_object_object_foreach(obj, key, val) + { + _json_to_lua(L, val); + lua_setfield(L, -2, key); + } + break; + + case json_type_array: + lua_newtable(L); + for (n = 0; n < json_object_array_length(obj); n++) + { + _json_to_lua(L, json_object_array_get_idx(obj, n)); + lua_rawseti(L, -2, n + 1); + } + break; + + case json_type_boolean: + lua_pushboolean(L, json_object_get_boolean(obj)); + break; + + case json_type_int: + lua_pushinteger(L, json_object_get_int(obj)); + break; + + case json_type_double: + lua_pushnumber(L, json_object_get_double(obj)); + break; + + case json_type_string: + lua_pushstring(L, json_object_get_string(obj)); + break; + + case json_type_null: + lua_pushnil(L); + break; + } +} + +static int json_parse_get(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (!s->obj || s->err) + lua_pushnil(L); + else + _json_to_lua(L, s->obj); + + return 1; +} + +static int _lua_test_array(lua_State *L, int index) +{ + int max = 0; + lua_Number idx; + + lua_pushnil(L); + + /* check for non-integer keys */ + while (lua_next(L, index)) + { + if (lua_type(L, -2) != LUA_TNUMBER) + goto out; + + idx = lua_tonumber(L, -2); + + if (idx != (lua_Number)(lua_Integer)idx) + goto out; + + if (idx <= 0) + goto out; + + if (idx > max) + max = idx; + + lua_pop(L, 1); + continue; + +out: + lua_pop(L, 2); + return 0; + } + + /* check for holes */ + //for (i = 1; i <= max; i++) + //{ + // lua_rawgeti(L, index, i); + // + // if (lua_isnil(L, -1)) + // { + // lua_pop(L, 1); + // return 0; + // } + // + // lua_pop(L, 1); + //} + + return max; +} + +static struct json_object * _lua_to_json(lua_State *L, int index) +{ + lua_Number nd, ni; + struct json_object *obj; + const char *key; + int i, max; + + switch (lua_type(L, index)) + { + case LUA_TTABLE: + max = _lua_test_array(L, index); + + if (max > 0) + { + obj = json_object_new_array(); + + if (!obj) + return NULL; + + for (i = 1; i <= max; i++) + { + lua_rawgeti(L, index, i); + + json_object_array_put_idx(obj, i - 1, + _lua_to_json(L, lua_gettop(L))); + + lua_pop(L, 1); + } + + return obj; + } + + obj = json_object_new_object(); + + if (!obj) + return NULL; + + lua_pushnil(L); + + while (lua_next(L, index)) + { + lua_pushvalue(L, -2); + key = lua_tostring(L, -1); + + json_object_object_add(obj, key, + _lua_to_json(L, lua_gettop(L) - 1)); + + lua_pop(L, 2); + } + + return obj; + + case LUA_TNIL: + return NULL; + + case LUA_TBOOLEAN: + return json_object_new_boolean(lua_toboolean(L, index)); + + case LUA_TNUMBER: + nd = lua_tonumber(L, index); + ni = lua_tointeger(L, index); + + if (nd == ni) + return json_object_new_int(nd); + + return json_object_new_double(nd); + + case LUA_TSTRING: + return json_object_new_string(lua_tostring(L, index)); + } + + return NULL; +} + +static int json_parse_set(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + s->err = 0; + s->obj = _lua_to_json(L, 2); + + return 0; +} + +static int json_tostring(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(s->obj, flags)); + return 1; +} + +static int json_gc(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (s->obj) + json_object_put(s->obj); + + if (s->tok) + json_tokener_free(s->tok); + + return 0; +} + + +static const luaL_reg jsonc_methods[] = { + { "new", json_new }, + { "parse", json_parse }, + { "stringify", json_stringify }, + + { } +}; + +static const luaL_reg jsonc_parser_methods[] = { + { "parse", json_parse_chunk }, + { "get", json_parse_get }, + { "set", json_parse_set }, + { "stringify", json_tostring }, + + { "__gc", json_gc }, + { "__tostring", json_tostring }, + + { } +}; + + +int luaopen_luci_jsonc(lua_State *L) +{ + luaL_register(L, LUCI_JSONC, jsonc_methods); + + luaL_newmetatable(L, LUCI_JSONC_PARSER); + luaL_register(L, NULL, jsonc_parser_methods); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + return 1; +} diff --git a/libs/luci-lib-jsonc/src/jsonc.luadoc b/libs/luci-lib-jsonc/src/jsonc.luadoc new file mode 100644 index 000000000..2ee9cebdc --- /dev/null +++ b/libs/luci-lib-jsonc/src/jsonc.luadoc @@ -0,0 +1,134 @@ +--- LuCI JSON parsing and serialization library. +-- The luci.jsonc class is a high level Lua binding to the JSON-C library to +-- allow reading and writing JSON data with minimal overhead. +module "luci.jsonc" + +---[[ +Construct a new luci.jsonc.parser instance. +@class function +@sort 1 +@name new +@return A `luci.jsonc.parser` object representing a JSON-C tokener. +@usage `parser = luci.jsonc.new()` +]] + +---[[ +Parse a complete JSON string and convert it into a Lua data structure. +@class function +@sort 2 +@name parse +@param json A string containing the JSON data to parse, must be either a + JSON array or a JSON object. +@return On success, a table containing the parsed JSON data is returned, on + failure the function returns `nil` and a string containing the reason of + the parse error. +@usage `data = luci.jsonc.parse('{ "name": "John", "age": 34 }') +print(data.name) -- "John"` +@see stringify +]] + +---[[ +Convert given Lua data into a JSON string. + +This function recursively converts the given Lua data into a JSON string, +ignoring any unsupported data. Lua tables are converted into JSON arrays if they +only contain integer keys, mixed tables are turned into JSON objects with any +existing numeric keys converted into strings. + +Lua functions, coroutines and userdata objects are ignored and Lua numbers are +converted to integers if they do not contain fractional values. + +@class function +@sort 3 +@name stringify +@param data The Lua data to convert, can be a table, string, boolean or number. +@param pretty A boolean value indicating whether the resulting JSON should be + pretty printed. +@return Returns a string containing the JSON representation of the given Lua + data. +@usage `json = luci.jsonc.stringify({ item = true, values = { 1, 2, 3 } }) +print(json) -- '{"item":true,"values":[1,2,3]}'` +@see parse +]] + + +--- LuCI JSON parser instance. +-- A JSON parser instance is useful to parse JSON data chunk by chunk, without +-- the need to assemble all data in advance. +-- @cstyle instance +module "luci.jsonc.parser" + +---[[ +Parses one chunk of JSON data. + +@class function +@sort 1 +@name parser.parse +@see parser.get +@param json String containing the JSON fragment to parse +@return <ul> + <li>`true` if a complete JSON object has been parsed and no further input is + expected.</li> + <li>`false` if further input is required</li> + <li>`nil` if an error was encountered while parsing the current chunk. + In this case a string describing the parse error is returned as second + value.</li></ul> +@usage `parser = luci.jsonc.new() + +while true do + chunk = ... -- fetch a cunk of data, e.g. from a socket + finish, errmsg = <b>parser.parse(chunk)</b> + + if finish == nil then + error("Cannot parse JSON: " .. errmsg) + end + + if finish == true then + break + end +end` +]] + +---[[ +Convert parsed JSON data into Lua table. + +@class function +@sort 2 +@name parser.get +@see parser.parse +@return Parsed JSON object converted into a Lua table or `nil` if the parser + didn't finish or encountered an error. +@usage `parser = luci.jsonc.new() +parser:parse('{ "example": "test" }') + +data = parser:get() +print(data.example) -- "test"` +]] + +---[[ +Put Lua data into the parser. + +@class function +@sort 3 +@name parser.set +@see parser.stringify +@param data Lua data to put into the parser object. The data is converted to an + internal JSON representation that can be dumped with `stringify()`. + The conversion follows the rules described in `luci.jsonc.stringify`. +@return Nothing is returned. +@usage `parser = luci.jsonc.new() +parser:set({ "some", "data" })` +]] + +---[[ +Serialize current parser state as JSON. + +@class function +@sort 4 +@name parser.stringify +@param pretty A boolean value indicating whether the resulting JSON should be pretty printed. +@return Returns the serialized JSON data of this parser instance. +@usage `parser = luci.jsonc.new() +parser:parse('{ "example": "test" }') +print(parser:serialize()) -- '{"example":"test"}'` +]] diff --git a/libs/luci-lib-nixio/docsrc/CHANGELOG.lua b/libs/luci-lib-nixio/docsrc/CHANGELOG.lua index fb1cf160b..aa3184140 100644 --- a/libs/luci-lib-nixio/docsrc/CHANGELOG.lua +++ b/libs/luci-lib-nixio/docsrc/CHANGELOG.lua @@ -1,5 +1,5 @@ --- Changes and improvements. -module "CHANGELOG" +module "nixio.CHANGELOG" --- Service Release. -- <ul> @@ -26,4 +26,4 @@ module "CHANGELOG" -- </ul> -- @class table -- @name 0.2 --- @return !
\ No newline at end of file +-- @return ! diff --git a/libs/luci-lib-nixio/docsrc/README.lua b/libs/luci-lib-nixio/docsrc/README.lua index b957a6990..ee3e3a216 100644 --- a/libs/luci-lib-nixio/docsrc/README.lua +++ b/libs/luci-lib-nixio/docsrc/README.lua @@ -1,5 +1,5 @@ --- General Information. -module "README" +module "nixio.README" --- General error handling information. -- <ul> @@ -92,4 +92,4 @@ module "README" -- @usage Tes -- @class table -- @name TLS-Crypto --- @return !
\ No newline at end of file +-- @return ! diff --git a/libs/luci-lib-nixio/src/Makefile b/libs/luci-lib-nixio/src/Makefile index aaa4292bd..a7e9a77d9 100644 --- a/libs/luci-lib-nixio/src/Makefile +++ b/libs/luci-lib-nixio/src/Makefile @@ -1,4 +1,4 @@ -OS = $(shell uname) +OS = Linux AXTLS_VERSION = 1.2.1 AXTLS_DIR = axTLS AXTLS_FILE = $(AXTLS_DIR)-$(AXTLS_VERSION).tar.gz diff --git a/libs/luci-lib-rpcc/luasrc/rpcc.lua b/libs/luci-lib-rpcc/luasrc/rpcc.lua index fc52b7eb0..5558910bf 100644 --- a/libs/luci-lib-rpcc/luasrc/rpcc.lua +++ b/libs/luci-lib-rpcc/luasrc/rpcc.lua @@ -9,17 +9,10 @@ local nixio = require "nixio", require "nixio.util" local tostring, assert, setmetatable = tostring, assert, setmetatable local error = error ---- LuCI RPC Client. --- @cstyle instance module "luci.rpcc" RQLIMIT = 32 * nixio.const.buffersize ---- Create a new JSON-RPC stream client. --- @class function --- @param fd File descriptor --- @param v1 Use protocol version 1.0 --- @return RPC Client Client = util.class() function Client.__init__(self, fd, v1) @@ -29,11 +22,6 @@ function Client.__init__(self, fd, v1) self.v1 = v1 end ---- Request an RP call and get the response. --- @param method Remote method --- @param params Parameters --- @param notification Notification only? --- @return response function Client.request(self, method, params, notification) local oldchunk = self.decoder and self.decoder.chunk self.decoder = json.ActiveDecoder(self.fd:blocksource(nil, RQLIMIT)) @@ -58,9 +46,6 @@ function Client.request(self, method, params, notification) end end ---- Create a transparent RPC proxy. --- @param prefix Method prefix --- @return RPC Proxy object function Client.proxy(self, prefix) prefix = prefix or "" return setmetatable({}, { diff --git a/libs/luci-lib-rpcc/luasrc/rpcc.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc.luadoc new file mode 100644 index 000000000..8c90ab524 --- /dev/null +++ b/libs/luci-lib-rpcc/luasrc/rpcc.luadoc @@ -0,0 +1,36 @@ +---[[ +LuCI RPC Client. + +@cstyle instance +module "luci.rpcc" +]] + +---[[ +Create a new JSON-RPC stream client. + +@class function +@param fd File descriptor +@param v1 Use protocol version 1.0 +@return RPC Client +]] + +---[[ +Request an RP call and get the response. + +@class function +@name Client.request +@param method Remote method +@param params Parameters +@param notification Notification only? +@return response +]] + +---[[ +Create a transparent RPC proxy. + +@class function +@name Client.proxy +@param prefix Method prefix +@return RPC Proxy object +]] + diff --git a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua index 666d58585..275c39699 100644 --- a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua +++ b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua @@ -5,16 +5,11 @@ local util = require "luci.util" local rawget, setmetatable = rawget, setmetatable local ipairs = ipairs ---- Transparent UCI over RPC client. --- @cstyle instance module "luci.rpcc.ruci" local Proxy = util.class() ---- Create a new UCI over RPC proxy. --- @param rpccl RPC client --- @return Network transparent UCI module function factory(rpccl) return { cursor = function(...) diff --git a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc new file mode 100644 index 000000000..980ef46d9 --- /dev/null +++ b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc @@ -0,0 +1,16 @@ +---[[ +Transparent UCI over RPC client. + +@cstyle instance +module "luci.rpcc.ruci" +]] + +---[[ +Create a new UCI over RPC proxy. + +@class function +@name factory +@param rpccl RPC client +@return Network transparent UCI module +]] + @@ -147,6 +147,7 @@ define Package/$(PKG_NAME)/install if [ -d $(PKG_BUILD_DIR)/luasrc ]; then \ $(INSTALL_DIR) $(1)$(LUCI_LIBRARYDIR); \ cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)$(LUCI_LIBRARYDIR)/; \ + $(FIND) $(1)$(LUCI_LIBRARYDIR)/ -type f -name '*.luadoc' | $(XARGS) rm; \ $(if $(CONFIG_LUCI_SRCDIET),$(call SrcDiet,$(1)$(LUCI_LIBRARYDIR)/),true); \ else true; fi if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \ diff --git a/modules/luci-base/Makefile b/modules/luci-base/Makefile index 8337fea71..80bbda107 100644 --- a/modules/luci-base/Makefile +++ b/modules/luci-base/Makefile @@ -12,7 +12,7 @@ LUCI_TYPE:=mod LUCI_BASENAME:=base LUCI_TITLE:=LuCI core libraries -LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +rpcd +LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2 PKG_SOURCE_URL:=https://luasrcdiet.googlecode.com/files diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua index 155d31b10..8b8d1fa34 100644 --- a/modules/luci-base/luasrc/dispatcher.lua +++ b/modules/luci-base/luasrc/dispatcher.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI web dispatcher. local fs = require "nixio.fs" local sys = require "luci.sys" local util = require "luci.util" @@ -23,9 +22,6 @@ local index = nil local fi ---- Build the URL relative to the server webroot from given virtual path. --- @param ... Virtual path --- @return Relative URL function build_url(...) local path = {...} local url = { http.getenv("SCRIPT_NAME") or "" } @@ -49,9 +45,6 @@ function build_url(...) return table.concat(url, "") end ---- Check whether a dispatch node shall be visible --- @param node Dispatch node --- @return Boolean indicating whether the node should be visible function node_visible(node) if node then return not ( @@ -64,9 +57,6 @@ function node_visible(node) return false end ---- Return a sorted table of visible childs within a given node --- @param node Dispatch node --- @return Ordered table of child node names function node_childs(node) local rv = { } if node then @@ -86,9 +76,6 @@ function node_childs(node) end ---- Send a 404 error code and render the "error404" template if available. --- @param message Custom error message (optional) --- @return false function error404(message) http.status(404, "Not Found") message = message or "Not Found" @@ -101,9 +88,6 @@ function error404(message) return false end ---- Send a 500 error code and render the "error500" template if available. --- @param message Custom error message (optional)# --- @return false function error500(message) util.perror(message) if not context.template_header_sent then @@ -128,16 +112,29 @@ function authenticator.htmlauth(validator, accs, default) return user end - require("luci.i18n") - require("luci.template") - context.path = {} - luci.template.render("sysauth", {duser=default, fuser=user}) + if context.urltoken.stok then + context.urltoken.stok = nil + + local cookie = 'sysauth=%s; expires=%s; path=%s/' %{ + http.getcookie('sysauth') or 'x', + 'Thu, 01 Jan 1970 01:00:00 GMT', + build_url() + } + + http.header("Set-Cookie", cookie) + http.redirect(build_url()) + else + require("luci.i18n") + require("luci.template") + context.path = {} + http.status(403, "Forbidden") + luci.template.render("sysauth", {duser=default, fuser=user}) + end + return false end ---- Dispatch an HTTP request. --- @param request LuCI HTTP Request object function httpdispatch(request, prefix) http.context.request = request @@ -176,8 +173,6 @@ function httpdispatch(request, prefix) --context._disable_memtrace() end ---- Dispatches a LuCI virtual path. --- @param request Virtual path function dispatch(request) --context._disable_memtrace = require "luci.debug".trap_memtrace("l") local ctx = context @@ -340,15 +335,15 @@ function dispatch(request) if not util.contains(accs, user) then if authen then - ctx.urltoken.stok = nil local user, sess = authen(sys.user.checkpasswd, accs, def) + local token if not user or not util.contains(accs, user) then return else if not sess then - local sdat = util.ubus("session", "create", { timeout = luci.config.sauth.sessiontime }) + local sdat = util.ubus("session", "create", { timeout = tonumber(luci.config.sauth.sessiontime) }) if sdat then - local token = sys.uniqueid(16) + token = sys.uniqueid(16) util.ubus("session", "set", { ubus_rpc_session = sdat.ubus_rpc_session, values = { @@ -358,14 +353,19 @@ function dispatch(request) } }) sess = sdat.ubus_rpc_session - ctx.urltoken.stok = token end end - if sess then - http.header("Set-Cookie", "sysauth=" .. sess.."; path="..build_url()) + if sess and token then + http.header("Set-Cookie", 'sysauth=%s; path=%s/' %{ + sess, build_url() + }) + + ctx.urltoken.stok = token ctx.authsession = sess ctx.authuser = user + + http.redirect(build_url(unpack(ctx.requestpath))) end end else @@ -383,6 +383,9 @@ function dispatch(request) end if track.setuser then + -- trigger ubus connection before dropping root privs + util.ubus() + sys.process.setuser(track.setuser) end @@ -445,7 +448,6 @@ function dispatch(request) end end ---- Generate the dispatching index using the native file-cache based strategy. function createindex() local controllers = { } local base = "%s/controller/" % util.libpath() @@ -509,7 +511,6 @@ function createindex() end end ---- Create the dispatching tree from the index. -- Build the index before if it does not exist yet. function createtree() if not index then @@ -548,9 +549,6 @@ function createtree() return tree end ---- Register a tree modifier. --- @param func Modifier function --- @param order Modifier order value (optional) function modifier(func, order) context.modifiers[#context.modifiers+1] = { func = func, @@ -560,12 +558,6 @@ function modifier(func, order) } end ---- Clone a node of the dispatching tree to another position. --- @param path Virtual path destination --- @param clone Virtual path source --- @param title Destination node title (optional) --- @param order Destination node order value (optional) --- @return Dispatching tree node function assign(path, clone, title, order) local obj = node(unpack(path)) obj.nodes = nil @@ -579,12 +571,6 @@ function assign(path, clone, title, order) return obj end ---- Create a new dispatching node and define common parameters. --- @param path Virtual path --- @param target Target function to call when dispatched. --- @param title Destination node title --- @param order Destination node order value (optional) --- @return Dispatching tree node function entry(path, target, title, order) local c = node(unpack(path)) @@ -596,17 +582,11 @@ function entry(path, target, title, order) return c end ---- Fetch or create a dispatching node without setting the target module or -- enabling the node. --- @param ... Virtual path --- @return Dispatching tree node function get(...) return _create_node({...}) end ---- Fetch or create a new dispatching node. --- @param ... Virtual path --- @return Dispatching tree node function node(...) local c = _create_node({...}) @@ -666,13 +646,10 @@ function _firstchild() dispatch(path) end ---- Alias the first (lowest order) page automatically function firstchild() return { type = "firstchild", target = _firstchild } end ---- Create a redirect to another dispatching node. --- @param ... Virtual path destination function alias(...) local req = {...} return function(...) @@ -684,9 +661,6 @@ function alias(...) end end ---- Rewrite the first x path values of the request. --- @param n Number of path values to replace --- @param ... Virtual path to replace removed path values with function rewrite(n, ...) local req = {...} return function(...) @@ -725,9 +699,6 @@ local function _call(self, ...) end end ---- Create a function-call dispatching target. --- @param name Target function of local controller --- @param ... Additional parameters passed to the function function call(name, ...) return {type = "call", argv = {...}, name = name, target = _call} end @@ -737,8 +708,6 @@ local _template = function(self, ...) require "luci.template".render(self.view) end ---- Create a template render dispatching target. --- @param name Template to be rendered function template(name) return {type = "template", view = name, target = _template} end @@ -844,8 +813,6 @@ local function _cbi(self, ...) end end ---- Create a CBI model dispatching target. --- @param model CBI model to be rendered function cbi(model, config) return {type = "cbi", config = config, model = model, target = _cbi} end @@ -858,9 +825,6 @@ local function _arcombine(self, ...) target:target(unpack(argv)) end ---- Create a combined dispatching target for non argv and argv requests. --- @param trg1 Overview Target --- @param trg2 Detail Target function arcombine(trg1, trg2) return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}} end @@ -889,19 +853,12 @@ local function _form(self, ...) tpl.render("footer") end ---- Create a CBI form model dispatching target. --- @param model CBI form model tpo be rendered function form(model) return {type = "cbi", model = model, target = _form} end ---- Access the luci.i18n translate() api. --- @class function --- @name translate --- @param text Text to translate translate = i18n.translate ---- No-op function used to mark translation entries for menu labels. -- This function does not actually translate the given argument but -- is used by build/i18n-scan.pl to find translatable entries. function _(text) diff --git a/modules/luci-base/luasrc/dispatcher.luadoc b/modules/luci-base/luasrc/dispatcher.luadoc new file mode 100644 index 000000000..743463c74 --- /dev/null +++ b/modules/luci-base/luasrc/dispatcher.luadoc @@ -0,0 +1,220 @@ +---[[ +LuCI web dispatcher. +]] +module "luci.dispatcher" + +---[[ +Build the URL relative to the server webroot from given virtual path. + +@class function +@name build_url +@param ... Virtual path +@return Relative URL +]] + +---[[ +Check whether a dispatch node shall be visible + +@class function +@name node_visible +@param node Dispatch node +@return Boolean indicating whether the node should be visible +]] + +---[[ +Return a sorted table of visible childs within a given node + +@class function +@name node_childs +@param node Dispatch node +@return Ordered table of child node names +]] + +---[[ +Send a 404 error code and render the "error404" template if available. + +@class function +@name error404 +@param message Custom error message (optional) +@return false +]] + +---[[ +Send a 500 error code and render the "error500" template if available. + +@class function +@name error500 +@param message Custom error message (optional)# +@return false +]] + +---[[ +Dispatch an HTTP request. + +@class function +@name httpdispatch +@param request LuCI HTTP Request object +]] + +---[[ +Dispatches a LuCI virtual path. + +@class function +@name dispatch +@param request Virtual path +]] + +---[[ +Generate the dispatching index using the native file-cache based strategy. + + +@class function +@name createindex +]] + +---[[ +Create the dispatching tree from the index. + +Build the index before if it does not exist yet. + +@class function +@name createtree +]] + +---[[ +Register a tree modifier. + +@class function +@name modifier +@param func Modifier function +@param order Modifier order value (optional) +]] + +---[[ +Clone a node of the dispatching tree to another position. + +@class function +@name assign +@param path Virtual path destination +@param clone Virtual path source +@param title Destination node title (optional) +@param order Destination node order value (optional) +@return Dispatching tree node +]] + +---[[ +Create a new dispatching node and define common parameters. + +@class function +@name entry +@param path Virtual path +@param target Target function to call when dispatched. +@param title Destination node title +@param order Destination node order value (optional) +@return Dispatching tree node +]] + +---[[ +Fetch or create a dispatching node without setting the target module or + +enabling the node. +@class function +@name get +@param ... Virtual path +@return Dispatching tree node +]] + +---[[ +Fetch or create a new dispatching node. + +@class function +@name node +@param ... Virtual path +@return Dispatching tree node +]] + +---[[ +Alias the first (lowest order) page automatically + + +@class function +@name firstchild +]] + +---[[ +Create a redirect to another dispatching node. + +@class function +@name alias +@param ... Virtual path destination +]] + +---[[ +Rewrite the first x path values of the request. + +@class function +@name rewrite +@param n Number of path values to replace +@param ... Virtual path to replace removed path values with +]] + +---[[ +Create a function-call dispatching target. + +@class function +@name call +@param name Target function of local controller +@param ... Additional parameters passed to the function +]] + +---[[ +Create a template render dispatching target. + +@class function +@name template +@param name Template to be rendered +]] + +---[[ +Create a CBI model dispatching target. + +@class function +@name cbi +@param model CBI model to be rendered +]] + +---[[ +Create a combined dispatching target for non argv and argv requests. + +@class function +@name arcombine +@param trg1 Overview Target +@param trg2 Detail Target +]] + +---[[ +Create a CBI form model dispatching target. + +@class function +@name form +@param model CBI form model tpo be rendered +]] + +---[[ +Access the luci.i18n translate() api. + +@class function +@name translate +@param text Text to translate +]] + +---[[ +No-op function used to mark translation entries for menu labels. + +This function does not actually translate the given argument but +is used by build/i18n-scan.pl to find translatable entries. + +@class function +@name _ +]] + diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua index a5329e51d..a92d8affb 100644 --- a/modules/luci-base/luasrc/http.lua +++ b/modules/luci-base/luasrc/http.lua @@ -11,7 +11,6 @@ local table = require "table" local ipairs, pairs, next, type, tostring, error = ipairs, pairs, next, type, tostring, error ---- LuCI Web Framework high-level HTTP functions. module "luci.http" context = util.threadlocal() @@ -101,7 +100,6 @@ function Request._parse_input(self) self.parsed_input = true end ---- Close the HTTP-Connection. function close() if not context.eoh then context.eoh = true @@ -114,52 +112,31 @@ function close() end end ---- Return the request content if the request was of unknown type. --- @return HTTP request body --- @return HTTP request body length function content() return context.request:content() end ---- Get a certain HTTP input value or a table of all input values. --- @param name Name of the GET or POST variable to fetch --- @param noparse Don't parse POST data before getting the value --- @return HTTP input value or table of all input value function formvalue(name, noparse) return context.request:formvalue(name, noparse) end ---- Get a table of all HTTP input values with a certain prefix. --- @param prefix Prefix --- @return Table of all HTTP input values with given prefix function formvaluetable(prefix) return context.request:formvaluetable(prefix) end ---- Get the value of a certain HTTP-Cookie. --- @param name Cookie Name --- @return String containing cookie data function getcookie(name) return context.request:getcookie(name) end ---- Get the value of a certain HTTP environment variable -- or the environment table itself. --- @param name Environment variable --- @return HTTP environment value or environment table function getenv(name) return context.request:getenv(name) end ---- Set a handler function for incoming user file uploads. --- @param callback Handler function function setfilehandler(callback) return context.request:setfilehandler(callback) end ---- Send a HTTP-Header. --- @param key Header key --- @param value Header value function header(key, value) if not context.headers then context.headers = {} @@ -168,8 +145,6 @@ function header(key, value) coroutine.yield(2, key, value) end ---- Set the mime type of following content data. --- @param mime Mimetype of following content function prepare_content(mime) if not context.headers or not context.headers["content-type"] then if mime == "application/xhtml+xml" then @@ -183,15 +158,10 @@ function prepare_content(mime) end end ---- Get the RAW HTTP input source --- @return HTTP LTN12 source function source() return context.request.input end ---- Set the HTTP status code and status message. --- @param code Status code --- @param message Status message function status(code, message) code = code or 200 message = message or "OK" @@ -199,12 +169,8 @@ function status(code, message) coroutine.yield(1, code, message) end ---- Send a chunk of content data to the client. -- This function is as a valid LTN12 sink. -- If the content chunk is nil this function will automatically invoke close. --- @param content Content chunk --- @param src_err Error object from source (optional) --- @see close function write(content, src_err) if not content then if src_err then @@ -237,24 +203,16 @@ function write(content, src_err) end end ---- Splice data from a filedescriptor to the client. --- @param fp File descriptor --- @param size Bytes to splice (optional) function splice(fd, size) coroutine.yield(6, fd, size) end ---- Redirects the client to a new URL and closes the connection. --- @param url Target URL function redirect(url) status(302, "Found") header("Location", url) close() end ---- Create a querystring out of a table of key - value pairs. --- @param table Query string source table --- @return Encoded HTTP query string function build_querystring(q) local s = { "?" } @@ -269,56 +227,10 @@ function build_querystring(q) return table.concat(s, "") end ---- Return the URL-decoded equivalent of a string. --- @param str URL-encoded string --- @param no_plus Don't decode + to " " --- @return URL-decoded string --- @see urlencode urldecode = protocol.urldecode ---- Return the URL-encoded equivalent of a string. --- @param str Source string --- @return URL-encoded string --- @see urldecode urlencode = protocol.urlencode ---- Send the given data as JSON encoded string. --- @param data Data to send function write_json(x) - if x == nil then - write("null") - elseif type(x) == "table" then - local k, v - if type(next(x)) == "number" then - write("[ ") - for k, v in ipairs(x) do - write_json(v) - if next(x, k) then - write(", ") - end - end - write(" ]") - else - write("{ ") - for k, v in pairs(x) do - write("%q: " % k) - write_json(v) - if next(x, k) then - write(", ") - end - end - write(" }") - end - elseif type(x) == "number" or type(x) == "boolean" then - if (x ~= x) then - -- NaN is the only value that doesn't equal to itself. - write("Number.NaN") - else - write(tostring(x)) - end - else - write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c) - return '\\u%04x' % c:byte(1) - end)) - end + util.serialize_json(x, write) end diff --git a/modules/luci-base/luasrc/http.luadoc b/modules/luci-base/luasrc/http.luadoc new file mode 100644 index 000000000..4e31216a1 --- /dev/null +++ b/modules/luci-base/luasrc/http.luadoc @@ -0,0 +1,166 @@ +---[[ +LuCI Web Framework high-level HTTP functions. + +module "luci.http" +]] + +---[[ +Close the HTTP-Connection. + + +@class function +@name close +]] + +---[[ +Return the request content if the request was of unknown type. + +@class function +@name content +@return HTTP request body +@return HTTP request body length +]] + +---[[ +Get a certain HTTP input value or a table of all input values. + +@class function +@name formvalue +@param name Name of the GET or POST variable to fetch +@param noparse Don't parse POST data before getting the value +@return HTTP input value or table of all input value +]] + +---[[ +Get a table of all HTTP input values with a certain prefix. + +@class function +@name formvaluetable +@param prefix Prefix +@return Table of all HTTP input values with given prefix +]] + +---[[ +Get the value of a certain HTTP-Cookie. + +@class function +@name getcookie +@param name Cookie Name +@return String containing cookie data +]] + +---[[ +Get the value of a certain HTTP environment variable + +or the environment table itself. +@class function +@name getenv +@param name Environment variable +@return HTTP environment value or environment table +]] + +---[[ +Set a handler function for incoming user file uploads. + +@class function +@name setfilehandler +@param callback Handler function +]] + +---[[ +Send a HTTP-Header. + +@class function +@name header +@param key Header key +@param value Header value +]] + +---[[ +Set the mime type of following content data. + +@class function +@name prepare_content +@param mime Mimetype of following content +]] + +---[[ +Get the RAW HTTP input source + +@class function +@name source +@return HTTP LTN12 source +]] + +---[[ +Set the HTTP status code and status message. + +@class function +@name status +@param code Status code +@param message Status message +]] + +---[[ +Send a chunk of content data to the client. + +This function is as a valid LTN12 sink. +If the content chunk is nil this function will automatically invoke close. +@class function +@name write +@param content Content chunk +@param src_err Error object from source (optional) +@see close +]] + +---[[ +Splice data from a filedescriptor to the client. + +@class function +@name splice +@param fp File descriptor +@param size Bytes to splice (optional) +]] + +---[[ +Redirects the client to a new URL and closes the connection. + +@class function +@name redirect +@param url Target URL +]] + +---[[ +Create a querystring out of a table of key - value pairs. + +@class function +@name build_querystring +@param table Query string source table +@return Encoded HTTP query string +]] + +---[[ +Return the URL-decoded equivalent of a string. + +@param str URL-encoded string +@param no_plus Don't decode + to " " +@return URL-decoded string +@see urlencode +]] + +---[[ +Return the URL-encoded equivalent of a string. + +@param str Source string +@return URL-encoded string +@see urldecode +]] + +---[[ +Send the given data as JSON encoded string. + +@class function +@name write_json +@param data Data to send +]] + diff --git a/modules/luci-base/luasrc/http/protocol.lua b/modules/luci-base/luasrc/http/protocol.lua index aeb7ea621..e9efb44cf 100644 --- a/modules/luci-base/luasrc/http/protocol.lua +++ b/modules/luci-base/luasrc/http/protocol.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol class. -- This class contains several functions useful for http message- and content -- decoding and to retrive form data from raw http messages. module("luci.http.protocol", package.seeall) @@ -10,12 +9,7 @@ local ltn12 = require("luci.ltn12") HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size ---- Decode an urlencoded string - optionally without decoding -- the "+" sign to " " - and return the decoded string. --- @param str Input string in x-www-urlencoded format --- @param no_plus Don't decode "+" signs to spaces --- @return The decoded string --- @see urlencode function urldecode( str, no_plus ) local function __chrdec( hex ) @@ -33,15 +27,10 @@ function urldecode( str, no_plus ) return str end ---- Extract and split urlencoded data pairs, separated bei either "&" or ";" -- from given url or string. Returns a table with urldecoded values. -- Simple parameters are stored as string values associated with the parameter -- name within the table. Parameters with multiple values are stored as array -- containing the corresponding values. --- @param url The url or string which contains x-www-urlencoded form data --- @param tbl Use the given table for storing values (optional) --- @return Table containing the urldecoded parameters --- @see urlencode_params function urldecode_params( url, tbl ) local params = tbl or { } @@ -73,10 +62,6 @@ function urldecode_params( url, tbl ) return params end ---- Encode given string to x-www-urlencoded format. --- @param str String to encode --- @return String containing the encoded data --- @see urldecode function urlencode( str ) local function __chrenc( chr ) @@ -95,12 +80,8 @@ function urlencode( str ) return str end ---- Encode each key-value-pair in given table to x-www-urlencoded format, -- separated by "&". Tables are encoded as parameters with multiple values by -- repeating the parameter name with each value. --- @param tbl Table with the values --- @return String containing encoded values --- @see urldecode_params function urlencode_params( tbl ) local enc = "" @@ -122,9 +103,6 @@ end -- (Internal function) -- Initialize given parameter and coerce string into table when the parameter -- already exists. --- @param tbl Table where parameter should be created --- @param key Parameter name --- @return Always nil local function __initval( tbl, key ) if tbl[key] == nil then tbl[key] = "" @@ -138,11 +116,6 @@ end -- (Internal function) -- Append given data to given parameter, either by extending the string value -- or by appending it to the last string in the parameter's value table. --- @param tbl Table containing the previously initialized parameter value --- @param key Parameter name --- @param chunk String containing the data to append --- @return Always nil --- @see __initval local function __appendval( tbl, key, chunk ) if type(tbl[key]) == "table" then tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk @@ -155,12 +128,6 @@ end -- Finish the value of given parameter, either by transforming the string value -- or - in the case of multi value parameters - the last element in the -- associated values table. --- @param tbl Table containing the previously initialized parameter value --- @param key Parameter name --- @param handler Function which transforms the parameter value --- @return Always nil --- @see __initval --- @see __appendval local function __finishval( tbl, key, handler ) if handler then if type(tbl[key]) == "table" then @@ -259,10 +226,7 @@ process_states['headers'] = function( msg, chunk ) end ---- Creates a ltn12 source from the given socket. The source will return it's -- data line by line with the trailing \r\n stripped of. --- @param sock Readable network socket --- @return Ltn12 source function function header_source( sock ) return ltn12.source.simplify( function() @@ -289,7 +253,6 @@ function header_source( sock ) end ) end ---- Decode a mime encoded http message body with multipart/form-data -- Content-Type. Stores all extracted data associated with its parameter name -- in the params table withing the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. @@ -300,12 +263,6 @@ end -- o Table containing decoded (name, file) and raw (headers) mime header data -- o String value containing a chunk of the file data -- o Boolean which indicates wheather the current chunk is the last one (eof) --- @param src Ltn12 source function --- @param msg HTTP message object --- @param filecb File callback function (optional) --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function mimedecode_message_body( src, msg, filecb ) if msg and msg.env.CONTENT_TYPE then @@ -449,15 +406,9 @@ function mimedecode_message_body( src, msg, filecb ) return ltn12.pump.all( src, snk ) end ---- Decode an urlencoded http message body with application/x-www-urlencoded -- Content-Type. Stores all extracted data associated with its parameter name -- in the params table withing the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. --- @param src Ltn12 source function --- @param msg HTTP message object --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function urldecode_message_body( src, msg ) local tlen = 0 @@ -507,12 +458,8 @@ function urldecode_message_body( src, msg ) return ltn12.pump.all( src, snk ) end ---- Try to extract an http message header including information like protocol -- version, message headers and resulting CGI environment variables from the -- given ltn12 source. --- @param src Ltn12 source function --- @return HTTP message object --- @see parse_message_body function parse_message_header( src ) local ok = true @@ -582,19 +529,12 @@ function parse_message_header( src ) return msg end ---- Try to extract and decode a http message body from the given ltn12 source. -- This function will examine the Content-Type within the given message object -- to select the appropriate content decoder. -- Currently the application/x-www-urlencoded and application/form-data -- mime types are supported. If the encountered content encoding can't be -- handled then the whole message body will be stored unaltered as "content" -- property within the given message object. --- @param src Ltn12 source function --- @param msg HTTP message object --- @param filecb File data callback (optional, see mimedecode_message_body()) --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function parse_message_body( src, msg, filecb ) -- Is it multipart/mime ? if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and @@ -655,8 +595,6 @@ function parse_message_body( src, msg, filecb ) end end ---- Table containing human readable messages for several http status codes. --- @class table statusmsg = { [200] = "OK", [206] = "Partial Content", diff --git a/modules/luci-base/luasrc/http/protocol.luadoc b/modules/luci-base/luasrc/http/protocol.luadoc new file mode 100644 index 000000000..67a60d9e7 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol.luadoc @@ -0,0 +1,142 @@ +---[[ +LuCI http protocol class. + +This class contains several functions useful for http message- and content +decoding and to retrive form data from raw http messages. +]] +module "luci.http.protocol" + +---[[ +Decode an urlencoded string - optionally without decoding + +the "+" sign to " " - and return the decoded string. +@class function +@name urldecode +@param str Input string in x-www-urlencoded format +@param no_plus Don't decode "+" signs to spaces +@return The decoded string +@see urlencode +]] + +---[[ +Extract and split urlencoded data pairs, separated bei either "&" or ";" + +from given url or string. Returns a table with urldecoded values. +Simple parameters are stored as string values associated with the parameter +name within the table. Parameters with multiple values are stored as array +containing the corresponding values. +@class function +@name urldecode_params +@param url The url or string which contains x-www-urlencoded form data +@param tbl Use the given table for storing values (optional) +@return Table containing the urldecoded parameters +@see urlencode_params +]] + +---[[ +Encode given string to x-www-urlencoded format. + +@class function +@name urlencode +@param str String to encode +@return String containing the encoded data +@see urldecode +]] + +---[[ +Encode each key-value-pair in given table to x-www-urlencoded format, + +separated by "&". Tables are encoded as parameters with multiple values by +repeating the parameter name with each value. +@class function +@name urlencode_params +@param tbl Table with the values +@return String containing encoded values +@see urldecode_params +]] + +---[[ +Creates a ltn12 source from the given socket. The source will return it's + +data line by line with the trailing \r\n stripped of. +@class function +@name header_source +@param sock Readable network socket +@return Ltn12 source function +]] + +---[[ +Decode a mime encoded http message body with multipart/form-data + +Content-Type. Stores all extracted data associated with its parameter name +in the params table withing the given message object. Multiple parameter +values are stored as tables, ordinary ones as strings. +If an optional file callback function is given then it is feeded with the +file contents chunk by chunk and only the extracted file name is stored +within the params table. The callback function will be called subsequently +with three arguments: + o Table containing decoded (name, file) and raw (headers) mime header data + o String value containing a chunk of the file data + o Boolean which indicates wheather the current chunk is the last one (eof) +@class function +@name mimedecode_message_body +@param src Ltn12 source function +@param msg HTTP message object +@param filecb File callback function (optional) +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Decode an urlencoded http message body with application/x-www-urlencoded + +Content-Type. Stores all extracted data associated with its parameter name +in the params table withing the given message object. Multiple parameter +values are stored as tables, ordinary ones as strings. +@class function +@name urldecode_message_body +@param src Ltn12 source function +@param msg HTTP message object +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Try to extract an http message header including information like protocol + +version, message headers and resulting CGI environment variables from the +given ltn12 source. +@class function +@name parse_message_header +@param src Ltn12 source function +@return HTTP message object +@see parse_message_body +]] + +---[[ +Try to extract and decode a http message body from the given ltn12 source. + +This function will examine the Content-Type within the given message object +to select the appropriate content decoder. +Currently the application/x-www-urlencoded and application/form-data +mime types are supported. If the encountered content encoding can't be +handled then the whole message body will be stored unaltered as "content" +property within the given message object. +@class function +@name parse_message_body +@param src Ltn12 source function +@param msg HTTP message object +@param filecb File data callback (optional, see mimedecode_message_body()) +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Table containing human readable messages for several http status codes. + +@class table +]] + diff --git a/modules/luci-base/luasrc/http/protocol/conditionals.lua b/modules/luci-base/luasrc/http/protocol/conditionals.lua index 1d40425ff..d31a4e38a 100644 --- a/modules/luci-base/luasrc/http/protocol/conditionals.lua +++ b/modules/luci-base/luasrc/http/protocol/conditionals.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - HTTP/1.1 bits. -- This class provides basic ETag handling and implements most of the -- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . module("luci.http.protocol.conditionals", package.seeall) @@ -9,22 +8,14 @@ module("luci.http.protocol.conditionals", package.seeall) local date = require("luci.http.protocol.date") ---- Implement 14.19 / ETag. --- @param stat A file.stat structure --- @return String containing the generated tag suitable for ETag headers function mk_etag( stat ) if stat ~= nil then return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime ) end end ---- 14.24 / If-Match -- Test whether the given message object contains an "If-Match" header and -- compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_match( req, stat ) local h = req.headers local etag = mk_etag( stat ) @@ -43,14 +34,8 @@ function if_match( req, stat ) return true end ---- 14.25 / If-Modified-Since -- Test whether the given message object contains an "If-Modified-Since" header -- and compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed --- @return Table containing extra HTTP headers if the precondition failed function if_modified_since( req, stat ) local h = req.headers @@ -72,14 +57,8 @@ function if_modified_since( req, stat ) return true end ---- 14.26 / If-None-Match -- Test whether the given message object contains an "If-None-Match" header and -- compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed --- @return Table containing extra HTTP headers if the precondition failed function if_none_match( req, stat ) local h = req.headers local etag = mk_etag( stat ) @@ -105,26 +84,16 @@ function if_none_match( req, stat ) return true end ---- 14.27 / If-Range -- The If-Range header is currently not implemented due to the lack of general -- byte range stuff in luci.http.protocol . This function will always return -- false, 412 to indicate a failed precondition. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_range( req, stat ) -- Sorry, no subranges (yet) return false, 412 end ---- 14.28 / If-Unmodified-Since -- Test whether the given message object contains an "If-Unmodified-Since" -- header and compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_unmodified_since( req, stat ) local h = req.headers diff --git a/modules/luci-base/luasrc/http/protocol/conditionals.luadoc b/modules/luci-base/luasrc/http/protocol/conditionals.luadoc new file mode 100644 index 000000000..9cfe02dd5 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/conditionals.luadoc @@ -0,0 +1,85 @@ +---[[ +LuCI http protocol implementation - HTTP/1.1 bits. + +This class provides basic ETag handling and implements most of the +conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . +]] +module "luci.http.protocol.conditionals" + +---[[ +Implement 14.19 / ETag. + +@class function +@name mk_etag +@param stat A file.stat structure +@return String containing the generated tag suitable for ETag headers +]] + +---[[ +14.24 / If-Match + +Test whether the given message object contains an "If-Match" header and +compare it against the given stat object. +@class function +@name if_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.25 / If-Modified-Since + +Test whether the given message object contains an "If-Modified-Since" header +and compare it against the given stat object. +@class function +@name if_modified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.26 / If-None-Match + +Test whether the given message object contains an "If-None-Match" header and +compare it against the given stat object. +@class function +@name if_none_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.27 / If-Range + +The If-Range header is currently not implemented due to the lack of general +byte range stuff in luci.http.protocol . This function will always return +false, 412 to indicate a failed precondition. +@class function +@name if_range +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.28 / If-Unmodified-Since + +Test whether the given message object contains an "If-Unmodified-Since" +header and compare it against the given stat object. +@class function +@name if_unmodified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + diff --git a/modules/luci-base/luasrc/http/protocol/date.lua b/modules/luci-base/luasrc/http/protocol/date.lua index 3105f37f6..e440219a9 100644 --- a/modules/luci-base/luasrc/http/protocol/date.lua +++ b/modules/luci-base/luasrc/http/protocol/date.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - date helper class. -- This class contains functions to parse, compare and format http dates. module("luci.http.protocol.date", package.seeall) @@ -13,9 +12,6 @@ MONTHS = { "Sep", "Oct", "Nov", "Dec" } ---- Return the time offset in seconds between the UTC and given time zone. --- @param tz Symbolic or numeric timezone specifier --- @return Time offset to UTC in seconds function tz_offset(tz) if type(tz) == "string" then @@ -39,9 +35,6 @@ function tz_offset(tz) return 0 end ---- Parse given HTTP date string and convert it to unix epoch time. --- @param data String containing the date --- @return Unix epoch time function to_unix(date) local wd, day, mon, yr, hr, min, sec, tz = date:match( @@ -75,19 +68,10 @@ function to_unix(date) return 0 end ---- Convert the given unix epoch time to valid HTTP date string. --- @param time Unix epoch time --- @return String containing the formatted date function to_http(time) return os.date( "%a, %d %b %Y %H:%M:%S GMT", time ) end ---- Compare two dates which can either be unix epoch times or HTTP date strings. --- @param d1 The first date or epoch time to compare --- @param d2 The first date or epoch time to compare --- @return -1 - if d1 is lower then d2 --- @return 0 - if both dates are equal --- @return 1 - if d1 is higher then d2 function compare(d1, d2) if d1:match("[^0-9]") then d1 = to_unix(d1) end diff --git a/modules/luci-base/luasrc/http/protocol/date.luadoc b/modules/luci-base/luasrc/http/protocol/date.luadoc new file mode 100644 index 000000000..d6f1c8d65 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/date.luadoc @@ -0,0 +1,46 @@ +---[[ +LuCI http protocol implementation - date helper class. + +This class contains functions to parse, compare and format http dates. +]] +module "luci.http.protocol.date" + +---[[ +Return the time offset in seconds between the UTC and given time zone. + +@class function +@name tz_offset +@param tz Symbolic or numeric timezone specifier +@return Time offset to UTC in seconds +]] + +---[[ +Parse given HTTP date string and convert it to unix epoch time. + +@class function +@name to_unix +@param data String containing the date +@return Unix epoch time +]] + +---[[ +Convert the given unix epoch time to valid HTTP date string. + +@class function +@name to_http +@param time Unix epoch time +@return String containing the formatted date +]] + +---[[ +Compare two dates which can either be unix epoch times or HTTP date strings. + +@class function +@name compare +@param d1 The first date or epoch time to compare +@param d2 The first date or epoch time to compare +@return -1 - if d1 is lower then d2 +@return 0 - if both dates are equal +@return 1 - if d1 is higher then d2 +]] + diff --git a/modules/luci-base/luasrc/http/protocol/mime.lua b/modules/luci-base/luasrc/http/protocol/mime.lua index 15da15cf8..2b99d8e74 100644 --- a/modules/luci-base/luasrc/http/protocol/mime.lua +++ b/modules/luci-base/luasrc/http/protocol/mime.lua @@ -1,15 +1,12 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - mime helper class. -- This class provides functions to guess mime types from file extensions and -- vice versa. module("luci.http.protocol.mime", package.seeall) require("luci.util") ---- MIME mapping table containg extension - mimetype relations. --- @class table MIME_TYPES = { ["txt"] = "text/plain"; ["js"] = "text/javascript"; @@ -54,10 +51,7 @@ MIME_TYPES = { ["avi"] = "video/x-msvideo"; } ---- Extract extension from a filename and return corresponding mime-type or -- "application/octet-stream" if the extension is unknown. --- @param filename The filename for which the mime type is guessed --- @return String containign the determined mime type function to_mime(filename) if type(filename) == "string" then local ext = filename:match("[^%.]+$") @@ -70,10 +64,7 @@ function to_mime(filename) return "application/octet-stream" end ---- Return corresponding extension for a given mime type or nil if the -- given mime-type is unknown. --- @param mimetype The mimetype to retrieve the extension from --- @return String with the extension or nil for unknown type function to_ext(mimetype) if type(mimetype) == "string" then for ext, type in luci.util.kspairs( MIME_TYPES ) do diff --git a/modules/luci-base/luasrc/http/protocol/mime.luadoc b/modules/luci-base/luasrc/http/protocol/mime.luadoc new file mode 100644 index 000000000..195b5fcc8 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/mime.luadoc @@ -0,0 +1,34 @@ +---[[ +LuCI http protocol implementation - mime helper class. + +This class provides functions to guess mime types from file extensions and +vice versa. +]] +module "luci.http.protocol.mime" + +---[[ +MIME mapping table containg extension - mimetype relations. + +@class table +]] + +---[[ +Extract extension from a filename and return corresponding mime-type or + +"application/octet-stream" if the extension is unknown. +@class function +@name to_mime +@param filename The filename for which the mime type is guessed +@return String containign the determined mime type +]] + +---[[ +Return corresponding extension for a given mime type or nil if the + +given mime-type is unknown. +@class function +@name to_ext +@param mimetype The mimetype to retrieve the extension from +@return String with the extension or nil for unknown type +]] + diff --git a/modules/luci-base/luasrc/i18n.lua b/modules/luci-base/luasrc/i18n.lua index dd84a59f8..bcb16d5c0 100644 --- a/modules/luci-base/luasrc/i18n.lua +++ b/modules/luci-base/luasrc/i18n.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Licensed to the public under the Apache License 2.0. ---- LuCI translation library. module("luci.i18n", package.seeall) require("luci.util") @@ -13,27 +12,16 @@ loaded = {} context = luci.util.threadlocal() default = "en" ---- Clear the translation table. function clear() end ---- Load a translation and copy its data into the translation table. --- @param file Language file --- @param lang Two-letter language code --- @param force Force reload even if already loaded (optional) --- @return Success status function load(file, lang, force) end ---- Load a translation file using the default translation language. -- Alternatively load the translation of the fallback language. --- @param file Language file --- @param force Force reload even if already loaded (optional) function loadc(file, force) end ---- Set the context default translation language. --- @param lang Two-letter language code function setlanguage(lang) context.lang = lang:gsub("_", "-") context.parent = (context.lang:match("^([a-z][a-z])_")) @@ -46,36 +34,22 @@ function setlanguage(lang) return context.lang end ---- Return the translated value for a specific translation key. --- @param key Default translation text --- @return Translated string function translate(key) return tparser.translate(key) or key end ---- Return the translated value for a specific translation key and use it as sprintf pattern. --- @param key Default translation text --- @param ... Format parameters --- @return Translated and formatted string function translatef(key, ...) return tostring(translate(key)):format(...) end ---- Return the translated value for a specific translation key -- and ensure that the returned value is a Lua string value. -- This is the same as calling <code>tostring(translate(...))</code> --- @param key Default translation text --- @return Translated string function string(key) return tostring(translate(key)) end ---- Return the translated value for a specific translation key and use it as sprintf pattern. -- Ensure that the returned value is a Lua string value. -- This is the same as calling <code>tostring(translatef(...))</code> --- @param key Default translation text --- @param ... Format parameters --- @return Translated and formatted string function stringf(key, ...) return tostring(translate(key)):format(...) end diff --git a/modules/luci-base/luasrc/i18n.luadoc b/modules/luci-base/luasrc/i18n.luadoc new file mode 100644 index 000000000..aa38841e1 --- /dev/null +++ b/modules/luci-base/luasrc/i18n.luadoc @@ -0,0 +1,84 @@ +---[[ +LuCI translation library. +]] +module "luci.i18n" + +---[[ +Clear the translation table. + + +@class function +@name clear +]] + +---[[ +Load a translation and copy its data into the translation table. + +@class function +@name load +@param file Language file +@param lang Two-letter language code +@param force Force reload even if already loaded (optional) +@return Success status +]] + +---[[ +Load a translation file using the default translation language. + +Alternatively load the translation of the fallback language. +@class function +@name loadc +@param file Language file +@param force Force reload even if already loaded (optional) +]] + +---[[ +Set the context default translation language. + +@class function +@name setlanguage +@param lang Two-letter language code +]] + +---[[ +Return the translated value for a specific translation key. + +@class function +@name translate +@param key Default translation text +@return Translated string +]] + +---[[ +Return the translated value for a specific translation key and use it as sprintf pattern. + +@class function +@name translatef +@param key Default translation text +@param ... Format parameters +@return Translated and formatted string +]] + +---[[ +Return the translated value for a specific translation key + +and ensure that the returned value is a Lua string value. +This is the same as calling <code>tostring(translate(...))</code> +@class function +@name string +@param key Default translation text +@return Translated string +]] + +---[[ +Return the translated value for a specific translation key and use it as sprintf pattern. + +Ensure that the returned value is a Lua string value. +This is the same as calling <code>tostring(translatef(...))</code> +@class function +@name stringf +@param key Default translation text +@param ... Format parameters +@return Translated and formatted string +]] + diff --git a/modules/luci-base/luasrc/ip.lua b/modules/luci-base/luasrc/ip.lua deleted file mode 100644 index d8aaea91d..000000000 --- a/modules/luci-base/luasrc/ip.lua +++ /dev/null @@ -1,661 +0,0 @@ --- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> --- Copyright 2008 Steven Barth <steven@midlink.org> --- Licensed to the public under the Apache License 2.0. - ---- LuCI IP calculation library. -module( "luci.ip", package.seeall ) - -require "nixio" -local bit = nixio.bit -local util = require "luci.util" - ---- Boolean; true if system is little endian -LITTLE_ENDIAN = not util.bigendian() - ---- Boolean; true if system is big endian -BIG_ENDIAN = not LITTLE_ENDIAN - ---- Specifier for IPv4 address family -FAMILY_INET4 = 0x04 - ---- Specifier for IPv6 address family -FAMILY_INET6 = 0x06 - - -local function __bless(x) - return setmetatable( x, { - __index = luci.ip.cidr, - __add = luci.ip.cidr.add, - __sub = luci.ip.cidr.sub, - __lt = luci.ip.cidr.lower, - __eq = luci.ip.cidr.equal, - __le = - function(...) - return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...) - end - } ) -end - -local function __array16( x, family ) - local list - - if type(x) == "number" then - list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) } - - elseif type(x) == "string" then - if x:find(":") then x = IPv6(x) else x = IPv4(x) end - if x then - assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" ) - list = { unpack(x[2]) } - end - - elseif type(x) == "table" and type(x[2]) == "table" then - assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" ) - list = { unpack(x[2]) } - - elseif type(x) == "table" then - list = { unpack(x) } - end - - assert( list, "Invalid operand" ) - - return list -end - -local function __mask16(bits) - return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 ) -end - -local function __not16(bits) - return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF ) -end - -local function __maxlen(family) - return ( family == FAMILY_INET4 ) and 32 or 128 -end - -local function __sublen(family) - return ( family == FAMILY_INET4 ) and 30 or 127 -end - - ---- Convert given short value to network byte order on little endian hosts --- @param x Unsigned integer value between 0x0000 and 0xFFFF --- @return Byte-swapped value --- @see htonl --- @see ntohs -function htons(x) - if LITTLE_ENDIAN then - return bit.bor( - bit.rshift( x, 8 ), - bit.band( bit.lshift( x, 8 ), 0xFF00 ) - ) - else - return x - end -end - ---- Convert given long value to network byte order on little endian hosts --- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF --- @return Byte-swapped value --- @see htons --- @see ntohl -function htonl(x) - if LITTLE_ENDIAN then - return bit.bor( - bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ), - htons( bit.rshift( x, 16 ) ) - ) - else - return x - end -end - ---- Convert given short value to host byte order on little endian hosts --- @class function --- @name ntohs --- @param x Unsigned integer value between 0x0000 and 0xFFFF --- @return Byte-swapped value --- @see htonl --- @see ntohs -ntohs = htons - ---- Convert given short value to host byte order on little endian hosts --- @class function --- @name ntohl --- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF --- @return Byte-swapped value --- @see htons --- @see ntohl -ntohl = htonl - - ---- Parse given IPv4 address in dotted quad or CIDR notation. If an optional --- netmask is given as second argument and the IP address is encoded in CIDR --- notation then the netmask parameter takes precedence. If neither a CIDR --- encoded prefix nor a netmask parameter is given, then a prefix length of --- 32 bit is assumed. --- @param address IPv4 address in dotted quad or CIDR notation --- @param netmask IPv4 netmask in dotted quad notation (optional) --- @return luci.ip.cidr instance or nil if given address was invalid --- @see IPv6 --- @see Hex -function IPv4(address, netmask) - address = address or "0.0.0.0/0" - - local obj = __bless({ FAMILY_INET4 }) - - local data = {} - local prefix = address:match("/(.+)") - address = address:gsub("/.+","") - address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "") - - if netmask then - prefix = obj:prefix(netmask) - elseif prefix then - prefix = tonumber(prefix) - if not prefix or prefix < 0 or prefix > 32 then return nil end - else - prefix = 32 - end - - local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$") - - b1 = tonumber(b1) - b2 = tonumber(b2) - b3 = tonumber(b3) - b4 = tonumber(b4) - - if b1 and b1 <= 255 and - b2 and b2 <= 255 and - b3 and b3 <= 255 and - b4 and b4 <= 255 and - prefix - then - table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 }) - table.insert(obj, prefix) - return obj - end -end - ---- Parse given IPv6 address in full, compressed, mixed or CIDR notation. --- If an optional netmask is given as second argument and the IP address is --- encoded in CIDR notation then the netmask parameter takes precedence. --- If neither a CIDR encoded prefix nor a netmask parameter is given, then a --- prefix length of 128 bit is assumed. --- @param address IPv6 address in full/compressed/mixed or CIDR notation --- @param netmask IPv6 netmask in full/compressed/mixed notation (optional) --- @return luci.ip.cidr instance or nil if given address was invalid --- @see IPv4 --- @see Hex -function IPv6(address, netmask) - address = address or "::/0" - - local obj = __bless({ FAMILY_INET6 }) - - local data = {} - local prefix = address:match("/(.+)") - address = address:gsub("/.+","") - address = address:gsub("^%[(.*)%]$", "%1") - - if netmask then - prefix = obj:prefix(netmask) - elseif prefix then - prefix = tonumber(prefix) - if not prefix or prefix < 0 or prefix > 128 then return nil end - else - prefix = 128 - end - - local borderl = address:sub(1, 1) == ":" and 2 or 1 - local borderh, zeroh, chunk, block, i - - if #address > 45 then return nil end - - repeat - borderh = address:find(":", borderl, true) - if not borderh then break end - - block = tonumber(address:sub(borderl, borderh - 1), 16) - if block and block <= 0xFFFF then - data[#data+1] = block - else - if zeroh or borderh - borderl > 1 then return nil end - zeroh = #data + 1 - end - - borderl = borderh + 1 - until #data == 7 - - chunk = address:sub(borderl) - if #chunk > 0 and #chunk <= 4 then - block = tonumber(chunk, 16) - if not block or block > 0xFFFF then return nil end - - data[#data+1] = block - elseif #chunk > 4 then - if #data == 7 or #chunk > 15 then return nil end - borderl = 1 - for i=1, 4 do - borderh = chunk:find(".", borderl, true) - if not borderh and i < 4 then return nil end - borderh = borderh and borderh - 1 - - block = tonumber(chunk:sub(borderl, borderh)) - if not block or block > 255 then return nil end - - if i == 1 or i == 3 then - data[#data+1] = block * 256 - else - data[#data] = data[#data] + block - end - - borderl = borderh and borderh + 2 - end - end - - if zeroh then - if #data == 8 then return nil end - while #data < 8 do - table.insert(data, zeroh, 0) - end - end - - if #data == 8 and prefix then - table.insert(obj, data) - table.insert(obj, prefix) - return obj - end -end - ---- Transform given hex-encoded value to luci.ip.cidr instance of specified --- address family. --- @param hex String containing hex encoded value --- @param prefix Prefix length of CIDR instance (optional, default is 32/128) --- @param family Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6 --- @param swap Bool indicating whether to swap byteorder on low endian host --- @return luci.ip.cidr instance or nil if given value was invalid --- @see IPv4 --- @see IPv6 -function Hex( hex, prefix, family, swap ) - family = ( family ~= nil ) and family or FAMILY_INET4 - swap = ( swap == nil ) and true or swap - prefix = prefix or __maxlen(family) - - local len = __maxlen(family) - local tmp = "" - local data = { } - local i - - for i = 1, (len/4) - #hex do tmp = tmp .. '0' end - - if swap and LITTLE_ENDIAN then - for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end - else - tmp = tmp .. hex - end - - hex = tmp - - for i = 1, ( len / 4 ), 4 do - local n = tonumber( hex:sub( i, i+3 ), 16 ) - if n then - data[#data+1] = n - else - return nil - end - end - - return __bless({ family, data, prefix }) -end - - ---- LuCI IP Library / CIDR instances --- @class module --- @cstyle instance --- @name luci.ip.cidr -cidr = util.class() - ---- Test whether the instance is a IPv4 address. --- @return Boolean indicating a IPv4 address type --- @see cidr.is6 -function cidr.is4( self ) - return self[1] == FAMILY_INET4 -end - ---- Test whether this instance is an IPv4 RFC1918 private address --- @return Boolean indicating whether this instance is an RFC1918 address -function cidr.is4rfc1918( self ) - if self[1] == FAMILY_INET4 then - return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or - ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or - (self[2][1] == 0xC0A8) - end - return false -end - ---- Test whether this instance is an IPv4 link-local address (Zeroconf) --- @return Boolean indicating whether this instance is IPv4 link-local -function cidr.is4linklocal( self ) - if self[1] == FAMILY_INET4 then - return (self[2][1] == 0xA9FE) - end - return false -end - ---- Test whether the instance is a IPv6 address. --- @return Boolean indicating a IPv6 address type --- @see cidr.is4 -function cidr.is6( self ) - return self[1] == FAMILY_INET6 -end - ---- Test whether this instance is an IPv6 link-local address --- @return Boolean indicating whether this instance is IPv6 link-local -function cidr.is6linklocal( self ) - if self[1] == FAMILY_INET6 then - return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF) - end - return false -end - ---- Return a corresponding string representation of the instance. --- If the prefix length is lower then the maximum possible prefix length for the --- corresponding address type then the address is returned in CIDR notation, --- otherwise the prefix will be left out. -function cidr.string( self ) - local str - if self:is4() then - str = string.format( - "%d.%d.%d.%d", - bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF), - bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF) - ) - if self[3] < 32 then - str = str .. "/" .. self[3] - end - elseif self:is6() then - str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) ) - if self[3] < 128 then - str = str .. "/" .. self[3] - end - end - return str -end - ---- Test whether the value of the instance is lower then the given address. --- This function will throw an exception if the given address has a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is lower --- @see cidr.higher --- @see cidr.equal -function cidr.lower( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return self[2][i] < addr[2][i] - end - end - return false -end - ---- Test whether the value of the instance is higher then the given address. --- This function will throw an exception if the given address has a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is higher --- @see cidr.lower --- @see cidr.equal -function cidr.higher( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return self[2][i] > addr[2][i] - end - end - return false -end - ---- Test whether the value of the instance is equal to the given address. --- This function will throw an exception if the given address is a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is equal --- @see cidr.lower --- @see cidr.higher -function cidr.equal( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return false - end - end - return true -end - ---- Return the prefix length of this CIDR instance. --- @param mask Override instance prefix with given netmask (optional) --- @return Prefix length in bit -function cidr.prefix( self, mask ) - local prefix = self[3] - - if mask then - prefix = 0 - - local stop = false - local obj = type(mask) ~= "table" - and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask - - if not obj then return nil end - - local _, word - for _, word in ipairs(obj[2]) do - if word == 0xFFFF then - prefix = prefix + 16 - else - local bitmask = bit.lshift(1, 15) - while bit.band(word, bitmask) == bitmask do - prefix = prefix + 1 - bitmask = bit.lshift(1, 15 - (prefix % 16)) - end - - break - end - end - end - - return prefix -end - ---- Return a corresponding CIDR representing the network address of this --- instance. --- @param bits Override prefix length of this instance (optional) --- @return CIDR instance containing the network address --- @see cidr.host --- @see cidr.broadcast --- @see cidr.mask -function cidr.network( self, bits ) - local data = { } - bits = bits or self[3] - - local i - for i = 1, math.floor( bits / 16 ) do - data[#data+1] = self[2][i] - end - - if #data < #self[2] then - data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) ) - - for i = #data + 1, #self[2] do - data[#data+1] = 0 - end - end - - return __bless({ self[1], data, __maxlen(self[1]) }) -end - ---- Return a corresponding CIDR representing the host address of this --- instance. This is intended to extract the host address from larger subnet. --- @return CIDR instance containing the network address --- @see cidr.network --- @see cidr.broadcast --- @see cidr.mask -function cidr.host( self ) - return __bless({ self[1], self[2], __maxlen(self[1]) }) -end - ---- Return a corresponding CIDR representing the netmask of this instance. --- @param bits Override prefix length of this instance (optional) --- @return CIDR instance containing the netmask --- @see cidr.network --- @see cidr.host --- @see cidr.broadcast -function cidr.mask( self, bits ) - local data = { } - bits = bits or self[3] - - for i = 1, math.floor( bits / 16 ) do - data[#data+1] = 0xFFFF - end - - if #data < #self[2] then - data[#data+1] = __mask16(bits) - - for i = #data + 1, #self[2] do - data[#data+1] = 0 - end - end - - return __bless({ self[1], data, __maxlen(self[1]) }) -end - ---- Return CIDR containing the broadcast address of this instance. --- @return CIDR instance containing the netmask, always nil for IPv6 --- @see cidr.network --- @see cidr.host --- @see cidr.mask -function cidr.broadcast( self ) - -- IPv6 has no broadcast addresses (XXX: assert() instead?) - if self[1] == FAMILY_INET4 then - local data = { unpack(self[2]) } - local offset = math.floor( self[3] / 16 ) + 1 - - if offset <= #data then - data[offset] = bit.bor( data[offset], __not16(self[3]) ) - for i = offset + 1, #data do data[i] = 0xFFFF end - - return __bless({ self[1], data, __maxlen(self[1]) }) - end - end -end - ---- Test whether this instance fully contains the given CIDR instance. --- @param addr CIDR instance to test against --- @return Boolean indicating whether this instance contains the given CIDR -function cidr.contains( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - - if self:prefix() <= addr:prefix() then - return self:network() == addr:network(self:prefix()) - end - - return false -end - ---- Add specified amount of hosts to this instance. --- @param amount Number of hosts to add to this instance --- @param inplace Boolen indicating whether to alter values inplace (optional) --- @return CIDR representing the new address or nil on overflow error --- @see cidr.sub -function cidr.add( self, amount, inplace ) - local pos - local data = { unpack(self[2]) } - local shorts = __array16( amount, self[1] ) - - for pos = #data, 1, -1 do - local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 - if ( data[pos] + add ) > 0xFFFF then - data[pos] = ( data[pos] + add ) % 0xFFFF - if pos > 1 then - data[pos-1] = data[pos-1] + ( add - data[pos] ) - else - return nil - end - else - data[pos] = data[pos] + add - end - end - - if inplace then - self[2] = data - return self - else - return __bless({ self[1], data, self[3] }) - end -end - ---- Substract specified amount of hosts from this instance. --- @param amount Number of hosts to substract from this instance --- @param inplace Boolen indicating whether to alter values inplace (optional) --- @return CIDR representing the new address or nil on underflow error --- @see cidr.add -function cidr.sub( self, amount, inplace ) - local pos - local data = { unpack(self[2]) } - local shorts = __array16( amount, self[1] ) - - for pos = #data, 1, -1 do - local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 - if ( data[pos] - sub ) < 0 then - data[pos] = ( sub - data[pos] ) % 0xFFFF - if pos > 1 then - data[pos-1] = data[pos-1] - ( sub + data[pos] ) - else - return nil - end - else - data[pos] = data[pos] - sub - end - end - - if inplace then - self[2] = data - return self - else - return __bless({ self[1], data, self[3] }) - end -end - ---- Return CIDR containing the lowest available host address within this subnet. --- @return CIDR containing the host address, nil if subnet is too small --- @see cidr.maxhost -function cidr.minhost( self ) - if self[3] <= __sublen(self[1]) then - -- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6 - return self:network():add(1, true) - end -end - ---- Return CIDR containing the highest available host address within the subnet. --- @return CIDR containing the host address, nil if subnet is too small --- @see cidr.minhost -function cidr.maxhost( self ) - if self[3] <= __sublen(self[1]) then - local i - local data = { unpack(self[2]) } - local offset = math.floor( self[3] / 16 ) + 1 - - data[offset] = bit.bor( data[offset], __not16(self[3]) ) - for i = offset + 1, #data do data[i] = 0xFFFF end - data = __bless({ self[1], data, __maxlen(self[1]) }) - - -- Last address in reserved for Broadcast Address in IPv4 - if data[1] == FAMILY_INET4 then data:sub(1, true) end - - return data - end -end diff --git a/modules/luci-base/luasrc/ltn12.lua b/modules/luci-base/luasrc/ltn12.lua index b59fb8c48..3a7268cca 100644 --- a/modules/luci-base/luasrc/ltn12.lua +++ b/modules/luci-base/luasrc/ltn12.lua @@ -39,7 +39,6 @@ local string = require("string") local table = require("table") local base = _G ---- Diego Nehab's LTN12 - Filters, sources, sinks and pumps. -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts module("luci.ltn12") @@ -56,16 +55,8 @@ _VERSION = "LTN12 1.0.1" -- Filter stuff ----------------------------------------------------------------------------- ---- LTN12 Filter constructors --- @class module --- @name luci.ltn12.filter ---- Return a high level filter that cycles a low-level filter -- by passing it each chunk and updating a context between calls. --- @param low Low-level filter --- @param ctx Context --- @param extra Extra argument passed to the low-level filter --- @return LTN12 filter function filter.cycle(low, ctx, extra) base.assert(low) return function(chunk) @@ -75,10 +66,7 @@ function filter.cycle(low, ctx, extra) end end ---- Chain a bunch of filters together. -- (thanks to Wim Couwenberg) --- @param ... filters to be chained --- @return LTN12 filter function filter.chain(...) local n = table.getn(arg) local top, index = 1, 1 @@ -112,34 +100,22 @@ end -- Source stuff ----------------------------------------------------------------------------- ---- LTN12 Source constructors --- @class module --- @name luci.ltn12.source -- create an empty source local function empty() return nil end ---- Create an empty source. --- @return LTN12 source function source.empty() return empty end ---- Return a source that just outputs an error. --- @param err Error object --- @return LTN12 source function source.error(err) return function() return nil, err end end ---- Create a file source. --- @param handle File handle ready for reading --- @param io_err IO error object --- @return LTN12 source function source.file(handle, io_err) if handle then return function() @@ -151,9 +127,6 @@ function source.file(handle, io_err) else return source.error(io_err or "unable to open file") end end ---- Turn a fancy source into a simple source. --- @param src fancy source --- @return LTN12 source function source.simplify(src) base.assert(src) return function() @@ -164,9 +137,6 @@ function source.simplify(src) end end ---- Create a string source. --- @param s Data --- @return LTN12 source function source.string(s) if s then local i = 1 @@ -179,9 +149,6 @@ function source.string(s) else return source.empty() end end ---- Creates rewindable source. --- @param src LTN12 source to be made rewindable --- @return LTN12 source function source.rewind(src) base.assert(src) local t = {} @@ -196,10 +163,6 @@ function source.rewind(src) end end ---- Chain a source and a filter together. --- @param src LTN12 source --- @param f LTN12 filter --- @return LTN12 source function source.chain(src, f) base.assert(src and f) local last_in, last_out = "", "" @@ -247,11 +210,8 @@ function source.chain(src, f) end end ---- Create a source that produces contents of several sources. -- Sources will be used one after the other, as if they were concatenated -- (thanks to Wim Couwenberg) --- @param ... LTN12 sources --- @return LTN12 source function source.cat(...) local src = table.remove(arg, 1) return function() @@ -268,13 +228,7 @@ end -- Sink stuff ----------------------------------------------------------------------------- ---- LTN12 sink constructors --- @class module --- @name luci.ltn12.sink ---- Create a sink that stores into a table. --- @param t output table to store into --- @return LTN12 sink function sink.table(t) t = t or {} local f = function(chunk, err) @@ -284,9 +238,6 @@ function sink.table(t) return f, t end ---- Turn a fancy sink into a simple sink. --- @param snk fancy sink --- @return LTN12 sink function sink.simplify(snk) base.assert(snk) return function(chunk, err) @@ -297,10 +248,6 @@ function sink.simplify(snk) end end ---- Create a file sink. --- @param handle file handle to write to --- @param io_err IO error --- @return LTN12 sink function sink.file(handle, io_err) if handle then return function(chunk, err) @@ -317,25 +264,16 @@ local function null() return 1 end ---- Create a sink that discards data. --- @return LTN12 sink function sink.null() return null end ---- Create a sink that just returns an error. --- @param err Error object --- @return LTN12 sink function sink.error(err) return function() return nil, err end end ---- Chain a sink with a filter. --- @param f LTN12 filter --- @param snk LTN12 sink --- @return LTN12 sink function sink.chain(f, snk) base.assert(f and snk) return function(chunk, err) @@ -356,15 +294,7 @@ end -- Pump stuff ----------------------------------------------------------------------------- ---- LTN12 pump functions --- @class module --- @name luci.ltn12.pump ---- Pump one chunk from the source to the sink. --- @param src LTN12 source --- @param snk LTN12 sink --- @return Chunk of data or nil if an error occured --- @return Error object function pump.step(src, snk) local chunk, src_err = src() local ret, snk_err = snk(chunk, src_err) @@ -372,12 +302,6 @@ function pump.step(src, snk) else return nil, src_err or snk_err end end ---- Pump all data from a source to a sink, using a step function. --- @param src LTN12 source --- @param snk LTN12 sink --- @param step step function (optional) --- @return 1 if the operation succeeded otherwise nil --- @return Error object function pump.all(src, snk, step) base.assert(src and snk) step = step or pump.step diff --git a/modules/luci-base/luasrc/model/ipkg.lua b/modules/luci-base/luasrc/model/ipkg.lua index 216caa5cc..587637272 100644 --- a/modules/luci-base/luasrc/model/ipkg.lua +++ b/modules/luci-base/luasrc/model/ipkg.lua @@ -15,7 +15,6 @@ local table = table local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase" local icfg = "/etc/opkg.conf" ---- LuCI OPKG call abstraction library module "luci.model.ipkg" @@ -93,54 +92,31 @@ local function _lookup(act, pkg) end ---- Return information about installed and available packages. --- @param pkg Limit output to a (set of) packages --- @return Table containing package information function info(pkg) return _lookup("info", pkg) end ---- Return the package status of one or more packages. --- @param pkg Limit output to a (set of) packages --- @return Table containing package status information function status(pkg) return _lookup("status", pkg) end ---- Install one or more packages. --- @param ... List of packages to install --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function install(...) return _action("install", ...) end ---- Determine whether a given package is installed. --- @param pkg Package --- @return Boolean function installed(pkg) local p = status(pkg)[pkg] return (p and p.Status and p.Status.installed) end ---- Remove one or more packages. --- @param ... List of packages to install --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function remove(...) return _action("remove", ...) end ---- Update package lists. --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function update() return _action("update") end ---- Upgrades all installed packages. --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function upgrade() return _action("upgrade") end @@ -174,33 +150,19 @@ function _list(action, pat, cb) end end ---- List all packages known to opkg. --- @param pat Only find packages matching this pattern, nil lists all packages --- @param cb Callback function invoked for each package, receives name, version and description as arguments --- @return nothing function list_all(pat, cb) _list("list", pat, cb) end ---- List installed packages. --- @param pat Only find packages matching this pattern, nil lists all packages --- @param cb Callback function invoked for each package, receives name, version and description as arguments --- @return nothing function list_installed(pat, cb) _list("list_installed", pat, cb) end ---- Find packages that match the given pattern. --- @param pat Find packages whose names or descriptions match this pattern, nil results in zero results --- @param cb Callback function invoked for each patckage, receives name, version and description as arguments --- @return nothing function find(pat, cb) _list("find", pat, cb) end ---- Determines the overlay root used by opkg. --- @return String containing the directory path of the overlay root. function overlay_root() local od = "/" local fd = io.open(icfg, "r") diff --git a/modules/luci-base/luasrc/model/ipkg.luadoc b/modules/luci-base/luasrc/model/ipkg.luadoc new file mode 100644 index 000000000..cf0985f94 --- /dev/null +++ b/modules/luci-base/luasrc/model/ipkg.luadoc @@ -0,0 +1,109 @@ +---[[ +LuCI OPKG call abstraction library + +module "luci.model.ipkg" +]] + +---[[ +Return information about installed and available packages. + +@class function +@name info +@param pkg Limit output to a (set of) packages +@return Table containing package information +]] + +---[[ +Return the package status of one or more packages. + +@class function +@name status +@param pkg Limit output to a (set of) packages +@return Table containing package status information +]] + +---[[ +Install one or more packages. + +@class function +@name install +@param ... List of packages to install +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Determine whether a given package is installed. + +@class function +@name installed +@param pkg Package +@return Boolean +]] + +---[[ +Remove one or more packages. + +@class function +@name remove +@param ... List of packages to install +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Update package lists. + +@class function +@name update +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Upgrades all installed packages. + +@class function +@name upgrade +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +List all packages known to opkg. + +@class function +@name list_all +@param pat Only find packages matching this pattern, nil lists all packages +@param cb Callback function invoked for each package, receives name, version and description as arguments +@return nothing +]] + +---[[ +List installed packages. + +@class function +@name list_installed +@param pat Only find packages matching this pattern, nil lists all packages +@param cb Callback function invoked for each package, receives name, version and description as arguments +@return nothing +]] + +---[[ +Find packages that match the given pattern. + +@class function +@name find +@param pat Find packages whose names or descriptions match this pattern, nil results in zero results +@param cb Callback function invoked for each patckage, receives name, version and description as arguments +@return nothing +]] + +---[[ +Determines the overlay root used by opkg. + +@class function +@name overlay_root +@return String containing the directory path of the overlay root. +]] + diff --git a/modules/luci-base/luasrc/model/uci.lua b/modules/luci-base/luasrc/model/uci.lua index 8ac82773f..165913774 100644 --- a/modules/luci-base/luasrc/model/uci.lua +++ b/modules/luci-base/luasrc/model/uci.lua @@ -12,26 +12,18 @@ local require, getmetatable = require, getmetatable local error, pairs, ipairs = error, pairs, ipairs local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack ---- LuCI UCI model library. -- The typical workflow for UCI is: Get a cursor instance from the -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), -- save the changes to the staging area via Cursor.save and finally -- Cursor.commit the data to the actual config files. -- LuCI then needs to Cursor.apply the changes so deamons etc. are -- reloaded. --- @cstyle instance module "luci.model.uci" ---- Create a new UCI-Cursor. --- @class function --- @name cursor --- @return UCI-Cursor cursor = uci.cursor APIVERSION = uci.APIVERSION ---- Create a new Cursor initialized to the state directory. --- @return UCI cursor function cursor_state() return cursor(nil, "/var/state") end @@ -42,9 +34,6 @@ inst_state = cursor_state() local Cursor = getmetatable(inst) ---- Applies UCI configuration changes --- @param configlist List of UCI configurations --- @param command Don't apply only return the command function Cursor.apply(self, configlist, command) configlist = self:_affected(configlist) if command then @@ -56,10 +45,6 @@ function Cursor.apply(self, configlist, command) end ---- Delete all sections of a given type that match certain criteria. --- @param config UCI config --- @param type UCI section type --- @param comparator Function that will be called for each section and -- returns a boolean whether to delete the current section (optional) function Cursor.delete_all(self, config, stype, comparator) local del = {} @@ -90,12 +75,6 @@ function Cursor.delete_all(self, config, stype, comparator) end end ---- Create a new section and initialize it with data. --- @param config UCI config --- @param type UCI section type --- @param name UCI section name (optional) --- @param values Table of key - value pairs to initialize the section with --- @return Name of created section function Cursor.section(self, config, type, name, values) local stat = true if name then @@ -112,10 +91,6 @@ function Cursor.section(self, config, type, name, values) return stat and name end ---- Updated the data of a section using data from a table. --- @param config UCI config --- @param section UCI section name (optional) --- @param values Table of key - value pairs to update the section with function Cursor.tset(self, config, section, values) local stat = true for k, v in pairs(values) do @@ -126,21 +101,11 @@ function Cursor.tset(self, config, section, values) return stat end ---- Get a boolean option and return it's value as true or false. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return Boolean function Cursor.get_bool(self, ...) local val = self:get(...) return ( val == "1" or val == "true" or val == "yes" or val == "on" ) end ---- Get an option or list and return values as table. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return UCI value function Cursor.get_list(self, config, section, option) if config and section and option then local val = self:get(config, section, option) @@ -149,12 +114,6 @@ function Cursor.get_list(self, config, section, option) return nil end ---- Get the given option from the first section with the given type. --- @param config UCI config --- @param type UCI section type --- @param option UCI option (optional) --- @param default Default value (optional) --- @return UCI value function Cursor.get_first(self, conf, stype, opt, def) local rv = def @@ -178,12 +137,6 @@ function Cursor.get_first(self, conf, stype, opt, def) return rv end ---- Set given values as list. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @param value UCI value --- @return Boolean whether operation succeeded function Cursor.set_list(self, config, section, option, value) if config and section and option then return self:set( @@ -238,10 +191,8 @@ function Cursor._affected(self, configlist) return reloadlist end ---- Create a sub-state of this cursor. The sub-state is tied to the parent -- curser, means it the parent unloads or loads configs, the sub state will -- do so as well. --- @return UCI state cursor tied to the parent cursor function Cursor.substate(self) Cursor._substates = Cursor._substates or { } Cursor._substates[self] = Cursor._substates[self] or cursor_state() @@ -265,118 +216,18 @@ function Cursor.unload(self, ...) end ---- Add an anonymous section. --- @class function --- @name Cursor.add --- @param config UCI config --- @param type UCI section type --- @return Name of created section - ---- Get a table of saved but uncommitted changes. --- @class function --- @name Cursor.changes --- @param config UCI config --- @return Table of changes --- @see Cursor.save - ---- Commit saved changes. --- @class function --- @name Cursor.commit --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.revert --- @see Cursor.save - ---- Deletes a section or an option. --- @class function --- @name Cursor.delete --- @param config UCI config --- @param section UCI section name --- @param option UCI option (optional) --- @return Boolean whether operation succeeded - ---- Call a function for every section of a certain type. --- @class function --- @name Cursor.foreach --- @param config UCI config --- @param type UCI section type --- @param callback Function to be called --- @return Boolean whether operation succeeded - ---- Get a section type or an option --- @class function --- @name Cursor.get --- @param config UCI config --- @param section UCI section name --- @param option UCI option (optional) --- @return UCI value - ---- Get all sections of a config or all values of a section. --- @class function --- @name Cursor.get_all --- @param config UCI config --- @param section UCI section name (optional) --- @return Table of UCI sections or table of UCI values - ---- Manually load a config. --- @class function --- @name Cursor.load --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.save --- @see Cursor.unload - ---- Revert saved but uncommitted changes. --- @class function --- @name Cursor.revert --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.commit --- @see Cursor.save - ---- Saves changes made to a config to make them committable. --- @class function --- @name Cursor.save --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.load --- @see Cursor.unload - ---- Set a value or create a named section. --- @class function --- @name Cursor.set --- @param config UCI config --- @param section UCI section name --- @param option UCI option or UCI section type --- @param value UCI value or nil if you want to create a section --- @return Boolean whether operation succeeded - ---- Get the configuration directory. --- @class function --- @name Cursor.get_confdir --- @return Configuration directory - ---- Get the directory for uncomitted changes. --- @class function --- @name Cursor.get_savedir --- @return Save directory - ---- Set the configuration directory. --- @class function --- @name Cursor.set_confdir --- @param directory UCI configuration directory --- @return Boolean whether operation succeeded - ---- Set the directory for uncommited changes. --- @class function --- @name Cursor.set_savedir --- @param directory UCI changes directory --- @return Boolean whether operation succeeded - ---- Discard changes made to a config. --- @class function --- @name Cursor.unload --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.load --- @see Cursor.save + + + + + + + + + + + + + + + diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc new file mode 100644 index 000000000..1c208669d --- /dev/null +++ b/modules/luci-base/luasrc/model/uci.luadoc @@ -0,0 +1,291 @@ +---[[ +LuCI UCI model library. + +The typical workflow for UCI is: Get a cursor instance from the +cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), +save the changes to the staging area via Cursor.save and finally +Cursor.commit the data to the actual config files. +LuCI then needs to Cursor.apply the changes so deamons etc. are +reloaded. +@cstyle instance +module "luci.model.uci" +]] + +---[[ +Create a new UCI-Cursor. + +@class function +@name cursor +@return UCI-Cursor +]] + +---[[ +Create a new Cursor initialized to the state directory. + +@class function +@name cursor_state +@return UCI cursor +]] + +---[[ +Applies UCI configuration changes + +@class function +@name Cursor.apply +@param configlist List of UCI configurations +@param command Don't apply only return the command +]] + +---[[ +Delete all sections of a given type that match certain criteria. + +@class function +@name Cursor.delete_all +@param config UCI config +@param type UCI section type +@param comparator Function that will be called for each section and +returns a boolean whether to delete the current section (optional) +]] + +---[[ +Create a new section and initialize it with data. + +@class function +@name Cursor.section +@param config UCI config +@param type UCI section type +@param name UCI section name (optional) +@param values Table of key - value pairs to initialize the section with +@return Name of created section +]] + +---[[ +Updated the data of a section using data from a table. + +@class function +@name Cursor.tset +@param config UCI config +@param section UCI section name (optional) +@param values Table of key - value pairs to update the section with +]] + +---[[ +Get a boolean option and return it's value as true or false. + +@class function +@name Cursor.get_bool +@param config UCI config +@param section UCI section name +@param option UCI option +@return Boolean +]] + +---[[ +Get an option or list and return values as table. + +@class function +@name Cursor.get_list +@param config UCI config +@param section UCI section name +@param option UCI option +@return UCI value +]] + +---[[ +Get the given option from the first section with the given type. + +@class function +@name Cursor.get_first +@param config UCI config +@param type UCI section type +@param option UCI option (optional) +@param default Default value (optional) +@return UCI value +]] + +---[[ +Set given values as list. + +@class function +@name Cursor.set_list +@param config UCI config +@param section UCI section name +@param option UCI option +@param value UCI value +@return Boolean whether operation succeeded +]] + +---[[ +Create a sub-state of this cursor. The sub-state is tied to the parent + +curser, means it the parent unloads or loads configs, the sub state will +do so as well. +@class function +@name Cursor.substate +@return UCI state cursor tied to the parent cursor +]] + +---[[ +Add an anonymous section. + +@class function +@name Cursor.add +@param config UCI config +@param type UCI section type +@return Name of created section +]] + +---[[ +Get a table of saved but uncommitted changes. + +@class function +@name Cursor.changes +@param config UCI config +@return Table of changes +@see Cursor.save +]] + +---[[ +Commit saved changes. + +@class function +@name Cursor.commit +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.revert +@see Cursor.save +]] + +---[[ +Deletes a section or an option. + +@class function +@name Cursor.delete +@param config UCI config +@param section UCI section name +@param option UCI option (optional) +@return Boolean whether operation succeeded +]] + +---[[ +Call a function for every section of a certain type. + +@class function +@name Cursor.foreach +@param config UCI config +@param type UCI section type +@param callback Function to be called +@return Boolean whether operation succeeded +]] + +---[[ +Get a section type or an option + +@class function +@name Cursor.get +@param config UCI config +@param section UCI section name +@param option UCI option (optional) +@return UCI value +]] + +---[[ +Get all sections of a config or all values of a section. + +@class function +@name Cursor.get_all +@param config UCI config +@param section UCI section name (optional) +@return Table of UCI sections or table of UCI values +]] + +---[[ +Manually load a config. + +@class function +@name Cursor.load +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.save +@see Cursor.unload +]] + +---[[ +Revert saved but uncommitted changes. + +@class function +@name Cursor.revert +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.commit +@see Cursor.save +]] + +---[[ +Saves changes made to a config to make them committable. + +@class function +@name Cursor.save +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.load +@see Cursor.unload +]] + +---[[ +Set a value or create a named section. + +@class function +@name Cursor.set +@param config UCI config +@param section UCI section name +@param option UCI option or UCI section type +@param value UCI value or nil if you want to create a section +@return Boolean whether operation succeeded +]] + +---[[ +Get the configuration directory. + +@class function +@name Cursor.get_confdir +@return Configuration directory +]] + +---[[ +Get the directory for uncomitted changes. + +@class function +@name Cursor.get_savedir +@return Save directory +]] + +---[[ +Set the configuration directory. + +@class function +@name Cursor.set_confdir +@param directory UCI configuration directory +@return Boolean whether operation succeeded +]] + +---[[ +Set the directory for uncommited changes. + +@class function +@name Cursor.set_savedir +@param directory UCI changes directory +@return Boolean whether operation succeeded +]] + +---[[ +Discard changes made to a config. + +@class function +@name Cursor.unload +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.load +@see Cursor.save +]] + diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index 1e594e1c8..3977da3ed 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -16,27 +16,14 @@ local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select ---- LuCI Linux and POSIX system utilities. module "luci.sys" ---- Execute a given shell command and return the error code --- @class function --- @name call --- @param ... Command to call --- @return Error code of the command function call(...) return os.execute(...) / 256 end ---- Execute a given shell command and capture its standard output --- @class function --- @name exec --- @param command Command to call --- @return String containg the return the output of the command exec = luci.util.exec ---- Retrieve information about currently mounted file systems. --- @return Table containing mount information function mounts() local data = {} local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"} @@ -82,20 +69,11 @@ function mounts() return data end ---- Retrieve environment variables. If no variable is given then a table -- containing the whole environment is returned otherwise this function returns -- the corresponding string value for the given name or nil if no such variable -- exists. --- @class function --- @name getenv --- @param var Name of the environment variable to retrieve (optional) --- @return String containg the value of the specified variable --- @return Table containing all variables if no variable name is given getenv = nixio.getenv ---- Get or set the current hostname. --- @param String containing a new hostname to set (optional) --- @return String containing the system hostname function hostname(newname) if type(newname) == "string" and #newname > 0 then fs.writefile( "/proc/sys/kernel/hostname", newname ) @@ -105,11 +83,6 @@ function hostname(newname) end end ---- Returns the contents of a documented referred by an URL. --- @param url The URL to retrieve --- @param stream Return a stream instead of a buffer --- @param target Directly write to target file name --- @return String containing the contents of given the URL function httpget(url, stream, target) if not target then local source = stream and io.popen or luci.util.exec @@ -120,46 +93,30 @@ function httpget(url, stream, target) end end ---- Initiate a system reboot. --- @return Return value of os.execute() function reboot() return os.execute("reboot >/dev/null 2>&1") end ---- Retrieves the output of the "logread" command. --- @return String containing the current log buffer function syslog() return luci.util.exec("logread") end ---- Retrieves the output of the "dmesg" command. --- @return String containing the current log buffer function dmesg() return luci.util.exec("dmesg") end ---- Generates a random id with specified length. --- @param bytes Number of bytes for the unique id --- @return String containing hex encoded id function uniqueid(bytes) local rand = fs.readfile("/dev/urandom", bytes) return rand and nixio.bin.hexlify(rand) end ---- Returns the current system uptime stats. --- @return String containing total uptime in seconds function uptime() return nixio.sysinfo().uptime end ---- LuCI system utilities / network related functions. --- @class module --- @name luci.sys.net net = {} ---- Returns the current arp-table entries as two-dimensional table. --- @return Table of table containing the current arp entries. -- The following fields are defined for arp entry objects: -- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } function net.arptable(callback) @@ -269,8 +226,6 @@ local function _nethints(what, callback) end end ---- Returns a two-dimensional table of mac address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "mac", "name" ] function net.mac_hints(callback) @@ -293,8 +248,6 @@ function net.mac_hints(callback) end end ---- Returns a two-dimensional table of IPv4 address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "ip", "name" ] function net.ipv4_hints(callback) @@ -317,8 +270,6 @@ function net.ipv4_hints(callback) end end ---- Returns a two-dimensional table of IPv6 address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "ip", "name" ] function net.ipv6_hints(callback) @@ -341,8 +292,6 @@ function net.ipv6_hints(callback) end end ---- Returns conntrack information --- @return Table with the currently tracked IP connections function net.conntrack(callback) local connt = {} if fs.access("/proc/net/nf_conntrack", "r") then @@ -387,57 +336,6 @@ function net.conntrack(callback) return connt end ---- Determine the current IPv4 default route. If multiple default routes exist, --- return the one with the lowest metric. --- @return Table with the properties of the current default route. --- The following fields are defined: --- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", --- "flags", "device" } -function net.defaultroute() - local route - - net.routes(function(rt) - if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then - route = rt - end - end) - - return route -end - ---- Determine the current IPv6 default route. If multiple default routes exist, --- return the one with the lowest metric. --- @return Table with the properties of the current default route. --- The following fields are defined: --- { "source", "dest", "nexthop", "metric", "refcount", "usecount", --- "flags", "device" } -function net.defaultroute6() - local route - - net.routes6(function(rt) - if rt.dest:prefix() == 0 and rt.device ~= "lo" and - (not route or route.metric > rt.metric) - then - route = rt - end - end) - - if not route then - local global_unicast = luci.ip.IPv6("2000::/3") - net.routes6(function(rt) - if rt.dest:equal(global_unicast) and - (not route or route.metric > rt.metric) - then - route = rt - end - end) - end - - return route -end - ---- Determine the names of available network interfaces. --- @return Table containing all current interface names function net.devices() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -449,8 +347,6 @@ function net.devices() end ---- Return information about available network interfaces. --- @return Table containing all current interface names and their information function net.deviceinfo() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -479,21 +375,6 @@ function net.deviceinfo() end --- Determine the MAC address belonging to the given IP address. --- @param ip IPv4 address --- @return String containing the MAC address or nil if it cannot be found -function net.ip4mac(ip) - local mac = nil - net.arptable(function(e) - if e["IP address"] == ip then - mac = e["HW address"] - end - end) - return mac -end - ---- Returns the current kernel routing table entries. --- @return Table of tables with properties of the corresponding routes. -- The following fields are defined for route entry tables: -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", -- "flags", "device" } @@ -539,8 +420,6 @@ function net.routes(callback) return routes end ---- Returns the current ipv6 kernel routing table entries. --- @return Table of tables with properties of the corresponding routes. -- The following fields are defined for route entry tables: -- { "source", "dest", "nexthop", "metric", "refcount", "usecount", -- "flags", "device" } @@ -602,30 +481,18 @@ function net.routes6(callback) end end ---- Tests whether the given host responds to ping probes. --- @param host String containing a hostname or IPv4 address --- @return Number containing 0 on success and >= 1 on error function net.pingtest(host) return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") end ---- LuCI system utilities / process related functions. --- @class module --- @name luci.sys.process process = {} ---- Get the current process id. --- @class function --- @name process.info --- @return Number containing the current pid function process.info(key) local s = {uid = nixio.getuid(), gid = nixio.getgid()} return not key and s or s[key] end ---- Retrieve information about currently running processes. --- @return Table containing process information function process.list() local data = {} local k @@ -658,51 +525,22 @@ function process.list() return data end ---- Set the gid of a process identified by given pid. --- @param gid Number containing the Unix group id --- @return Boolean indicating successful operation --- @return String containing the error message if failed --- @return Number containing the error code if failed function process.setgroup(gid) return nixio.setgid(gid) end ---- Set the uid of a process identified by given pid. --- @param uid Number containing the Unix user id --- @return Boolean indicating successful operation --- @return String containing the error message if failed --- @return Number containing the error code if failed function process.setuser(uid) return nixio.setuid(uid) end ---- Send a signal to a process identified by given pid. --- @class function --- @name process.signal --- @param pid Number containing the process id --- @param sig Signal to send (default: 15 [SIGTERM]) --- @return Boolean indicating successful operation --- @return Number containing the error code if failed process.signal = nixio.kill ---- LuCI system utilities / user related functions. --- @class module --- @name luci.sys.user user = {} ---- Retrieve user informations for given uid. --- @class function --- @name getuser --- @param uid Number containing the Unix user id --- @return Table containing the following fields: -- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" } user.getuser = nixio.getpw ---- Retrieve the current user password hash. --- @param username String containing the username to retrieve the password for --- @return String containing the hash or nil if no password is set. --- @return Password database entry function user.getpasswd(username) local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username) local pwh = pwe and (pwe.pwdp or pwe.passwd) @@ -713,10 +551,6 @@ function user.getpasswd(username) end end ---- Test whether given string matches the password of a given system user. --- @param username String containing the Unix user name --- @param pass String containing the password to compare --- @return Boolean indicating wheather the passwords are equal function user.checkpasswd(username, pass) local pwh, pwe = user.getpasswd(username) if pwe then @@ -725,10 +559,6 @@ function user.checkpasswd(username, pass) return false end ---- Change the password of given user. --- @param username String containing the Unix user name --- @param password String containing the password to compare --- @return Number containing 0 on success and >= 1 on error function user.setpasswd(username, password) if password then password = password:gsub("'", [['"'"']]) @@ -745,14 +575,8 @@ function user.setpasswd(username, password) end ---- LuCI system utilities / wifi related functions. --- @class module --- @name luci.sys.wifi wifi = {} ---- Get wireless information for given interface. --- @param ifname String containing the interface name --- @return A wrapped iwinfo object instance function wifi.getiwinfo(ifname) local stat, iwinfo = pcall(require, "iwinfo") @@ -798,14 +622,9 @@ function wifi.getiwinfo(ifname) end ---- LuCI system utilities / init related functions. --- @class module --- @name luci.sys.init init = {} init.dir = "/etc/init.d/" ---- Get the names of all installed init scripts --- @return Table containing the names of all inistalled init scripts function init.names() local names = { } for name in fs.glob(init.dir.."*") do @@ -814,9 +633,6 @@ function init.names() return names end ---- Get the index of he given init script --- @param name Name of the init script --- @return Numeric index value function init.index(name) if fs.access(init.dir..name) then return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null" @@ -830,37 +646,22 @@ local function init_action(action, name) end end ---- Test whether the given init script is enabled --- @param name Name of the init script --- @return Boolean indicating whether init is enabled function init.enabled(name) return (init_action("enabled", name) == 0) end ---- Enable the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.enable(name) return (init_action("enable", name) == 1) end ---- Disable the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.disable(name) return (init_action("disable", name) == 0) end ---- Start the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.start(name) return (init_action("start", name) == 0) end ---- Stop the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.stop(name) return (init_action("stop", name) == 0) end diff --git a/modules/luci-base/luasrc/sys.luadoc b/modules/luci-base/luasrc/sys.luadoc new file mode 100644 index 000000000..72a16a1ab --- /dev/null +++ b/modules/luci-base/luasrc/sys.luadoc @@ -0,0 +1,396 @@ +---[[ +LuCI Linux and POSIX system utilities. + +module "luci.sys" +]] + +---[[ +Execute a given shell command and return the error code + +@class function +@name call +@param ... Command to call +@return Error code of the command +]] + +---[[ +Execute a given shell command and capture its standard output + +@class function +@name exec +@param command Command to call +@return String containg the return the output of the command +]] + +---[[ +Retrieve information about currently mounted file systems. + +@class function +@name mounts +@return Table containing mount information +]] + +---[[ +Retrieve environment variables. If no variable is given then a table + +containing the whole environment is returned otherwise this function returns +the corresponding string value for the given name or nil if no such variable +exists. +@class function +@name getenv +@param var Name of the environment variable to retrieve (optional) +@return String containg the value of the specified variable +@return Table containing all variables if no variable name is given +]] + +---[[ +Get or set the current hostname. + +@class function +@name hostname +@param String containing a new hostname to set (optional) +@return String containing the system hostname +]] + +---[[ +Returns the contents of a documented referred by an URL. + +@class function +@name httpget +@param url The URL to retrieve +@param stream Return a stream instead of a buffer +@param target Directly write to target file name +@return String containing the contents of given the URL +]] + +---[[ +Initiate a system reboot. + +@class function +@name reboot +@return Return value of os.execute() +]] + +---[[ +Retrieves the output of the "logread" command. + +@class function +@name syslog +@return String containing the current log buffer +]] + +---[[ +Retrieves the output of the "dmesg" command. + +@class function +@name dmesg +@return String containing the current log buffer +]] + +---[[ +Generates a random id with specified length. + +@class function +@name uniqueid +@param bytes Number of bytes for the unique id +@return String containing hex encoded id +]] + +---[[ +Returns the current system uptime stats. + +@class function +@name uptime +@return String containing total uptime in seconds +]] + +---[[ +LuCI system utilities / network related functions. + +@class module +@name luci.sys.net +]] + +---[[ +Returns the current arp-table entries as two-dimensional table. + +@class function +@name net.arptable +@return Table of table containing the current arp entries. +-- The following fields are defined for arp entry objects: +-- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } +]] + +---[[ +Returns a two-dimensional table of mac address hints. + +@class function +@name net.mac_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "mac", "name" ] +]] + +---[[ +Returns a two-dimensional table of IPv4 address hints. + +@class function +@name net.ipv4_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "ip", "name" ] +]] + +---[[ +Returns a two-dimensional table of IPv6 address hints. + +@class function +@name net.ipv6_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "ip", "name" ] +]] + +---[[ +Returns conntrack information + +@class function +@name net.conntrack +@return Table with the currently tracked IP connections +]] + +---[[ +Determine the names of available network interfaces. + +@class function +@name net.devices +@return Table containing all current interface names +]] + +---[[ +Return information about available network interfaces. + +@class function +@name net.deviceinfo +@return Table containing all current interface names and their information +]] + +---[[ +Returns the current kernel routing table entries. + +@class function +@name net.routes +@return Table of tables with properties of the corresponding routes. +-- The following fields are defined for route entry tables: +-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", +-- "flags", "device" } +]] + +---[[ +Returns the current ipv6 kernel routing table entries. + +@class function +@name net.routes6 +@return Table of tables with properties of the corresponding routes. +-- The following fields are defined for route entry tables: +-- { "source", "dest", "nexthop", "metric", "refcount", "usecount", +-- "flags", "device" } +]] + +---[[ +Tests whether the given host responds to ping probes. + +@class function +@name net.pingtest +@param host String containing a hostname or IPv4 address +@return Number containing 0 on success and >= 1 on error +]] + +---[[ +LuCI system utilities / process related functions. + +@class module +@name luci.sys.process +]] + +---[[ +Get the current process id. + +@class function +@name process.info +@return Number containing the current pid +]] + +---[[ +Retrieve information about currently running processes. + +@class function +@name process.list +@return Table containing process information +]] + +---[[ +Set the gid of a process identified by given pid. + +@class function +@name process.setgroup +@param gid Number containing the Unix group id +@return Boolean indicating successful operation +@return String containing the error message if failed +@return Number containing the error code if failed +]] + +---[[ +Set the uid of a process identified by given pid. + +@class function +@name process.setuser +@param uid Number containing the Unix user id +@return Boolean indicating successful operation +@return String containing the error message if failed +@return Number containing the error code if failed +]] + +---[[ +Send a signal to a process identified by given pid. + +@class function +@name process.signal +@param pid Number containing the process id +@param sig Signal to send (default: 15 [SIGTERM]) +@return Boolean indicating successful operation +@return Number containing the error code if failed +]] + +---[[ +LuCI system utilities / user related functions. + +@class module +@name luci.sys.user +]] + +---[[ +Retrieve user informations for given uid. + +@class function +@name getuser +@param uid Number containing the Unix user id +@return Table containing the following fields: +-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" } +]] + +---[[ +Retrieve the current user password hash. + +@class function +@name user.getpasswd +@param username String containing the username to retrieve the password for +@return String containing the hash or nil if no password is set. +@return Password database entry +]] + +---[[ +Test whether given string matches the password of a given system user. + +@class function +@name user.checkpasswd +@param username String containing the Unix user name +@param pass String containing the password to compare +@return Boolean indicating wheather the passwords are equal +]] + +---[[ +Change the password of given user. + +@class function +@name user.setpasswd +@param username String containing the Unix user name +@param password String containing the password to compare +@return Number containing 0 on success and >= 1 on error +]] + +---[[ +LuCI system utilities / wifi related functions. + +@class module +@name luci.sys.wifi +]] + +---[[ +Get wireless information for given interface. + +@class function +@name wifi.getiwinfo +@param ifname String containing the interface name +@return A wrapped iwinfo object instance +]] + +---[[ +LuCI system utilities / init related functions. + +@class module +@name luci.sys.init +]] + +---[[ +Get the names of all installed init scripts + +@class function +@name init.names +@return Table containing the names of all inistalled init scripts +]] + +---[[ +Get the index of he given init script + +@class function +@name init.index +@param name Name of the init script +@return Numeric index value +]] + +---[[ +Test whether the given init script is enabled + +@class function +@name init.enabled +@param name Name of the init script +@return Boolean indicating whether init is enabled +]] + +---[[ +Enable the given init script + +@class function +@name init.enable +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Disable the given init script + +@class function +@name init.disable +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Start the given init script + +@class function +@name init.start +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Stop the given init script + +@class function +@name init.stop +@param name Name of the init script +@return Boolean indicating success +]] + diff --git a/modules/luci-base/luasrc/sys/iptparser.lua b/modules/luci-base/luasrc/sys/iptparser.lua index 6715937c6..2b81e0ee3 100644 --- a/modules/luci-base/luasrc/sys/iptparser.lua +++ b/modules/luci-base/luasrc/sys/iptparser.lua @@ -21,15 +21,8 @@ luci.ip = require "luci.ip" local tonumber, ipairs, table = tonumber, ipairs, table ---- LuCI iptables parser and query library --- @cstyle instance module("luci.sys.iptparser") ---- Create a new iptables parser object. --- @class function --- @name IptParser --- @param family Number specifying the address family. 4 for IPv4, 6 for IPv6 --- @return IptParser instance IptParser = luci.util.class() function IptParser.__init__( self, family ) @@ -50,7 +43,6 @@ function IptParser.__init__( self, family ) self:_parse_rules() end ---- Find all firewall rules that match the given criteria. Expects a table with -- search criteria as only argument. If args is nil or an empty table then all -- rules will be returned. -- @@ -108,8 +100,6 @@ end -- This will match all rules with target "-j REJECT", -- protocol "-p tcp" (or "-p all") -- and the option "--reject-with tcp-reset". --- @params args Table containing the search arguments (optional) --- @return Table of matching rule tables function IptParser.find( self, args ) local args = args or { } @@ -205,9 +195,7 @@ function IptParser.find( self, args ) end ---- Rebuild the internal lookup table, for example when rules have changed -- through external commands. --- @return nothing function IptParser.resync( self ) self._rules = { } self._chain = nil @@ -215,16 +203,11 @@ function IptParser.resync( self ) end ---- Find the names of all tables. --- @return Table of table names. function IptParser.tables( self ) return self._tables end ---- Find the names of all chains within the given table name. --- @param table String containing the table name --- @return Table of chain names in the order they occur. function IptParser.chains( self, table ) local lookup = { } local chains = { } @@ -238,19 +221,12 @@ function IptParser.chains( self, table ) end ---- Return the given firewall chain within the given table name. --- @param table String containing the table name --- @param chain String containing the chain name --- @return Table containing the fields "policy", "packets", "bytes" -- and "rules". The "rules" field is a table of rule tables. function IptParser.chain( self, table, chain ) return self._chains[table:lower()] and self._chains[table:lower()][chain] end ---- Test whether the given target points to a custom chain. --- @param target String containing the target action --- @return Boolean indicating whether target is a custom chain. function IptParser.is_custom_target( self, target ) for _, r in ipairs(self._rules) do if r.chain == target then diff --git a/modules/luci-base/luasrc/sys/iptparser.luadoc b/modules/luci-base/luasrc/sys/iptparser.luadoc new file mode 100644 index 000000000..071e7d52e --- /dev/null +++ b/modules/luci-base/luasrc/sys/iptparser.luadoc @@ -0,0 +1,69 @@ +---[[ +LuCI iptables parser and query library + +@cstyle instance +]] +module "luci.sys.iptparser" + +---[[ +Create a new iptables parser object. + +@class function +@name IptParser +@param family Number specifying the address family. 4 for IPv4, 6 for IPv6 +@return IptParser instance +]] + +---[[ +Find all firewall rules that match the given criteria. Expects a table with + +search criteria as only argument. If args is nil or an empty table then all +rules will be returned. +]] + +---[[ +Rebuild the internal lookup table, for example when rules have changed + +through external commands. +@class function +@name IptParser.resync +@return nothing +]] + +---[[ +Find the names of all tables. + +@class function +@name IptParser.tables +@return Table of table names. +]] + +---[[ +Find the names of all chains within the given table name. + +@class function +@name IptParser.chains +@param table String containing the table name +@return Table of chain names in the order they occur. +]] + +---[[ +Return the given firewall chain within the given table name. + +@class function +@name IptParser.chain +@param table String containing the table name +@param chain String containing the chain name +@return Table containing the fields "policy", "packets", "bytes" +-- and "rules". The "rules" field is a table of rule tables. +]] + +---[[ +Test whether the given target points to a custom chain. + +@class function +@name IptParser.is_custom_target +@param target String containing the target action +@return Boolean indicating whether target is a custom chain. +]] + diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua index 633749669..97a3608ea 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua @@ -86,7 +86,7 @@ TZ = { { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' }, { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' }, { 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' }, - { 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' }, + { 'America/Cancun', 'EST5' }, { 'America/Caracas', 'VET4:30' }, { 'America/Cayenne', 'GFT3' }, { 'America/Cayman', 'EST5' }, @@ -178,7 +178,7 @@ TZ = { { 'America/Rio Branco', 'ACT5' }, { 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' }, { 'America/Santarem', 'BRT3' }, - { 'America/Santiago', 'CLT4CLST,M9.1.6/24,M4.4.6/24' }, + { 'America/Santiago', 'CLT3' }, { 'America/Santo Domingo', 'AST4' }, { 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' }, { 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' }, @@ -207,7 +207,7 @@ TZ = { { 'Antarctica/Macquarie', 'MIST-11' }, { 'Antarctica/Mawson', 'MAWT-5' }, { 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' }, - { 'Antarctica/Palmer', 'CLT4CLST,M9.1.6/24,M4.4.6/24' }, + { 'Antarctica/Palmer', 'CLT3' }, { 'Antarctica/Rothera', 'ROTT3' }, { 'Antarctica/Syowa', 'SYOT-3' }, { 'Antarctica/Troll', 'UTC0CEST-2,M3.5.0/1,M10.5.0/3' }, @@ -384,7 +384,7 @@ TZ = { { 'Pacific/Bougainville', 'BST-11' }, { 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' }, { 'Pacific/Chuuk', 'CHUT-10' }, - { 'Pacific/Easter', 'EAST6EASST,M9.1.6/22,M4.4.6/22' }, + { 'Pacific/Easter', 'EAST5' }, { 'Pacific/Efate', 'VUT-11' }, { 'Pacific/Enderbury', 'PHOT-13' }, { 'Pacific/Fakaofo', 'TKT-13' }, diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua index facfd0c8b..c8c908b6e 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua @@ -45,8 +45,7 @@ OFFSET = { uyst = -7200, -- UYST fnt = -7200, -- FNT srt = -10800, -- SRT - clt = -14400, -- CLT - clst = -10800, -- CLST + clt = -10800, -- CLT egt = -3600, -- EGT egst = 0, -- EGST nst = -12600, -- NST @@ -135,8 +134,7 @@ OFFSET = { chast = 45900, -- CHAST chadt = 49500, -- CHADT chut = 36000, -- CHUT - east = -21600, -- EAST - easst = -18000, -- EASST + east = -18000, -- EAST vut = 39600, -- VUT phot = 46800, -- PHOT tkt = 46800, -- TKT diff --git a/modules/luci-base/luasrc/tools/webadmin.lua b/modules/luci-base/luasrc/tools/webadmin.lua index 9adac29bf..8273175de 100644 --- a/modules/luci-base/luasrc/tools/webadmin.lua +++ b/modules/luci-base/luasrc/tools/webadmin.lua @@ -1,11 +1,12 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> --- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> +-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. module("luci.tools.webadmin", package.seeall) -local uci = require("luci.model.uci") -require("luci.sys") -require("luci.ip") + +local util = require "luci.util" +local uci = require "luci.model.uci" +local ip = require "luci.ip" function byte_format(byte) local suff = {"B", "KB", "MB", "GB", "TB"} @@ -47,49 +48,6 @@ function date_format(secs) end end -function network_get_addresses(net) - local state = uci.cursor_state() - state:load("network") - local addr = {} - local ipv4 = state:get("network", net, "ipaddr") - local mav4 = state:get("network", net, "netmask") - local ipv6 = state:get("network", net, "ip6addr") - - if ipv4 and #ipv4 > 0 then - if mav4 and #mav4 == 0 then mav4 = nil end - - ipv4 = luci.ip.IPv4(ipv4, mav4) - - if ipv4 then - table.insert(addr, ipv4:string()) - end - end - - if ipv6 then - table.insert(addr, ipv6) - end - - state:foreach("network", "alias", - function (section) - if section.interface == net then - if section.ipaddr and section.netmask then - local ipv4 = luci.ip.IPv4(section.ipaddr, section.netmask) - - if ipv4 then - table.insert(addr, ipv4:string()) - end - end - - if section.ip6addr then - table.insert(addr, section.ip6addr) - end - end - end - ) - - return addr -end - function cbi_add_networks(field) uci.cursor():foreach("network", "interface", function (section) @@ -102,29 +60,12 @@ function cbi_add_networks(field) end function cbi_add_knownips(field) - for i, dataset in ipairs(luci.sys.net.arptable()) do - field:value(dataset["IP address"]) - end -end - -function network_get_zones(net) - local state = uci.cursor_state() - if not state:load("firewall") then - return nil - end - - local zones = {} - - state:foreach("firewall", "zone", - function (section) - local znet = section.network or section.name - if luci.util.contains(luci.util.split(znet, " "), net) then - table.insert(zones, section.name) - end + local _, n + for _, n in ipairs(ip.neighbors({ family = 4 })) do + if n.dest then + field:value(n.dest:string()) end - ) - - return zones + end end function firewall_find_zone(name) @@ -142,21 +83,23 @@ function firewall_find_zone(name) end function iface_get_network(iface) - local state = uci.cursor_state() - state:load("network") - local net - - state:foreach("network", "interface", - function (section) - local ifname = state:get( - "network", section[".name"], "ifname" - ) - - if iface == ifname then - net = section[".name"] + local link = ip.link(tostring(iface)) + if link.master then + iface = link.master + end + + local cur = uci.cursor() + local dump = util.ubus("network.interface", "dump", { }) + if dump then + local _, net + for _, net in ipairs(dump.interface) do + if net.l3_device == iface or net.device == iface then + -- cross check with uci to filter out @name style aliases + local uciname = cur:get("network", net.interface, "ifname") + if not uciname or uciname:sub(1, 1) ~= "@" then + return net.interface + end end end - ) - - return net + end end diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua index 42de3dd72..8b28b1752 100644 --- a/modules/luci-base/luasrc/util.lua +++ b/modules/luci-base/luasrc/util.lua @@ -20,7 +20,6 @@ local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring local require, pcall, xpcall = require, pcall, xpcall local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit ---- LuCI utility functions. module "luci.util" -- @@ -54,7 +53,6 @@ local function _instantiate(class, ...) return inst end ---- Create a Class object (Python-style object model). -- The class object can be instantiated by calling itself. -- Any class functions or shared parameters can be attached to this object. -- Attaching a table to the class object makes this table shared between @@ -64,10 +62,6 @@ end -- to the __init__ function of this class - if such a function exists. -- The __init__ function must be used to set any object parameters that are not shared -- with other objects of this class. Any return values will be ignored. --- @param base The base class to inherit from (optional) --- @return A class object --- @see instanceof --- @see clone function class(base) return setmetatable({}, { __call = _instantiate, @@ -75,12 +69,6 @@ function class(base) }) end ---- Test whether the given object is an instance of the given class. --- @param object Object instance --- @param class Class object to test against --- @return Boolean indicating whether the object is an instance --- @see class --- @see clone function instanceof(object, class) local meta = getmetatable(object) while meta and meta.__index do @@ -117,10 +105,8 @@ local tl_meta = { end } ---- Create a new or get an already existing thread local store associated with -- the current active coroutine. A thread local store is private a table object -- whose values can't be accessed from outside of the running coroutine. --- @return Table value representing the corresponding thread local store function threadlocal(tbl) return setmetatable(tbl or {}, tl_meta) end @@ -130,17 +116,10 @@ end -- Debugging routines -- ---- Write given object to stderr. --- @param obj Value to write to stderr --- @return Boolean indicating whether the write operation was successful function perror(obj) return io.stderr:write(tostring(obj) .. "\n") end ---- Recursively dumps a table to stdout, useful for testing and debugging. --- @param t Table value to dump --- @param maxdepth Maximum depth --- @return Always nil function dumptable(t, maxdepth, i, seen) i = i or 0 seen = seen or setmetatable({}, {__mode="k"}) @@ -163,31 +142,19 @@ end -- String and data manipulation routines -- ---- Create valid XML PCDATA from given string. --- @param value String value containing the data to escape --- @return String value containing the escaped data function pcdata(value) return value and tparser.pcdata(tostring(value)) end ---- Strip HTML tags from given string. --- @param value String containing the HTML text --- @return String with HTML tags stripped of function striptags(value) return value and tparser.striptags(tostring(value)) end ---- Splits given string on a defined separator sequence and return a table -- containing the resulting substrings. The optional max parameter specifies -- the number of bytes to process, regardless of the actual length of the given -- string. The optional last parameter, regex, specifies whether the separator -- sequence is interpreted as regular expression. --- @param str String value containing the data to split up --- @param pat String with separator pattern (optional, defaults to "\n") --- @param max Maximum times to split (optional) --- @param regex Boolean indicating whether to interpret the separator -- pattern as regular expression (optional, default is false) --- @return Table containing the resulting substrings function split(str, pat, max, regex) pat = pat or "\n" max = max or #str @@ -221,29 +188,19 @@ function split(str, pat, max, regex) return t end ---- Remove leading and trailing whitespace from given string value. --- @param str String value containing whitespace padded data --- @return String value with leading and trailing space removed function trim(str) return (str:gsub("^%s*(.-)%s*$", "%1")) end ---- Count the occurences of given substring in given string. --- @param str String to search in --- @param pattern String containing pattern to find --- @return Number of found occurences function cmatch(str, pat) local count = 0 for _ in str:gmatch(pat) do count = count + 1 end return count end ---- Return a matching iterator for the given value. The iterator will return -- one token per invocation, the tokens are separated by whitespace. If the -- input value is a table, it is transformed into a string first. A nil value -- will result in a valid interator which aborts with the first invocation. --- @param val The value to scan (table, string or nil) --- @return Iterator which returns one token per call function imatch(v) if type(v) == "table" then local k = nil @@ -268,7 +225,6 @@ function imatch(v) return function() end end ---- Parse certain units from the given string and return the canonical integer -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant. -- Recognized units are: -- o "y" - one year (60*60*24*366) @@ -283,8 +239,6 @@ end -- o "kib" - one si kilobyte (1000) -- o "mib" - one si megabyte (1000*1000) -- o "gib" - one si gigabyte (1000*1000*1000) --- @param ustr String containing a numerical value with trailing unit --- @return Number containing the canonical value function parse_units(ustr) local val = 0 @@ -336,10 +290,6 @@ string.cmatch = cmatch string.parse_units = parse_units ---- Appends numerically indexed tables or single objects to a given table. --- @param src Target table --- @param ... Objects to insert --- @return Target table function append(src, ...) for i, a in ipairs({...}) do if type(a) == "table" then @@ -353,19 +303,10 @@ function append(src, ...) return src end ---- Combines two or more numerically indexed tables and single objects into one table. --- @param tbl1 Table value to combine --- @param tbl2 Table value to combine --- @param ... More tables to combine --- @return Table value containing all values of given tables function combine(...) return append({}, ...) end ---- Checks whether the given table contains the given value. --- @param table Table value --- @param value Value to search within the given table --- @return Boolean indicating whether the given value occurs within table function contains(table, value) for k, v in pairs(table) do if value == v then @@ -375,20 +316,13 @@ function contains(table, value) return false end ---- Update values in given table with the values from the second given table. -- Both table are - in fact - merged together. --- @param t Table which should be updated --- @param updates Table containing the values to update --- @return Always nil function update(t, updates) for k, v in pairs(updates) do t[k] = v end end ---- Retrieve all keys of given associative table. --- @param t Table to extract keys from --- @return Sorted table containing the keys function keys(t) local keys = { } if t then @@ -399,10 +333,6 @@ function keys(t) return keys end ---- Clones the given object and return it's copy. --- @param object Table value to clone --- @param deep Boolean indicating whether to do recursive cloning --- @return Cloned table value function clone(object, deep) local copy = {} @@ -417,8 +347,6 @@ function clone(object, deep) end ---- Create a dynamic table which automatically creates subtables. --- @return Dynamic Table function dtable() return setmetatable({}, { __index = function(tbl, key) @@ -457,12 +385,7 @@ function _serialize_table(t, seen) return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data end ---- Recursively serialize given data to lua code, suitable for restoring -- with loadstring(). --- @param val Value containing the data to serialize --- @return String value containing the serialized code --- @see restore_data --- @see get_bytecode function serialize_data(val, seen) seen = seen or setmetatable({}, {__mode="k"}) @@ -483,11 +406,6 @@ function serialize_data(val, seen) end end ---- Restore data previously serialized with serialize_data(). --- @param str String containing the data to restore --- @return Value containing the restored data structure --- @see serialize_data --- @see get_bytecode function restore_data(str) return loadstring("return " .. str)() end @@ -497,10 +415,7 @@ end -- Byte code manipulation routines -- ---- Return the current runtime bytecode of the given data. The byte code -- will be stripped before it is returned. --- @param val Value to return as bytecode --- @return String value containing the bytecode of the given data function get_bytecode(val) local code @@ -513,11 +428,8 @@ function get_bytecode(val) return code -- and strip_bytecode(code) end ---- Strips unnescessary lua bytecode from given string. Information like line -- numbers and debugging numbers will be discarded. Original version by -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html) --- @param code String value containing the original lua byte code --- @return String value containing the stripped lua byte code function strip_bytecode(code) local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12) local subint @@ -607,27 +519,17 @@ function _sortiter( t, f ) end end ---- Return a key, value iterator which returns the values sorted according to -- the provided callback function. --- @param t The table to iterate --- @param f A callback function to decide the order of elements --- @return Function value containing the corresponding iterator function spairs(t,f) return _sortiter( t, f ) end ---- Return a key, value iterator for the given table. -- The table pairs are sorted by key. --- @param t The table to iterate --- @return Function value containing the corresponding iterator function kspairs(t) return _sortiter( t ) end ---- Return a key, value iterator for the given table. -- The table pairs are sorted by value. --- @param t The table to iterate --- @return Function value containing the corresponding iterator function vspairs(t) return _sortiter( t, function (a,b) return t[a] < t[b] end ) end @@ -637,15 +539,10 @@ end -- System utility functions -- ---- Test whether the current system is operating in big endian mode. --- @return Boolean value indicating whether system is big endian function bigendian() return string.byte(string.dump(function() end), 7) == 0 end ---- Execute given commandline and gather stdout. --- @param command String containing command to execute --- @return String containing the command's stdout function exec(command) local pp = io.popen(command) local data = pp:read("*a") @@ -654,9 +551,6 @@ function exec(command) return data end ---- Return a line-buffered iterator over the output of given command. --- @param command String containing the command to execute --- @return Iterator function execi(command) local pp = io.popen(command) @@ -687,11 +581,6 @@ function execl(command) return data end ---- Issue an ubus call. --- @param object String containing the ubus object to call --- @param method String containing the ubus method to call --- @param values Table containing the values to pass --- @return Table containin the ubus result function ubus(object, method, data) if not _ubus_connection then _ubus_connection = _ubus.connect() @@ -710,8 +599,60 @@ function ubus(object, method, data) end end ---- Returns the absolute path to LuCI base directory. --- @return String containing the directory path +function serialize_json(x, cb) + local rv, push = nil, cb + if not push then + rv = { } + push = function(tok) rv[#rv+1] = tok end + end + + if x == nil then + push("null") + elseif type(x) == "table" then + -- test if table is array like + local k, v + local n1, n2 = 0, 0 + for k in pairs(x) do n1 = n1 + 1 end + for k in ipairs(x) do n2 = n2 + 1 end + + if n1 == n2 and n1 > 0 then + push("[") + for k = 1, n2 do + if k > 1 then + push(",") + end + serialize_json(x[k], push) + end + push("]") + else + push("{") + for k, v in pairs(x) do + push("%q:" % tostring(k)) + serialize_json(v, push) + if next(x, k) then + push(",") + end + end + push("}") + end + elseif type(x) == "number" or type(x) == "boolean" then + if (x ~= x) then + -- NaN is the only value that doesn't equal to itself. + push("Number.NaN") + else + push(tostring(x)) + end + else + push('"%s"' % tostring(x):gsub('["%z\1-\31]', + function(c) return '\\u%04x' % c:byte(1) end)) + end + + if not cb then + return table.concat(rv, "") + end +end + + function libpath() return require "nixio.fs".dirname(ldebug.__file__) end @@ -751,11 +692,6 @@ local function copcall_id(trace, ...) return ... end ---- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function --- @param f Lua function to be called protected --- @param err Custom error handler --- @param ... Parameters passed to the function --- @return A boolean whether the function call succeeded and the return -- values of either the function or the error handler function coxpcall(f, err, ...) local res, co = oldpcall(coroutine.create, f) @@ -770,10 +706,6 @@ function coxpcall(f, err, ...) return performResume(err, co, ...) end ---- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function --- @param f Lua function to be called protected --- @param ... Parameters passed to the function --- @return A boolean whether the function call succeeded and the returns -- values of the function or the error object function copcall(f, ...) return coxpcall(f, copcall_id, ...) diff --git a/modules/luci-base/luasrc/util.luadoc b/modules/luci-base/luasrc/util.luadoc new file mode 100644 index 000000000..1c09b7a9a --- /dev/null +++ b/modules/luci-base/luasrc/util.luadoc @@ -0,0 +1,378 @@ +---[[ +LuCI utility functions. + +module "luci.util" +]] + +---[[ +Create a Class object (Python-style object model). + +The class object can be instantiated by calling itself. +Any class functions or shared parameters can be attached to this object. +Attaching a table to the class object makes this table shared between +all instances of this class. For object parameters use the __init__ function. +Classes can inherit member functions and values from a base class. +Class can be instantiated by calling them. All parameters will be passed +to the __init__ function of this class - if such a function exists. +The __init__ function must be used to set any object parameters that are not shared +with other objects of this class. Any return values will be ignored. +@class function +@name class +@param base The base class to inherit from (optional) +@return A class object +@see instanceof +@see clone +]] + +---[[ +Test whether the given object is an instance of the given class. + +@class function +@name instanceof +@param object Object instance +@param class Class object to test against +@return Boolean indicating whether the object is an instance +@see class +@see clone +]] + +---[[ +Create a new or get an already existing thread local store associated with + +the current active coroutine. A thread local store is private a table object +whose values can't be accessed from outside of the running coroutine. +@class function +@name threadlocal +@return Table value representing the corresponding thread local store +]] + +---[[ +Write given object to stderr. + +@class function +@name perror +@param obj Value to write to stderr +@return Boolean indicating whether the write operation was successful +]] + +---[[ +Recursively dumps a table to stdout, useful for testing and debugging. + +@class function +@name dumptable +@param t Table value to dump +@param maxdepth Maximum depth +@return Always nil +]] + +---[[ +Create valid XML PCDATA from given string. + +@class function +@name pcdata +@param value String value containing the data to escape +@return String value containing the escaped data +]] + +---[[ +Strip HTML tags from given string. + +@class function +@name striptags +@param value String containing the HTML text +@return String with HTML tags stripped of +]] + +---[[ +Splits given string on a defined separator sequence and return a table + +containing the resulting substrings. The optional max parameter specifies +the number of bytes to process, regardless of the actual length of the given +string. The optional last parameter, regex, specifies whether the separator +sequence is interpreted as regular expression. +@class function +@name split +@param str String value containing the data to split up +@param pat String with separator pattern (optional, defaults to "\n") +@param max Maximum times to split (optional) +@param regex Boolean indicating whether to interpret the separator +-- pattern as regular expression (optional, default is false) +@return Table containing the resulting substrings +]] + +---[[ +Remove leading and trailing whitespace from given string value. + +@class function +@name trim +@param str String value containing whitespace padded data +@return String value with leading and trailing space removed +]] + +---[[ +Count the occurences of given substring in given string. + +@class function +@name cmatch +@param str String to search in +@param pattern String containing pattern to find +@return Number of found occurences +]] + +---[[ +Return a matching iterator for the given value. The iterator will return + +one token per invocation, the tokens are separated by whitespace. If the +input value is a table, it is transformed into a string first. A nil value +will result in a valid interator which aborts with the first invocation. +@class function +@name imatch +@param val The value to scan (table, string or nil) +@return Iterator which returns one token per call +]] + +---[[ +Parse certain units from the given string and return the canonical integer + +value or 0 if the unit is unknown. Upper- or lower case is irrelevant. +Recognized units are: +-- o "y" - one year (60*60*24*366) + o "m" - one month (60*60*24*31) + o "w" - one week (60*60*24*7) + o "d" - one day (60*60*24) + o "h" - one hour (60*60) + o "min" - one minute (60) + o "kb" - one kilobyte (1024) + o "mb" - one megabyte (1024*1024) + o "gb" - one gigabyte (1024*1024*1024) + o "kib" - one si kilobyte (1000) + o "mib" - one si megabyte (1000*1000) + o "gib" - one si gigabyte (1000*1000*1000) +@class function +@name parse_units +@param ustr String containing a numerical value with trailing unit +@return Number containing the canonical value +]] + +---[[ +Appends numerically indexed tables or single objects to a given table. + +@class function +@name append +@param src Target table +@param ... Objects to insert +@return Target table +]] + +---[[ +Combines two or more numerically indexed tables and single objects into one table. + +@class function +@name combine +@param tbl1 Table value to combine +@param tbl2 Table value to combine +@param ... More tables to combine +@return Table value containing all values of given tables +]] + +---[[ +Checks whether the given table contains the given value. + +@class function +@name contains +@param table Table value +@param value Value to search within the given table +@return Boolean indicating whether the given value occurs within table +]] + +---[[ +Update values in given table with the values from the second given table. + +Both table are - in fact - merged together. +@class function +@name update +@param t Table which should be updated +@param updates Table containing the values to update +@return Always nil +]] + +---[[ +Retrieve all keys of given associative table. + +@class function +@name keys +@param t Table to extract keys from +@return Sorted table containing the keys +]] + +---[[ +Clones the given object and return it's copy. + +@class function +@name clone +@param object Table value to clone +@param deep Boolean indicating whether to do recursive cloning +@return Cloned table value +]] + +---[[ +Create a dynamic table which automatically creates subtables. + +@class function +@name dtable +@return Dynamic Table +]] + +---[[ +Recursively serialize given data to lua code, suitable for restoring + +with loadstring(). +@class function +@name serialize_data +@param val Value containing the data to serialize +@return String value containing the serialized code +@see restore_data +@see get_bytecode +]] + +---[[ +Restore data previously serialized with serialize_data(). + +@class function +@name restore_data +@param str String containing the data to restore +@return Value containing the restored data structure +@see serialize_data +@see get_bytecode +]] + +---[[ +Return the current runtime bytecode of the given data. The byte code + +will be stripped before it is returned. +@class function +@name get_bytecode +@param val Value to return as bytecode +@return String value containing the bytecode of the given data +]] + +---[[ +Strips unnescessary lua bytecode from given string. Information like line + +numbers and debugging numbers will be discarded. Original version by +Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html) +@class function +@name strip_bytecode +@param code String value containing the original lua byte code +@return String value containing the stripped lua byte code +]] + +---[[ +Return a key, value iterator which returns the values sorted according to + +the provided callback function. +@class function +@name spairs +@param t The table to iterate +@param f A callback function to decide the order of elements +@return Function value containing the corresponding iterator +]] + +---[[ +Return a key, value iterator for the given table. + +The table pairs are sorted by key. +@class function +@name kspairs +@param t The table to iterate +@return Function value containing the corresponding iterator +]] + +---[[ +Return a key, value iterator for the given table. + +The table pairs are sorted by value. +@class function +@name vspairs +@param t The table to iterate +@return Function value containing the corresponding iterator +]] + +---[[ +Test whether the current system is operating in big endian mode. + +@class function +@name bigendian +@return Boolean value indicating whether system is big endian +]] + +---[[ +Execute given commandline and gather stdout. + +@class function +@name exec +@param command String containing command to execute +@return String containing the command's stdout +]] + +---[[ +Return a line-buffered iterator over the output of given command. + +@class function +@name execi +@param command String containing the command to execute +@return Iterator +]] + +---[[ +Issue an ubus call. + +@class function +@name ubus +@param object String containing the ubus object to call +@param method String containing the ubus method to call +@param values Table containing the values to pass +@return Table containin the ubus result +]] + +---[[ +Convert data structure to JSON + +@class function +@name serialize_json +@param data The data to serialize +@param writer A function to write a chunk of JSON data (optional) +@return String containing the JSON if called without write callback +]] + +---[[ +Returns the absolute path to LuCI base directory. + +@class function +@name libpath +@return String containing the directory path +]] + +---[[ +This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function + +@class function +@name coxpcall +@param f Lua function to be called protected +@param err Custom error handler +@param ... Parameters passed to the function +@return A boolean whether the function call succeeded and the return +-- values of either the function or the error handler +]] + +---[[ +This is a coroutine-safe drop-in replacement for Lua's "pcall"-function + +@class function +@name copcall +@param f Lua function to be called protected +@param ... Parameters passed to the function +@return A boolean whether the function call succeeded and the returns +-- values of the function or the error object +]] + diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/index.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/index.lua index 74a3fd9ad..d00d546b6 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/index.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/index.lua @@ -28,13 +28,17 @@ end function action_logout() local dsp = require "luci.dispatcher" local utl = require "luci.util" - if dsp.context.authsession then - utl.ubus("session", "destroy", { - ubus_rpc_session = dsp.context.authsession - }) + local sid = dsp.context.authsession + + if sid then + utl.ubus("session", "destroy", { ubus_rpc_session = sid }) + dsp.context.urltoken.stok = nil + + luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s/" %{ + sid, 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url() + }) end - luci.http.header("Set-Cookie", "sysauth=; path=" .. dsp.build_url()) luci.http.redirect(luci.dispatcher.build_url()) end diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua index 055142b53..52e347d07 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua @@ -178,13 +178,7 @@ function action_flashops() local image_tmp = "/tmp/firmware.img" local function image_supported() - -- XXX: yay... - return ( 0 == os.execute( - ". /lib/functions.sh; " .. - "include /lib/upgrade; " .. - "platform_check_image %q >/dev/null" - % image_tmp - ) ) + return (os.execute("sysupgrade -T %q >/dev/null" % image_tmp) == 0) end local function image_checksum() diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua index 88e81bb18..997a9274d 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua @@ -1,7 +1,7 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Licensed to the public under the Apache License 2.0. -local sys = require "luci.sys" +local ipc = require "luci.ip" m = Map("dhcp", translate("DHCP and DNS"), translate("Dnsmasq is a combined <abbr title=\"Dynamic Host Configuration Protocol" .. @@ -232,12 +232,11 @@ ip.datatype = "or(ip4addr,'ignore')" hostid = s:option(Value, "hostid", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Suffix (hex)")) -sys.net.arptable(function(entry) - ip:value(entry["IP address"]) - mac:value( - entry["HW address"], - entry["HW address"] .. " (" .. entry["IP address"] .. ")" - ) +ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then + ip:value(n.dest:string()) + mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() }) + end end) function ip.validate(self, value, section) diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua index da7c1181f..fafacf35c 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua @@ -1,9 +1,9 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> --- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org> +-- Copyright 2010-2015 Jo-Philipp Wich <jow@openwrt.org> -- Licensed to the public under the Apache License 2.0. -require("luci.sys") -require("luci.util") +local ipc = require "luci.ip" + m = Map("dhcp", translate("Hostnames")) s = m:section(TypedSection, "domain", translate("Host entries")) @@ -19,12 +19,10 @@ ip = s:option(Value, "ip", translate("IP address")) ip.datatype = "ipaddr" ip.rmempty = true -local arptable = luci.sys.net.arptable() or {} -for i, dataset in ipairs(arptable) do - ip:value( - dataset["IP address"], - "%s (%s)" %{ dataset["IP address"], dataset["HW address"] } - ) -end +ipc.neighbors({ }, function(n) + if n.mac and n.dest and not n.dest:is6linklocal() then + ip:value(n.dest:string(), "%s (%s)" %{ n.dest:string(), n.mac }) + end +end) return m diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua index eabb25768..e092be6c3 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua @@ -14,6 +14,7 @@ local has_dnsmasq = fs.access("/etc/config/dhcp") local has_firewall = fs.access("/etc/config/firewall") m = Map("network", translate("Interfaces") .. " - " .. arg[1]:upper(), translate("On this page you can configure the network interfaces. You can bridge several interfaces by ticking the \"bridge interfaces\" field and enter the names of several network interfaces separated by spaces. You can also use <abbr title=\"Virtual Local Area Network\">VLAN</abbr> notation <samp>INTERFACE.VLANNR</samp> (<abbr title=\"for example\">e.g.</abbr>: <samp>eth0.1</samp>).")) +m.redirect = luci.dispatcher.build_url("admin", "network", "network") m:chain("wireless") if has_firewall then @@ -134,7 +135,7 @@ end -- dhcp setup was requested, create section and reload page if m:formvalue("cbid.dhcp._enable._enable") then - m.uci:section("dhcp", "dhcp", nil, { + m.uci:section("dhcp", "dhcp", arg[1], { interface = arg[1], start = "100", limit = "150", diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua index 01580f101..ac02b156e 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua @@ -1,14 +1,14 @@ -- Copyright 2008 Steven Barth <steven@midlink.org> -- Licensed to the public under the Apache License 2.0. -require("luci.tools.webadmin") +local wa = require "luci.tools.webadmin" +local fs = require "nixio.fs" + m = Map("network", translate("Routes"), translate("Routes specify over which interface and gateway a certain host or network " .. "can be reached.")) -local routes6 = luci.sys.net.routes6() - s = m:section(TypedSection, "route", translate("Static IPv4 Routes")) s.addremove = true s.anonymous = true @@ -16,7 +16,7 @@ s.anonymous = true s.template = "cbi/tblsection" iface = s:option(ListValue, "interface", translate("Interface")) -luci.tools.webadmin.cbi_add_networks(iface) +wa.cbi_add_networks(iface) t = s:option(Value, "target", translate("Target"), translate("Host-<abbr title=\"Internet Protocol Address\">IP</abbr> or Network")) t.datatype = "ip4addr" @@ -41,7 +41,7 @@ mtu.placeholder = 1500 mtu.datatype = "range(64,9000)" mtu.rmempty = true -if routes6 then +if fs.access("/proc/net/ipv6_route") then s = m:section(TypedSection, "route6", translate("Static IPv6 Routes")) s.addremove = true s.anonymous = true @@ -49,7 +49,7 @@ if routes6 then s.template = "cbi/tblsection" iface = s:option(ListValue, "interface", translate("Interface")) - luci.tools.webadmin.cbi_add_networks(iface) + wa.cbi_add_networks(iface) t = s:option(Value, "target", translate("Target"), translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Address or Network (CIDR)")) t.datatype = "ip6addr" diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua index fff8b2f30..dd619b314 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua @@ -17,6 +17,7 @@ m = Map("wireless", "", m:chain("network") m:chain("firewall") +m.redirect = luci.dispatcher.build_url("admin/network/wireless") local ifsection diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm b/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm index ea60a7f07..b7c44f907 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm @@ -1,12 +1,12 @@ <%# Copyright 2008-2009 Steven Barth <steven@midlink.org> - Copyright 2008-2013 Jo-Philipp Wich <jow@openwrt.org> + Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> Licensed to the public under the Apache License 2.0. -%> <%- - local sys = require "luci.sys" + local ip = require "luci.ip" local fs = require "nixio.fs" local utl = require "luci.util" local uci = require "luci.model.uci".cursor() @@ -90,7 +90,9 @@ local devices = ntm:get_wifidevs() local arpcache = { } - sys.net.arptable(function(e) arpcache[e["HW address"]:upper()] = e["IP address"] end) + ip.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then arpcache[n.mac:upper()] = n.dest:string() end + end) local netlist = { } local netdevs = { } diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm b/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm index 2d9a4a3dc..82dd3a7df 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm @@ -1,15 +1,33 @@ <%# Copyright 2008-2009 Steven Barth <steven@midlink.org> - Copyright 2008-2009 Jo-Philipp Wich <jow@openwrt.org> + Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> Licensed to the public under the Apache License 2.0. -%> <%- - require "luci.sys" require "luci.tools.webadmin" require "nixio.fs" + local ip = require "luci.ip" local style = true + local _, v + + local rtn = { + [255] = "local", + [254] = "main", + [253] = "default", + [0] = "unspec" + } + + if nixio.fs.access("/etc/iproute2/rt_tables") then + local ln + for ln in io.lines("/etc/iproute2/rt_tables") do + local i, n = ln:match("^(%d+)%s+(%S+)") + if i and n then + rtn[tonumber(i)] = n + end + end + end -%> <%+header%> @@ -18,7 +36,7 @@ <h2><a id="content" name="content"><%:Routes%></a></h2> <div class="cbi-map-descr"><%:The following rules are currently active on this system.%></div> - <fieldset class="cbi-section" id="cbi-table-table"> + <fieldset class="cbi-section"> <legend>ARP</legend> <div class="cbi-section-node"> <table class="cbi-section-table"> @@ -28,19 +46,26 @@ <th class="cbi-section-table-cell"><%:Interface%></th> </tr> - <% luci.sys.net.arptable(function(e) %> + <% + for _, v in ipairs(ip.neighbors({ family = 4 })) do + if v.mac then + %> <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>"> - <td class="cbi-value-field"><%=e["IP address"]%></td> - <td class="cbi-value-field"><%=e["HW address"]%></td> - <td class="cbi-value-field"><%=e["Device"]%></td> + <td class="cbi-value-field"><%=v.dest%></td> + <td class="cbi-value-field"><%=v.mac%></td> + <td class="cbi-value-field"><%=v.dev%></td> </tr> - <% style = not style; end) %> + <% + style = not style + end + end + %> </table> </div> </fieldset> <br /> - <fieldset class="cbi-section" id="cbi-table-table"> + <fieldset class="cbi-section"> <legend><%_Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes%></legend> <div class="cbi-section-node"> @@ -50,25 +75,27 @@ <th class="cbi-section-table-cell"><%:Target%></th> <th class="cbi-section-table-cell"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Gateway%></th> <th class="cbi-section-table-cell"><%:Metric%></th> + <th class="cbi-section-table-cell"><%:Table%></th> </tr> - <% luci.sys.net.routes(function(rt) %> + <% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %> <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>"> - <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(rt.device) or rt.device%></td> - <td class="cbi-value-field"><%=rt.dest:string()%></td> - <td class="cbi-value-field"><%=rt.gateway:string()%></td> - <td class="cbi-value-field"><%=rt.metric%></td> + <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%></td> + <td class="cbi-value-field"><%=v.dest%></td> + <td class="cbi-value-field"><%=v.gw%></td> + <td class="cbi-value-field"><%=v.metric or 0%></td> + <td class="cbi-value-field"><%=rtn[v.table] or v.table%></td> </tr> - <% style = not style; end) %> + <% style = not style end %> </table> </div> </fieldset> <br /> - <% if nixio.fs.access("/proc/net/ipv6_route") then - style = true - fe80 = luci.ip.IPv6("fe80::/10") + <% + if nixio.fs.access("/proc/net/ipv6_route") then + style = true %> - <fieldset class="cbi-section" id="cbi-table-table"> + <fieldset class="cbi-section"> <legend><%_Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes%></legend> <div class="cbi-section-node"> @@ -76,17 +103,55 @@ <tr class="cbi-section-table-titles"> <th class="cbi-section-table-cell"><%:Network%></th> <th class="cbi-section-table-cell"><%:Target%></th> - <th class="cbi-section-table-cell"><%_<abbr title="Internet Protocol Version 6">IPv6</abbr>-Gateway%></th> + <th class="cbi-section-table-cell"><%:Source%></th> <th class="cbi-section-table-cell"><%:Metric%></th> + <th class="cbi-section-table-cell"><%:Table%></th> + </tr> + <% + for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do + if v.dest and not v.dest:is6linklocal() then + %> + <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>"> + <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></td> + <td class="cbi-value-field"><%=v.dest%></td> + <td class="cbi-value-field"><%=v.from%></td> + <td class="cbi-value-field"><%=v.metric or 0%></td> + <td class="cbi-value-field"><%=rtn[v.table] or v.table%></td> + </tr> + <% + style = not style + end + end + %> + </table> + </div> + </fieldset> + <br /> + + <fieldset class="cbi-section"> + <legend><%:IPv6 Neighbours%></legend> + + <div class="cbi-section-node"> + <table class="cbi-section-table"> + <tr class="cbi-section-table-titles"> + <th class="cbi-section-table-cell"><%:IPv6-Address%></th> + <th class="cbi-section-table-cell"><%:MAC-Address%></th> + <th class="cbi-section-table-cell"><%:Interface%></th> </tr> - <% luci.sys.net.routes6(function(rt) if fe80:contains(rt.dest) then return end %> + <% + for _, v in ipairs(ip.neighbors({ family = 6 })) do + if v.dest and not v.dest:is6linklocal() and v.mac then + %> <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>"> - <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(rt.device) or '(' .. rt.device .. ')'%></td> - <td class="cbi-value-field"><%=rt.dest:string()%></td> - <td class="cbi-value-field"><%=rt.source:string()%></td> - <td class="cbi-value-field"><%=rt.metric_raw:upper()%></td> + <td class="cbi-value-field"><%=v.dest%></td> + <td class="cbi-value-field"><%=v.mac%></td> + <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></td> </tr> - <% style = not style; end) %> + <% + style = not style + end + end + %> </table> </div> </fieldset> diff --git a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua index 5d3d8ad22..9a1c1fea4 100644 --- a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua +++ b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua @@ -3,7 +3,7 @@ -- Licensed to the public under the Apache License 2.0. local uci = require "luci.model.uci".cursor() -local sys = require "luci.sys" +local ipc = require "luci.ip" local wa = require "luci.tools.webadmin" local fs = require "nixio.fs" @@ -86,12 +86,12 @@ s2.template = "cbi/tblsection" name = s2:option(Value, "name", translate("Hostname")) mac = s2:option(Value, "mac", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address")) ip = s2:option(Value, "ip", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address")) -sys.net.arptable(function(entry) - ip:value(entry["IP address"]) - mac:value( - entry["HW address"], - entry["HW address"] .. " (" .. entry["IP address"] .. ")" - ) + +ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then + ip:value(n.dest:string()) + mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() }) + end end) return m diff --git a/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm b/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm index 715ac756f..42a58f9dd 100644 --- a/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm +++ b/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm @@ -2,6 +2,7 @@ local utl = require "luci.util" local sys = require "luci.sys" local twa = require "luci.tools.webadmin" +local ip = require "luci.ip" -- System @@ -22,12 +23,12 @@ local load = string.format("%.2f, %.2f, %.2f", loads[1] / 65535.0, loads[2] / 65 local mem = string.format( "%.2f MB (%.2f %s, %.2f %s, %.2f %s)", - memory.total / 1024 / 1024, - (memory.total - memory.free) / 1024 / 1024, + meminfo.total / 1024 / 1024, + (meminfo.total - meminfo.free) / 1024 / 1024, tostring(i18n.translate("used")), - memory.free / 1024 / 1024, + meminfo.free / 1024 / 1024, tostring(i18n.translate("free")), - memory.buffered / 1024 / 1024, + meminfo.buffered / 1024 / 1024, tostring(i18n.translate("buffered")) ) @@ -48,27 +49,6 @@ for _, dev in ipairs(devices) do end local has_iwinfo = pcall(require, "iwinfo") --- Routes -local defroutev4 = sys.net.defaultroute() -local defroutev6 = sys.net.defaultroute6() - -if defroutev4 then - defroutev4.dest = defroutev4.dest:string() - defroutev4.gateway = defroutev4.gateway:string() -else - -- probably policy routing activated, try olsr-default table - local dr4 = sys.exec("ip r s t olsr-default") - if dr4 then - defroutev4 = { } - defroutev4.dest, defroutev4.gateway, defroutev4.device, defroutev4.metric = dr4:match("^(%w+) via (%d+.%d+.%d+.%d+) dev ([%w-]+) +metric (%d+)") - end -end - -if defroutev6 then - defroutev6.dest = defroutev6.dest:string() - defroutev6.nexthop = defroutev6.nexthop:string() -end - if luci.http.formvalue("status") == "1" then local rv = { } for dev in pairs(netdevs) do @@ -86,22 +66,29 @@ if luci.http.formvalue("status") == "1" then rv[#rv+1] = j end - if defroutev6 then - def6 = { - gateway = defroutev6.nexthop, - dest = defroutev6.dest, - dev = defroutev6.device, - metr = defroutev6.metric + + -- Find default routes + + local _, r, def4, def6 + + for _, r in ipairs(ip.routes({ type = 1, dest_exact = "0.0.0.0/0" })) do + def4 = { + gateway = r.gw:string(), + dest = r.dest:string(), + dev = r.dev, + metr = r.metric or 0 } + break end - if defroutev4 then - def4 = { - gateway = defroutev4.gateway, - dest = defroutev4.dest, - dev = defroutev4.device, - metr = defroutev4.metric + for _, r in ipairs(ip.routes({ type = 1, dest_exact = "::/0" })) do + def6 = { + gateway = r.gw:string(), + dest = r.dest:string(), + dev = r.dev, + metr = r.metric or 0 } + break end rv[#rv+1] = { diff --git a/protocols/luci-proto-ipv6/Makefile b/protocols/luci-proto-ipv6/Makefile index f0ca605f8..e749bc9e8 100644 --- a/protocols/luci-proto-ipv6/Makefile +++ b/protocols/luci-proto-ipv6/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite +LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite/aiccu LUCI_DEPENDS:= include ../../luci.mk diff --git a/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua new file mode 100644 index 000000000..e3ae020f5 --- /dev/null +++ b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua @@ -0,0 +1,136 @@ +-- Copyright 2015 Paul Oranje <por@xs4all.nl> +-- Licensed to the public under the Apache License 2.0. + +local map, section, net = ... + +-- config read by /lib/netifd/proto/aiccu.sh +local username, password, protocol, server, tunnelid, ip6prefix, requiretls, nat, heartbeat, + verbose, ntpsynctimeout, ip6addr, sourcerouting, defaultroute + +-- generic parameters +local metric, ttl, mtu + + +username = section:taboption("general", Value, "username", + translate("Server username"), + translate("SIXXS-handle[/Tunnel-ID]")) +username.datatype = "string" + +password = section:taboption("general", Value, "password", + translate("Server password"), + translate("Server password, enter the specific password of the tunnel when the username contains the tunnel ID")) +password.datatype = "string" +password.password = true + + +--[[ SIXXS supports only TIC as tunnel broker protocol, no use setting it. +protocol = section:taboption("general", ListValue, "protocol", + translate("Tunnel broker protocol"), + translate("SIXXS supports TIC only, for static tunnels using IP protocol 41 (RFC4213) use 6in4 instead")) + +protocol:value("tic", "TIC") +protocol:value("tsp", "TSP") +protocol:value("l2tp", "L2TP") +protocol.default = "tic" +protocol.optional = true +--]] + + +server = section:taboption("general", Value, "server", + translate("Tunnel setup server"), + translate("Optional, specify to override default server (tic.sixxs.net)")) +server.datatype = "host" +server.optional = true + + +tunnelid = section:taboption("general", Value, "tunnelid", + translate("Tunnel ID"), + translate("Optional, use when the SIXXS account has more than one tunnel")) +tunnelid.datatype = "string" +tunnelid.optional = true + + +local ip6prefix = section:taboption("general", Value, "ip6prefix", + translate("IPv6 prefix"), + translate("Routed IPv6 prefix for downstream interfaces")) +ip6prefix.datatype = "ip6addr" +ip6prefix.optional = true + + +heartbeat = s:taboption("general", ListValue, "heartbeat", + translate("Tunnel type"), + translate("Also see <a href=\"https://www.sixxs.net/faq/connectivity/?faq=comparison\">Tunneling Comparison</a> on SIXXS")) +heartbeat:value("0", translate("AYIYA")) +heartbeat:value("1", translate("Heartbeat")) +heartbeat.default = "0" + + +nat = section:taboption("general", Flag, "nat", + translate("Behind NAT"), + translate("The tunnel end-point is behind NAT, defaults to disabled and only applies to AYIYA")) +nat.optional = true +nat.default = disabled + + +requiretls = section:taboption("general", Flag, "requiretls", + translate("Require TLS"), + translate("Connection to server fails when TLS cannot be used")) +requiretls.optional = true +requiretls.default = requiretls.enabled + + +verbose = section:taboption("advanced", Flag, "verbose", + translate("Verbose"), + translate("Verbose logging by aiccu daemon")) +verbose.optional = true +verbose.default = disabled + + +ntpsynctimeout = section:taboption("advanced", Value, "ntpsynctimeout", + translate("NTP sync time-out"), + translate("Wait for NTP sync that many seconds, seting to 0 disables waiting (optional)")) +ntpsynctimeout.datatype = "uinteger" +ntpsynctimeout.placeholder = "90" +ntpsynctimeout.optional = true + + +ip6addr = section:taboption("advanced", Value, "ip6addr", + translate("Local IPv6 address"), + translate("IPv6 address delegated to the local tunnel endpoint (optional)")) +ip6addr.datatype = "ip6addr" +ip6addr.optional = true + + +defaultroute = section:taboption("advanced", Flag, "defaultroute", + translate("Default route"), + translate("Whether to create an IPv6 default route over the tunnel")) +defaultroute.default = defaultroute.enabled +defaultroute.optional = true + + +sourcerouting = section:taboption("advanced", Flag, "sourcerouting", + translate("Source routing"), + translate("Whether to route only packets from delegated prefixes")) +sourcerouting.default = sourcerouting.enabled +sourcerouting.optional = true + + +metric = section:taboption("advanced", Value, "metric", + translate("Use gateway metric")) +metric.datatype = "uinteger" +metric.placeholder = "0" +metric:depends("defaultroute", defaultroute.enabled) + + +ttl = section:taboption("advanced", Value, "ttl", + translate("Use TTL on tunnel interface")) +ttl.datatype = "range(1,255)" +ttl.placeholder = "64" + + +mtu = section:taboption("advanced", Value, "mtu", + translate("Use MTU on tunnel interface"), + translate("minimum 1280, maximum 1480")) +mtu.datatype = "range(1280,1480)" +mtu.placeholder = "1280" + diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua new file mode 100644 index 000000000..5896a278c --- /dev/null +++ b/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua @@ -0,0 +1,49 @@ +-- Copyright 2015 Paul Oranje <por@xs4all.nl> +-- Licensed to the public under GPLv2 + +local netmod = luci.model.network +local interface = luci.model.network.interface + +local proto = netmod:register_protocol("aiccu") + +function proto.get_i18n(self) + return luci.i18n.translate("AICCU (SIXXS)") +end + +function proto.ifname(self) + return "aiccu-" .. self.sid +end + +function proto.get_interface(self) + return interface(self:ifname(), self) +end + +function proto.is_installed(self) + return nixio.fs.access("/lib/netifd/proto/aiccu.sh") +end + +function proto.opkg_package(self) + return "aiccu" +end + +function proto.is_floating(self) + return true +end + +function proto.is_virtual(self) + return true +end + +function proto.get_interfaces(self) + return nil +end + +function proto.contains_interface(self, ifname) + if self:is_floating() then + return (netmod:ifnameof(ifc) == self:ifname()) + else + return netmod.protocol.contains_interface(self, ifc) + end +end + +netmod:register_pattern_virtual("^aiccu-%%w") diff --git a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua index e0e63d863..7b213127f 100644 --- a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua +++ b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua @@ -19,6 +19,9 @@ port = section:taboption("general", Value, "port", translate("VPN Server port")) port.placeholder = "443" port.datatype = "port" +ifname = section:taboption("general", Value, "interface", translate("Output Interface")) +ifname.template = "cbi/network_netlist" + section:taboption("general", Value, "serverhash", translate("VPN Server's certificate SHA1 hash")) section:taboption("general", Value, "authgroup", translate("AuthGroup")) diff --git a/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css b/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css index 9b5ee3a80..b19a6a07a 100644 --- a/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css +++ b/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css @@ -421,6 +421,10 @@ fieldset legend { /* IE6-7 */ } +form .cbi-tab-descr { + line-height: 18px; + margin-bottom: 18px; +} form .clearfix, form .cbi-value { @@ -1929,9 +1933,9 @@ td.cbi-value-field var { float: left; } -.uci-change-legend-label>ins, -.uci-change-legend-label>del, -.uci-change-legend-label>var { +.uci-change-legend-label > ins, +.uci-change-legend-label > del, +.uci-change-legend-label > var { float: left; margin-right: 4px; width: 10px; diff --git a/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/mobile.css b/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/mobile.css index e9d9b61aa..b74f20904 100644 --- a/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/mobile.css +++ b/themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/mobile.css @@ -1,3 +1,21 @@ header h3 a, header .brand { display:none !important; } + +@media screen and (max-device-width: 600px) { + #maincontent.container { + margin-top: 30px; + } +} + +@media screen and (max-device-width: 360px) { + #maincontent.container { + margin-top: 60px; + } +} + +@media screen and (max-device-width: 200px) { + #maincontent.container { + margin-top: 230px; + } +}
\ No newline at end of file diff --git a/themes/luci-theme-bootstrap/root/etc/uci-defaults/luci-theme-bootstrap b/themes/luci-theme-bootstrap/root/etc/uci-defaults/luci-theme-bootstrap index ddefd3b04..09299e092 100755 --- a/themes/luci-theme-bootstrap/root/etc/uci-defaults/luci-theme-bootstrap +++ b/themes/luci-theme-bootstrap/root/etc/uci-defaults/luci-theme-bootstrap @@ -1,6 +1,7 @@ #!/bin/sh uci batch <<-EOF set luci.themes.Bootstrap=/luci-static/bootstrap + set luci.main.mediaurlbase=/luci-static/bootstrap commit luci EOF exit 0 diff --git a/themes/luci-theme-freifunk-bno/root/etc/uci-defaults/luci-theme-freifunk-bno b/themes/luci-theme-freifunk-bno/root/etc/uci-defaults/luci-theme-freifunk-bno index e5fb8a12d..a19f2bb41 100755 --- a/themes/luci-theme-freifunk-bno/root/etc/uci-defaults/luci-theme-freifunk-bno +++ b/themes/luci-theme-freifunk-bno/root/etc/uci-defaults/luci-theme-freifunk-bno @@ -1,6 +1,6 @@ #!/bin/sh uci batch <<-EOF set luci.themes.Freifunk_BNO=/luci-static/freifunk-bno + set luci.main.mediaurlbase=/luci-static/freifunk-bno commit luci EOF - diff --git a/themes/luci-theme-freifunk-generic/luasrc/view/themes/freifunk-generic/header.htm b/themes/luci-theme-freifunk-generic/luasrc/view/themes/freifunk-generic/header.htm index d74916aea..20a41be85 100644 --- a/themes/luci-theme-freifunk-generic/luasrc/view/themes/freifunk-generic/header.htm +++ b/themes/luci-theme-freifunk-generic/luasrc/view/themes/freifunk-generic/header.htm @@ -10,6 +10,7 @@ local util = require "luci.util" local http = require "luci.http" local disp = require "luci.dispatcher" + local version = require "luci.version" local sysinfo = util.ubus("system", "info") or { } local loadinfo = sysinfo.load or { 0, 0, 0 } @@ -122,7 +123,7 @@ <%end%> </div> <div class="header_right"> - <%=luci.version.distversion%><br /> + <%=version.distversion%><br /> <%:Load%>: <%="%.2f" % (loadinfo[1] / 65535.0)%> <%="%.2f" % (loadinfo[2] / 65535.0)%> <%="%.2f" % (loadinfo[3] / 65535.0)%><br /> <%:Hostname%>: <%=boardinfo.hostname or "?"%><br /> <span id="xhr_poll_status" style="display:none" onclick="XHR.running() ? XHR.halt() : XHR.run()"> diff --git a/themes/luci-theme-freifunk-generic/root/etc/uci-defaults/luci-theme-freifunk-generic b/themes/luci-theme-freifunk-generic/root/etc/uci-defaults/luci-theme-freifunk-generic index 131f54e21..017e7f7ba 100644 --- a/themes/luci-theme-freifunk-generic/root/etc/uci-defaults/luci-theme-freifunk-generic +++ b/themes/luci-theme-freifunk-generic/root/etc/uci-defaults/luci-theme-freifunk-generic @@ -1,4 +1,5 @@ uci batch <<-EOF - set luci.themes.Freifunk_Generic=/luci-static/freifunk-generic + set luci.themes.Freifunk_Generic=/luci-static/freifunk-generic + set luci.main.mediaurlbase=/luci-static/freifunk-generic commit luci EOF diff --git a/themes/luci-theme-openwrt/root/etc/uci-defaults/luci-theme-openwrt b/themes/luci-theme-openwrt/root/etc/uci-defaults/luci-theme-openwrt index d8dea0467..aa0ba0c23 100755 --- a/themes/luci-theme-openwrt/root/etc/uci-defaults/luci-theme-openwrt +++ b/themes/luci-theme-openwrt/root/etc/uci-defaults/luci-theme-openwrt @@ -1,6 +1,6 @@ #!/bin/sh uci batch <<-EOF set luci.themes.OpenWrt=/luci-static/openwrt.org + set luci.main.mediaurlbase=/luci-static/openwrt.org commit luci EOF - |