diff options
112 files changed, 7890 insertions, 2216 deletions
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 e1a084a98d..025bc1795b 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 d116f06a46..18906cd1c2 100644 --- a/applications/luci-app-ddns/Makefile +++ b/applications/luci-app-ddns/Makefile @@ -6,17 +6,32 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI Support for Dynamic DNS (ddns-scripts) -LUCI_DEPENDS:=+ddns-scripts +luci-mod-admin-full +# LuCI specific settings +LUCI_TITLE:=LuCI Support for Dynamic DNS Client (ddns-scripts) +LUCI_DEPENDS:=+luci-mod-admin-full +ddns-scripts 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.1.1 + +# Release == build +# increase on changes of translation files +PKG_RELEASE:=0 + +PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> +define Package/$(PKG_NAME)/config +# shown in make menuconfig <Help> +help + $(LUCI_TITLE) + + Version : $(PKG_VERSION)-$(PKG_RELEASE) + Maintainer: $(PKG_MAINTAINER) +endef + include ../../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 3cd52e5e3c..56d2b66e21 100644 --- a/applications/luci-app-ddns/luasrc/controller/ddns.lua +++ b/applications/luci-app-ddns/luasrc/controller/ddns.lua @@ -15,19 +15,21 @@ 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.1.0-2" -- 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) @@ -44,17 +46,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 +59,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( @@ -182,7 +178,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 +206,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 +234,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 057b8c8aa2..77753c03a7 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/detail.lua @@ -22,7 +22,7 @@ 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 +has_force = has_ssl and has_dnstcp -- Force IP Protocoll -- html constants -- ########################################################### font_red = "<font color='red'>" 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 15da0289f1..009ba99eb8 100644 --- a/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua +++ b/applications/luci-app-ddns/luasrc/model/cbi/ddns/hints.lua @@ -11,7 +11,8 @@ local DDNS = require "luci.tools.ddns" -- ddns multiused functions has_ssl = DDNS.check_ssl() -- HTTPS support has_proxy = DDNS.check_proxy() -- Proxy support has_dnstcp = DDNS.check_bind_host() -- DNS TCP support -need_update = CTRL.update_needed() -- correct ddns-scripts version +-- 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">]] 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 2944ec52f9..83b10e9366 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">]] @@ -28,8 +29,17 @@ 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 " .. diff --git a/applications/luci-app-ddns/luasrc/tools/ddns.lua b/applications/luci-app-ddns/luasrc/tools/ddns.lua index 5c8ced50b3..2fbcff8bad 100644 --- a/applications/luci-app-ddns/luasrc/tools/ddns.lua +++ b/applications/luci-app-ddns/luasrc/tools/ddns.lua @@ -96,33 +96,29 @@ function get_pid(section) return pid end --- read version information for given package if installed -function ipkg_version(package) - if not package then +-- 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 - local info = OPKG.info(package) - local data = {} - local version = "" - local i = 0 - for k, v in pairs(info) do - if v.Package == package and v.Status.installed then - version = v.Version - i = i + 1 - end + return (tonumber(SYS.call( + [[opkg compare-versions "]] .. ver1 .. [[" "]] .. comp .. [[" "]] .. ver2 .. [["]] + )) == 1) +end + +-- read version information for given package if installed +function ipkg_ver_installed(pkg) + if not pkg then + return nil end - if i > 1 then -- more then one valid record - return data + -- opkg list-installed [pkg] | cut -d " " -f 3 - return version as sting + local ver = SYS.exec([[opkg list-installed ]] .. pkg .. [[ | cut -d " " -f 3 ]]) + if (#ver > 0) then + return ver 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 nil 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 d516837b2b..0bcfd44283 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 7cb28e282e..cbe76abc6e 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/overview_startstop.htm b/applications/luci-app-ddns/luasrc/view/ddns/overview_startstop.htm index 8255aa63f3..327028cbea 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 37c54be25f..6cca0e75d9 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%>"; @@ -182,8 +155,9 @@ ); } + var ddns_ov_XHR = new XHR(); // force to immediate show status on page load (not waiting for XHR.poll) - XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + ddns_ov_XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, data) { _data2elements(data); } @@ -192,7 +166,7 @@ // define only ONE XHR.poll in a page because if one is running it blocks the other one // optimum is to define on Map or Section Level from here you can reach all elements // we need update every 15 seconds only - XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + ddns_ov_XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, 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 db9d1d1a01..7c60726e24 100644 --- a/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm +++ b/applications/luci-app-ddns/luasrc/view/ddns/system_status.htm @@ -106,7 +106,7 @@ break; } - // monitored interfacce + // monitored interface if (data[i].iface == "_nonet_") tr.insertCell(-1).innerHTML = '<em><%:Config error%></em>'; else @@ -125,14 +125,15 @@ } } + var ddns_status_XHR = new XHR(); // force to immediate show status (not waiting for XHR.poll) - XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + ddns_status_XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, data) { _data2elements(x, data); } ); - XHR.poll(10, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, + ddns_status_XHR.poll(10, '<%=luci.dispatcher.build_url("admin", "services", "ddns", "status")%>', null, function(x, data) { _data2elements(x, data); } diff --git a/applications/luci-app-ddns/po/de/ddns.po b/applications/luci-app-ddns/po/de/ddns.po index 9706dbd106..b965a91fd3 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-01-17 18:28+0100\n" +"PO-Revision-Date: 2015-01-17 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,15 @@ msgstr "" msgid "&" msgstr "&" +msgid "-- custom --" +msgstr "-- benutzerdefiniert --" + +msgid "Advanced Settings" +msgstr "Erweiterte Einstellungen" + +msgid "Applying changes" +msgstr "Änderungen anwenden" + msgid "Basic Settings" msgstr "Grundlegende Einstellungen" @@ -36,9 +45,6 @@ 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 "Build" -msgstr "Build" - msgid "" "BusyBox's nslookup and Wget do not support to specify the IP version to use " "for communication with DDNS Provider." @@ -56,15 +62,15 @@ msgstr "" 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 "Configuration" +msgstr "Einstellungen" + msgid "Configure here the details for selected Dynamic DNS service" msgstr "Konfiguriere hier die Details für den gewählten Dynamik DNS Dienst" @@ -153,6 +159,9 @@ msgstr "" msgid "Details for" msgstr "Details für" +msgid "Disabled" +msgstr "Deaktiviert" + msgid "Dynamic DNS" msgstr "Dynamisches DNS" @@ -166,6 +175,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 +190,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" @@ -203,12 +218,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" @@ -233,6 +242,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 +260,9 @@ msgstr "" msgid "IPv6 not supported" msgstr "IPv6 nicht unterstützt" +msgid "IPv6-Address" +msgstr "IPv6-Adresse" + msgid "" "If this service section is disabled it could not be started.<br />Neither " "from LuCI interface nor from console" @@ -261,6 +276,12 @@ 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" @@ -280,6 +301,9 @@ msgstr "" msgid "Last Update" msgstr "Letztes Aktualisierung" +msgid "Loading" +msgstr "Lade" + msgid "Log File Viewer" msgstr "Protokolldatei" @@ -296,6 +320,9 @@ 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 +338,9 @@ msgstr "Keine Daten" msgid "No logging" msgstr "Keine Protokollierung" +msgid "Notice" +msgstr "Notiz" + msgid "OPTIONAL: Force the usage of pure IPv4/IPv6 only communication." msgstr "" "OPTIONAL: Erzwingt die Verwendung einer reinen IPv4/IPv6 Kommunikation." @@ -338,12 +368,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,17 +416,14 @@ 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" @@ -435,9 +471,24 @@ 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 "" @@ -482,9 +533,6 @@ msgstr "Konnte Server nicht finden:" msgid "config error" msgstr "Konfigurationsfehler" -msgid "custom" -msgstr "benutzerdefiniert" - msgid "days" msgstr "Tage" @@ -500,9 +548,6 @@ 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 "hours" msgstr "Stunden" @@ -512,15 +557,9 @@ 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 +584,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 +600,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" diff --git a/applications/luci-app-ddns/po/templates/ddns.pot b/applications/luci-app-ddns/po/templates/ddns.pot index c3b8c9367d..4b983cec1e 100644 --- a/applications/luci-app-ddns/po/templates/ddns.pot +++ b/applications/luci-app-ddns/po/templates/ddns.pot @@ -4,6 +4,15 @@ msgstr "Content-Type: text/plain; charset=UTF-8" msgid "&" msgstr "" +msgid "-- custom --" +msgstr "" + +msgid "Advanced Settings" +msgstr "" + +msgid "Applying changes" +msgstr "" + msgid "Basic Settings" msgstr "" @@ -18,9 +27,6 @@ msgid "" "separate Configurations i.e. 'myddns_ipv4' and 'myddns_ipv6'" msgstr "" -msgid "Build" -msgstr "" - msgid "" "BusyBox's nslookup and Wget do not support to specify the IP version to use " "for communication with DDNS Provider." @@ -34,13 +40,13 @@ msgstr "" msgid "Check Interval" msgstr "" -msgid "Check for changed IP every" +msgid "Collecting data..." msgstr "" -msgid "Check-time unit" +msgid "Config error" msgstr "" -msgid "Config error" +msgid "Configuration" msgstr "" msgid "Configure here the details for selected Dynamic DNS service" @@ -109,6 +115,9 @@ msgstr "" msgid "Details for" msgstr "" +msgid "Disabled" +msgstr "" + msgid "Dynamic DNS" msgstr "" @@ -120,6 +129,12 @@ msgstr "" msgid "Enable secure communication with DDNS provider" msgstr "" +msgid "Enabled" +msgstr "" + +msgid "Error" +msgstr "" + msgid "Error Retry Counter" msgstr "" @@ -129,7 +144,7 @@ msgstr "" msgid "Event Network" msgstr "" -msgid "Event interface" +msgid "File" msgstr "" msgid "File not found" @@ -155,12 +170,6 @@ msgstr "" msgid "Force TCP on DNS" msgstr "" -msgid "Force update every" -msgstr "" - -msgid "Force-time unit" -msgstr "" - msgid "Forced IP Version don't matched" msgstr "" @@ -185,6 +194,9 @@ msgstr "" msgid "IP address version" msgstr "" +msgid "IPv4-Address" +msgstr "" + msgid "IPv6 address must be given in square brackets" msgstr "" @@ -197,6 +209,9 @@ msgstr "" msgid "IPv6 not supported" msgstr "" +msgid "IPv6-Address" +msgstr "" + msgid "" "If this service section is disabled it could not be started.<br />Neither " "from LuCI interface nor from console" @@ -206,6 +221,12 @@ 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" @@ -220,6 +241,9 @@ msgstr "" msgid "Last Update" msgstr "" +msgid "Loading" +msgstr "" + msgid "Log File Viewer" msgstr "" @@ -234,6 +258,9 @@ msgid "" "protocol." msgstr "" +msgid "Network" +msgstr "" + msgid "Network on which the ddns-updater scripts will be started" msgstr "" @@ -249,6 +276,9 @@ msgstr "" msgid "No logging" msgstr "" +msgid "Notice" +msgstr "" + msgid "OPTIONAL: Force the usage of pure IPv4/IPv6 only communication." msgstr "" @@ -270,12 +300,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,16 +348,13 @@ 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" @@ -361,9 +397,24 @@ 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 "" @@ -397,9 +448,6 @@ msgstr "" msgid "config error" msgstr "" -msgid "custom" -msgstr "" - msgid "days" msgstr "" @@ -415,9 +463,6 @@ msgstr "" msgid "file or directory not found or not 'IGNORE'" msgstr "" -msgid "h" -msgstr "" - msgid "hours" msgstr "" @@ -427,15 +472,9 @@ msgstr "" msgid "installed" msgstr "" -msgid "interface" -msgstr "" - msgid "invalid - Sample" msgstr "" -msgid "min" -msgstr "" - msgid "minimum value '0'" msgstr "" @@ -460,9 +499,6 @@ msgstr "" msgid "nc (netcat) can not connect" msgstr "" -msgid "network" -msgstr "" - msgid "never" msgstr "" @@ -478,7 +514,7 @@ msgstr "" msgid "or" msgstr "" -msgid "or greater" +msgid "or higher" msgstr "" msgid "please disable" 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 3726f643df..3c46e228f7 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 bb7fe3f2e9..91bddc73dd 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 ef0e53daf4..d1cc155fa5 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 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 e2a08acf36..8cf35b79b6 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/luasrc/controller/olsr.lua b/applications/luci-app-olsr/luasrc/controller/olsr.lua index 3615372909..74deb716c4 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 0000000000..4c1fc578a8 --- /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 + +# LuCI specific settings +LUCI_TITLE:=LuCI Support for Privoxy WEB proxy +LUCI_DEPENDS:=+luci-mod-admin-full +privoxy +LUCI_PKGARCH:=all + +PKG_NAME:=luci-app-privoxy + +# Version == major.minor.patch +# increase "minor" on new functionality and "patch" on patches/optimization +PKG_VERSION:=1.0.2 + +# Release == build +# increase on changes of translation files +PKG_RELEASE:=0 + +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com> + +define Package/$(PKG_NAME)/config +# shown in make menuconfig <Help> +help + $(LUCI_TITLE) + + Version : $(PKG_VERSION)-$(PKG_RELEASE) + Maintainer: $(PKG_MAINTAINER) +endef + +include ../../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 0000000000..de73d0c5df --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/controller/privoxy.lua @@ -0,0 +1,117 @@ +-- 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 SYS = require "luci.sys" + +PRIVOXY_MIN = "3.0.22-0" -- minimum version of service required + +function index() + local _sys = require "luci.sys" + local _verinst = _sys.exec([[opkg list-installed ]] .. "privoxy" .. [[ | cut -d " " -f 3 ]]) + local _cmd = [[opkg compare-versions "]] .. _verinst .. [[" ">=" "]] .. "3.0.22-0" .. [["]] + local _verok = tonumber(_sys.call(_cmd)) + + -- check config file and version + if not nixio.fs.access("/etc/config/privoxy") or (_verok == 0) then + entry( {"admin", "services", "privoxy"}, cbi("privoxy/apperror", + {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true }), _("Privoxy WEB proxy"), 59) + else + entry( {"admin", "services", "privoxy"}, cbi("privoxy/detail"), _("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 +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 + +-- get the "name" of the current active theme +function get_theme() + local _uci = UCI.cursor() + local _base = _uci:get("luci", "main", "mediaurlbase") -- only pathname + _uci:unload("luci") + + for k, v in pairs(luci.config.themes) do + if k:sub(1, 1) ~= "." and v == _base then + return k + end + end + return nil +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/apperror.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua new file mode 100755 index 0000000000..fcbb88074d --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua @@ -0,0 +1,47 @@ +-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com> +-- Licensed under the Apache License, Version 2.0 + +local CTRL = require "luci.controller.privoxy" -- this application's controller +local DISP = require "luci.dispatcher" +local SYS = require "luci.sys" + +local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]] + +-- cbi-map -- ################################################################## +local m = Map("privoxy") +m.title = [[</a><a href="javascript:alert(']] + .. translate("Version Information") + .. [[\n\nluci-app-privoxy]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] + .. SYS.exec([[opkg list-installed ]] .. [[luci-app-privoxy]] .. [[ | cut -d " " -f 3 ]]) + .. [[\n\nprivoxy ]] .. translate("required") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher") + .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] + .. SYS.exec([[opkg list-installed ]] .. [[privoxy]] .. [[ | cut -d " " -f 3 ]]) + .. [[\n\n]] + .. [[')">]] + .. translate("Privoxy WEB proxy") +m.description = 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.") + +-- cbi-section -- ############################################################## +local s = m:section(SimpleSection) +s.title = [[<font color="red">]] .. [[<strong>]] + .. translate("Software update required") + .. [[</strong>]] .. [[</font>]] + +-- old privoxy sofware version -------------------------------------------------------------- +local v = s:option(DummyValue, "_update_needed") +v.titleref = DISP.build_url("admin", "system", "packages") +v.rawhtml = true +--v.title = [[<h3>]] .. [[<font color="red">]] .. [[<strong>]] +-- .. translate("Software update required") +-- .. [[</strong>]] .. [[</font>]] .. [[</h3>]] .. [[<br />]] +v.value = [[<h3>]] .. [[<strong>]] + .. translate("The currently installed 'privoxy' package is not supported by LuCI application.") + .. [[<br />]] + .. translate("Please update to the current version!") + .. [[</strong>]] .. [[</h3>]] +return m diff --git a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua new file mode 100755 index 0000000000..2c846a1f99 --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua @@ -0,0 +1,928 @@ +-- Copyright 2014 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 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 LFLF = (CTRL.get_theme() == "Bootstrap") and [[<br /><br /><br />]] or [[]] +local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]] + +-- cbi-map -- ################################################################## +local m = Map("privoxy") +m.title = [[</a><a href="javascript:alert(']] + .. translate("Version Information") + .. [[\n\nluci-app-privoxy]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] + .. SYS.exec([[opkg list-installed ]] .. [[luci-app-privoxy]] .. [[ | cut -d " " -f 3 ]]) + .. [[\n\nprivoxy ]] .. translate("required") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher") + .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]] + .. [[\n\t]] .. translate("Version") .. [[:\t]] + .. SYS.exec([[opkg list-installed ]] .. [[privoxy]] .. [[ | cut -d " " -f 3 ]]) + .. [[\n\n]] + .. [[')">]] + .. translate("Privoxy WEB proxy") +m.description = 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>]] +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.") + .. LFLF ) +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.") + .. LFLF ) +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.") + .. LFLF ) +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.") + .. LFLF ) + +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", DynamicList, "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 td = ns:taboption("filter", Value, "templdir") +td.title_base = "Template Directory" +td.title = string.format(HELP, "TEMPLDIR", td.title_base ) +td.description = translate("An alternative directory where the templates are loaded from.") + .. [[<br />]] + .. translate("No trailing '/', please.") +td.placeholder = "/etc/privoxy/templates" +td.rmempty = true +function td.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 + +-- 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 = "sites.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 ----------------------------------------------------------------------- +local d1 = ns:taboption("debug", Flag, "debug_1") +d1.title = string.format(HELP, "DEBUG", "Debug 1" ) +d1.description = translate("Log the destination for each request Privoxy let through. See also 'Debug 1024'.") +d1.rmempty = true +function d1.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d2 = ns:taboption("debug", Flag, "debug_2") +d2.title = string.format(HELP, "DEBUG", "Debug 2" ) +d2.description = translate("Show each connection status") +d2.rmempty = true +function d2.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d3 = ns:taboption("debug", Flag, "debug_4") +d3.title = string.format(HELP, "DEBUG", "Debug 4" ) +d3.description = translate("Show I/O status") +d3.rmempty = true +function d3.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d4 = ns:taboption("debug", Flag, "debug_8") +d4.title = string.format(HELP, "DEBUG", "Debug 8" ) +d4.description = translate("Show header parsing") +d4.rmempty = true +function d4.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d5 = ns:taboption("debug", Flag, "debug_16") +d5.title = string.format(HELP, "DEBUG", "Debug 16" ) +d5.description = translate("Log all data written to the network") +d5.rmempty = true +function d5.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d6 = ns:taboption("debug", Flag, "debug_32") +d6.title = string.format(HELP, "DEBUG", "Debug 32" ) +d6.description = translate("Debug force feature") +d6.rmempty = true +function d6.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d7 = ns:taboption("debug", Flag, "debug_64") +d7.title = string.format(HELP, "DEBUG", "Debug 64" ) +d7.description = translate("Debug regular expression filters") +d7.rmempty = true +function d7.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d8 = ns:taboption("debug", Flag, "debug_128") +d8.title = string.format(HELP, "DEBUG", "Debug 128" ) +d8.description = translate("Debug redirects") +d8.rmempty = true +function d8.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d9 = ns:taboption("debug", Flag, "debug_256") +d9.title = string.format(HELP, "DEBUG", "Debug 256" ) +d9.description = translate("Debug GIF de-animation") +d9.rmempty = true +function d9.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d10 = ns:taboption("debug", Flag, "debug_512") +d10.title = string.format(HELP, "DEBUG", "Debug 512" ) +d10.description = translate("Common Log Format") +d10.rmempty = true +function d10.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d11 = ns:taboption("debug", Flag, "debug_1024") +d11.title = string.format(HELP, "DEBUG", "Debug 1024" ) +d11.description = translate("Log the destination for requests Privoxy didn't let through, and the reason why.") +d11.rmempty = true +function d11.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d12 = ns:taboption("debug", Flag, "debug_2048") +d12.title = string.format(HELP, "DEBUG", "Debug 2048" ) +d12.description = translate("CGI user interface") +d12.rmempty = true +function d12.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d13 = ns:taboption("debug", Flag, "debug_4096") +d13.title = string.format(HELP, "DEBUG", "Debug 4096" ) +d13.description = translate("Startup banner and warnings.") +d13.rmempty = true +function d13.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +local d14 = ns:taboption("debug", Flag, "debug_8192") +d14.title = string.format(HELP, "DEBUG", "Debug 8192" ) +d14.description = translate("Non-fatal errors - *we highly recommended enabling this*") +d14.rmempty = true +function d14.parse(self, section) + CTRL.flag_parse(self, section) +end + +-- debug ----------------------------------------------------------------------- +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 ----------------------------------------------------------------------- +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 0000000000..3e190709fb --- /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 0000000000..b9de8864e4 --- /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 0000000000..6ee3af47b7 --- /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 0000000000..8f836bef0f --- /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 0000000000..3405479b54 --- /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 4068cdbf54..0d738326a0 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 97d0400822..4add43559f 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 67bb2fc49e..831fa75f65 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 0f8bdc2c47..e566e9b508 100755 --- a/applications/luci-app-splash/root/usr/sbin/luci-splash +++ b/applications/luci-app-splash/root/usr/sbin/luci-splash @@ -2,14 +2,11 @@ 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 fs = require "nixio.fs" local ip = require "luci.ip" @@ -139,6 +136,30 @@ function ipvalid(ipaddr) return false end +function mac_to_ip(mac) + ipc.neighbors({ family = 4 }, function(n) + if n.mac == mac and n.dest then + return n.dest:string() + end + end) +end + +function mac_to_dev(mac) + ipc.neighbors({ family = 4 }, function(n) + if n.mac == mac and n.dev then + return n.dev + end + end) +end + +function ip_to_mac(ip) + ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest and n.dest:equal(ip) then + return n.mac + end + end) +end + function main(argv) local cmd = table.remove(argv, 1) local arg = argv[1] @@ -157,7 +178,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 +187,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 +292,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 +361,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 +598,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 +616,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 +641,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 +650,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 +691,6 @@ end -- Show client info function list() - local arpcache = get_arpcache() -- Find traffic usage local function traffic(lease) local traffic_in = 0 @@ -722,12 +719,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 +734,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/contrib/luadoc/hostfiles/bin/luadoc b/build/luadoc/doc.lua index ba99a3775e..383dde29cd 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 9e4b9de3c5..9e4b9de3c5 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 0b75f84cb9..0b75f84cb9 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 2d725389c9..2d725389c9 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 e77fb74417..e77fb74417 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 2e35392ad6..2e35392ad6 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 67926b4a75..68f486404f 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 a870ff8bba..29d403e00f 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 b4b9f5c3b6..b4b9f5c3b6 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 bc0f98a5af..f9f9749519 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 0fe365202e..0fe365202e 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 daa708628b..0798c1be07 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 5cd023953a..5cd023953a 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 1e880b8830..1e880b8830 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 649515de68..649515de68 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 adf84f9f0e..adf84f9f0e 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 17a305889d..ef925f8c73 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 d03df82dfc..e9d0354830 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 acaaac8281..e1058d5906 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 2f7f573450..a9fc760e7a 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 4a90dae355..d3f0403263 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 81a96f6a83..0000000000 --- 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/libs/luci-lib-httpclient/luasrc/httpclient.lua b/libs/luci-lib-httpclient/luasrc/httpclient.lua index 94c2e9ecac..c76cc542ed 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 0000000000..eb80dcb258 --- /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 0000000000..76abd27d2a --- /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 0000000000..b91966c536 --- /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 0000000000..00738832c4 --- /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 57b12c9405..416b25faa5 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 0000000000..d48dba13b9 --- /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 0000000000..6a63dab5ef --- /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 0000000000..e15fbac382 --- /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 0000000000..49cb21f5bc --- /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 0000000000..2ee9cebdc8 --- /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 fb1cf160b6..aa31841402 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 b957a69903..ee3e3a216c 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 aaa4292bd9..a7e9a77d99 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 fc52b7eb07..5558910bf1 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 0000000000..8c90ab5247 --- /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 666d58585f..275c396992 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 0000000000..980ef46d97 --- /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 8337fea711..80bbda107a 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 155d31b10f..795b62bedb 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,22 @@ 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 + http.header("Set-Cookie", "sysauth=; path="..build_url()) + 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 +166,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,7 +328,6 @@ 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) if not user or not util.contains(accs, user) then return @@ -364,6 +351,7 @@ function dispatch(request) if sess then http.header("Set-Cookie", "sysauth=" .. sess.."; path="..build_url()) + http.redirect(build_url(unpack(ctx.requestpath))) ctx.authsession = sess ctx.authuser = user end @@ -445,7 +433,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 +496,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 +534,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 +543,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 +556,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 +567,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 +631,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 +646,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 +684,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 +693,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 +798,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 +810,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 +838,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 0000000000..743463c74f --- /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 a5329e51d6..a92d8affb6 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 0000000000..4e31216a1e --- /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 aeb7ea6211..e9efb44cfa 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 0000000000..67a60d9e7a --- /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 1d40425ff9..d31a4e38a4 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 0000000000..9cfe02dd50 --- /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 3105f37f63..e440219a9c 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 0000000000..d6f1c8d658 --- /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 15da15cf8b..2b99d8e74e 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 0000000000..195b5fcc89 --- /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 dd84a59f84..bcb16d5c04 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 0000000000..aa38841e17 --- /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 d8aaea91d2..0000000000 --- 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 b59fb8c48a..3a7268ccae 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 216caa5cca..587637272d 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 0000000000..cf0985f94a --- /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 8ac82773f3..1659137742 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 0000000000..1c208669d1 --- /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 1e594e1c88..3977da3eda 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 0000000000..72a16a1ab0 --- /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 6715937c69..2b81e0ee38 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 0000000000..071e7d52e4 --- /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 6337496692..97a3608ea7 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 facfd0c8bf..c8c908b6ea 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 9adac29bfa..8273175de7 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 42de3dd729..8b28b1752d 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 0000000000..1c09b7a9ab --- /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/system.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua index 055142b53e..52e347d07b 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 88e81bb18c..997a9274d2 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 da7c1181f7..fafacf35c5 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/routes.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua index 01580f1016..ac02b156e9 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/view/admin_network/wifi_overview.htm b/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm index ea60a7f078..b7c44f9073 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 2d9a4a3dc8..82dd3a7dfe 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 5d3d8ad22a..9a1c1fea45 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 715ac756fd..72bd136c5e 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 @@ -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-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua index e0e63d8631..7b213127f8 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")) |