From: Jo-Philipp Wich Date: Sun, 8 Feb 2015 18:57:20 +0000 (+0100) Subject: Merge pull request #302 from chris5560/master X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=commitdiff_plain;h=76dfdc1b1e7a62b363954f01f71f5a1e77d5249a;hp=55ab4e4ce221091480f8c693ceefb1d0ac32c18f Merge pull request #302 from chris5560/master luci-themes: luci.main.mediaurlbase not set correctly --- diff --git a/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua b/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua index e1a084a98..025bc1795 100644 --- a/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua +++ b/applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua @@ -2,8 +2,8 @@ -- Copyright 2008 Jo-Philipp Wich -- 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-firewall/luasrc/view/firewall/cbi_addforward.htm b/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm index 3726f643d..3c46e228f 100644 --- a/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm +++ b/applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm @@ -6,7 +6,8 @@ for _, z in ipairs(fw:get_zones()) do if z:name() ~= "wan" then izl[#izl+1] = z - elseif z:name() ~= "lan" then + end + if z:name() ~= "lan" then ezl[#ezl+1] = z end end diff --git a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua index 23f6f5c84..d1cc155fa 100644 --- a/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua +++ b/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua @@ -90,6 +90,10 @@ local pip = s:taboption("general", Flag, "predictable_ips", translate("Predictab translate("The assigned IPs will be selected deterministically")) pip.default = "1" +local compr = s:taboption("general", Flag, "compression", translate("Enable compression"), + translate("Enable compression")) +compr.default = "1" + local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"), translate("Enable UDP channel support; this must be enabled unless you know what you are doing")) udp.default = "1" diff --git a/applications/luci-app-olsr/luasrc/controller/olsr.lua b/applications/luci-app-olsr/luasrc/controller/olsr.lua index 361537290..74deb716c 100644 --- a/applications/luci-app-olsr/luasrc/controller/olsr.lua +++ b/applications/luci-app-olsr/luasrc/controller/olsr.lua @@ -1,5 +1,8 @@ module("luci.controller.olsr", package.seeall) +local neigh_table = nil +local ifaddr_table = nil + function index() local ipv4,ipv6 if nixio.fs.access("/etc/config/olsrd") then @@ -93,6 +96,46 @@ function action_json() http.write('{"v4":' .. jsonreq4 .. ', "v6":' .. jsonreq6 .. '}') end + +local function local_mac_lookup(ipaddr) + local _, ifa, dev + + ipaddr = tostring(ipaddr) + + if not ifaddr_table then + ifaddr_table = nixio.getifaddrs() + end + + -- ipaddr -> ifname + for _, ifa in ipairs(ifaddr_table) do + if ifa.addr == ipaddr then + dev = ifa.name + break + end + end + + -- ifname -> macaddr + for _, ifa in ipairs(ifaddr_table) do + if ifa.name == dev and ifa.family == "packet" then + return ifa.addr + end + end +end + +local function remote_mac_lookup(ipaddr) + local _, n + + if not neigh_table then + neigh_table = luci.ip.neighbors() + end + + for _, n in ipairs(neigh_table) do + if n.mac and n.dest and n.dest:equal(ipaddr) then + return n.mac + end + end +end + function action_neigh(json) local data, has_v4, has_v6, error = fetch_jsoninfo('links') @@ -107,17 +150,13 @@ function action_neigh(json) local sys = require "luci.sys" local assoclist = {} --local neightbl = require "neightbl" + local ntm = require "luci.model.network" local ipc = require "luci.ip" + local nxo = require "nixio" + local defaultgw - luci.sys.net.routes(function(r) - if r.dest:prefix() == 0 then - defaultgw = r.gateway:string() - end - end) - - if not defaultgw then - defaultgw = luci.util.exec("ip route list exact 0.0.0.0/0 table all"):match(" via (%S+)") - end + ipc.routes({ family = 4, type = 1, dest_exact = "0.0.0.0/0" }, + function(rt) defaultgw = rt.gw end) local function compare(a,b) if a.proto == b.proto then @@ -138,14 +177,10 @@ function action_neigh(json) end for k, v in ipairs(data) do - local interface local snr = 0 local signal = 0 local noise = 0 - local arptable = sys.net.arptable() local mac = "" - local rmac = "" - local lmac = "" local ip local neihgt = {} @@ -155,45 +190,11 @@ function action_neigh(json) v.hostname = hostname end end - if v.proto == '4' then - uci:foreach("network", "interface",function(vif) - if vif.ipaddr and vif.ipaddr == v.localIP then - interface = vif['.name'] or vif.interface - lmac = string.lower(vif.macaddr or "") - return - end - end) - for _, arpt in ipairs(arptable) do - ip = arpt['IP address'] - if ip == v.remoteIP then - rmac = string.lower(arpt['HW address'] or "") - end - end - elseif v.proto == '6' then - uci:foreach("network", "interface",function(vif) - local name = vif['.name'] - local net = ntm:get_network(name) - local device = net and net:get_interface() - local locip = ipc.IPv6(v.localIP) - if device and device:ip6addrs() and locip then - for _, a in ipairs(device:ip6addrs()) do - if not a:is6linklocal() then - if a:host() == locip:host() then - interface = name - --neihgt = neightbl.get(device.ifname) or {} - end - end - end - end - end) - --[[ - for ip,mac in pairs(neihgt) do - if ip == v.remoteIP then - rmac = mac - end - end - ]]-- - end + + local interface = ntm:get_status_by_address(v.localIP) + local lmac = local_mac_lookup(v.localIP) + local rmac = remote_mac_lookup(v.remoteIP) + for _, val in ipairs(assoclist) do if val.network == interface and val.list then for assocmac, assot in pairs(val.list) do diff --git a/applications/luci-app-privoxy/Makefile b/applications/luci-app-privoxy/Makefile new file mode 100644 index 000000000..4c1fc578a --- /dev/null +++ b/applications/luci-app-privoxy/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2008-2015 The LuCI Team +# +# 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 + +define Package/$(PKG_NAME)/config +# shown in make menuconfig +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 000000000..de73d0c5d --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/controller/privoxy.lua @@ -0,0 +1,117 @@ +-- Copyright 2014 Christian Schoenebeck +-- 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 000000000..fcbb88074 --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua @@ -0,0 +1,47 @@ +-- Copyright 2014 Christian Schoenebeck +-- 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 = [[%s]] + +-- cbi-map -- ################################################################## +local m = Map("privoxy") +m.title = [[]] + .. 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 = [[]] .. [[]] + .. translate("Software update required") + .. [[]] .. [[]] + +-- old privoxy sofware version -------------------------------------------------------------- +local v = s:option(DummyValue, "_update_needed") +v.titleref = DISP.build_url("admin", "system", "packages") +v.rawhtml = true +--v.title = [[

]] .. [[]] .. [[]] +-- .. translate("Software update required") +-- .. [[]] .. [[]] .. [[

]] .. [[
]] +v.value = [[

]] .. [[]] + .. translate("The currently installed 'privoxy' package is not supported by LuCI application.") + .. [[
]] + .. translate("Please update to the current version!") + .. [[
]] .. [[

]] +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 000000000..2c846a1f9 --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua @@ -0,0 +1,928 @@ +-- Copyright 2014 Christian Schoenebeck +-- 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 [[


]] or [[]] +local HELP = [[
%s]] + +-- cbi-map -- ################################################################## +local m = Map("privoxy") +m.title = [[]] + .. 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.") + .. [[
]] + .. translate("For help use link at the relevant option") + .. [[]] +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.") + .. [[
]] + .. translate("The value of this option only matters if the experimental trust mechanism has been activated.") + .. [[]] +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).") + .. [[
]] + .. 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.") + .. [[
]] + .. 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.") + .. [[
]] + .. 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!") + .. [[
match-all.action := ]] + .. translate("Actions that are applied to all sites and maybe overruled later on.") + .. [[
default.action := ]] + .. translate("Main actions file") + .. [[
user.action := ]] + .. 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.") + .. [[
]] + .. translate("It is NOT recommended for the casual user.") + .. [[]] +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.") + .. [[
]] + .. 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.") + .. [[
]] + .. translate("Please read Privoxy manual for details!") + .. [[]] +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.") + .. [[
]] + .. translate("Please read Privoxy manual for details!") + .. [[]] +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.") + .. [[
]] + .. 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.") + .. [[
]] + .. 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.") + .. [[
]] + .. translate("This option will be removed in future releases as it has been obsoleted by the more general header taggers.") + .. [[]] +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.") + .. [[
]] + .. translate("Enabling this option is NOT recommended if there is no parent proxy that requires authentication!") + .. [[]] +--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.") + .. [[
]] + .. 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.") + .. [[
]] + .. 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.") + .. [[
]] + .. 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.") + .. [[
]] + .. translate("This option is only there for debugging purposes. It will drastically reduce performance.") + .. [[]] +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 000000000..3e190709f --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm @@ -0,0 +1,56 @@ + + + + +<%+cbi/valueheader%> + +
+ +<% +-- one button on top, one at the buttom +%> + /> + +

+ +<% +-- 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 +%> + +

+ +<% +-- one button on top, one at the buttom +%> + /> + +<%+cbi/valuefooter%> + diff --git a/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm new file mode 100644 index 000000000..b9de8864e --- /dev/null +++ b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm @@ -0,0 +1,49 @@ + + + + +<%+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%;" type="button" onclick="onclick_startstop(this.id)" + <%= + attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle) .. ifattr(self.disabled, "disabled") + %> /> +<% end %> + +<%+cbi/valuefooter%> + diff --git a/applications/luci-app-privoxy/po/de/privoxy.po b/applications/luci-app-privoxy/po/de/privoxy.po new file mode 100644 index 000000000..6ee3af47b --- /dev/null +++ b/applications/luci-app-privoxy/po/de/privoxy.po @@ -0,0 +1,496 @@ +msgid "" +msgstr "" +"Project-Id-Version: luci-app-privoxy\n" +"POT-Creation-Date: 2015-01-18 21:48+0100\n" +"PO-Revision-Date: 2015-01-18 21:51+0100\n" +"Last-Translator: Christian Schoenebeck \n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgid "" +"A URL to be displayed in the error page that users will see if access to an " +"untrusted page is denied." +msgstr "" +"Ein Link auf der Fehlerseite, der Benutzern angezeigt wird, wenn der Zugang " +"zu einer nicht vertrauenswürdigen Seite verweigert wird." + +msgid "" +"A URL to documentation about the local Privoxy setup, configuration or " +"policies." +msgstr "" +"Ein Link zur Dokumentation über die lokale Privoxy Konfiguration und die " +"Sicherheitseinstellungen." + +msgid "Access Control" +msgstr "Zugriffskontrolle" + +msgid "Actions that are applied to all sites and maybe overruled later on." +msgstr "" +"Aktionen, die für alle Websites angewendet werden, und vielleicht später " +"überschrieben werden." + +msgid "An alternative directory where the templates are loaded from." +msgstr "Eine alternatives Verzeichnis, aus dem die Vorlagen geladen werden." + +msgid "An email address to reach the Privoxy administrator." +msgstr "Eine E-Mail-Adresse, um die Privoxy-Administrator zu erreichen." + +msgid "" +"Assumed server-side keep-alive timeout (in seconds) if not specified by the " +"server." +msgstr "" +"Angenommenes serverseitiges Keep-Alive-Timeout (in Sekunden), falls nicht " +"vom Server festgelegt." + +msgid "CGI user interface" +msgstr "Protokolliert die CGI Benutzer Schnittstelle" + +msgid "Common Log Format" +msgstr "Gemeinsames Protokollformat" + +msgid "" +"Configure here the routing of HTTP requests through a chain of multiple " +"proxies. Note that parent proxies can severely decrease your privacy level. " +"Also specified here are SOCKS proxies." +msgstr "" +"Konfigurieren Sie hier das Weiterleiten von HTTP-Anforderungen durch eine " +"Kette von mehreren Proxies. Beachten Sie, dass übergeordnete Proxies Ihre " +"Privatsphäre stark verringern können. Auch hier angegeben werden SOCKS-" +"Proxies." + +msgid "Debug GIF de-animation" +msgstr "Protokolliert die GIF de-animation" + +msgid "Debug force feature" +msgstr "Protokolliert die 'Force' Eigenschaft" + +msgid "Debug redirects" +msgstr "Protokolliert Weiterleitungen" + +msgid "Debug regular expression filters" +msgstr "Protokolliert Filter für reguläre Ausdrücke" + +msgid "Directory does not exist!" +msgstr "Verzeichnis existiert nicht!" + +msgid "Disabled == Transparent Proxy Mode" +msgstr "Deaktiviert == Transparent Proxy Betrieb" + +msgid "Enable proxy authentication forwarding" +msgstr "Aktivieren die Weiterleitung von Proxy-Authentifizierungen" + +msgid "" +"Enable/Disable autostart of Privoxy on system startup and interface events" +msgstr "" +"Aktivieren / Deaktivieren des Autostart von Privoxy beim Systemstart und " +"Schnittstellenereignissen." + +msgid "Enable/Disable filtering when Privoxy starts." +msgstr "Aktivieren / Deaktivieren der Filterung, wenn Privoxy startet." + +msgid "Enabled" +msgstr "Aktiviert" + +msgid "" +"Enabling this option is NOT recommended if there is no parent proxy that " +"requires authentication!" +msgstr "" +"Die Aktivierung dieser Option wird NICHT empfohlen, wenn es keinen " +"übergeordneten Proxy gibt, der eine Authentifizierung erfordert!" + +msgid "File '%s' not found inside Configuration Directory" +msgstr "Datei '%s' nicht im Konfigurationsverzeichnis gefunden!" + +msgid "File not found or empty" +msgstr "Datei nicht gefunden oder leer" + +msgid "Files and Directories" +msgstr "Dateien und Verzeichnisse" + +msgid "For help use link at the relevant option" +msgstr "" +"Für Hilfe zur Verwendung, benutzen Sie die Verknüpfung der betreffenden " +"Option." + +msgid "Forwarding" +msgstr "Weiterleitung" + +msgid "" +"If enabled, Privoxy hides the 'go there anyway' link. The user obviously " +"should not be able to bypass any blocks." +msgstr "" +"Wenn aktiviert, verbirgt Privoxy den Link 'go there anyway'. Normalerweise " +"sollten Benutzer nicht in der Lage sein, Blockierungen zu umgehen." + +msgid "" +"If you intend to operate Privoxy for more users than just yourself, it might " +"be a good idea to let them know how to reach you, what you block and why you " +"do that, your policies, etc." +msgstr "" +"Wenn Sie beabsichtigen, Privoxy für mehr Nutzer als nur sich selbst zu " +"betreiben, ist es eine gute Idee, sie wissen zu lassen, wie sie Sie " +"erreichen können, was Sie blockieren und warum Sie das tun, etc." + +msgid "Invalid email address" +msgstr "Ungültige Email Adresse" + +msgid "It is NOT recommended for the casual user." +msgstr "Es wird NICHT für den gelegentlichen Anwender empfohlen." + +msgid "Local Set-up" +msgstr "Lokale Einstellungen" + +msgid "Location of the Privoxy User Manual." +msgstr "Ort des Privoxy Benutzer Handbuches" + +msgid "Log File Viewer" +msgstr "Protokolldatei" + +msgid "Log all data read from the network" +msgstr "Protokolliert alle Daten, die vom Netzwerk gelesen werden." + +msgid "Log all data written to the network" +msgstr "Protokolliert alle Daten, die auf das Netzwerk geschrieben werden." + +msgid "Log the applying actions" +msgstr "Protokiolliert angewendete Aktionen" + +msgid "" +"Log the destination for each request Privoxy let through. See also 'Debug " +"1024'." +msgstr "" +"Protokolliert das Ziel für jede Anforderung die Privoxy durchlässt. Siehe " +"auch 'Debug 1024'." + +msgid "" +"Log the destination for requests Privoxy didn't let through, and the reason " +"why." +msgstr "" +"Protokolliert das Ziel für Anfragen die Privoxy nicht durchgelassen hat, und " +"den Grund dafür." + +msgid "Logging" +msgstr "Protokollierung" + +msgid "Main actions file" +msgstr "Wichtige Aktionen-Datei" + +msgid "Mandatory Input: No Data given!" +msgstr "Pflichtfeld: Keine Daten angegeben!" + +msgid "Mandatory Input: No Directory given!" +msgstr "Pflichtfeld: Kein Verzeichnis angegeben!" + +msgid "Mandatory Input: No File given!" +msgstr "Pflichtfeld: Keine Datei angegeben!" + +msgid "Mandatory Input: No Port given!" +msgstr "Pflichtfeld: Kein Port angegeben!" + +msgid "Mandatory Input: No files given!" +msgstr "Pflichtfeld: Keine Dateien angegeben!" + +msgid "Mandatory Input: No valid IPv4 address or host given!" +msgstr "" +"Pflichtfeld: Keine gültige IPv4 Adresse oder gültiger Hostname angegeben!" + +msgid "Mandatory Input: No valid IPv6 address given!" +msgstr "Pflichtfeld: Keine gültige IPv6 Adresse angegeben!" + +msgid "Mandatory Input: No valid Port given!" +msgstr "Pflichtfeld: Keine gültige Port Nummer angegeben!" + +msgid "Maximum number of client connections that will be served." +msgstr "Maximale Anzahl von Client-Verbindungen." + +msgid "Maximum size (in KB) of the buffer for content filtering." +msgstr "Maximale Größe (in KB) des Puffers für die Inhaltsfilterung." + +msgid "Miscellaneous" +msgstr "Verschiedenes" + +msgid "No trailing '/', please." +msgstr "Bitte kein '/' am Ende." + +msgid "Non-fatal errors - *we highly recommended enabling this*" +msgstr "" +"Protokolliert nicht schwerwiegende Fehler - * Es wird dringend empfohlen, " +"dieses zu aktivieren *" + +msgid "" +"Number of seconds after which a socket times out if no data is received." +msgstr "" +"Anzahl der Sekunden, nach der eine Socket Timeout erfolgt, wenn keine Daten " +"empfangen werden." + +msgid "" +"Number of seconds after which an open connection will no longer be reused." +msgstr "" +"Anzahl von Sekunden, nach der eine offene Verbindung nicht mehr " +"wiederverwendet wird." + +msgid "Please press [Read] button" +msgstr "Bitte Protokolldatei einlesen" + +msgid "Please read Privoxy manual for details!" +msgstr "Bitte lesen Sie das Privoxy Handbuch für Details!" + +msgid "Please update to the current version!" +msgstr "Aktualisieren Sie bitte auf die aktuelle Version!" + +msgid "Privoxy WEB proxy" +msgstr "Privoxy WEB proxy" + +msgid "" +"Privoxy can (and normally does) use a number of other files for additional " +"configuration, help and logging. This section of the configuration file " +"tells Privoxy where to find those other files." +msgstr "" +"Privoxy verwendet (was in der Regel der Fall ist), eine Reihe von anderen " +"Dateien für eine zusätzliche Konfiguration, Hilfe und Protokollierung. " +"Dieser Abschnitt der Konfigurationsdatei definiert, wo diese Dateien zu " +"finden sind." + +msgid "" +"Privoxy is a non-caching web proxy with advanced filtering capabilities for " +"enhancing privacy, modifying web page data and HTTP headers, controlling " +"access, and removing ads and other obnoxious Internet junk." +msgstr "" +"Privoxy ist ein non-caching Web-Proxy mit erweiterten Filterfunktion zur " +"Verbesserung der Privatsphäre. Er modifiziert Webseitendaten und HTTP-" +"Header, kontrolliert den Zugang und das Entfernen von Anzeigen und anderem " +"abscheulichen Internet Schrott." + +msgid "Read / Reread log file" +msgstr "Protokolldatei (neu) lesen" + +msgid "Show I/O status" +msgstr "Protokolliert den I/O Status" + +msgid "Show each connection status" +msgstr "Protokolliert jeden Verbindungsstatus" + +msgid "Show header parsing" +msgstr "Protokolliert das 'Header parsing'" + +msgid "Software update required" +msgstr "Softwareaktualisierung nötig" + +msgid "Start" +msgstr "Start" + +msgid "Start / Stop" +msgstr "Start / Stopp" + +msgid "Start/Stop Privoxy WEB Proxy" +msgstr "Start/Stopp Privoxy WEB Proxy" + +msgid "Startup banner and warnings." +msgstr "Protokolliert Start-Meldungen und Warnungen" + +msgid "Syntax:" +msgstr "Syntax:" + +msgid "Syntax: Client header names delimited by spaces." +msgstr "Syntax: Client header Namen getrennt durch Leerzeichen." + +msgid "Syntax: target_pattern http_parent[:port]" +msgstr "Syntax: target_pattern http_parent[:port]" + +msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" +msgstr "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" + +msgid "" +"The actions file(s) to use. Multiple actionsfile lines are permitted, and " +"are in fact recommended!" +msgstr "" +"Die zu verwendenden Aktion-Datei(en). Mehrere Dateien sind gestattet und " +"empfohlen!" + +msgid "" +"The address and TCP port on which Privoxy will listen for client requests." +msgstr "" +"Die Adresse und das TCP-Port, auf dem Privoxy auf Client-Anforderungen " +"wartet." + +msgid "" +"The compression level that is passed to the zlib library when compressing " +"buffered content." +msgstr "" +"Die Komprimierungsstufe (0-9), die der zlib-Bibliothek beim Komprimieren " +"gepufferten Inhaltes übergeben wird." + +msgid "" +"The currently installed 'privoxy' package is not supported by LuCI " +"application." +msgstr "" +"Das aktuell installierte 'privoxy' Paket wird von dieser LuCI Anwendung " +"NICHT unterstützt." + +msgid "" +"The directory where all logging takes place (i.e. where the logfile is " +"located)." +msgstr "Das Verzeichnis in dem die Protokolldatei gespeichert wird." + +msgid "The directory where the other configuration files are located." +msgstr "Das Verzeichnis in dem weitere Konfigurationsdateien gespeichert sind." + +msgid "" +"The filter files contain content modification rules that use regular " +"expressions." +msgstr "" +"Die Filterdateien enthalten Änderung des Inhalts, die reguläre Ausdrücke " +"als Regeln verwenden." + +msgid "The hostname shown on the CGI pages." +msgstr "Der Hostname der auf CGI-Seiten angezeigt wird." + +msgid "The log file to use. File name, relative to log directory." +msgstr "" +"Zu verwendende Protokolldatei. Dateiname relativ zum Protokoll-Verzeichnis." + +msgid "The order in which client headers are sorted before forwarding them." +msgstr "" +"Die Reihenfolge, in der Client-Header sortiert werden, bevor sie " +"weitergeleitet werden." + +msgid "" +"The status code Privoxy returns for pages blocked with +handle-as-empty-" +"document." +msgstr "" +"Ob Statuscode 200(OK) oder 403(forbidden) für Seiten gemeldet wird, die " +"durch den Filter 'handle-as-empty-document' blockiert werden." + +msgid "" +"The trust mechanism is an experimental feature for building white-lists and " +"should be used with care." +msgstr "" +"Der Trust-Mechanismus ist eine experimentelle Funktion für den Aufbau von " +"White-Listen und sollte mit Vorsicht verwendet werden." + +msgid "" +"The value of this option only matters if the experimental trust mechanism " +"has been activated." +msgstr "" +"Der Wert dieser Option ist nur wirksam, wenn der experimentelle Trust-" +"Mechanismus aktiviert wurde." + +msgid "" +"This option is only there for debugging purposes. It will drastically reduce " +"performance." +msgstr "" +"Diese Option ist ausschließlich zur Fehlersuche. Es wird drastisch die " +"Leistung beeinträchtigt." + +msgid "" +"This option will be removed in future releases as it has been obsoleted by " +"the more general header taggers." +msgstr "Diese Option wird in zukünftigen Versionen entfernt werden." + +msgid "" +"This tab controls the security-relevant aspects of Privoxy's configuration." +msgstr "" +"Diese Registerkarte steuert die sicherheitsrelevanten Aspekte der Privoxy " +"Konfiguration." + +msgid "" +"Through which SOCKS proxy (and optionally to which parent HTTP proxy) " +"specific requests should be routed." +msgstr "" +"An welchen SOCKS-Proxy (und gegebenenfalls an welchen übergeordneten HTTP-" +"Proxy) spezifischen Anforderungen weitergeleitet werden." + +msgid "To which parent HTTP proxy specific requests should be routed." +msgstr "" +"An welchen übergeordneten HTTP-Proxy spezifischen Anforderungen " +"weitergeleitet werden." + +msgid "User customizations" +msgstr "Benutzerdefinierte Anpassungen" + +msgid "Value is not a number" +msgstr "Eingabe ist keine Zahl" + +msgid "Value not between 0 and 9" +msgstr "Wert nicht zwischen 0 und 9" + +msgid "Value not between 1 and 4096" +msgstr "Wert nicht zwischen 1 und 4096" + +msgid "Value not greater 0 or empty" +msgstr "Wert nicht größer 0 oder leer" + +msgid "Value range 1 to 4096, no entry defaults to 4096" +msgstr "Wertebereich: 1 bis 4096; Keine Angabe setzt 4096." + +msgid "Version" +msgstr "Version" + +msgid "Version Information" +msgstr "Versionsinformation" + +msgid "Whether intercepted requests should be treated as valid." +msgstr "Ob abgefangen Anfragen als gültig behandelt werden." + +msgid "" +"Whether or not Privoxy recognizes special HTTP headers to change toggle " +"state." +msgstr "" +"Ob Privoxy erkannte spezielle HTTP-Header zur Änderung des Toggle-Status " +"verwendet.." + +msgid "Whether or not buffered content is compressed before delivery." +msgstr "" +"Ob gepufferte Inhalte vor der Weiterleitung komprimiert werden oder nicht." + +msgid "" +"Whether or not outgoing connections that have been kept alive should be " +"shared between different incoming connections." +msgstr "" +"Ob ausgehende Verbindungen, die am Leben gehalten werden, für verschiedenen " +"eingehenden Verbindungen gemeinsam genutzt werden oder nicht." + +msgid "Whether or not pipelined requests should be served." +msgstr "Ob Pipeline-Anfragen bedient werden oder nicht." + +msgid "Whether or not proxy authentication through Privoxy should work." +msgstr "" +"Ob Proxy-Authentifizierungen durch Privoxy weitergeleitet werden oder nicht." + +msgid "Whether or not the web-based actions file editor may be used." +msgstr "De-/Aktiviert den webbasierte Action-Datei Editor." + +msgid "Whether or not the web-based toggle feature may be used." +msgstr "De-Aktiviert die webbasierte Umschaltfunktion." + +msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected." +msgstr "" +"Ob Anfragen an Privoxy CGI-Seiten gesperrt oder umgeleitet werden können " +"oder nicht." + +msgid "" +"Whether the CGI interface should stay compatible with broken HTTP clients." +msgstr "" +"Ob die CGI-Schnittstelle mit broken HTTP-Clients kompatibel bleibt oder " +"nicht." + +msgid "Whether to run only one server thread." +msgstr "Ob nur ein Server-Thread ausgeführt wird." + +msgid "Who can access what." +msgstr "Wer kann auf Was zugreifen." + +msgid "installed" +msgstr "installiert" + +msgid "or higher" +msgstr "oder höher" + +msgid "required" +msgstr "benötigt" diff --git a/applications/luci-app-privoxy/po/templates/privoxy.pot b/applications/luci-app-privoxy/po/templates/privoxy.pot new file mode 100644 index 000000000..8f836bef0 --- /dev/null +++ b/applications/luci-app-privoxy/po/templates/privoxy.pot @@ -0,0 +1,405 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "" +"A URL to be displayed in the error page that users will see if access to an " +"untrusted page is denied." +msgstr "" + +msgid "" +"A URL to documentation about the local Privoxy setup, configuration or " +"policies." +msgstr "" + +msgid "Access Control" +msgstr "" + +msgid "Actions that are applied to all sites and maybe overruled later on." +msgstr "" + +msgid "An alternative directory where the templates are loaded from." +msgstr "" + +msgid "An email address to reach the Privoxy administrator." +msgstr "" + +msgid "" +"Assumed server-side keep-alive timeout (in seconds) if not specified by the " +"server." +msgstr "" + +msgid "CGI user interface" +msgstr "" + +msgid "Common Log Format" +msgstr "" + +msgid "" +"Configure here the routing of HTTP requests through a chain of multiple " +"proxies. Note that parent proxies can severely decrease your privacy level. " +"Also specified here are SOCKS proxies." +msgstr "" + +msgid "Debug GIF de-animation" +msgstr "" + +msgid "Debug force feature" +msgstr "" + +msgid "Debug redirects" +msgstr "" + +msgid "Debug regular expression filters" +msgstr "" + +msgid "Directory does not exist!" +msgstr "" + +msgid "Disabled == Transparent Proxy Mode" +msgstr "" + +msgid "Enable proxy authentication forwarding" +msgstr "" + +msgid "" +"Enable/Disable autostart of Privoxy on system startup and interface events" +msgstr "" + +msgid "Enable/Disable filtering when Privoxy starts." +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "" +"Enabling this option is NOT recommended if there is no parent proxy that " +"requires authentication!" +msgstr "" + +msgid "File '%s' not found inside Configuration Directory" +msgstr "" + +msgid "File not found or empty" +msgstr "" + +msgid "Files and Directories" +msgstr "" + +msgid "For help use link at the relevant option" +msgstr "" + +msgid "Forwarding" +msgstr "" + +msgid "" +"If enabled, Privoxy hides the 'go there anyway' link. The user obviously " +"should not be able to bypass any blocks." +msgstr "" + +msgid "" +"If you intend to operate Privoxy for more users than just yourself, it might " +"be a good idea to let them know how to reach you, what you block and why you " +"do that, your policies, etc." +msgstr "" + +msgid "Invalid email address" +msgstr "" + +msgid "It is NOT recommended for the casual user." +msgstr "" + +msgid "Local Set-up" +msgstr "" + +msgid "Location of the Privoxy User Manual." +msgstr "" + +msgid "Log File Viewer" +msgstr "" + +msgid "Log all data read from the network" +msgstr "" + +msgid "Log all data written to the network" +msgstr "" + +msgid "Log the applying actions" +msgstr "" + +msgid "" +"Log the destination for each request Privoxy let through. See also 'Debug " +"1024'." +msgstr "" + +msgid "" +"Log the destination for requests Privoxy didn't let through, and the reason " +"why." +msgstr "" + +msgid "Logging" +msgstr "" + +msgid "Main actions file" +msgstr "" + +msgid "Mandatory Input: No Data given!" +msgstr "" + +msgid "Mandatory Input: No Directory given!" +msgstr "" + +msgid "Mandatory Input: No File given!" +msgstr "" + +msgid "Mandatory Input: No Port given!" +msgstr "" + +msgid "Mandatory Input: No files given!" +msgstr "" + +msgid "Mandatory Input: No valid IPv4 address or host given!" +msgstr "" + +msgid "Mandatory Input: No valid IPv6 address given!" +msgstr "" + +msgid "Mandatory Input: No valid Port given!" +msgstr "" + +msgid "Maximum number of client connections that will be served." +msgstr "" + +msgid "Maximum size (in KB) of the buffer for content filtering." +msgstr "" + +msgid "Miscellaneous" +msgstr "" + +msgid "No trailing '/', please." +msgstr "" + +msgid "Non-fatal errors - *we highly recommended enabling this*" +msgstr "" + +msgid "" +"Number of seconds after which a socket times out if no data is received." +msgstr "" + +msgid "" +"Number of seconds after which an open connection will no longer be reused." +msgstr "" + +msgid "Please press [Read] button" +msgstr "" + +msgid "Please read Privoxy manual for details!" +msgstr "" + +msgid "Please update to the current version!" +msgstr "" + +msgid "Privoxy WEB proxy" +msgstr "" + +msgid "" +"Privoxy can (and normally does) use a number of other files for additional " +"configuration, help and logging. This section of the configuration file " +"tells Privoxy where to find those other files." +msgstr "" + +msgid "" +"Privoxy is a non-caching web proxy with advanced filtering capabilities for " +"enhancing privacy, modifying web page data and HTTP headers, controlling " +"access, and removing ads and other obnoxious Internet junk." +msgstr "" + +msgid "Read / Reread log file" +msgstr "" + +msgid "Show I/O status" +msgstr "" + +msgid "Show each connection status" +msgstr "" + +msgid "Show header parsing" +msgstr "" + +msgid "Software update required" +msgstr "" + +msgid "Start" +msgstr "" + +msgid "Start / Stop" +msgstr "" + +msgid "Start/Stop Privoxy WEB Proxy" +msgstr "" + +msgid "Startup banner and warnings." +msgstr "" + +msgid "Syntax:" +msgstr "" + +msgid "Syntax: Client header names delimited by spaces." +msgstr "" + +msgid "Syntax: target_pattern http_parent[:port]" +msgstr "" + +msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]" +msgstr "" + +msgid "" +"The actions file(s) to use. Multiple actionsfile lines are permitted, and " +"are in fact recommended!" +msgstr "" + +msgid "" +"The address and TCP port on which Privoxy will listen for client requests." +msgstr "" + +msgid "" +"The compression level that is passed to the zlib library when compressing " +"buffered content." +msgstr "" + +msgid "" +"The currently installed 'privoxy' package is not supported by LuCI " +"application." +msgstr "" + +msgid "" +"The directory where all logging takes place (i.e. where the logfile is " +"located)." +msgstr "" + +msgid "The directory where the other configuration files are located." +msgstr "" + +msgid "" +"The filter files contain content modification rules that use regular " +"expressions." +msgstr "" + +msgid "The hostname shown on the CGI pages." +msgstr "" + +msgid "The log file to use. File name, relative to log directory." +msgstr "" + +msgid "The order in which client headers are sorted before forwarding them." +msgstr "" + +msgid "" +"The status code Privoxy returns for pages blocked with +handle-as-empty-" +"document." +msgstr "" + +msgid "" +"The trust mechanism is an experimental feature for building white-lists and " +"should be used with care." +msgstr "" + +msgid "" +"The value of this option only matters if the experimental trust mechanism " +"has been activated." +msgstr "" + +msgid "" +"This option is only there for debugging purposes. It will drastically reduce " +"performance." +msgstr "" + +msgid "" +"This option will be removed in future releases as it has been obsoleted by " +"the more general header taggers." +msgstr "" + +msgid "" +"This tab controls the security-relevant aspects of Privoxy's configuration." +msgstr "" + +msgid "" +"Through which SOCKS proxy (and optionally to which parent HTTP proxy) " +"specific requests should be routed." +msgstr "" + +msgid "To which parent HTTP proxy specific requests should be routed." +msgstr "" + +msgid "User customizations" +msgstr "" + +msgid "Value is not a number" +msgstr "" + +msgid "Value not between 0 and 9" +msgstr "" + +msgid "Value not between 1 and 4096" +msgstr "" + +msgid "Value not greater 0 or empty" +msgstr "" + +msgid "Value range 1 to 4096, no entry defaults to 4096" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Version Information" +msgstr "" + +msgid "Whether intercepted requests should be treated as valid." +msgstr "" + +msgid "" +"Whether or not Privoxy recognizes special HTTP headers to change toggle " +"state." +msgstr "" + +msgid "Whether or not buffered content is compressed before delivery." +msgstr "" + +msgid "" +"Whether or not outgoing connections that have been kept alive should be " +"shared between different incoming connections." +msgstr "" + +msgid "Whether or not pipelined requests should be served." +msgstr "" + +msgid "Whether or not proxy authentication through Privoxy should work." +msgstr "" + +msgid "Whether or not the web-based actions file editor may be used." +msgstr "" + +msgid "Whether or not the web-based toggle feature may be used." +msgstr "" + +msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected." +msgstr "" + +msgid "" +"Whether the CGI interface should stay compatible with broken HTTP clients." +msgstr "" + +msgid "Whether to run only one server thread." +msgstr "" + +msgid "Who can access what." +msgstr "" + +msgid "installed" +msgstr "" + +msgid "or higher" +msgstr "" + +msgid "required" +msgstr "" diff --git a/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy b/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy new file mode 100755 index 000000000..3405479b5 --- /dev/null +++ b/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy @@ -0,0 +1,12 @@ +#!/bin/sh + +# no longer needed for "Save and Apply" to restart privoxy +# luci-app-privoxy calls /etc/init.d/privoxy reload +uci -q batch <<-EOF >/dev/null + delete ucitrack.@privoxy[-1] + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache + +exit 0 diff --git a/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua b/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua index 4068cdbf5..0d738326a 100644 --- a/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua +++ b/applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua @@ -1,23 +1,24 @@ -- Copyright 2008 Steven Barth --- Copyright 2008 Jo-Philipp Wich +-- Copyright 2008-2015 Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. local uci = require "luci.model.uci".cursor() local bit = require "nixio".bit +local ip = require "luci.ip" -------------------- Init -------------------- -- -- Find link-local address -- -LL_PREFIX = luci.ip.IPv6("fe80::/64") function find_ll() - for _, r in ipairs(luci.sys.net.routes6()) do - if LL_PREFIX:contains(r.dest) and r.dest:higher(LL_PREFIX) then - return r.dest:sub(LL_PREFIX) + local _, r + for _, r in ipairs(ip.routes({ family = 6, dest = "fe80::/64" })) do + if r.dest:higher("fe80:0:0:0:ff:fe00:0:0") then + return (r.dest - "fe80::") end end - return luci.ip.IPv6("::") + return ip.IPv6("::") end -- @@ -33,15 +34,15 @@ local ipv4_netsz = uci:get("siit", "ipv4", "netsize") or "24" -- -- Find IPv4 allocation pool -- -local gv4_net = luci.ip.IPv4(ipv4_pool) +local gv4_net = ip.IPv4(ipv4_pool) -- -- Generate ULA -- -local ula = luci.ip.IPv6("::/64") +local ula = ip.IPv6("::/64") for _, prefix in ipairs({ ula_prefix, ula_global, ula_subnet }) do - ula = ula:add(luci.ip.IPv6(prefix)) + ula = ula:add(ip.IPv6(prefix)) end ula = ula:add(find_ll()) @@ -72,7 +73,7 @@ uci:foreach("wireless", "wifi-device", lanip = f:field(Value, "ipaddr", "LAN IPv4 subnet") function lanip.formvalue(self, section) local val = self.map:formvalue(self:cbid(section)) - local net = luci.ip.IPv4("%s/%i" %{ val, ipv4_netsz }) + local net = ip.IPv4("%s/%i" %{ val, ipv4_netsz }) if net then if gv4_net:contains(net) then @@ -110,7 +111,7 @@ function mode.write(self, section, value) -- -- Find LAN IPv4 range -- - local lan_net = luci.ip.IPv4( + local lan_net = ip.IPv4( ( lanip:formvalue(section) or "172.16.0.1" ) .. "/" .. ipv4_netsz ) @@ -182,7 +183,7 @@ function mode.write(self, section, value) }) -- use full siit subnet - siit_route = luci.ip.IPv6(siit_prefix .. "/96") + siit_route = ip.IPv6(siit_prefix .. "/96") -- v4 <-> siit route uci:delete_all("network", "route", @@ -212,7 +213,7 @@ function mode.write(self, section, value) }) -- derive siit subnet from lan config - siit_route = luci.ip.IPv6( + siit_route = ip.IPv6( siit_prefix .. "/" .. (96 + lan_net:prefix()) ):add(lan_net[2]) @@ -301,7 +302,7 @@ function mode.write(self, section, value) -- siit0 route uci:delete_all("network", "route6", - function(s) return siit_route:contains(luci.ip.IPv6(s.target)) end) + function(s) return siit_route:contains(ip.IPv6(s.target)) end) uci:section("network", "route6", nil, { interface = "siit0", diff --git a/applications/luci-app-splash/luasrc/controller/splash/splash.lua b/applications/luci-app-splash/luasrc/controller/splash/splash.lua index 97d040082..4add43559 100644 --- a/applications/luci-app-splash/luasrc/controller/splash/splash.lua +++ b/applications/luci-app-splash/luasrc/controller/splash/splash.lua @@ -23,14 +23,26 @@ function index() page.leaf = true end +function ip_to_mac(ip) + local ipc = require "luci.ip" + local i, n + + for i, n in ipairs(ipc.neighbors()) do + if n.mac and n.dest and n.dest:equal(ip) then + return n.mac + end + end +end + function action_dispatch() local uci = luci.model.uci.cursor_state() - local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR")) or "" + local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR")) or "" local access = false uci:foreach("luci_splash", "lease", function(s) if s.mac and s.mac:lower() == mac then access = true end end) + uci:foreach("luci_splash", "whitelist", function(s) if s.mac and s.mac:lower() == mac then access = true end end) @@ -51,13 +63,13 @@ function blacklist() end function action_activate() - local ip = luci.http.getenv("REMOTE_ADDR") or "127.0.0.1" - local mac = luci.sys.net.ip4mac(ip:match("^[\[::ffff:]*(%d+.%d+%.%d+%.%d+)\]*$")) + local ipc = require "luci.ip" + local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR") or "127.0.0.1") or "" local uci_state = require "luci.model.uci".cursor_state() local blacklisted = false if mac and luci.http.formvalue("accept") then uci:foreach("luci_splash", "blacklist", - function(s) if s.mac:lower() == mac or s.mac == mac then blacklisted = true end + function(s) if s.mac and s.mac:lower() == mac then blacklisted = true end end) if blacklisted then luci.http.redirect(luci.dispatcher.build_url("splash" ,"blocked")) diff --git a/applications/luci-app-splash/luasrc/view/admin_status/splash.htm b/applications/luci-app-splash/luasrc/view/admin_status/splash.htm index 67bb2fc49..831fa75f6 100644 --- a/applications/luci-app-splash/luasrc/view/admin_status/splash.htm +++ b/applications/luci-app-splash/luasrc/view/admin_status/splash.htm @@ -9,6 +9,7 @@ local utl = require "luci.util" local ipt = require "luci.sys.iptparser".IptParser() local uci = require "luci.model.uci".cursor_state() local wat = require "luci.tools.webadmin" +local ipc = require "luci.ip" local fs = require "nixio.fs" local clients = { } @@ -100,10 +101,12 @@ if fs.access(leasefile) then end end -for i, a in ipairs(luci.sys.net.arptable()) do - local c = clients[a["HW address"]:lower()] - if c and not c.ip then - c.ip = a["IP address"] +for i, n in ipairs(ipc.neighbors({ family = 4 })) do + if n.mac and n.dest then + local c = clients[n.mac] + if c and not c.ip then + c.ip = n.dest:string() + end end end diff --git a/applications/luci-app-splash/root/usr/sbin/luci-splash b/applications/luci-app-splash/root/usr/sbin/luci-splash index 0f8bdc2c4..e566e9b50 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/build/luadoc/doc.lua b/build/luadoc/doc.lua new file mode 100755 index 000000000..383dde29c --- /dev/null +++ b/build/luadoc/doc.lua @@ -0,0 +1,126 @@ +#!/usr/bin/env lua +------------------------------------------------------------------------------- +-- LuaDoc launcher. +-- @release $Id: luadoc.lua.in,v 1.1 2008/02/17 06:42:51 jasonsantos Exp $ +------------------------------------------------------------------------------- + +--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. + +local function print_version () + print (string.format("%s\n%s\n%s", + luadoc._VERSION, + luadoc._DESCRIPTION, + luadoc._COPYRIGHT)) +end + +------------------------------------------------------------------------------- +-- Print usage message. + +local function print_help () + print ("Usage: "..arg[0]..[[ [options|files] +Generate documentation from files. Available options are: + -d path output directory path + -t path template directory path + -h, --help print this help and exit + --noindexpage do not generate global index page + --nofiles do not generate documentation for files + --nomodules do not generate documentation for modules + --doclet doclet_module doclet module to generate output + --taglet taglet_module taglet module to parse input code + -q, --quiet suppress all normal output + -v, --version print version information]]) +end + +local function off_messages (arg, i, options) + options.verbose = nil +end + +------------------------------------------------------------------------------- +-- Process options. TODO: use getopts. +-- @class table +-- @name OPTIONS + +local OPTIONS = { + d = function (arg, i, options) + local dir = arg[i+1] + if string.sub (dir, -2) ~= "/" then + dir = dir..'/' + end + options.output_dir = dir + return 1 + end, + t = function (arg, i, options) + local dir = arg[i+1] + if string.sub (dir, -2) ~= "/" then + dir = dir..'/' + end + options.template_dir = dir + return 1 + end, + h = print_help, + help = print_help, + q = off_messages, + quiet = off_messages, + v = print_version, + version = print_version, + doclet = function (arg, i, options) + options.doclet = arg[i+1] + return 1 + end, + taglet = function (arg, i, options) + options.taglet = arg[i+1] + return 1 + end, +} + +------------------------------------------------------------------------------- + +local function process_options (arg) + local files = {} + local options = require "luadoc.config" + local i = 1 + while i <= #arg do + local argi = arg[i] + if string.sub (argi, 1, 1) ~= '-' then + table.insert (files, argi) + else + local opt = string.sub (argi, 2) + if string.sub (opt, 1, 1) == '-' then + opt = string.gsub (opt, "%-", "") + end + if OPTIONS[opt] then + if OPTIONS[opt] (arg, i, options) then + i = i + 1 + end + else + options[opt] = 1 + end + end + i = i+1 + end + return files, options +end + +------------------------------------------------------------------------------- +-- Main function. Process command-line parameters and call luadoc processor. + +function main (arg) + -- Process options + local argc = #arg + if argc < 1 then + print_help () + return + end + local files, options = process_options (arg) + return luadoc.main(files, options) +end + +main(arg) diff --git a/build/luadoc/luadoc/config.lua b/build/luadoc/luadoc/config.lua new file mode 100644 index 000000000..9e4b9de3c --- /dev/null +++ b/build/luadoc/luadoc/config.lua @@ -0,0 +1,34 @@ +------------------------------------------------------------------------------- +-- LuaDoc configuration file. This file contains the default options for +-- luadoc operation. These options can be overriden by the command line tool +-- @see luadoc.print_help +-- @release $Id: config.lua,v 1.6 2007/04/18 14:28:39 tomas Exp $ +------------------------------------------------------------------------------- + +module "luadoc.config" + +------------------------------------------------------------------------------- +-- Default options +-- @class table +-- @name default_options +-- @field output_dir default output directory for generated documentation, used +-- by several doclets +-- @field taglet parser used to analyze source code input +-- @field doclet documentation generator +-- @field template_dir directory with documentation templates, used by the html +-- doclet +-- @field verbose command line tool configuration to output processing +-- information + +local default_options = { + output_dir = "", + taglet = "luadoc.taglet.standard", + doclet = "luadoc.doclet.html", + -- TODO: find a way to define doclet specific options + template_dir = "luadoc/doclet/html/", + nomodules = false, + nofiles = false, + verbose = true, +} + +return default_options diff --git a/build/luadoc/luadoc/doclet/debug.lua b/build/luadoc/luadoc/doclet/debug.lua new file mode 100644 index 000000000..0b75f84cb --- /dev/null +++ b/build/luadoc/luadoc/doclet/debug.lua @@ -0,0 +1,46 @@ +----------------------------------------------------------------- +-- LuaDoc debugging facilities. +-- @release $Id: debug.lua,v 1.3 2007/04/18 14:28:39 tomas Exp $ +----------------------------------------------------------------- + +module "luadoc.doclet.debug" + +function printline() + print(string.rep('-', 79)) +end + +----------------------------------------------------------------- +-- Print debug information about document +-- @param doc Table with the structured documentation. + +function start (doc) + print("Files:") + for _, filepath in ipairs(doc.files) do + print('\t', filepath) + end + printline() + + print("Modules:") + for _, modulename in ipairs(doc.modules) do + print('\t', modulename) + end + printline() + + for i, v in pairs(doc.files) do + print('\t', i, v) + end + printline() + for i, v in pairs(doc.files[doc.files[1]]) do + print(i, v) + end + + printline() + for i, v in pairs(doc.files[doc.files[1]].doc[1]) do + print(i, v) + end + printline() + print("Params") + for i, v in pairs(doc.files[doc.files[1]].doc[1].param) do + print(i, v) + end +end diff --git a/build/luadoc/luadoc/doclet/formatter.lua b/build/luadoc/luadoc/doclet/formatter.lua new file mode 100644 index 000000000..2d725389c --- /dev/null +++ b/build/luadoc/luadoc/doclet/formatter.lua @@ -0,0 +1,84 @@ +------------------------------------------------------------------------------- +-- Doclet to format source code according to LuaDoc standard tags. This doclet +-- (re)write .lua files adding missing standard tags. Texts are formatted to +-- 80 columns and function parameters are added based on code analysis. +-- +-- @release $Id: formatter.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $ +------------------------------------------------------------------------------- + +local util = require "luadoc.util" +local assert, ipairs, pairs, type = assert, ipairs, pairs, type +local string = require"string" +local table = require"table" + +module "luadoc.doclet.formatter" + +options = { + output_dir = "./", +} + +------------------------------------------------------------------------------- +-- Assembly the output filename for an input file. +-- TODO: change the name of this function +function out_file (filename) + local h = filename + h = options.output_dir..h + return h +end + +------------------------------------------------------------------------------- +-- Generate a new lua file for each input lua file. If the user does not +-- specify a different output directory input files will be rewritten. +-- @param doc documentation table + +function start (doc) + local todo = "" + + -- Process files + for i, file_doc in ipairs(doc.files) do + -- assembly the filename + local filename = out_file(file_doc.name) + luadoc.logger:info(string.format("generating file `%s'", filename)) + + -- TODO: confirm file overwrite + local f = posix.open(filename, "w") + assert(f, string.format("could not open `%s' for writing", filename)) + + for _, block in ipairs(file_doc.doc) do + + -- write reorganized comments + f:write(string.rep("-", 80).."\n") + + -- description + f:write(util.comment(util.wrap(block.description, 77))) + f:write("\n") + + if block.class == "function" then + -- parameters + table.foreachi(block.param, function (_, param_name) + f:write(util.comment(util.wrap(string.format("@param %s %s", param_name, block.param[param_name] or todo), 77))) + f:write("\n") + end) + + -- return + if type(block.ret) == "table" then + table.foreachi(block.ret, function (_, ret) + f:write(util.comment(util.wrap(string.format("@return %s", ret), 77)).."\n") + end) + else + f:write(util.comment(util.wrap(string.format("@return %s", block.ret or todo), 77)).."\n") + end + end + + -- TODO: usage + -- TODO: see + + -- write code + for _, line in ipairs(block.code) do + f:write(line.."\n") + end + end + + f:close() + end +end diff --git a/build/luadoc/luadoc/doclet/html.lua b/build/luadoc/luadoc/doclet/html.lua new file mode 100644 index 000000000..e77fb7441 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html.lua @@ -0,0 +1,275 @@ +------------------------------------------------------------------------------- +-- Doclet that generates HTML output. This doclet generates a set of html files +-- based on a group of templates. The main templates are: +--
    +--
  • index.lp: index of modules and files;
  • +--
  • file.lp: documentation for a lua file;
  • +--
  • module.lp: documentation for a lua module;
  • +--
  • function.lp: documentation for a lua function. This is a +-- sub-template used by the others.
  • +--
+-- +-- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $ +------------------------------------------------------------------------------- + +local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type +local io = require"io" +local posix = require "nixio.fs" +local lp = require "luadoc.lp" +local luadoc = require"luadoc" +local package = package +local string = require"string" +local table = require"table" + +module "luadoc.doclet.html" + +------------------------------------------------------------------------------- +-- Looks for a file `name' in given path. Removed from compat-5.1 +-- @param path String with the path. +-- @param name String with the name to look for. +-- @return String with the complete path of the file found +-- or nil in case the file is not found. + +local function search (path, name) + for c in string.gfind(path, "[^;]+") do + c = string.gsub(c, "%?", name) + local f = io.open(c) + if f then -- file exist? + f:close() + return c + end + end + return nil -- file not found +end + +------------------------------------------------------------------------------- +-- Include the result of a lp template into the current stream. + +function include (template, env) + -- template_dir is relative to package.path + local templatepath = options.template_dir .. template + + -- search using package.path (modified to search .lp instead of .lua + local search_path = string.gsub(package.path, "%.lua", "") + local templatepath = search(search_path, templatepath) + assert(templatepath, string.format("template `%s' not found", template)) + + env = env or {} + env.table = table + env.io = io + env.lp = lp + env.ipairs = ipairs + env.tonumber = tonumber + env.tostring = tostring + env.type = type + env.luadoc = luadoc + env.options = options + + return lp.include(templatepath, env) +end + +------------------------------------------------------------------------------- +-- Returns a link to a html file, appending "../" to the link to make it right. +-- @param html Name of the html file to link to +-- @return link to the html file + +function link (html, from) + local h = html + from = from or "" + string.gsub(from, "/", function () h = "../" .. h end) + return h +end + +------------------------------------------------------------------------------- +-- Returns the name of the html file to be generated from a module. +-- Files with "lua" or "luadoc" extensions are replaced by "html" extension. +-- @param modulename Name of the module to be processed, may be a .lua file or +-- a .luadoc file. +-- @return name of the generated html file for the module + +function module_link (modulename, doc, from) + -- TODO: replace "." by "/" to create directories? + -- TODO: how to deal with module names with "/"? + assert(modulename) + assert(doc) + from = from or "" + + if doc.modules[modulename] == nil then +-- logger:error(string.format("unresolved reference to module `%s'", modulename)) + return + end + + local href = "modules/" .. modulename .. ".html" + string.gsub(from, "/", function () href = "../" .. href end) + return href +end + +------------------------------------------------------------------------------- +-- Returns the name of the html file to be generated from a lua(doc) file. +-- Files with "lua" or "luadoc" extensions are replaced by "html" extension. +-- @param to Name of the file to be processed, may be a .lua file or +-- a .luadoc file. +-- @param from path of where am I, based on this we append ..'s to the +-- beginning of path +-- @return name of the generated html file + +function file_link (to, from) + assert(to) + from = from or "" + + local href = to + href = string.gsub(href, "lua$", "html") + href = string.gsub(href, "luadoc$", "html") + href = "files/" .. href + string.gsub(from, "/", function () href = "../" .. href end) + return href +end + +------------------------------------------------------------------------------- +-- Returns a link to a function or to a table +-- @param fname name of the function or table to link to. +-- @param doc documentation table +-- @param kind String specying the kinf of element to link ("functions" or "tables"). + +function link_to (fname, doc, module_doc, file_doc, from, kind) + assert(fname) + assert(doc) + from = from or "" + kind = kind or "functions" + + if file_doc then + for _, func_name in pairs(file_doc[kind]) do + if func_name == fname then + return file_link(file_doc.name, from) .. "#" .. fname + end + end + end + + if module_doc and module_doc[kind] then + for func_name, tbl in pairs(module_doc[kind]) do + if func_name == fname then + return "#" .. fname + end + end + end + + local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$") + assert(fname) + + -- if fname does not specify a module, use the module_doc + if string.len(modulename) == 0 and module_doc then + modulename = module_doc.name + end + + local module_doc = doc.modules[modulename] + if not module_doc then +-- logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename)) + return + end + + for _, func_name in pairs(module_doc[kind]) do + if func_name == fname then + return module_link(modulename, doc, from) .. "#" .. fname + end + end + +-- logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename)) +end + +------------------------------------------------------------------------------- +-- Make a link to a file, module or function + +function symbol_link (symbol, doc, module_doc, file_doc, from) + assert(symbol) + assert(doc) + + local href = +-- file_link(symbol, from) or + module_link(symbol, doc, from) or + link_to(symbol, doc, module_doc, file_doc, from, "functions") or + link_to(symbol, doc, module_doc, file_doc, from, "tables") + + if not href then + logger:error(string.format("unresolved reference to symbol `%s'", symbol)) + end + + return href or "" +end + +------------------------------------------------------------------------------- +-- Assembly the output filename for an input file. +-- TODO: change the name of this function +function out_file (filename) + local h = filename + h = string.gsub(h, "lua$", "html") + h = string.gsub(h, "luadoc$", "html") + h = "files/" .. h +-- h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1") + h = options.output_dir .. h + return h +end + +------------------------------------------------------------------------------- +-- Assembly the output filename for a module. +-- TODO: change the name of this function +function out_module (modulename) + local h = modulename .. ".html" + h = "modules/" .. h + h = options.output_dir .. h + return h +end + +----------------------------------------------------------------- +-- Generate the output. +-- @param doc Table with the structured documentation. + +function start (doc) + -- Generate index file + if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then + local filename = options.output_dir.."index.html" + logger:info(string.format("generating file `%s'", filename)) + local f = posix.open(filename, "w") + assert(f, string.format("could not open `%s' for writing", filename)) + io.output(f) + include("index.lp", { doc = doc }) + f:close() + end + + -- Process modules + if not options.nomodules then + for _, modulename in ipairs(doc.modules) do + local module_doc = doc.modules[modulename] + -- assembly the filename + local filename = out_module(modulename) + logger:info(string.format("generating file `%s'", filename)) + + local f = posix.open(filename, "w") + assert(f, string.format("could not open `%s' for writing", filename)) + io.output(f) + include("module.lp", { doc = doc, module_doc = module_doc }) + f:close() + end + end + + -- Process files + if not options.nofiles then + for _, filepath in ipairs(doc.files) do + local file_doc = doc.files[filepath] + -- assembly the filename + local filename = out_file(file_doc.name) + logger:info(string.format("generating file `%s'", filename)) + + local f = posix.open(filename, "w") + assert(f, string.format("could not open `%s' for writing", filename)) + io.output(f) + include("file.lp", { doc = doc, file_doc = file_doc} ) + f:close() + end + end + + -- copy extra files + local f = posix.open(options.output_dir.."luadoc.css", "w") + io.output(f) + include("luadoc.css") + f:close() +end diff --git a/build/luadoc/luadoc/doclet/html/constant.lp b/build/luadoc/luadoc/doclet/html/constant.lp new file mode 100644 index 000000000..2e35392ad --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/constant.lp @@ -0,0 +1,28 @@ +<% +if module_doc then + from = "modules/"..module_doc.name +elseif file_doc then + from = "files/.."..file_doc.name +else + from = "" +end +%> + +
<%=const.private and "local " or ""%><%=const.name:gsub(".+%.","")%>
+
+<%=const.description or ""%> + +<%if type(const.see) == "string" then %> +

See also:

+ <%=const.see%> +<%elseif type(const.see) == "table" and #const.see > 0 then %> +

See also:

+ +
diff --git a/build/luadoc/luadoc/doclet/html/file.lp b/build/luadoc/luadoc/doclet/html/file.lp new file mode 100644 index 000000000..68f486404 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/file.lp @@ -0,0 +1,112 @@ + + + + Reference + + + + + +
+ +
+ +
+
+
+ +
+ + + +
+ +

File <%=file_doc.name%>

+ +<%if file_doc.description then%> +

<%=file_doc.description%>

+<%end%> +<%if file_doc.author then%> +

<%= #file_doc.author>1 and "Authors" or "Author" %>: + +<%for _, author in ipairs(file_doc.author) do%> + +<%end%> +
<%= author %>
+

+<%end%> +<%if file_doc.copyright then%> +

Copyright ©<%=file_doc.copyright%>

+<%end%> +<%if file_doc.release then%> +

Release: <%=file_doc.release%>

+<%end%> + +<%if #file_doc.functions > 0 then%> +

Functions

+ +<%for _, func_name in ipairs(file_doc.functions) do + local func_data = file_doc.functions[func_name]%> + + + + +<%end%> +
<%=func_data.private and "local " or ""%><%=func_name%> (<%=table.concat(func_data.param or {}, ", ")%>)<%=func_data.summary%>
+<%end%> + + +<%if #file_doc.tables > 0 then%> +

Tables

+ +<%for _, tab_name in ipairs(file_doc.tables) do%> + + + + +<%end%> +
<%=tab_name%><%=file_doc.tables[tab_name].summary%>
+<%end%> + + +
+
+ + + +<%if #file_doc.functions > 0 then%> +

Functions

+
+<%for _, func_name in ipairs(file_doc.functions) do%> +<%=luadoc.doclet.html.include("function.lp", { doc=doc, file_doc=file_doc, func=file_doc.functions[func_name] })%> +<%end%> +
+<%end%> + + +<%if #file_doc.tables > 0 then%> +

Tables

+
+<%for _, tab_name in ipairs(file_doc.tables) do%> +<%=luadoc.doclet.html.include("table.lp", { doc=doc, file_doc=file_doc, tab=file_doc.tables[tab_name] })%> +<%end%> +
+<%end%> + + + +
+ +
+ +
+

Valid XHTML 1.0!

+
+ +
+ + diff --git a/build/luadoc/luadoc/doclet/html/function.lp b/build/luadoc/luadoc/doclet/html/function.lp new file mode 100644 index 000000000..29d403e00 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/function.lp @@ -0,0 +1,64 @@ +<% +if module_doc then + from = "modules/"..module_doc.name +elseif file_doc then + from = "files/.."..file_doc.name +else + from = "" +end +%> + +
<%=func.private and "local " or ""%><%=func.printname%> (<%=table.concat(func.param or {}, ", ")%>)
+
+<%=func.description or ""%> + +<%if type(func.param) == "table" and #func.param > 0 then%> +

Parameters

+
    + <%for p = 1, #func.param do%> +
  • + <%=func.param[p]%>: <%=func.param[func.param[p]] or ""%> +
  • + <%end%> +
+<%end%> + + +<%if type(func.usage) == "string" then%> +

Usage:

+<%=func.usage%> +<%elseif type(func.usage) == "table" then%> +

Usage

+
    + <%for _, usage in ipairs(func.usage) do%> +
  • <%= usage %> + <%end%> +
+<%end%> + +<%if type(func.ret) == "string" then%> +

Return value:

+<%=func.ret%> +<%elseif type(func.ret) == "table" then%> +

Return values:

+
    + <%for _, ret in ipairs(func.ret) do%> +
  1. <%= ret %> + <%end%> +
+<%end%> + +<%if type(func.see) == "string" then %> +

See also:

+ <%=func.see%> +<%elseif type(func.see) == "table" and #func.see > 0 then %> +

See also:

+ +<%end%> +
diff --git a/build/luadoc/luadoc/doclet/html/index.lp b/build/luadoc/luadoc/doclet/html/index.lp new file mode 100644 index 000000000..b4b9f5c3b --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/index.lp @@ -0,0 +1,67 @@ + + + + Reference + " type="text/css" /> + + + + +
+ +
+ +
+
+
+ +
+ + + +
+ + +<%if not options.nomodules and #doc.modules > 0 then%> +

Modules

+ + +<%for _, modulename in ipairs(doc.modules) do%> + + + + +<%end%> +
<%=modulename%><%=doc.modules[modulename].summary%>
+<%end%> + + + +<%if not options.nofiles and #doc.files > 0 then%> +

Files

+ + +<%for _, filepath in ipairs(doc.files) do%> + + + + +<%end%> +
<%=filepath%>
+<%end%> + +
+ +
+ +
+

Valid XHTML 1.0!

+
+ +
+ + diff --git a/build/luadoc/luadoc/doclet/html/luadoc.css b/build/luadoc/luadoc/doclet/html/luadoc.css new file mode 100644 index 000000000..f9f974951 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/luadoc.css @@ -0,0 +1,285 @@ +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color:#ffffff; margin:0px; +} + +code { + font-family: "Andale Mono", monospace; +} + +tt { + font-family: "Andale Mono", monospace; +} + +body, td, th { font-size: 11pt; } + +h1, h2, h3, h4 { margin-left: 0em; } + +textarea, pre, tt { font-size:10pt; } +body, td, th { color:#000000; } +small { font-size:0.85em; } +h1 { font-size:1.5em; } +h2 { font-size:1.25em; } +h3 { font-size:1.15em; } +h4 { font-size:1.06em; } + +a:link { font-weight:bold; color: #004080; text-decoration: none; } +a:visited { font-weight:bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration:underline; } +hr { color:#cccccc } +img { border-width: 0px; } + + +h3 { padding: 1em 0 0.5em; } + +p { margin-left: 1em; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; + margin-left: 0em; +} + +blockquote { margin-left: 3em; } + +pre.example { + background-color: rgb(245, 245, 245); + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-style: solid; + border-right-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-top-color: silver; + border-right-color: silver; + border-bottom-color: silver; + border-left-color: silver; + padding: 1em; + margin-left: 1em; + margin-right: 1em; + font-family: "Andale Mono", monospace; + font-size: smaller; +} + + +hr { + margin-left: 0em; + background: #00007f; + border: 0px; + height: 1px; +} + +ul { list-style-type: disc; } + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } +table.index ul { padding-top: 0em; margin-top: 0em; } + +table { + border: 1px solid black; + border-collapse: collapse; + margin: 1em auto; +} +th { + border: 1px solid black; + padding: 0.5em; +} +td { + border: 1px solid black; + padding: 0.5em; +} +div.header, div.footer { margin-left: 0em; } + +#container +{ + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product +{ + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#product_logo +{ +} + +#product_name +{ +} + +#product_description +{ +} + +#main +{ + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation +{ + float: left; + width: 18em; + margin: 0; + vertical-align: top; + background-color: #f0f0f0; + overflow:visible; +} + +#navigation h1 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align:left; + margin:0px; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + padding: 0; + margin: 1px; +} + +#navigation li +{ + text-indent: -1em; + margin: 0em 0em 0em 0.5em; + display: block; + padding: 3px 0px 0px 12px; +} + +#navigation li li a +{ + padding: 0px 3px 0px -1em; +} + +#content +{ + margin-left: 18em; + padding: 1em; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about +{ + clear: both; + margin: 0; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight:bold; color: #004080; text-decoration: underline; } + + #main { background-color: #ffffff; border-left: 0px; } + #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; } + + #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; } + + #navigation { display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list td +{ + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; } +table.module_list td.summary { width: 100%; } + +table.file_list +{ + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.file_list td +{ + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.file_list td.name { background-color: #f0f0f0; } +table.file_list td.summary { width: 100%; } + + +table.function_list +{ + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td +{ + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; } +table.function_list td.summary { width: 100%; } + + +table.table_list +{ + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.table_list td +{ + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +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: 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;} +dl.table h3 {padding: 0; margin: 0; font-size: medium;} + +#TODO: make module_list, file_list, function_list, table_list inherit from a list + diff --git a/build/luadoc/luadoc/doclet/html/menu.lp b/build/luadoc/luadoc/doclet/html/menu.lp new file mode 100644 index 000000000..0fe365202 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/menu.lp @@ -0,0 +1,55 @@ +<% +if module_doc then + from = "modules/"..module_doc.name +elseif file_doc then + from = "files/.."..file_doc.name +else + from = "" +end +%> + +

LuaDoc

+
    + <%if not module_doc and not file_doc then%> +
  • Index
  • + <%else%> +
  • ">Index
  • + <%end%> +
+ + + +<%if not options.nomodules and #doc.modules > 0 then%> +

Modules

+
    +<%for _, modulename in ipairs(doc.modules) do + if module_doc and module_doc.name == modulename then%> +
  • <%=modulename%>
  • + <%else%> +
  • + <%=modulename%> +
  • +<% end +end%> +
+<%end%> + + + +<%if not options.nofiles and #doc.files > 0 then%> +

Files

+
    +<%for _, filepath in ipairs(doc.files) do + if file_doc and file_doc.name == filepath then%> +
  • <%=filepath%>
  • + <%else%> +
  • + <%=filepath%> +
  • +<% end +end%> +
+<%end%> + + + diff --git a/build/luadoc/luadoc/doclet/html/module.lp b/build/luadoc/luadoc/doclet/html/module.lp new file mode 100644 index 000000000..0798c1be0 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/module.lp @@ -0,0 +1,155 @@ + + + + Reference + + + + + +
+ +
+ +
+
+
+ +
+ + + +
+ +

<%=( oop and "Object Instance" or "Class" )%> <%=module_doc.name%>

+ +

<%=module_doc.description%>

+<%if module_doc.author then%> +

<%= #module_doc.author>1 and "Authors" or "Author" %>: + +<%for _, author in ipairs(module_doc.author) do%> + +<%end%> +
<%= author %>
+

+<%end%> +<%if module_doc.copyright then%> +

Copyright© <%=module_doc.copyright%>

+<%end%> +<%if module_doc.release then%> +

Release: <%=module_doc.release%>

+<%end%> + +<%if #module_doc.constants > 0 then %> +

Constants

+ +<%for _, const_name in ipairs(module_doc.constants) do + local const_data = module_doc.constants[const_name]%> + + + + +<%end%> +
<%=const_data.private and "local " or ""%><%=(const_name:gsub(".+%.",""))%><%=const_data.summary%>
+<%end%> + +<% local funcs = { }; if #module_doc.functions > 0 then %> +

Functions

+ +<% +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 +%> + + + + +<%end%> +
<%=func_data.private and "local " or ""%><%=func_data.printname%> (<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)<%=module_doc.functions[func_name].summary%>
+<%end%> + + +<% local tabs = { }; if #module_doc.tables > 0 then%> +

Tables

+ +<% +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 +%> + + + + +<%end%> +
<%=tab_name%><%=module_doc.tables[tab_name].summary%>
+<%end%> + + +
+
+ +<%if #module_doc.functions > 0 then%> +

Functions

+
+<%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%> +
+<%end%> + +<%if #module_doc.tables > 0 then%> +

Tables

+
+<%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%> +
+<%end%> + + +
+ +
+ +
+

Valid XHTML 1.0!

+
+ +
+ + diff --git a/build/luadoc/luadoc/doclet/html/table.lp b/build/luadoc/luadoc/doclet/html/table.lp new file mode 100644 index 000000000..5cd023953 --- /dev/null +++ b/build/luadoc/luadoc/doclet/html/table.lp @@ -0,0 +1,15 @@ +
<%=tab.name%>
+
<%=tab.description%> + +<%if type(tab.field) == "table" and #tab.field > 0 then%> +Fields +
    + <%for p = 1, #tab.field do%> +
  • + <%=tab.field[p]%>: <%=tab.field[tab.field[p]] or ""%> +
  • + <%end%> +
+<%end%> + +
diff --git a/build/luadoc/luadoc/doclet/raw.lua b/build/luadoc/luadoc/doclet/raw.lua new file mode 100644 index 000000000..1e880b883 --- /dev/null +++ b/build/luadoc/luadoc/doclet/raw.lua @@ -0,0 +1,12 @@ +----------------------------------------------------------------- +-- @release $Id: raw.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $ +----------------------------------------------------------------- + +module "luadoc.doclet.raw" + +----------------------------------------------------------------- +-- Generate the output. +-- @param doc Table with the structured documentation. + +function start (doc) +end diff --git a/build/luadoc/luadoc/init.lua b/build/luadoc/luadoc/init.lua new file mode 100644 index 000000000..649515de6 --- /dev/null +++ b/build/luadoc/luadoc/init.lua @@ -0,0 +1,50 @@ +------------------------------------------------------------------------------- +-- LuaDoc main function. +-- @release $Id: init.lua,v 1.4 2008/02/17 06:42:51 jasonsantos Exp $ +------------------------------------------------------------------------------- + +local require = require + +local util = require "luadoc.util" + +logger = {} + +module ("luadoc") + +------------------------------------------------------------------------------- +-- LuaDoc version number. + +_COPYRIGHT = "Copyright (c) 2003-2007 The Kepler Project" +_DESCRIPTION = "Documentation Generator Tool for the Lua language" +_VERSION = "LuaDoc 3.0.1" + +------------------------------------------------------------------------------- +-- Main function +-- @see luadoc.doclet.html, luadoc.doclet.formatter, luadoc.doclet.raw +-- @see luadoc.taglet.standard + +function main (files, options) + logger = util.loadlogengine(options) + + -- load config file + if options.config ~= nil then + -- load specified config file + dofile(options.config) + else + -- load default config file + require("luadoc.config") + end + + local taglet = require(options.taglet) + local doclet = require(options.doclet) + + -- analyze input + taglet.options = options + taglet.logger = logger + local doc = taglet.start(files) + + -- generate output + doclet.options = options + doclet.logger = logger + doclet.start(doc) +end diff --git a/build/luadoc/luadoc/lp.lua b/build/luadoc/luadoc/lp.lua new file mode 100644 index 000000000..adf84f9f0 --- /dev/null +++ b/build/luadoc/luadoc/lp.lua @@ -0,0 +1,130 @@ +---------------------------------------------------------------------------- +-- Lua Pages Template Preprocessor. +-- +-- @release $Id: lp.lua,v 1.7 2007/04/18 14:28:39 tomas Exp $ +---------------------------------------------------------------------------- + +local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv +local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub +local concat, tinsert = table.concat, table.insert +local open = io.open + +module (...) + +---------------------------------------------------------------------------- +-- function to do output +local outfunc = "io.write" +-- accepts the old expression field: `$| |$' +local compatmode = true + +-- +-- Builds a piece of Lua code which outputs the (part of the) given string. +-- @param s String. +-- @param i Number with the initial position in the string. +-- @param f Number with the final position in the string (default == -1). +-- @return String with the correspondent Lua code which outputs the part of the string. +-- +local function out (s, i, f) + s = strsub(s, i, f or -1) + if s == "" then return s end + -- we could use `%q' here, but this way we have better control + s = gsub(s, "([\\\n\'])", "\\%1") + -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it + s = gsub(s, "\r", "\\r") + return format(" %s('%s'); ", outfunc, s) +end + + +---------------------------------------------------------------------------- +-- Translate the template to Lua code. +-- @param s String to translate. +-- @return String with translated code. +---------------------------------------------------------------------------- +function translate (s) + if compatmode then + s = gsub(s, "$|(.-)|%$", "") + s = gsub(s, "", "") + end + s = gsub(s, "<%%(.-)%%>", "") + local res = {} + local start = 1 -- start of untranslated part in `s' + while true do + local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start) + if not ip then break end + tinsert(res, out(s, start, ip-1)) + if target ~= "" and target ~= "lua" then + -- not for Lua; pass whole instruction to the output + tinsert(res, out(s, ip, fp)) + else + if exp == "=" then -- expression? + tinsert(res, format(" %s(%s);", outfunc, code)) + else -- command + tinsert(res, format(" %s ", code)) + end + end + start = fp + 1 + end + tinsert(res, out(s, start)) + return concat(res) +end + + +---------------------------------------------------------------------------- +-- Defines the name of the output function. +-- @param f String with the name of the function which produces output. + +function setoutfunc (f) + outfunc = f +end + +---------------------------------------------------------------------------- +-- Turns on or off the compatibility with old CGILua 3.X behavior. +-- @param c Boolean indicating if the compatibility mode should be used. + +function setcompatmode (c) + compatmode = c +end + +---------------------------------------------------------------------------- +-- Internal compilation cache. + +local cache = {} + +---------------------------------------------------------------------------- +-- Translates a template into a Lua function. +-- Does NOT execute the resulting function. +-- Uses a cache of templates. +-- @param string String with the template to be translated. +-- @param chunkname String with the name of the chunk, for debugging purposes. +-- @return Function with the resulting translation. + +function compile (string, chunkname) + local f, err = cache[string] + if f then return f end + f, err = loadstring (translate (string), chunkname) + if not f then error (err, 3) end + cache[string] = f + return f +end + +---------------------------------------------------------------------------- +-- Translates and executes a template in a given file. +-- The translation creates a Lua function which will be executed in an +-- optionally given environment. +-- @param filename String with the name of the file containing the template. +-- @param env Table with the environment to run the resulting function. + +function include (filename, env) + -- read the whole contents of the file + local fh = assert (open (filename)) + local src = fh:read("*a") + fh:close() + -- translates the file into a function + local prog = compile (src, '@'..filename) + local _env + if env then + _env = getfenv (prog) + setfenv (prog, env) + end + prog () +end diff --git a/build/luadoc/luadoc/taglet/standard.lua b/build/luadoc/luadoc/taglet/standard.lua new file mode 100644 index 000000000..ef925f8c7 --- /dev/null +++ b/build/luadoc/luadoc/taglet/standard.lua @@ -0,0 +1,567 @@ +------------------------------------------------------------------------------- +-- @release $Id: standard.lua,v 1.39 2007/12/21 17:50:48 tomas Exp $ +------------------------------------------------------------------------------- + +local assert, pairs, tostring, type = assert, pairs, tostring, type +local io = require "io" +local posix = require "nixio.fs" +local luadoc = require "luadoc" +local util = require "luadoc.util" +local tags = require "luadoc.taglet.standard.tags" +local string = require "string" +local table = require "table" + +module 'luadoc.taglet.standard' + +------------------------------------------------------------------------------- +-- Creates an iterator for an array base on a class type. +-- @param t array to iterate over +-- @param class name of the class to iterate over + +function class_iterator (t, class) + return function () + local i = 1 + return function () + while t[i] and t[i].class ~= class do + i = i + 1 + end + local v = t[i] + i = i + 1 + return v + end + end +end + +-- Patterns for function recognition +local identifiers_list_pattern = "%s*(.-)%s*" +local identifier_pattern = "[^%(%s]+" +local function_patterns = { + "^()%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)", + "^%s*(local%s)%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)", + "^()%s*("..identifier_pattern..")%s*%=%s*function%s*%("..identifiers_list_pattern.."%)", +} + +------------------------------------------------------------------------------- +-- Checks if the line contains a function definition +-- @param line string with line text +-- @return function information or nil if no function definition found + +local function check_function (line) + line = util.trim(line) + + local info = table.foreachi(function_patterns, function (_, pattern) + local r, _, l, id, param = string.find(line, pattern) + if r ~= nil then + return { + name = id, + private = (l == "local"), + param = { } --util.split("%s*,%s*", param), + } + end + end) + + -- TODO: remove these assert's? + if info ~= nil then + assert(info.name, "function name undefined") + assert(info.param, string.format("undefined parameter list for function `%s'", info.name)) + end + + return info +end + +------------------------------------------------------------------------------- +-- Checks if the line contains a module definition. +-- @param line string with line text +-- @param currentmodule module already found, if any +-- @return the name of the defined module, or nil if there is no module +-- definition + +local function check_module (line, currentmodule) + line = util.trim(line) + + -- module"x.y" + -- module'x.y' + -- module[[x.y]] + -- module("x.y") + -- module('x.y') + -- module([[x.y]]) + -- module(...) + + local r, _, modulename = string.find(line, "^module%s*[%s\"'(%[]+([^,\"')%]]+)") + if r then + -- found module definition + logger:debug(string.format("found module `%s'", modulename)) + return modulename + end + return currentmodule +end + +-- Patterns for constant recognition +local constant_patterns = { + "^()%s*([A-Z][A-Z0-9_]*)%s*=", + "^%s*(local%s)%s*([A-Z][A-Z0-9_]*)%s*=", +} + +------------------------------------------------------------------------------- +-- Checks if the line contains a constant definition +-- @param line string with line text +-- @return constant information or nil if no constant definition found + +local function check_constant (line) + line = util.trim(line) + + local info = table.foreachi(constant_patterns, function (_, pattern) + local r, _, l, id = string.find(line, pattern) + if r ~= nil then + return { + name = id, + private = (l == "local"), + } + end + end) + + -- TODO: remove these assert's? + if info ~= nil then + assert(info.name, "constant name undefined") + end + + return info +end + +------------------------------------------------------------------------------- +-- Extracts summary information from a description. The first sentence of each +-- doc comment should be a summary sentence, containing a concise but complete +-- description of the item. It is important to write crisp and informative +-- initial sentences that can stand on their own +-- @param description text with item description +-- @return summary string or nil if description is nil + +local function parse_summary (description) + -- summary is never nil... + description = description or "" + + -- append an " " at the end to make the pattern work in all cases + description = description.." " + + -- read until the first period followed by a space or tab + local summary = string.match(description, "(.-%.)[%s\t]") + + -- if pattern did not find the first sentence, summary is the whole description + summary = summary or description + + return summary +end + +------------------------------------------------------------------------------- +-- @param f file handle +-- @param line current line being parsed +-- @param modulename module already found, if any +-- @return current line +-- @return code block +-- @return modulename if found + +local function parse_code (f, line, modulename) + local code = {} + while line ~= nil do + if string.find(line, "^[\t ]*%-%-%-") then + -- reached another luadoc block, end this parsing + return line, code, modulename + else + -- look for a module definition + modulename = check_module(line, modulename) + + table.insert(code, line) + line = f:read() + end + end + -- reached end of file + return line, code, modulename +end + +------------------------------------------------------------------------------- +-- Parses the information inside a block comment +-- @param block block with comment field +-- @return block parameter + +local function parse_comment (block, first_line, modulename) + + -- get the first non-empty line of code + local code = table.foreachi(block.code, function(_, line) + if not util.line_empty(line) then + -- `local' declarations are ignored in two cases: + -- when the `nolocals' option is turned on; and + -- when the first block of a file is parsed (this is + -- necessary to avoid confusion between the top + -- local declarations and the `module' definition. + if (options.nolocals or first_line) and line:find"^%s*local" then + return + end + return line + end + end) + + -- parse first line of code + if code ~= nil then + local func_info = check_function(code) + local module_name = check_module(code) + local const_info = check_constant(code) + if func_info then + block.class = "function" + block.name = func_info.name + block.param = func_info.param + block.private = func_info.private + elseif const_info then + block.class = "constant" + block.name = const_info.name + block.private = const_info.private + elseif module_name then + block.class = "module" + block.name = module_name + block.param = {} + else + block.param = {} + end + else + -- TODO: comment without any code. Does this means we are dealing + -- with a file comment? + end + + -- parse @ tags + local currenttag = "description" + local currenttext + + table.foreachi(block.comment, function (_, line) + line = util.trim_comment(line) + + local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)") + if r ~= nil then + -- found new tag, add previous one, and start a new one + -- TODO: what to do with invalid tags? issue an error? or log a warning? + tags.handle(currenttag, block, currenttext) + + currenttag = tag + currenttext = text + else + currenttext = util.concat(currenttext, "\n" .. line) + assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line)) + end + end) + tags.handle(currenttag, block, currenttext) + + -- extracts summary information from the description + block.summary = parse_summary(block.description) + assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description)) + + if block.name and block.class == "module" then + modulename = block.name + end + + return block, modulename +end + +------------------------------------------------------------------------------- +-- Parses a block of comment, started with ---. Read until the next block of +-- comment. +-- @param f file handle +-- @param line being parsed +-- @param modulename module already found, if any +-- @return line +-- @return block parsed +-- @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 (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) + + -- parse information in block comment + block, modulename = parse_comment(block, first, modulename) + + return line, block, modulename + else + table.insert(block.comment, line) + line = f:read() + end + end + -- reached end of file + + -- parse information in block comment + block, modulename = parse_comment(block, first, modulename) + + return line, block, modulename +end + +------------------------------------------------------------------------------- +-- Parses a file documented following luadoc format. +-- @param filepath full path of file to parse +-- @param doc table with documentation +-- @return table with documentation + +function parse_file (filepath, doc, handle, prev_line, prev_block, prev_modname) + local blocks = { prev_block } + local modulename = prev_modname + + -- read each line + local f = handle or io.open(filepath, "r") + local i = 1 + local line = prev_line or f:read() + local first = true + while line ~= nil do + + if string.find(line, "^[\t ]*%-%-%-") then + -- reached a luadoc block + local block, newmodname + line, block, newmodname = parse_block(f, line, modulename, first) + + if modulename and newmodname and newmodname ~= modulename then + doc = parse_file( nil, doc, f, line, block, newmodname ) + else + table.insert(blocks, block) + modulename = newmodname + end + else + -- look for a module definition + local newmodname = check_module(line, modulename) + + if modulename and newmodname and newmodname ~= modulename then + parse_file( nil, doc, f ) + else + modulename = newmodname + end + + -- TODO: keep beginning of file somewhere + + line = f:read() + end + first = false + i = i + 1 + end + + if not handle then + f:close() + end + + if filepath then + -- store blocks in file hierarchy + assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath)) + table.insert(doc.files, filepath) + doc.files[filepath] = { + type = "file", + name = filepath, + doc = blocks, + -- functions = class_iterator(blocks, "function"), + -- tables = class_iterator(blocks, "table"), + } + -- + local first = doc.files[filepath].doc[1] + if first and modulename then + doc.files[filepath].author = first.author + doc.files[filepath].copyright = first.copyright + doc.files[filepath].description = first.description + doc.files[filepath].release = first.release + doc.files[filepath].summary = first.summary + end + end + + -- if module definition is found, store in module hierarchy + if modulename ~= nil then + if modulename == "..." then + assert( filepath, "Can't determine name for virtual module from filepatch" ) + modulename = string.gsub (filepath, "%.lua$", "") + modulename = string.gsub (modulename, "/", ".") + end + if doc.modules[modulename] ~= nil then + -- module is already defined, just add the blocks + table.foreachi(blocks, function (_, v) + table.insert(doc.modules[modulename].doc, v) + end) + else + -- TODO: put this in a different module + table.insert(doc.modules, modulename) + doc.modules[modulename] = { + type = "module", + name = modulename, + doc = blocks, +-- functions = class_iterator(blocks, "function"), +-- tables = class_iterator(blocks, "table"), + author = first and first.author, + copyright = first and first.copyright, + description = "", + release = first and first.release, + summary = "", + } + + -- find module description + for m in class_iterator(blocks, "module")() do + doc.modules[modulename].description = util.concat( + doc.modules[modulename].description, + m.description) + doc.modules[modulename].summary = util.concat( + doc.modules[modulename].summary, + m.summary) + if m.author then + doc.modules[modulename].author = m.author + end + if m.copyright then + doc.modules[modulename].copyright = m.copyright + end + if m.release then + doc.modules[modulename].release = m.release + end + if m.name then + doc.modules[modulename].name = m.name + end + end + doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or "" + doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or "" + end + + -- make functions table + doc.modules[modulename].functions = {} + for f in class_iterator(blocks, "function")() do + if f and f.name then + table.insert(doc.modules[modulename].functions, f.name) + doc.modules[modulename].functions[f.name] = f + end + end + + -- make tables table + doc.modules[modulename].tables = {} + for t in class_iterator(blocks, "table")() do + if t and t.name then + table.insert(doc.modules[modulename].tables, t.name) + doc.modules[modulename].tables[t.name] = t + end + end + + -- make constants table + doc.modules[modulename].constants = {} + for c in class_iterator(blocks, "constant")() do + if c and c.name then + table.insert(doc.modules[modulename].constants, c.name) + doc.modules[modulename].constants[c.name] = c + end + end + end + + if filepath then + -- make functions table + doc.files[filepath].functions = {} + for f in class_iterator(blocks, "function")() do + if f and f.name then + table.insert(doc.files[filepath].functions, f.name) + doc.files[filepath].functions[f.name] = f + end + end + + -- make tables table + doc.files[filepath].tables = {} + for t in class_iterator(blocks, "table")() do + if t and t.name then + table.insert(doc.files[filepath].tables, t.name) + doc.files[filepath].tables[t.name] = t + end + end + end + + return doc +end + +------------------------------------------------------------------------------- +-- Checks if the file is terminated by ".lua" or ".luadoc" and calls the +-- function that does the actual parsing +-- @param filepath full path of the file to parse +-- @param doc table with documentation +-- @return table with documentation +-- @see parse_file + +function file (filepath, doc) + local patterns = { "%.lua$", "%.luadoc$" } + local valid = table.foreachi(patterns, function (_, pattern) + if string.find(filepath, pattern) ~= nil then + return true + end + end) + + if valid then + logger:info(string.format("processing file `%s'", filepath)) + doc = parse_file(filepath, doc) + end + + return doc +end + +------------------------------------------------------------------------------- +-- Recursively iterates through a directory, parsing each file +-- @param path directory to search +-- @param doc table with documentation +-- @return table with documentation + +function directory (path, doc) + for f in posix.dir(path) do + local fullpath = path .. "/" .. f + local attr = posix.stat(fullpath) + assert(attr, string.format("error stating file `%s'", fullpath)) + + if attr.type == "reg" then + doc = file(fullpath, doc) + elseif attr.type == "dir" and f ~= "." and f ~= ".." then + doc = directory(fullpath, doc) + end + end + return doc +end + +-- Recursively sorts the documentation table +local function recsort (tab) + table.sort (tab) + -- sort list of functions by name alphabetically + for f, doc in pairs(tab) do + if doc.functions then + table.sort(doc.functions) + end + if doc.tables then + table.sort(doc.tables) + end + end +end + +------------------------------------------------------------------------------- + +function start (files, doc) + assert(files, "file list not specified") + + -- Create an empty document, or use the given one + doc = doc or { + files = {}, + modules = {}, + } + assert(doc.files, "undefined `files' field") + assert(doc.modules, "undefined `modules' field") + + table.foreachi(files, function (_, path) + local attr = posix.stat(path) + assert(attr, string.format("error stating path `%s'", path)) + + if attr.type == "reg" then + doc = file(path, doc) + elseif attr.type == "dir" then + doc = directory(path, doc) + end + end) + + -- order arrays alphabetically + recsort(doc.files) + recsort(doc.modules) + + return doc +end diff --git a/build/luadoc/luadoc/taglet/standard/tags.lua b/build/luadoc/luadoc/taglet/standard/tags.lua new file mode 100644 index 000000000..e9d035483 --- /dev/null +++ b/build/luadoc/luadoc/taglet/standard/tags.lua @@ -0,0 +1,191 @@ +------------------------------------------------------------------------------- +-- Handlers for several tags +-- @release $Id: tags.lua,v 1.8 2007/09/05 12:39:09 tomas Exp $ +------------------------------------------------------------------------------- + +local luadoc = require "luadoc" +local util = require "luadoc.util" +local string = require "string" +local table = require "table" +local assert, type, tostring, tonumber = assert, type, tostring, tonumber + +module "luadoc.taglet.standard.tags" + +------------------------------------------------------------------------------- + +local function author (tag, block, text) + block[tag] = block[tag] or {} + if not text then + luadoc.logger:warn("author `name' not defined [["..text.."]]: skipping") + return + end + table.insert (block[tag], text) +end + +------------------------------------------------------------------------------- +-- Set the class of a comment block. Classes can be "module", "function", +-- "table". The first two classes are automatic, extracted from the source code + +local function class (tag, block, text) + block[tag] = text +end + +------------------------------------------------------------------------------- + +local function cstyle (tag, block, text) + block[tag] = text +end + +------------------------------------------------------------------------------- + +local function sort (tag, block, text) + block[tag] = tonumber(text) or 0 +end + +------------------------------------------------------------------------------- + +local function copyright (tag, block, text) + block[tag] = text +end + +------------------------------------------------------------------------------- + +local function description (tag, block, text) + block[tag] = text +end + +------------------------------------------------------------------------------- + +local function field (tag, block, text) + if block["class"] ~= "table" then + luadoc.logger:warn("documenting `field' for block that is not a `table'") + end + block[tag] = block[tag] or {} + + local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)") + assert(name, "field name not defined") + + table.insert(block[tag], name) + block[tag][name] = desc +end + +------------------------------------------------------------------------------- +-- Set the name of the comment block. If the block already has a name, issue +-- an error and do not change the previous value + +local function name (tag, block, text) + if block[tag] and block[tag] ~= text then + luadoc.logger:error(string.format("block name conflict: `%s' -> `%s'", block[tag], text)) + end + + block[tag] = text +end + +------------------------------------------------------------------------------- +-- Processes a parameter documentation. +-- @param tag String with the name of the tag (it must be "param" always). +-- @param block Table with previous information about the block. +-- @param text String with the current line beeing processed. + +local function param (tag, block, text) + block[tag] = block[tag] or {} + -- TODO: make this pattern more flexible, accepting empty descriptions + local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)") + if not name then + luadoc.logger:warn("parameter `name' not defined [["..text.."]]: skipping") + return + end + local i = table.foreachi(block[tag], function (i, v) + if v == name then + return i + end + end) + if i == nil then + luadoc.logger:warn(string.format("documenting undefined parameter `%s'", name)) + table.insert(block[tag], name) + end + block[tag][name] = desc +end + +------------------------------------------------------------------------------- + +local function release (tag, block, text) + block[tag] = text +end + +------------------------------------------------------------------------------- + +local function ret (tag, block, text) + tag = "ret" + if type(block[tag]) == "string" then + block[tag] = { block[tag], text } + elseif type(block[tag]) == "table" then + table.insert(block[tag], text) + else + block[tag] = text + end +end + +------------------------------------------------------------------------------- +-- @see ret + +local function see (tag, block, text) + -- see is always an array + block[tag] = block[tag] or {} + + -- remove trailing "." + text = string.gsub(text, "(.*)%.$", "%1") + + local s = util.split("%s*,%s*", text) + + table.foreachi(s, function (_, v) + table.insert(block[tag], v) + end) +end + +------------------------------------------------------------------------------- +-- @see ret + +local function usage (tag, block, text) + if type(block[tag]) == "string" then + block[tag] = { block[tag], text } + elseif type(block[tag]) == "table" then + table.insert(block[tag], text) + else + block[tag] = text + end +end + +------------------------------------------------------------------------------- + +local handlers = {} +handlers["author"] = author +handlers["class"] = class +handlers["cstyle"] = cstyle +handlers["copyright"] = copyright +handlers["description"] = description +handlers["field"] = field +handlers["name"] = name +handlers["param"] = param +handlers["release"] = release +handlers["return"] = ret +handlers["see"] = see +handlers["sort"] = sort +handlers["usage"] = usage + +------------------------------------------------------------------------------- + +function handle (tag, block, text) + if not handlers[tag] then + luadoc.logger:error(string.format("undefined handler for tag `%s'", tag)) + return + end + + if text then + text = text:gsub("`([^\n]-)`", "%1") + text = text:gsub("`(.-)`", "
%1
") + end + +-- assert(handlers[tag], string.format("undefined handler for tag `%s'", tag)) + return handlers[tag](tag, block, text) +end diff --git a/build/luadoc/luadoc/util.lua b/build/luadoc/luadoc/util.lua new file mode 100644 index 000000000..e1058d590 --- /dev/null +++ b/build/luadoc/luadoc/util.lua @@ -0,0 +1,201 @@ +------------------------------------------------------------------------------- +-- General utilities. +-- @release $Id: util.lua,v 1.16 2008/02/17 06:42:51 jasonsantos Exp $ +------------------------------------------------------------------------------- + +local posix = require "nixio.fs" +local type, table, string, io, assert, tostring, setmetatable, pcall = type, table, string, io, assert, tostring, setmetatable, pcall + +------------------------------------------------------------------------------- +-- Module with several utilities that could not fit in a specific module + +module "luadoc.util" + +------------------------------------------------------------------------------- +-- Removes spaces from the begining and end of a given string +-- @param s string to be trimmed +-- @return trimmed string + +function trim (s) + return (string.gsub(s, "^%s*(.-)%s*$", "%1")) +end + +------------------------------------------------------------------------------- +-- Removes spaces from the begining and end of a given string, considering the +-- string is inside a lua comment. +-- @param s string to be trimmed +-- @return trimmed string +-- @see trim +-- @see string.gsub + +function trim_comment (s) + s = string.gsub(s, "^%s*%-%-+%[%[(.*)$", "%1") + s = string.gsub(s, "^%s*%-%-+(.*)$", "%1") + return s +end + +------------------------------------------------------------------------------- +-- Checks if a given line is empty +-- @param line string with a line +-- @return true if line is empty, false otherwise + +function line_empty (line) + return (string.len(trim(line)) == 0) +end + +------------------------------------------------------------------------------- +-- Appends two string, but if the first one is nil, use to second one +-- @param str1 first string, can be nil +-- @param str2 second string +-- @return str1 .. " " .. str2, or str2 if str1 is nil + +function concat (str1, str2) + if str1 == nil or string.len(str1) == 0 then + return str2 + else + return str1 .. " " .. str2 + end +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 +-- field 1 may be preceeded by leading whitespace +-- @usage split(",%s*", "Anna, Bob, Charlie,Dolores") +-- @usage split(""," x y") gives {"x","y"} +-- @usage split("%s+"," x y") gives {"", "x","y"} +-- @return array with strings +-- @see table.concat + +function split(delim, text) + local list = {} + if string.len(text) > 0 then + 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 + error("delim matches empty string!") + end + local first, last + while 1 do + if delim ~= "" then + first, last = string.find(text, delim, pos) + else + first, last = string.find(text, "%s+", pos) + if first == 1 then + pos = last+1 + first, last = string.find(text, "%s+", pos) + end + end + if first then -- found? + table.insert(list, string.sub(text, pos, first-1)) + pos = last+1 + else + table.insert(list, string.sub(text, pos)) + break + end + end + end + return list +end + +------------------------------------------------------------------------------- +-- Comments a paragraph. +-- @param text text to comment with "--", may contain several lines +-- @return commented text + +function comment (text) + text = string.gsub(text, "\n", "\n-- ") + return "-- " .. text +end + +------------------------------------------------------------------------------- +-- Wrap a string into a paragraph. +-- @param s string to wrap +-- @param w width to wrap to [80] +-- @param i1 indent of first line [0] +-- @param i2 indent of subsequent lines [0] +-- @return wrapped paragraph + +function wrap(s, w, i1, i2) + w = w or 80 + i1 = i1 or 0 + i2 = i2 or 0 + assert(i1 < w and i2 < w, "the indents must be less than the line width") + s = string.rep(" ", i1) .. s + local lstart, len = 1, string.len(s) + while len - lstart > w do + local i = lstart + w + while i > lstart and string.sub(s, i, i) ~= " " do i = i - 1 end + local j = i + while j > lstart and string.sub(s, j, j) == " " do j = j - 1 end + s = string.sub(s, 1, j) .. "\n" .. string.rep(" ", i2) .. + string.sub(s, i + 1, -1) + local change = i2 + 1 - (i - j) + lstart = j + change + len = len + change + end + return s +end + +------------------------------------------------------------------------------- +-- Opens a file, creating the directories if necessary +-- @param filename full path of the file to open (or create) +-- @param mode mode of opening +-- @return file handle + +function posix.open (filename, mode) + local f = io.open(filename, mode) + if f == nil then + filename = string.gsub(filename, "\\", "/") + local dir = "" + for d in string.gfind(filename, ".-/") do + dir = dir .. d + posix.mkdir(dir) + end + f = io.open(filename, mode) + end + return f +end + + +---------------------------------------------------------------------------------- +-- Creates a Logger with LuaLogging, if present. Otherwise, creates a mock logger. +-- @param options a table with options for the logging mechanism +-- @return logger object that will implement log methods + +function loadlogengine(options) + local logenabled = pcall(function() + 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 = {} + setmetatable(logger, noop) + end + + return logger +end diff --git a/build/makedocs.sh b/build/makedocs.sh index 2f7f57345..a9fc760e7 100755 --- a/build/makedocs.sh +++ b/build/makedocs.sh @@ -1,2 +1,14 @@ -luadoc -d $2 --no-files $(for f in $(find $1 -name '*.lua' -type f); do if grep -q -- "@return" $f; then echo $f; fi; done) -echo API-Documentation was created in $2. +#!/bin/bash + +topdir=$(pwd) + +[ -f "$topdir/build/makedocs.sh" -a -n "$1" ] || { + echo "Please execute as ./build/makedocs.sh [output directory]" >&2 + exit 1 +} + +( + cd "$topdir/build/luadoc/" + find "$topdir/libs/" "$topdir/modules/" -type f -name '*.lua' -or -name '*.luadoc' | \ + xargs grep -l '@return' | xargs ./doc.lua --no-files -d "$1" +) diff --git a/build/zoneinfo2lua.pl b/build/zoneinfo2lua.pl index 4a90dae35..d3f040326 100755 --- a/build/zoneinfo2lua.pl +++ b/build/zoneinfo2lua.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl # zoneinfo2lua.pl - Make Lua module from /usr/share/zoneinfo -# Execute from within /usr/share/zoneinfo +# Execute from within root of Luci feed, usually feeds/luci # $Id$ use strict; @@ -51,16 +51,7 @@ open(O, "> $tzdout/tzdata.lua") || die "open($tzdout/tzdata.lua): $!\n"; print STDERR "Writing time zones to $tzdout/tzdata.lua ... "; print O < $tzdout/tzoffset.lua") || die "open($tzdout/tzoffset.lua): $!\n"; print STDERR "Writing time offsets to $tzdout/tzoffset.lua ... "; print O < ---
  • index.lp: index of modules and files;
  • ---
  • file.lp: documentation for a lua file;
  • ---
  • module.lp: documentation for a lua module;
  • ---
  • function.lp: documentation for a lua function. This is a --- sub-template used by the others.
  • --- --- --- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $ -------------------------------------------------------------------------------- - -local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type -local io = require"io" -local posix = require "nixio.fs" -local lp = require "luadoc.lp" -local luadoc = require"luadoc" -local package = package -local string = require"string" -local table = require"table" - -module "luadoc.doclet.html" - -------------------------------------------------------------------------------- --- Looks for a file `name' in given path. Removed from compat-5.1 --- @param path String with the path. --- @param name String with the name to look for. --- @return String with the complete path of the file found --- or nil in case the file is not found. - -local function search (path, name) - for c in string.gfind(path, "[^;]+") do - c = string.gsub(c, "%?", name) - local f = io.open(c) - if f then -- file exist? - f:close() - return c - end - end - return nil -- file not found -end - -------------------------------------------------------------------------------- --- Include the result of a lp template into the current stream. - -function include (template, env) - -- template_dir is relative to package.path - local templatepath = options.template_dir .. template - - -- search using package.path (modified to search .lp instead of .lua - local search_path = string.gsub(package.path, "%.lua", "") - local templatepath = search(search_path, templatepath) - assert(templatepath, string.format("template `%s' not found", template)) - - env = env or {} - env.table = table - env.io = io - env.lp = lp - env.ipairs = ipairs - env.tonumber = tonumber - env.tostring = tostring - env.type = type - env.luadoc = luadoc - env.options = options - - return lp.include(templatepath, env) -end - -------------------------------------------------------------------------------- --- Returns a link to a html file, appending "../" to the link to make it right. --- @param html Name of the html file to link to --- @return link to the html file - -function link (html, from) - local h = html - from = from or "" - string.gsub(from, "/", function () h = "../" .. h end) - return h -end - -------------------------------------------------------------------------------- --- Returns the name of the html file to be generated from a module. --- Files with "lua" or "luadoc" extensions are replaced by "html" extension. --- @param modulename Name of the module to be processed, may be a .lua file or --- a .luadoc file. --- @return name of the generated html file for the module - -function module_link (modulename, doc, from) - -- TODO: replace "." by "/" to create directories? - -- TODO: how to deal with module names with "/"? - assert(modulename) - assert(doc) - from = from or "" - - if doc.modules[modulename] == nil then --- logger:error(string.format("unresolved reference to module `%s'", modulename)) - return - end - - local href = "modules/" .. modulename .. ".html" - string.gsub(from, "/", function () href = "../" .. href end) - return href -end - -------------------------------------------------------------------------------- --- Returns the name of the html file to be generated from a lua(doc) file. --- Files with "lua" or "luadoc" extensions are replaced by "html" extension. --- @param to Name of the file to be processed, may be a .lua file or --- a .luadoc file. --- @param from path of where am I, based on this we append ..'s to the --- beginning of path --- @return name of the generated html file - -function file_link (to, from) - assert(to) - from = from or "" - - local href = to - href = string.gsub(href, "lua$", "html") - href = string.gsub(href, "luadoc$", "html") - href = "files/" .. href - string.gsub(from, "/", function () href = "../" .. href end) - return href -end - -------------------------------------------------------------------------------- --- Returns a link to a function or to a table --- @param fname name of the function or table to link to. --- @param doc documentation table --- @param kind String specying the kinf of element to link ("functions" or "tables"). - -function link_to (fname, doc, module_doc, file_doc, from, kind) - assert(fname) - assert(doc) - from = from or "" - kind = kind or "functions" - - if file_doc then - for _, func_name in pairs(file_doc[kind]) do - if func_name == fname then - return file_link(file_doc.name, from) .. "#" .. fname - end - end - end - - if module_doc and module_doc[kind] then - for func_name, tbl in pairs(module_doc[kind]) do - if func_name == fname then - return "#" .. fname - end - end - end - - local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$") - assert(fname) - - -- if fname does not specify a module, use the module_doc - if string.len(modulename) == 0 and module_doc then - modulename = module_doc.name - end - - local module_doc = doc.modules[modulename] - if not module_doc then --- logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename)) - return - end - - for _, func_name in pairs(module_doc[kind]) do - if func_name == fname then - return module_link(modulename, doc, from) .. "#" .. fname - end - end - --- logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename)) -end - -------------------------------------------------------------------------------- --- Make a link to a file, module or function - -function symbol_link (symbol, doc, module_doc, file_doc, from) - assert(symbol) - assert(doc) - - local href = --- file_link(symbol, from) or - module_link(symbol, doc, from) or - link_to(symbol, doc, module_doc, file_doc, from, "functions") or - link_to(symbol, doc, module_doc, file_doc, from, "tables") - - if not href then - logger:error(string.format("unresolved reference to symbol `%s'", symbol)) - end - - return href or "" -end - -------------------------------------------------------------------------------- --- Assembly the output filename for an input file. --- TODO: change the name of this function -function out_file (filename) - local h = filename - h = string.gsub(h, "lua$", "html") - h = string.gsub(h, "luadoc$", "html") - h = "files/" .. h --- h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1") - h = options.output_dir .. h - return h -end - -------------------------------------------------------------------------------- --- Assembly the output filename for a module. --- TODO: change the name of this function -function out_module (modulename) - local h = modulename .. ".html" - h = "modules/" .. h - h = options.output_dir .. h - return h -end - ------------------------------------------------------------------ --- Generate the output. --- @param doc Table with the structured documentation. - -function start (doc) - -- Generate index file - if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then - local filename = options.output_dir.."index.html" - logger:info(string.format("generating file `%s'", filename)) - local f = posix.open(filename, "w") - assert(f, string.format("could not open `%s' for writing", filename)) - io.output(f) - include("index.lp", { doc = doc }) - f:close() - end - - -- Process modules - if not options.nomodules then - for _, modulename in ipairs(doc.modules) do - local module_doc = doc.modules[modulename] - -- assembly the filename - local filename = out_module(modulename) - logger:info(string.format("generating file `%s'", filename)) - - local f = posix.open(filename, "w") - assert(f, string.format("could not open `%s' for writing", filename)) - io.output(f) - include("module.lp", { doc = doc, module_doc = module_doc }) - f:close() - end - end - - -- Process files - if not options.nofiles then - for _, filepath in ipairs(doc.files) do - local file_doc = doc.files[filepath] - -- assembly the filename - local filename = out_file(file_doc.name) - logger:info(string.format("generating file `%s'", filename)) - - local f = posix.open(filename, "w") - assert(f, string.format("could not open `%s' for writing", filename)) - io.output(f) - include("file.lp", { doc = doc, file_doc = file_doc} ) - f:close() - end - end - - -- copy extra files - local f = posix.open(options.output_dir.."luadoc.css", "w") - io.output(f) - include("luadoc.css") - f:close() -end diff --git a/contrib/luadoc/lua/luadoc/doclet/html/constant.lp b/contrib/luadoc/lua/luadoc/doclet/html/constant.lp deleted file mode 100644 index 2e35392ad..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/constant.lp +++ /dev/null @@ -1,28 +0,0 @@ -<% -if module_doc then - from = "modules/"..module_doc.name -elseif file_doc then - from = "files/.."..file_doc.name -else - from = "" -end -%> - -
    <%=const.private and "local " or ""%><%=const.name:gsub(".+%.","")%>
    -
    -<%=const.description or ""%> - -<%if type(const.see) == "string" then %> -

    See also:

    - <%=const.see%> -<%elseif type(const.see) == "table" and #const.see > 0 then %> -

    See also:

    - -
    diff --git a/contrib/luadoc/lua/luadoc/doclet/html/file.lp b/contrib/luadoc/lua/luadoc/doclet/html/file.lp deleted file mode 100644 index 67926b4a7..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/file.lp +++ /dev/null @@ -1,112 +0,0 @@ - - - - Reference - - - - - -
    - -
    - -
    -
    -
    - -
    - - - -
    - -

    File <%=file_doc.name%>

    - -<%if file_doc.description then%> -

    <%=file_doc.description%>

    -<%end%> -<%if file_doc.author then%> -

    <%= #file_doc.author>1 and "Authors" or "Author" %>: - -<%for _, author in ipairs(file_doc.author) do%> - -<%end%> -
    <%= author %>
    -

    -<%end%> -<%if file_doc.copyright then%> -

    Copyright ©<%=file_doc.copyright%>

    -<%end%> -<%if file_doc.release then%> -

    Release: <%=file_doc.release%>

    -<%end%> - -<%if #file_doc.functions > 0 then%> -

    Functions

    - -<%for _, func_name in ipairs(file_doc.functions) do - local func_data = file_doc.functions[func_name]%> - - - - -<%end%> -
    <%=func_data.private and "local " or ""%><%=func_name%> (<%=table.concat(func_data.param, ", ")%>)<%=func_data.summary%>
    -<%end%> - - -<%if #file_doc.tables > 0 then%> -

    Tables

    - -<%for _, tab_name in ipairs(file_doc.tables) do%> - - - - -<%end%> -
    <%=tab_name%><%=file_doc.tables[tab_name].summary%>
    -<%end%> - - -
    -
    - - - -<%if #file_doc.functions > 0 then%> -

    Functions

    -
    -<%for _, func_name in ipairs(file_doc.functions) do%> -<%=luadoc.doclet.html.include("function.lp", { doc=doc, file_doc=file_doc, func=file_doc.functions[func_name] })%> -<%end%> -
    -<%end%> - - -<%if #file_doc.tables > 0 then%> -

    Tables

    -
    -<%for _, tab_name in ipairs(file_doc.tables) do%> -<%=luadoc.doclet.html.include("table.lp", { doc=doc, file_doc=file_doc, tab=file_doc.tables[tab_name] })%> -<%end%> -
    -<%end%> - - - -
    - -
    - -
    -

    Valid XHTML 1.0!

    -
    - -
    - - diff --git a/contrib/luadoc/lua/luadoc/doclet/html/function.lp b/contrib/luadoc/lua/luadoc/doclet/html/function.lp deleted file mode 100644 index a870ff8bb..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/function.lp +++ /dev/null @@ -1,64 +0,0 @@ -<% -if module_doc then - from = "modules/"..module_doc.name -elseif file_doc then - from = "files/.."..file_doc.name -else - from = "" -end -%> - -
    <%=func.private and "local " or ""%><%=(oop and func.name:gsub("%.",":") or func.name:gsub(".+%.",""))%> (<%=table.concat(func.param or {}, ", ")%>)
    -
    -<%=func.description or ""%> - -<%if type(func.param) == "table" and #func.param > 0 then%> -

    Parameters

    -
      - <%for p = 1, #func.param do%> -
    • - <%=func.param[p]%>: <%=func.param[func.param[p]] or ""%> -
    • - <%end%> -
    -<%end%> - - -<%if type(func.usage) == "string" then%> -

    Usage:

    -<%=func.usage%> -<%elseif type(func.usage) == "table" then%> -

    Usage

    -
      - <%for _, usage in ipairs(func.usage) do%> -
    • <%= usage %> - <%end%> -
    -<%end%> - -<%if type(func.ret) == "string" then%> -

    Return value:

    -<%=func.ret%> -<%elseif type(func.ret) == "table" then%> -

    Return values:

    -
      - <%for _, ret in ipairs(func.ret) do%> -
    1. <%= ret %> - <%end%> -
    -<%end%> - -<%if type(func.see) == "string" then %> -

    See also:

    - <%=func.see%> -<%elseif type(func.see) == "table" and #func.see > 0 then %> -

    See also:

    - -
    diff --git a/contrib/luadoc/lua/luadoc/doclet/html/index.lp b/contrib/luadoc/lua/luadoc/doclet/html/index.lp deleted file mode 100644 index b4b9f5c3b..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/index.lp +++ /dev/null @@ -1,67 +0,0 @@ - - - - Reference - " type="text/css" /> - - - - -
    - -
    - -
    -
    -
    - -
    - - - -
    - - -<%if not options.nomodules and #doc.modules > 0 then%> -

    Modules

    - - -<%for _, modulename in ipairs(doc.modules) do%> - - - - -<%end%> -
    <%=modulename%><%=doc.modules[modulename].summary%>
    -<%end%> - - - -<%if not options.nofiles and #doc.files > 0 then%> -

    Files

    - - -<%for _, filepath in ipairs(doc.files) do%> - - - - -<%end%> -
    <%=filepath%>
    -<%end%> - -
    - -
    - -
    -

    Valid XHTML 1.0!

    -
    - -
    - - diff --git a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css b/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css deleted file mode 100644 index bc0f98a5a..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css +++ /dev/null @@ -1,286 +0,0 @@ -body { - margin-left: 1em; - margin-right: 1em; - font-family: arial, helvetica, geneva, sans-serif; - background-color:#ffffff; margin:0px; -} - -code { - font-family: "Andale Mono", monospace; -} - -tt { - font-family: "Andale Mono", monospace; -} - -body, td, th { font-size: 11pt; } - -h1, h2, h3, h4 { margin-left: 0em; } - -textarea, pre, tt { font-size:10pt; } -body, td, th { color:#000000; } -small { font-size:0.85em; } -h1 { font-size:1.5em; } -h2 { font-size:1.25em; } -h3 { font-size:1.15em; } -h4 { font-size:1.06em; } - -a:link { font-weight:bold; color: #004080; text-decoration: none; } -a:visited { font-weight:bold; color: #006699; text-decoration: none; } -a:link:hover { text-decoration:underline; } -hr { color:#cccccc } -img { border-width: 0px; } - - -h3 { padding-top: 1em; } - -p { margin-left: 1em; } - -p.name { - font-family: "Andale Mono", monospace; - padding-top: 1em; - margin-left: 0em; -} - -blockquote { margin-left: 3em; } - -pre.example { - background-color: rgb(245, 245, 245); - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-style: solid; - border-right-style: solid; - border-bottom-style: solid; - border-left-style: solid; - border-top-color: silver; - border-right-color: silver; - border-bottom-color: silver; - border-left-color: silver; - padding: 1em; - margin-left: 1em; - margin-right: 1em; - font-family: "Andale Mono", monospace; - font-size: smaller; -} - - -hr { - margin-left: 0em; - background: #00007f; - border: 0px; - height: 1px; -} - -ul { list-style-type: disc; } - -table.index { border: 1px #00007f; } -table.index td { text-align: left; vertical-align: top; } -table.index ul { padding-top: 0em; margin-top: 0em; } - -table { - border: 1px solid black; - border-collapse: collapse; - margin-left: auto; - margin-right: auto; -} -th { - border: 1px solid black; - padding: 0.5em; -} -td { - border: 1px solid black; - padding: 0.5em; -} -div.header, div.footer { margin-left: 0em; } - -#container -{ - margin-left: 1em; - margin-right: 1em; - background-color: #f0f0f0; -} - -#product -{ - text-align: center; - border-bottom: 1px solid #cccccc; - background-color: #ffffff; -} - -#product big { - font-size: 2em; -} - -#product_logo -{ -} - -#product_name -{ -} - -#product_description -{ -} - -#main -{ - background-color: #f0f0f0; - border-left: 2px solid #cccccc; -} - -#navigation -{ - float: left; - width: 18em; - margin: 0; - vertical-align: top; - background-color: #f0f0f0; - overflow:visible; -} - -#navigation h1 { - background-color:#e7e7e7; - font-size:1.1em; - color:#000000; - text-align:left; - margin:0px; - padding:0.2em; - border-top:1px solid #dddddd; - border-bottom:1px solid #dddddd; -} - -#navigation ul -{ - font-size:1em; - list-style-type: none; - padding: 0; - margin: 1px; -} - -#navigation li -{ - text-indent: -1em; - margin: 0em 0em 0em 0.5em; - display: block; - padding: 3px 0px 0px 12px; -} - -#navigation li li a -{ - padding: 0px 3px 0px -1em; -} - -#content -{ - margin-left: 18em; - padding: 1em; - border-left: 2px solid #cccccc; - border-right: 2px solid #cccccc; - background-color: #ffffff; -} - -#about -{ - clear: both; - margin: 0; - padding: 5px; - border-top: 2px solid #cccccc; - background-color: #ffffff; -} - -@media print { - body { - font: 12pt "Times New Roman", "TimeNR", Times, serif; - } - a { font-weight:bold; color: #004080; text-decoration: underline; } - - #main { background-color: #ffffff; border-left: 0px; } - #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; } - - #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; } - - #navigation { display: none; - } - pre.example { - font-family: "Andale Mono", monospace; - font-size: 10pt; - page-break-inside: avoid; - } -} - -table.module_list td -{ - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -table.module_list td.name { background-color: #f0f0f0; } -table.module_list td.summary { width: 100%; } - -table.file_list -{ - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; -} -table.file_list td -{ - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -table.file_list td.name { background-color: #f0f0f0; } -table.file_list td.summary { width: 100%; } - - -table.function_list -{ - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; -} -table.function_list td -{ - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -table.function_list td.name { background-color: #f0f0f0; } -table.function_list td.summary { width: 100%; } - - -table.table_list -{ - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; -} -table.table_list td -{ - border-width: 1px; - padding: 3px; - border-style: solid; - border-color: #cccccc; -} -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.table dt {border-top: 1px solid #ccc; padding-top: 1em;} -dl.table dd {padding-bottom: 1em;} -dl.table h3 {padding: 0; margin: 0; font-size: medium;} - -#TODO: make module_list, file_list, function_list, table_list inherit from a list - diff --git a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp b/contrib/luadoc/lua/luadoc/doclet/html/menu.lp deleted file mode 100644 index 0fe365202..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp +++ /dev/null @@ -1,55 +0,0 @@ -<% -if module_doc then - from = "modules/"..module_doc.name -elseif file_doc then - from = "files/.."..file_doc.name -else - from = "" -end -%> - -

    LuaDoc

    -
      - <%if not module_doc and not file_doc then%> -
    • Index
    • - <%else%> -
    • ">Index
    • - <%end%> -
    - - - -<%if not options.nomodules and #doc.modules > 0 then%> -

    Modules

    -
      -<%for _, modulename in ipairs(doc.modules) do - if module_doc and module_doc.name == modulename then%> -
    • <%=modulename%>
    • - <%else%> -
    • - <%=modulename%> -
    • -<% end -end%> -
    -<%end%> - - - -<%if not options.nofiles and #doc.files > 0 then%> -

    Files

    -
      -<%for _, filepath in ipairs(doc.files) do - if file_doc and file_doc.name == filepath then%> -
    • <%=filepath%>
    • - <%else%> -
    • - <%=filepath%> -
    • -<% end -end%> -
    -<%end%> - - - diff --git a/contrib/luadoc/lua/luadoc/doclet/html/module.lp b/contrib/luadoc/lua/luadoc/doclet/html/module.lp deleted file mode 100644 index daa708628..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/module.lp +++ /dev/null @@ -1,120 +0,0 @@ - - - - Reference - - - - - -
    - -
    - -
    -
    -
    - -
    - - - -
    - -

    <%=( oop and "Object Instance" or "Class" )%> <%=module_doc.name%>

    - -

    <%=module_doc.description%>

    -<%if module_doc.author then%> -

    <%= #module_doc.author>1 and "Authors" or "Author" %>: - -<%for _, author in ipairs(module_doc.author) do%> - -<%end%> -
    <%= author %>
    -

    -<%end%> -<%if module_doc.copyright then%> -

    Copyright© <%=module_doc.copyright%>

    -<%end%> -<%if module_doc.release then%> -

    Release: <%=module_doc.release%>

    -<%end%> - -<%if #module_doc.constants > 0 then %> -

    Constants

    - -<%for _, const_name in ipairs(module_doc.constants) do - local const_data = module_doc.constants[const_name]%> - - - - -<%end%> -
    <%=const_data.private and "local " or ""%><%=(const_name:gsub(".+%.",""))%><%=const_data.summary%>
    -<%end%> - -<%if #module_doc.functions > 0 then %> -

    Functions

    - -<%for _, func_name in ipairs(module_doc.functions) do - local func_data = module_doc.functions[func_name]%> - - - - -<%end%> -
    <%=func_data.private and "local " or ""%><%=(oop and func_name:gsub("%.",":") or func_name:gsub(".+%.",""))%> (<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)<%=module_doc.functions[func_name].summary%>
    -<%end%> - - -<%if #module_doc.tables > 0 then%> -

    Tables

    - -<%for _, tab_name in ipairs(module_doc.tables) do%> - - - - -<%end%> -
    <%=tab_name%><%=module_doc.tables[tab_name].summary%>
    -<%end%> - - -
    -
    - -<%if #module_doc.functions > 0 then%> -

    Functions

    -
    -<%for _, func_name in ipairs(module_doc.functions) do%> -<%=luadoc.doclet.html.include("function.lp", { doc=doc, module_doc=module_doc, func=module_doc.functions[func_name], oop=oop })%> -<%end%> -
    -<%end%> - -<%if #module_doc.tables > 0 then%> -

    Tables

    -
    -<%for _, tab_name in ipairs(module_doc.tables) do%> -<%=luadoc.doclet.html.include("table.lp", { doc=doc, module_doc=module_doc, tab=module_doc.tables[tab_name] })%> -<%end%> -
    -<%end%> - - -
    - -
    - -
    -

    Valid XHTML 1.0!

    -
    - -
    - - diff --git a/contrib/luadoc/lua/luadoc/doclet/html/table.lp b/contrib/luadoc/lua/luadoc/doclet/html/table.lp deleted file mode 100644 index 5cd023953..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/html/table.lp +++ /dev/null @@ -1,15 +0,0 @@ -
    <%=tab.name%>
    -
    <%=tab.description%> - -<%if type(tab.field) == "table" and #tab.field > 0 then%> -Fields -
      - <%for p = 1, #tab.field do%> -
    • - <%=tab.field[p]%>: <%=tab.field[tab.field[p]] or ""%> -
    • - <%end%> -
    -<%end%> - -
    diff --git a/contrib/luadoc/lua/luadoc/doclet/raw.lua b/contrib/luadoc/lua/luadoc/doclet/raw.lua deleted file mode 100644 index 1e880b883..000000000 --- a/contrib/luadoc/lua/luadoc/doclet/raw.lua +++ /dev/null @@ -1,12 +0,0 @@ ------------------------------------------------------------------ --- @release $Id: raw.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $ ------------------------------------------------------------------ - -module "luadoc.doclet.raw" - ------------------------------------------------------------------ --- Generate the output. --- @param doc Table with the structured documentation. - -function start (doc) -end diff --git a/contrib/luadoc/lua/luadoc/init.lua b/contrib/luadoc/lua/luadoc/init.lua deleted file mode 100644 index 649515de6..000000000 --- a/contrib/luadoc/lua/luadoc/init.lua +++ /dev/null @@ -1,50 +0,0 @@ -------------------------------------------------------------------------------- --- LuaDoc main function. --- @release $Id: init.lua,v 1.4 2008/02/17 06:42:51 jasonsantos Exp $ -------------------------------------------------------------------------------- - -local require = require - -local util = require "luadoc.util" - -logger = {} - -module ("luadoc") - -------------------------------------------------------------------------------- --- LuaDoc version number. - -_COPYRIGHT = "Copyright (c) 2003-2007 The Kepler Project" -_DESCRIPTION = "Documentation Generator Tool for the Lua language" -_VERSION = "LuaDoc 3.0.1" - -------------------------------------------------------------------------------- --- Main function --- @see luadoc.doclet.html, luadoc.doclet.formatter, luadoc.doclet.raw --- @see luadoc.taglet.standard - -function main (files, options) - logger = util.loadlogengine(options) - - -- load config file - if options.config ~= nil then - -- load specified config file - dofile(options.config) - else - -- load default config file - require("luadoc.config") - end - - local taglet = require(options.taglet) - local doclet = require(options.doclet) - - -- analyze input - taglet.options = options - taglet.logger = logger - local doc = taglet.start(files) - - -- generate output - doclet.options = options - doclet.logger = logger - doclet.start(doc) -end diff --git a/contrib/luadoc/lua/luadoc/lp.lua b/contrib/luadoc/lua/luadoc/lp.lua deleted file mode 100644 index adf84f9f0..000000000 --- a/contrib/luadoc/lua/luadoc/lp.lua +++ /dev/null @@ -1,130 +0,0 @@ ----------------------------------------------------------------------------- --- Lua Pages Template Preprocessor. --- --- @release $Id: lp.lua,v 1.7 2007/04/18 14:28:39 tomas Exp $ ----------------------------------------------------------------------------- - -local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv -local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub -local concat, tinsert = table.concat, table.insert -local open = io.open - -module (...) - ----------------------------------------------------------------------------- --- function to do output -local outfunc = "io.write" --- accepts the old expression field: `$| |$' -local compatmode = true - --- --- Builds a piece of Lua code which outputs the (part of the) given string. --- @param s String. --- @param i Number with the initial position in the string. --- @param f Number with the final position in the string (default == -1). --- @return String with the correspondent Lua code which outputs the part of the string. --- -local function out (s, i, f) - s = strsub(s, i, f or -1) - if s == "" then return s end - -- we could use `%q' here, but this way we have better control - s = gsub(s, "([\\\n\'])", "\\%1") - -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it - s = gsub(s, "\r", "\\r") - return format(" %s('%s'); ", outfunc, s) -end - - ----------------------------------------------------------------------------- --- Translate the template to Lua code. --- @param s String to translate. --- @return String with translated code. ----------------------------------------------------------------------------- -function translate (s) - if compatmode then - s = gsub(s, "$|(.-)|%$", "") - s = gsub(s, "", "") - end - s = gsub(s, "<%%(.-)%%>", "") - local res = {} - local start = 1 -- start of untranslated part in `s' - while true do - local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start) - if not ip then break end - tinsert(res, out(s, start, ip-1)) - if target ~= "" and target ~= "lua" then - -- not for Lua; pass whole instruction to the output - tinsert(res, out(s, ip, fp)) - else - if exp == "=" then -- expression? - tinsert(res, format(" %s(%s);", outfunc, code)) - else -- command - tinsert(res, format(" %s ", code)) - end - end - start = fp + 1 - end - tinsert(res, out(s, start)) - return concat(res) -end - - ----------------------------------------------------------------------------- --- Defines the name of the output function. --- @param f String with the name of the function which produces output. - -function setoutfunc (f) - outfunc = f -end - ----------------------------------------------------------------------------- --- Turns on or off the compatibility with old CGILua 3.X behavior. --- @param c Boolean indicating if the compatibility mode should be used. - -function setcompatmode (c) - compatmode = c -end - ----------------------------------------------------------------------------- --- Internal compilation cache. - -local cache = {} - ----------------------------------------------------------------------------- --- Translates a template into a Lua function. --- Does NOT execute the resulting function. --- Uses a cache of templates. --- @param string String with the template to be translated. --- @param chunkname String with the name of the chunk, for debugging purposes. --- @return Function with the resulting translation. - -function compile (string, chunkname) - local f, err = cache[string] - if f then return f end - f, err = loadstring (translate (string), chunkname) - if not f then error (err, 3) end - cache[string] = f - return f -end - ----------------------------------------------------------------------------- --- Translates and executes a template in a given file. --- The translation creates a Lua function which will be executed in an --- optionally given environment. --- @param filename String with the name of the file containing the template. --- @param env Table with the environment to run the resulting function. - -function include (filename, env) - -- read the whole contents of the file - local fh = assert (open (filename)) - local src = fh:read("*a") - fh:close() - -- translates the file into a function - local prog = compile (src, '@'..filename) - local _env - if env then - _env = getfenv (prog) - setfenv (prog, env) - end - prog () -end diff --git a/contrib/luadoc/lua/luadoc/taglet/standard.lua b/contrib/luadoc/lua/luadoc/taglet/standard.lua deleted file mode 100644 index 17a305889..000000000 --- a/contrib/luadoc/lua/luadoc/taglet/standard.lua +++ /dev/null @@ -1,565 +0,0 @@ -------------------------------------------------------------------------------- --- @release $Id: standard.lua,v 1.39 2007/12/21 17:50:48 tomas Exp $ -------------------------------------------------------------------------------- - -local assert, pairs, tostring, type = assert, pairs, tostring, type -local io = require "io" -local posix = require "nixio.fs" -local luadoc = require "luadoc" -local util = require "luadoc.util" -local tags = require "luadoc.taglet.standard.tags" -local string = require "string" -local table = require "table" - -module 'luadoc.taglet.standard' - -------------------------------------------------------------------------------- --- Creates an iterator for an array base on a class type. --- @param t array to iterate over --- @param class name of the class to iterate over - -function class_iterator (t, class) - return function () - local i = 1 - return function () - while t[i] and t[i].class ~= class do - i = i + 1 - end - local v = t[i] - i = i + 1 - return v - end - end -end - --- Patterns for function recognition -local identifiers_list_pattern = "%s*(.-)%s*" -local identifier_pattern = "[^%(%s]+" -local function_patterns = { - "^()%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)", - "^%s*(local%s)%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)", - "^()%s*("..identifier_pattern..")%s*%=%s*function%s*%("..identifiers_list_pattern.."%)", -} - -------------------------------------------------------------------------------- --- Checks if the line contains a function definition --- @param line string with line text --- @return function information or nil if no function definition found - -local function check_function (line) - line = util.trim(line) - - local info = table.foreachi(function_patterns, function (_, pattern) - local r, _, l, id, param = string.find(line, pattern) - if r ~= nil then - return { - name = id, - private = (l == "local"), - param = { } --util.split("%s*,%s*", param), - } - end - end) - - -- TODO: remove these assert's? - if info ~= nil then - assert(info.name, "function name undefined") - assert(info.param, string.format("undefined parameter list for function `%s'", info.name)) - end - - return info -end - -------------------------------------------------------------------------------- --- Checks if the line contains a module definition. --- @param line string with line text --- @param currentmodule module already found, if any --- @return the name of the defined module, or nil if there is no module --- definition - -local function check_module (line, currentmodule) - line = util.trim(line) - - -- module"x.y" - -- module'x.y' - -- module[[x.y]] - -- module("x.y") - -- module('x.y') - -- module([[x.y]]) - -- module(...) - - local r, _, modulename = string.find(line, "^module%s*[%s\"'(%[]+([^,\"')%]]+)") - if r then - -- found module definition - logger:debug(string.format("found module `%s'", modulename)) - return modulename - end - return currentmodule -end - --- Patterns for constant recognition -local constant_patterns = { - "^()%s*([A-Z][A-Z0-9_]*)%s*=", - "^%s*(local%s)%s*([A-Z][A-Z0-9_]*)%s*=", -} - -------------------------------------------------------------------------------- --- Checks if the line contains a constant definition --- @param line string with line text --- @return constant information or nil if no constant definition found - -local function check_constant (line) - line = util.trim(line) - - local info = table.foreachi(constant_patterns, function (_, pattern) - local r, _, l, id = string.find(line, pattern) - if r ~= nil then - return { - name = id, - private = (l == "local"), - } - end - end) - - -- TODO: remove these assert's? - if info ~= nil then - assert(info.name, "constant name undefined") - end - - return info -end - -------------------------------------------------------------------------------- --- Extracts summary information from a description. The first sentence of each --- doc comment should be a summary sentence, containing a concise but complete --- description of the item. It is important to write crisp and informative --- initial sentences that can stand on their own --- @param description text with item description --- @return summary string or nil if description is nil - -local function parse_summary (description) - -- summary is never nil... - description = description or "" - - -- append an " " at the end to make the pattern work in all cases - description = description.." " - - -- read until the first period followed by a space or tab - local summary = string.match(description, "(.-%.)[%s\t]") - - -- if pattern did not find the first sentence, summary is the whole description - summary = summary or description - - return summary -end - -------------------------------------------------------------------------------- --- @param f file handle --- @param line current line being parsed --- @param modulename module already found, if any --- @return current line --- @return code block --- @return modulename if found - -local function parse_code (f, line, modulename) - local code = {} - while line ~= nil do - if string.find(line, "^[\t ]*%-%-%-") then - -- reached another luadoc block, end this parsing - return line, code, modulename - else - -- look for a module definition - modulename = check_module(line, modulename) - - table.insert(code, line) - line = f:read() - end - end - -- reached end of file - return line, code, modulename -end - -------------------------------------------------------------------------------- --- Parses the information inside a block comment --- @param block block with comment field --- @return block parameter - -local function parse_comment (block, first_line, modulename) - - -- get the first non-empty line of code - local code = table.foreachi(block.code, function(_, line) - if not util.line_empty(line) then - -- `local' declarations are ignored in two cases: - -- when the `nolocals' option is turned on; and - -- when the first block of a file is parsed (this is - -- necessary to avoid confusion between the top - -- local declarations and the `module' definition. - if (options.nolocals or first_line) and line:find"^%s*local" then - return - end - return line - end - end) - - -- parse first line of code - if code ~= nil then - local func_info = check_function(code) - local module_name = check_module(code) - local const_info = check_constant(code) - if func_info then - block.class = "function" - block.name = func_info.name - block.param = func_info.param - block.private = func_info.private - elseif const_info then - block.class = "constant" - block.name = const_info.name - block.private = const_info.private - elseif module_name then - block.class = "module" - block.name = module_name - block.param = {} - else - block.param = {} - end - else - -- TODO: comment without any code. Does this means we are dealing - -- with a file comment? - end - - -- parse @ tags - local currenttag = "description" - local currenttext - - table.foreachi(block.comment, function (_, line) - line = util.trim_comment(line) - - local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)") - if r ~= nil then - -- found new tag, add previous one, and start a new one - -- TODO: what to do with invalid tags? issue an error? or log a warning? - tags.handle(currenttag, block, currenttext) - - currenttag = tag - currenttext = text - else - currenttext = util.concat(currenttext, line) - assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line)) - end - end) - tags.handle(currenttag, block, currenttext) - - -- extracts summary information from the description - block.summary = parse_summary(block.description) - assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description)) - - if block.name and block.class == "module" then - modulename = block.name - end - - return block, modulename -end - -------------------------------------------------------------------------------- --- Parses a block of comment, started with ---. Read until the next block of --- comment. --- @param f file handle --- @param line being parsed --- @param modulename module already found, if any --- @return line --- @return block parsed --- @return modulename if found - -local function parse_block (f, line, modulename, first) - local block = { - comment = {}, - code = {}, - } - - while line ~= nil do - if 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) - - -- parse information in block comment - block, modulename = parse_comment(block, first, modulename) - - return line, block, modulename - else - table.insert(block.comment, line) - line = f:read() - end - end - -- reached end of file - - -- parse information in block comment - block, modulename = parse_comment(block, first, modulename) - - return line, block, modulename -end - -------------------------------------------------------------------------------- --- Parses a file documented following luadoc format. --- @param filepath full path of file to parse --- @param doc table with documentation --- @return table with documentation - -function parse_file (filepath, doc, handle, prev_line, prev_block, prev_modname) - local blocks = { prev_block } - local modulename = prev_modname - - -- read each line - local f = handle or io.open(filepath, "r") - local i = 1 - local line = prev_line or f:read() - local first = true - while line ~= nil do - - if string.find(line, "^[\t ]*%-%-%-") then - -- reached a luadoc block - local block, newmodname - line, block, newmodname = parse_block(f, line, modulename, first) - - if modulename and newmodname and newmodname ~= modulename then - doc = parse_file( nil, doc, f, line, block, newmodname ) - else - table.insert(blocks, block) - modulename = newmodname - end - else - -- look for a module definition - local newmodname = check_module(line, modulename) - - if modulename and newmodname and newmodname ~= modulename then - parse_file( nil, doc, f ) - else - modulename = newmodname - end - - -- TODO: keep beginning of file somewhere - - line = f:read() - end - first = false - i = i + 1 - end - - if not handle then - f:close() - end - - if filepath then - -- store blocks in file hierarchy - assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath)) - table.insert(doc.files, filepath) - doc.files[filepath] = { - type = "file", - name = filepath, - doc = blocks, - -- functions = class_iterator(blocks, "function"), - -- tables = class_iterator(blocks, "table"), - } - -- - local first = doc.files[filepath].doc[1] - if first and modulename then - doc.files[filepath].author = first.author - doc.files[filepath].copyright = first.copyright - doc.files[filepath].description = first.description - doc.files[filepath].release = first.release - doc.files[filepath].summary = first.summary - end - end - - -- if module definition is found, store in module hierarchy - if modulename ~= nil then - if modulename == "..." then - assert( filepath, "Can't determine name for virtual module from filepatch" ) - modulename = string.gsub (filepath, "%.lua$", "") - modulename = string.gsub (modulename, "/", ".") - end - if doc.modules[modulename] ~= nil then - -- module is already defined, just add the blocks - table.foreachi(blocks, function (_, v) - table.insert(doc.modules[modulename].doc, v) - end) - else - -- TODO: put this in a different module - table.insert(doc.modules, modulename) - doc.modules[modulename] = { - type = "module", - name = modulename, - doc = blocks, --- functions = class_iterator(blocks, "function"), --- tables = class_iterator(blocks, "table"), - author = first and first.author, - copyright = first and first.copyright, - description = "", - release = first and first.release, - summary = "", - } - - -- find module description - for m in class_iterator(blocks, "module")() do - doc.modules[modulename].description = util.concat( - doc.modules[modulename].description, - m.description) - doc.modules[modulename].summary = util.concat( - doc.modules[modulename].summary, - m.summary) - if m.author then - doc.modules[modulename].author = m.author - end - if m.copyright then - doc.modules[modulename].copyright = m.copyright - end - if m.release then - doc.modules[modulename].release = m.release - end - if m.name then - doc.modules[modulename].name = m.name - end - end - doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or "" - doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or "" - end - - -- make functions table - doc.modules[modulename].functions = {} - for f in class_iterator(blocks, "function")() do - if f and f.name then - table.insert(doc.modules[modulename].functions, f.name) - doc.modules[modulename].functions[f.name] = f - end - end - - -- make tables table - doc.modules[modulename].tables = {} - for t in class_iterator(blocks, "table")() do - if t and t.name then - table.insert(doc.modules[modulename].tables, t.name) - doc.modules[modulename].tables[t.name] = t - end - end - - -- make constants table - doc.modules[modulename].constants = {} - for c in class_iterator(blocks, "constant")() do - if c and c.name then - table.insert(doc.modules[modulename].constants, c.name) - doc.modules[modulename].constants[c.name] = c - end - end - end - - if filepath then - -- make functions table - doc.files[filepath].functions = {} - for f in class_iterator(blocks, "function")() do - if f and f.name then - table.insert(doc.files[filepath].functions, f.name) - doc.files[filepath].functions[f.name] = f - end - end - - -- make tables table - doc.files[filepath].tables = {} - for t in class_iterator(blocks, "table")() do - if t and t.name then - table.insert(doc.files[filepath].tables, t.name) - doc.files[filepath].tables[t.name] = t - end - end - end - - return doc -end - -------------------------------------------------------------------------------- --- Checks if the file is terminated by ".lua" or ".luadoc" and calls the --- function that does the actual parsing --- @param filepath full path of the file to parse --- @param doc table with documentation --- @return table with documentation --- @see parse_file - -function file (filepath, doc) - local patterns = { "%.lua$", "%.luadoc$" } - local valid = table.foreachi(patterns, function (_, pattern) - if string.find(filepath, pattern) ~= nil then - return true - end - end) - - if valid then - logger:info(string.format("processing file `%s'", filepath)) - doc = parse_file(filepath, doc) - end - - return doc -end - -------------------------------------------------------------------------------- --- Recursively iterates through a directory, parsing each file --- @param path directory to search --- @param doc table with documentation --- @return table with documentation - -function directory (path, doc) - for f in posix.dir(path) do - local fullpath = path .. "/" .. f - local attr = posix.stat(fullpath) - assert(attr, string.format("error stating file `%s'", fullpath)) - - if attr.type == "reg" then - doc = file(fullpath, doc) - elseif attr.type == "dir" and f ~= "." and f ~= ".." then - doc = directory(fullpath, doc) - end - end - return doc -end - --- Recursively sorts the documentation table -local function recsort (tab) - table.sort (tab) - -- sort list of functions by name alphabetically - for f, doc in pairs(tab) do - if doc.functions then - table.sort(doc.functions) - end - if doc.tables then - table.sort(doc.tables) - end - end -end - -------------------------------------------------------------------------------- - -function start (files, doc) - assert(files, "file list not specified") - - -- Create an empty document, or use the given one - doc = doc or { - files = {}, - modules = {}, - } - assert(doc.files, "undefined `files' field") - assert(doc.modules, "undefined `modules' field") - - table.foreachi(files, function (_, path) - local attr = posix.stat(path) - assert(attr, string.format("error stating path `%s'", path)) - - if attr.type == "reg" then - doc = file(path, doc) - elseif attr.type == "dir" then - doc = directory(path, doc) - end - end) - - -- order arrays alphabetically - recsort(doc.files) - recsort(doc.modules) - - return doc -end diff --git a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua b/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua deleted file mode 100644 index d03df82df..000000000 --- a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua +++ /dev/null @@ -1,178 +0,0 @@ -------------------------------------------------------------------------------- --- Handlers for several tags --- @release $Id: tags.lua,v 1.8 2007/09/05 12:39:09 tomas Exp $ -------------------------------------------------------------------------------- - -local luadoc = require "luadoc" -local util = require "luadoc.util" -local string = require "string" -local table = require "table" -local assert, type, tostring = assert, type, tostring - -module "luadoc.taglet.standard.tags" - -------------------------------------------------------------------------------- - -local function author (tag, block, text) - block[tag] = block[tag] or {} - if not text then - luadoc.logger:warn("author `name' not defined [["..text.."]]: skipping") - return - end - table.insert (block[tag], text) -end - -------------------------------------------------------------------------------- --- Set the class of a comment block. Classes can be "module", "function", --- "table". The first two classes are automatic, extracted from the source code - -local function class (tag, block, text) - block[tag] = text -end - -------------------------------------------------------------------------------- - -local function cstyle (tag, block, text) - block[tag] = text -end - -------------------------------------------------------------------------------- - -local function copyright (tag, block, text) - block[tag] = text -end - -------------------------------------------------------------------------------- - -local function description (tag, block, text) - block[tag] = text -end - -------------------------------------------------------------------------------- - -local function field (tag, block, text) - if block["class"] ~= "table" then - luadoc.logger:warn("documenting `field' for block that is not a `table'") - end - block[tag] = block[tag] or {} - - local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)") - assert(name, "field name not defined") - - table.insert(block[tag], name) - block[tag][name] = desc -end - -------------------------------------------------------------------------------- --- Set the name of the comment block. If the block already has a name, issue --- an error and do not change the previous value - -local function name (tag, block, text) - if block[tag] and block[tag] ~= text then - luadoc.logger:error(string.format("block name conflict: `%s' -> `%s'", block[tag], text)) - end - - block[tag] = text -end - -------------------------------------------------------------------------------- --- Processes a parameter documentation. --- @param tag String with the name of the tag (it must be "param" always). --- @param block Table with previous information about the block. --- @param text String with the current line beeing processed. - -local function param (tag, block, text) - block[tag] = block[tag] or {} - -- TODO: make this pattern more flexible, accepting empty descriptions - local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)") - if not name then - luadoc.logger:warn("parameter `name' not defined [["..text.."]]: skipping") - return - end - local i = table.foreachi(block[tag], function (i, v) - if v == name then - return i - end - end) - if i == nil then - luadoc.logger:warn(string.format("documenting undefined parameter `%s'", name)) - table.insert(block[tag], name) - end - block[tag][name] = desc -end - -------------------------------------------------------------------------------- - -local function release (tag, block, text) - block[tag] = text -end - -------------------------------------------------------------------------------- - -local function ret (tag, block, text) - tag = "ret" - if type(block[tag]) == "string" then - block[tag] = { block[tag], text } - elseif type(block[tag]) == "table" then - table.insert(block[tag], text) - else - block[tag] = text - end -end - -------------------------------------------------------------------------------- --- @see ret - -local function see (tag, block, text) - -- see is always an array - block[tag] = block[tag] or {} - - -- remove trailing "." - text = string.gsub(text, "(.*)%.$", "%1") - - local s = util.split("%s*,%s*", text) - - table.foreachi(s, function (_, v) - table.insert(block[tag], v) - end) -end - -------------------------------------------------------------------------------- --- @see ret - -local function usage (tag, block, text) - if type(block[tag]) == "string" then - block[tag] = { block[tag], text } - elseif type(block[tag]) == "table" then - table.insert(block[tag], text) - else - block[tag] = text - end -end - -------------------------------------------------------------------------------- - -local handlers = {} -handlers["author"] = author -handlers["class"] = class -handlers["cstyle"] = cstyle -handlers["copyright"] = copyright -handlers["description"] = description -handlers["field"] = field -handlers["name"] = name -handlers["param"] = param -handlers["release"] = release -handlers["return"] = ret -handlers["see"] = see -handlers["usage"] = usage - -------------------------------------------------------------------------------- - -function handle (tag, block, text) - if not handlers[tag] then - luadoc.logger:error(string.format("undefined handler for tag `%s'", tag)) - return - 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/contrib/luadoc/lua/luadoc/util.lua deleted file mode 100644 index acaaac828..000000000 --- a/contrib/luadoc/lua/luadoc/util.lua +++ /dev/null @@ -1,201 +0,0 @@ -------------------------------------------------------------------------------- --- General utilities. --- @release $Id: util.lua,v 1.16 2008/02/17 06:42:51 jasonsantos Exp $ -------------------------------------------------------------------------------- - -local posix = require "nixio.fs" -local type, table, string, io, assert, tostring, setmetatable, pcall = type, table, string, io, assert, tostring, setmetatable, pcall - -------------------------------------------------------------------------------- --- Module with several utilities that could not fit in a specific module - -module "luadoc.util" - -------------------------------------------------------------------------------- --- Removes spaces from the begining and end of a given string --- @param s string to be trimmed --- @return trimmed string - -function trim (s) - return (string.gsub(s, "^%s*(.-)%s*$", "%1")) -end - -------------------------------------------------------------------------------- --- Removes spaces from the begining and end of a given string, considering the --- string is inside a lua comment. --- @param s string to be trimmed --- @return trimmed string --- @see trim --- @see string.gsub - -function trim_comment (s) - s = string.gsub(s, "%-%-+(.*)$", "%1") - return trim(s) -end - -------------------------------------------------------------------------------- --- Checks if a given line is empty --- @param line string with a line --- @return true if line is empty, false otherwise - -function line_empty (line) - return (string.len(trim(line)) == 0) -end - -------------------------------------------------------------------------------- --- Appends two string, but if the first one is nil, use to second one --- @param str1 first string, can be nil --- @param str2 second string --- @return str1 .. " " .. str2, or str2 if str1 is nil - -function concat (str1, str2) - if str1 == nil or string.len(str1) == 0 then - return str2 - else - return str1 .. " " .. str2 - end -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 --- field 1 may be preceeded by leading whitespace --- @usage split(",%s*", "Anna, Bob, Charlie,Dolores") --- @usage split(""," x y") gives {"x","y"} --- @usage split("%s+"," x y") gives {"", "x","y"} --- @return array with strings --- @see table.concat - -function split(delim, text) - local list = {} - if string.len(text) > 0 then - 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 - error("delim matches empty string!") - end - local first, last - while 1 do - if delim ~= "" then - first, last = string.find(text, delim, pos) - else - first, last = string.find(text, "%s+", pos) - if first == 1 then - pos = last+1 - first, last = string.find(text, "%s+", pos) - end - end - if first then -- found? - table.insert(list, string.sub(text, pos, first-1)) - pos = last+1 - else - table.insert(list, string.sub(text, pos)) - break - end - end - end - return list -end - -------------------------------------------------------------------------------- --- Comments a paragraph. --- @param text text to comment with "--", may contain several lines --- @return commented text - -function comment (text) - text = string.gsub(text, "\n", "\n-- ") - return "-- " .. text -end - -------------------------------------------------------------------------------- --- Wrap a string into a paragraph. --- @param s string to wrap --- @param w width to wrap to [80] --- @param i1 indent of first line [0] --- @param i2 indent of subsequent lines [0] --- @return wrapped paragraph - -function wrap(s, w, i1, i2) - w = w or 80 - i1 = i1 or 0 - i2 = i2 or 0 - assert(i1 < w and i2 < w, "the indents must be less than the line width") - s = string.rep(" ", i1) .. s - local lstart, len = 1, string.len(s) - while len - lstart > w do - local i = lstart + w - while i > lstart and string.sub(s, i, i) ~= " " do i = i - 1 end - local j = i - while j > lstart and string.sub(s, j, j) == " " do j = j - 1 end - s = string.sub(s, 1, j) .. "\n" .. string.rep(" ", i2) .. - string.sub(s, i + 1, -1) - local change = i2 + 1 - (i - j) - lstart = j + change - len = len + change - end - return s -end - -------------------------------------------------------------------------------- --- Opens a file, creating the directories if necessary --- @param filename full path of the file to open (or create) --- @param mode mode of opening --- @return file handle - -function posix.open (filename, mode) - local f = io.open(filename, mode) - if f == nil then - filename = string.gsub(filename, "\\", "/") - local dir = "" - for d in string.gfind(filename, ".-/") do - dir = dir .. d - posix.mkdir(dir) - end - f = io.open(filename, mode) - end - return f -end - - ----------------------------------------------------------------------------------- --- Creates a Logger with LuaLogging, if present. Otherwise, creates a mock logger. --- @param options a table with options for the logging mechanism --- @return logger object that will implement log methods - -function loadlogengine(options) - local logenabled = pcall(function() - 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 = {} - setmetatable(logger, noop) - end - - return logger -end - diff --git a/libs/luci-lib-httpclient/luasrc/httpclient.lua b/libs/luci-lib-httpclient/luasrc/httpclient.lua index 94c2e9eca..c76cc542e 100644 --- a/libs/luci-lib-httpclient/luasrc/httpclient.lua +++ b/libs/luci-lib-httpclient/luasrc/httpclient.lua @@ -97,7 +97,11 @@ end function request_raw(uri, options) options = options or {} local pr, auth, host, port, path - + + if options.params then + uri = uri .. '?' .. http.urlencode_params(options.params) + end + if uri:find("%[") then if uri:find("@") then pr, auth, host, port, path = uri:match("(%w+)://(.+)@(%b[]):?([0-9]*)(.*)") @@ -176,20 +180,8 @@ function request_raw(uri, options) options.method = options.method or "POST" end - -- Assemble message - local message = {(options.method or "GET") .. " " .. path .. " " .. protocol} - - for k, v in pairs(headers) do - if type(v) == "string" or type(v) == "number" then - message[#message+1] = k .. ": " .. v - elseif type(v) == "table" then - for i, j in ipairs(v) do - message[#message+1] = k .. ": " .. j - end - end - end - if options.cookies then + local cookiedata = {} for _, c in ipairs(options.cookies) do local cdo = c.flags.domain local cpa = c.flags.path @@ -197,11 +189,29 @@ function request_raw(uri, options) and (cpa == path or cpa == "/" or cpa .. "/" == path:sub(#cpa+1)) and (not c.flags.secure or pr == "https") then - message[#message+1] = "Cookie: " .. c.key .. "=" .. c.value + cookiedata[#cookiedata+1] = c.key .. "=" .. c.value end end + if headers["Cookie"] then + headers["Cookie"] = headers["Cookie"] .. "; " .. table.concat(cookiedata, "; ") + else + headers["Cookie"] = table.concat(cookiedata, "; ") + end end + + -- Assemble message + local message = {(options.method or "GET") .. " " .. path .. " " .. protocol} + for k, v in pairs(headers) do + if type(v) == "string" or type(v) == "number" then + message[#message+1] = k .. ": " .. v + elseif type(v) == "table" then + for i, j in ipairs(v) do + message[#message+1] = k .. ": " .. j + end + end + end + message[#message+1] = "" message[#message+1] = "" @@ -323,7 +333,7 @@ function request_raw(uri, options) end end - return response.code, response, linesrc(true), sock + return response.code, response, linesrc(true)..sock:readall(), sock end function cookie_parse(cookiestr) diff --git a/libs/luci-lib-ip/Makefile b/libs/luci-lib-ip/Makefile new file mode 100644 index 000000000..eb80dcb25 --- /dev/null +++ b/libs/luci-lib-ip/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2015 LuCI Team +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Lua library for IP calculation and routing information +LUCI_DEPENDS:=+liblua +libnl-tiny + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-ip/src/Makefile b/libs/luci-lib-ip/src/Makefile new file mode 100644 index 000000000..76abd27d2 --- /dev/null +++ b/libs/luci-lib-ip/src/Makefile @@ -0,0 +1,17 @@ +IP_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/libnl-tiny/ +IP_LDFLAGS = -llua -lm -lnl-tiny +IP_OBJ = ip.o +IP_LIB = ip.so + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(IP_CFLAGS) $(FPIC) -c -o $@ $< + +compile: $(IP_OBJ) + $(CC) $(LDFLAGS) -shared -o $(IP_LIB) $(IP_OBJ) $(IP_LDFLAGS) + +install: compile + mkdir -p $(DESTDIR)/usr/lib/lua/luci + cp $(IP_LIB) $(DESTDIR)/usr/lib/lua/luci/$(IP_LIB) + +clean: + rm -f *.o *.so diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c new file mode 100644 index 000000000..b91966c53 --- /dev/null +++ b/libs/luci-lib-ip/src/ip.c @@ -0,0 +1,1392 @@ +/* +Copyright 2015 Jo-Philipp Wich + +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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LUCI_IP "luci.ip" +#define LUCI_IP_CIDR "luci.ip.cidr" + +#define RTA_INT(x) (*(int *)RTA_DATA(x)) +#define RTA_U32(x) (*(uint32_t *)RTA_DATA(x)) + +static int hz = 0; +static struct nl_sock *sock = NULL; + +typedef struct { + union { + struct in_addr v4; + struct in6_addr v6; + } addr; + int len; + int bits; + int family; + bool exact; +} cidr_t; + +struct dump_filter { + bool get; + int family; + int iif; + int oif; + int type; + int scope; + int proto; + int table; + cidr_t gw; + cidr_t from; + cidr_t src; + cidr_t dst; + struct ether_addr mac; +}; + +struct dump_state { + int index; + int pending; + int callback; + struct lua_State *L; + struct dump_filter *filter; +}; + + +static int _cidr_new(lua_State *L, int index, int family, bool mask); + +static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p) +{ + if (lua_type(L, index) == LUA_TUSERDATA) + return luaL_checkudata(L, index, LUCI_IP_CIDR); + + if (_cidr_new(L, index, p ? p->family : 0, false)) + return lua_touserdata(L, -1); + + luaL_error(L, "Invalid operand"); + return NULL; +} + +static bool parse_mask(int family, const char *mask, int *bits) +{ + char *e; + struct in_addr m; + struct in6_addr m6; + + if (family == AF_INET && inet_pton(AF_INET, mask, &m)) + { + for (*bits = 0, m.s_addr = ntohl(m.s_addr); + *bits < 32 && (m.s_addr << *bits) & 0x80000000; + ++*bits); + } + else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6)) + { + for (*bits = 0; + *bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128; + ++*bits); + } + else + { + *bits = strtoul(mask, &e, 10); + + if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128)) + return false; + } + + return true; +} + +static bool parse_cidr(const char *dest, cidr_t *pp) +{ + char *p, buf[INET6_ADDRSTRLEN * 2 + 2]; + uint8_t bitlen = 0; + + strncpy(buf, dest, sizeof(buf) - 1); + + p = strchr(buf, '/'); + + if (p) + *p++ = 0; + + if (inet_pton(AF_INET, buf, &pp->addr.v4)) + { + bitlen = 32; + pp->family = AF_INET; + pp->len = sizeof(struct in_addr); + } + else if (inet_pton(AF_INET6, buf, &pp->addr.v6)) + { + bitlen = 128; + pp->family = AF_INET6; + pp->len = sizeof(struct in6_addr); + } + else + return false; + + if (p) + { + if (!parse_mask(pp->family, p, &pp->bits)) + return false; + } + else + { + pp->bits = bitlen; + } + + return true; +} + +static int L_getint(lua_State *L, int index, const char *name) +{ + int rv = 0; + + lua_getfield(L, index, name); + + if (lua_type(L, -1) == LUA_TNUMBER) + rv = lua_tonumber(L, -1); + + lua_pop(L, 1); + + return rv; +} + +static const char * L_getstr(lua_State *L, int index, const char *name) +{ + const char *rv = NULL; + + lua_getfield(L, index, name); + + if (lua_type(L, -1) == LUA_TSTRING) + rv = lua_tostring(L, -1); + + lua_pop(L, 1); + + return rv; +} + +static void L_setint(struct lua_State *L, const char *name, uint32_t n) +{ + lua_pushinteger(L, n); + lua_setfield(L, -2, name); +} + +static void L_setbool(struct lua_State *L, const char *name, bool val) +{ + lua_pushboolean(L, val); + lua_setfield(L, -2, name); +} + +static void L_setaddr(struct lua_State *L, const char *name, + int family, void *addr, int bits) +{ + cidr_t *p; + + if (!addr) + return; + + p = lua_newuserdata(L, sizeof(*p)); + + if (!p) + return; + + if (family == AF_INET) + { + p->family = AF_INET; + p->bits = (bits < 0) ? 32 : bits; + p->len = sizeof(p->addr.v4); + p->addr.v4 = *(struct in_addr *)addr; + } + else + { + p->family = AF_INET6; + p->bits = (bits < 0) ? 128 : bits; + p->len = sizeof(p->addr.v6); + p->addr.v6 = *(struct in6_addr *)addr; + } + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + lua_setfield(L, -2, name); +} + +static void L_setstr(struct lua_State *L, const char *name, const char *val) +{ + lua_pushstring(L, val); + lua_setfield(L, -2, name); +} + +static void L_setdev(struct lua_State *L, const char *name, + struct nlattr *attr) +{ + char buf[32]; + + if (if_indextoname(RTA_INT(attr), buf)) + L_setstr(L, name, buf); +} + +static int L_checkbits(lua_State *L, int index, cidr_t *p) +{ + int bits; + + if (lua_gettop(L) < index || lua_isnil(L, index)) + { + bits = p->bits; + } + else if (lua_type(L, index) == LUA_TNUMBER) + { + bits = lua_tointeger(L, index); + + if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128)) + return luaL_error(L, "Invalid prefix size"); + } + else if (lua_type(L, index) == LUA_TSTRING) + { + if (!parse_mask(p->family, lua_tostring(L, index), &bits)) + return luaL_error(L, "Invalid netmask format"); + } + else + { + return luaL_error(L, "Invalid data type"); + } + + return bits; +} + +static int _cidr_new(lua_State *L, int index, int family, bool mask) +{ + uint32_t n; + const char *addr; + cidr_t cidr = { }, *cidrp; + + if (lua_type(L, index) == LUA_TNUMBER) + { + n = htonl(lua_tointeger(L, index)); + + if (family == AF_INET6) + { + cidr.family = AF_INET6; + cidr.bits = 128; + cidr.len = sizeof(cidr.addr.v6); + cidr.addr.v6.s6_addr[12] = n; + cidr.addr.v6.s6_addr[13] = (n >> 8); + cidr.addr.v6.s6_addr[14] = (n >> 16); + cidr.addr.v6.s6_addr[15] = (n >> 24); + } + else + { + cidr.family = AF_INET; + cidr.bits = 32; + cidr.len = sizeof(cidr.addr.v4); + cidr.addr.v4.s_addr = n; + } + } + else + { + addr = luaL_checkstring(L, index); + + if (!parse_cidr(addr, &cidr)) + return 0; + + if (family && cidr.family != family) + return 0; + + if (mask) + cidr.bits = L_checkbits(L, index + 1, &cidr); + } + + if (!(cidrp = lua_newuserdata(L, sizeof(*cidrp)))) + return 0; + + *cidrp = cidr; + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_new(lua_State *L) +{ + return _cidr_new(L, 1, 0, true); +} + +static int cidr_ipv4(lua_State *L) +{ + return _cidr_new(L, 1, AF_INET, true); +} + +static int cidr_ipv6(lua_State *L) +{ + return _cidr_new(L, 1, AF_INET6, true); +} + +static int cidr_is4(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, p->family == AF_INET); + return 1; +} + +static int cidr_is4rfc1918(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + uint32_t a = htonl(p->addr.v4.s_addr); + + lua_pushboolean(L, (p->family == AF_INET && + ((a >= 0x0A000000 && a <= 0x0AFFFFFF) || + (a >= 0xAC100000 && a <= 0xAC1FFFFF) || + (a >= 0xC0A80000 && a <= 0xC0A8FFFF)))); + + return 1; +} + +static int cidr_is4linklocal(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + uint32_t a = htonl(p->addr.v4.s_addr); + + lua_pushboolean(L, (p->family == AF_INET && + a >= 0xA9FE0000 && + a <= 0xA9FEFFFF)); + + return 1; +} + +static bool _is_mapped4(cidr_t *p) +{ + return (p->family == AF_INET6 && + p->addr.v6.s6_addr[0] == 0 && + p->addr.v6.s6_addr[1] == 0 && + p->addr.v6.s6_addr[2] == 0 && + p->addr.v6.s6_addr[3] == 0 && + p->addr.v6.s6_addr[4] == 0 && + p->addr.v6.s6_addr[5] == 0 && + p->addr.v6.s6_addr[6] == 0 && + p->addr.v6.s6_addr[7] == 0 && + p->addr.v6.s6_addr[8] == 0 && + p->addr.v6.s6_addr[9] == 0 && + p->addr.v6.s6_addr[10] == 0xFF && + p->addr.v6.s6_addr[11] == 0xFF); +} + +static int cidr_is6mapped4(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, _is_mapped4(p)); + return 1; +} + +static int cidr_is6(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, p->family == AF_INET6); + return 1; +} + +static int cidr_is6linklocal(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, (p->family == AF_INET6 && + p->addr.v6.s6_addr[0] == 0xFE && + p->addr.v6.s6_addr[1] >= 0x80 && + p->addr.v6.s6_addr[1] <= 0xBF)); + + return 1; +} + +static int _cidr_cmp(lua_State *L) +{ + cidr_t *a = L_checkcidr(L, 1, NULL); + cidr_t *b = L_checkcidr(L, 2, NULL); + + if (a->family != b->family) + return (a->family - b->family); + + return memcmp(&a->addr.v6, &b->addr.v6, a->len); +} + +static int cidr_lower(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) < 0); + return 1; +} + +static int cidr_higher(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) > 0); + return 1; +} + +static int cidr_equal(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) == 0); + return 1; +} + +static int cidr_lower_equal(lua_State *L) +{ + lua_pushboolean(L, _cidr_cmp(L) <= 0); + return 1; +} + +static int cidr_prefix(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + int bits = L_checkbits(L, 2, p); + + p->bits = bits; + lua_pushinteger(L, p->bits); + return 1; +} + +static void _apply_mask(cidr_t *p, int bits, bool inv) +{ + uint8_t b, i; + + if (bits <= 0) + { + memset(&p->addr.v6, inv * 0xFF, p->len); + } + else if (p->family == AF_INET && bits <= 32) + { + if (inv) + p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1); + else + p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1)); + } + else if (p->family == AF_INET6 && bits <= 128) + { + for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++) + { + b = (bits > 8) ? 8 : bits; + if (inv) + p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b))); + else + p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b)); + bits -= b; + } + } +} + +static int cidr_network(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2; + int bits = L_checkbits(L, 2, p1); + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + _apply_mask(p2, bits, false); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_host(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = lua_newuserdata(L, sizeof(*p2)); + + if (!p2) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_mask(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2; + int bits = L_checkbits(L, 2, p1); + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->family = p1->family; + + memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr)); + _apply_mask(p2, bits, false); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_broadcast(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + int bits = L_checkbits(L, 2, p1); + + if (p1->family == AF_INET6) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + *p2 = *p1; + p2->bits = (p1->family == AF_INET) ? 32 : 128; + _apply_mask(p2, bits, true); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_mapped4(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + + if (!_is_mapped4(p1)) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->family = AF_INET; + p2->bits = (p1->bits > 32) ? 32 : p1->bits; + memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4)); + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_contains(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = L_checkcidr(L, 2, NULL); + cidr_t a = *p1, b = *p2; + bool rv = false; + + if (p1->family == p2->family && p1->bits <= p2->bits) + { + _apply_mask(&a, p1->bits, false); + _apply_mask(&b, p1->bits, false); + + rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len); + } + + lua_pushboolean(L, rv); + return 1; +} + +#define S6_BYTE(a, i) \ + (a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1] + +static int _cidr_add_sub(lua_State *L, bool add) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2 = L_checkcidr(L, 2, p1); + cidr_t r = *p1; + bool inplace = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : false; + bool ok = true; + uint8_t i, carry; + uint32_t a, b; + + if (p1->family == p2->family) + { + if (p1->family == AF_INET6) + { + for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++) + { + if (add) + { + S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry; + carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256; + } + else + { + S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry); + carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry)); + } + } + + /* would over/underflow */ + if (carry) + { + memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6)); + ok = false; + } + } + else + { + a = ntohl(p1->addr.v4.s_addr); + b = ntohl(p2->addr.v4.s_addr); + + /* would over/underflow */ + if ((add && (UINT_MAX - a) < b) || (!add && a < b)) + { + r.addr.v4.s_addr = add * 0xFFFFFFFF; + ok = false; + } + else + { + r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); + } + } + } + else + { + ok = false; + } + + if (inplace) + { + *p1 = r; + lua_pushboolean(L, ok); + return 1; + } + + if (!(p1 = lua_newuserdata(L, sizeof(*p1)))) + return 0; + + *p1 = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_add(lua_State *L) +{ + return _cidr_add_sub(L, true); +} + +static int cidr_sub(lua_State *L) +{ + return _cidr_add_sub(L, false); +} + +static int cidr_minhost(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + cidr_t r = *p; + uint8_t i, rest, carry; + + _apply_mask(&r, r.bits, false); + + if (r.family == AF_INET6 && r.bits < 128) + { + r.bits = 128; + + for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++) + { + rest = (S6_BYTE(&r, i) + carry) > 255; + S6_BYTE(&r, i) += carry; + carry = rest; + } + } + else if (r.family == AF_INET && r.bits < 32) + { + r.bits = 32; + r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1); + } + + if (!(p = lua_newuserdata(L, sizeof(*p)))) + return 0; + + *p = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_maxhost(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + cidr_t r = *p; + + _apply_mask(&r, r.bits, true); + + if (r.family == AF_INET && r.bits < 32) + { + r.bits = 32; + r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1); + } + else if (r.family == AF_INET6) + { + r.bits = 128; + } + + if (!(p = lua_newuserdata(L, sizeof(*p)))) + return 0; + + *p = r; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_gc (lua_State *L) +{ + return 0; +} + +static int cidr_tostring (lua_State *L) +{ + char buf[INET6_ADDRSTRLEN]; + cidr_t *p = L_checkcidr(L, 1, NULL); + + if ((p->family == AF_INET && p->bits < 32) || + (p->family == AF_INET6 && p->bits < 128)) + { + lua_pushfstring(L, "%s/%d", + inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)), + p->bits); + } + else + { + lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf))); + } + + return 1; +} + +/* + * route functions + */ + +static bool diff_prefix(int family, void *addr, int bits, cidr_t *p) +{ + uint8_t i, b, r; + uint32_t m; + + if (!p->family) + return false; + + if (!addr || p->family != family || p->bits > bits) + return true; + + if (family == AF_INET6) + { + for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++) + { + b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0; + + if ((((struct in6_addr *)addr)->s6_addr[i] & b) != + (p->addr.v6.s6_addr[i] & b)) + return true; + + r -= ((r > 8) ? 8 : r); + } + } + else + { + m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0; + + if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m)) + return true; + } + + return (p->exact && p->bits != bits); +} + +static int cb_dump_route(struct nl_msg *msg, void *arg) +{ + struct dump_state *s = arg; + struct dump_filter *f = s->filter; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct rtmsg *rt = NLMSG_DATA(hdr); + struct nlattr *tb[RTA_MAX+1]; + struct in6_addr *src, *dst, *gw, *from, def = { }; + int iif, oif, bitlen; + uint32_t table; + + if (hdr->nlmsg_type != RTM_NEWROUTE || + (rt->rtm_family != AF_INET && rt->rtm_family != AF_INET6)) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*rt), tb, RTA_MAX, NULL); + + iif = tb[RTA_IIF] ? RTA_INT(tb[RTA_IIF]) : 0; + oif = tb[RTA_OIF] ? RTA_INT(tb[RTA_OIF]) : 0; + table = tb[RTA_TABLE] ? RTA_U32(tb[RTA_TABLE]) : rt->rtm_table; + from = tb[RTA_SRC] ? RTA_DATA(tb[RTA_SRC]) : NULL; + src = tb[RTA_PREFSRC] ? RTA_DATA(tb[RTA_PREFSRC]) : NULL; + dst = tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &def; + gw = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL; + + bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32; + + if ((f->type && rt->rtm_type != f->type) || + (f->family && rt->rtm_family != f->family) || + (f->proto && rt->rtm_protocol != f->proto) || + (f->scope && rt->rtm_scope != f->scope) || + (f->iif && iif != f->iif) || + (f->oif && oif != f->oif) || + (f->table && table != f->table) || + diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) || + diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, &f->dst) || + diff_prefix(rt->rtm_family, gw, bitlen, &f->gw) || + diff_prefix(rt->rtm_family, src, bitlen, &f->src)) + goto out; + + if (s->callback) + lua_pushvalue(s->L, 2); + + lua_newtable(s->L); + + L_setint(s->L, "type", rt->rtm_type); + L_setint(s->L, "family", (rt->rtm_family == AF_INET) ? 4 : 6); + + L_setaddr(s->L, "dest", rt->rtm_family, dst, rt->rtm_dst_len); + + if (gw) + L_setaddr(s->L, "gw", rt->rtm_family, gw, -1); + + if (from) + L_setaddr(s->L, "from", rt->rtm_family, from, rt->rtm_src_len); + + if (iif) + L_setdev(s->L, "iif", tb[RTA_IIF]); + + if (oif) + L_setdev(s->L, "dev", tb[RTA_OIF]); + + L_setint(s->L, "table", table); + L_setint(s->L, "proto", rt->rtm_protocol); + L_setint(s->L, "scope", rt->rtm_scope); + + if (src) + L_setaddr(s->L, "src", rt->rtm_family, src, -1); + + if (tb[RTA_PRIORITY]) + L_setint(s->L, "metric", RTA_U32(tb[RTA_PRIORITY])); + + if (rt->rtm_family == AF_INET6 && tb[RTA_CACHEINFO]) + { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + + if (ci->rta_expires) + { + if (ci->rta_expires) + L_setint(s->L, "expires", ci->rta_expires / hz); + + if (ci->rta_error != 0) + L_setint(s->L, "error", ci->rta_error); + } + } + + s->index++; + + if (s->callback) + lua_call(s->L, 1, 0); + else if (hdr->nlmsg_flags & NLM_F_MULTI) + lua_rawseti(s->L, -2, s->index); + +out: + s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI); + return NL_SKIP; +} + +static int +cb_done(struct nl_msg *msg, void *arg) +{ + struct dump_state *s = arg; + s->pending = 0; + return NL_STOP; +} + +static int +cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +{ + struct dump_state *s = arg; + s->pending = 0; + return NL_STOP; +} + +static int _error(lua_State *L, int code, const char *msg) +{ + lua_pushnil(L); + lua_pushnumber(L, code ? code : errno); + lua_pushstring(L, msg ? msg : strerror(errno)); + + return 3; +} + +static int _route_dump(lua_State *L, struct dump_filter *filter) +{ + int flags = NLM_F_REQUEST; + struct dump_state s = { + .L = L, + .pending = 1, + .index = 0, + .callback = lua_isfunction(L, 2), + .filter = filter + }; + + if (!hz) + hz = sysconf(_SC_CLK_TCK); + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg; + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct rtmsg rtm = { + .rtm_family = filter->family, + .rtm_dst_len = filter->dst.bits, + .rtm_src_len = filter->src.bits + }; + + if (!filter->get) + flags |= NLM_F_DUMP; + + msg = nlmsg_alloc_simple(RTM_GETROUTE, flags); + if (!msg) + goto out; + + nlmsg_append(msg, &rtm, sizeof(rtm), 0); + + if (filter->get) + nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &s); + + nl_send_auto_complete(sock, msg); + + if (!filter->get && !s.callback) + lua_newtable(L); + + while (s.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + +out: + nl_cb_put(cb); + return (s.callback == 0); +} + +static int route_get(lua_State *L) +{ + struct dump_filter filter = { .get = true }; + const char *dest = luaL_checkstring(L, 1); + + if (!parse_cidr(dest, &filter.dst)) + return _error(L, -1, "Invalid destination"); + + filter.family = filter.dst.family; + + return _route_dump(L, &filter); +} + +static int route_dump(lua_State *L) +{ + const char *s; + cidr_t p = { }; + struct dump_filter filter = { }; + + if (lua_type(L, 1) == LUA_TTABLE) + { + filter.family = L_getint(L, 1, "family"); + + if (filter.family == 4) + filter.family = AF_INET; + else if (filter.family == 6) + filter.family = AF_INET6; + else + filter.family = 0; + + if ((s = L_getstr(L, 1, "iif")) != NULL) + filter.iif = if_nametoindex(s); + + if ((s = L_getstr(L, 1, "oif")) != NULL) + filter.oif = if_nametoindex(s); + + filter.type = L_getint(L, 1, "type"); + filter.scope = L_getint(L, 1, "scope"); + filter.proto = L_getint(L, 1, "proto"); + filter.table = L_getint(L, 1, "table"); + + if ((s = L_getstr(L, 1, "gw")) != NULL && parse_cidr(s, &p)) + filter.gw = p; + + if ((s = L_getstr(L, 1, "from")) != NULL && parse_cidr(s, &p)) + filter.from = p; + + if ((s = L_getstr(L, 1, "src")) != NULL && parse_cidr(s, &p)) + filter.src = p; + + if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p)) + filter.dst = p; + + if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p)) + filter.from = p, filter.from.exact = true; + + if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p)) + filter.dst = p, filter.dst.exact = true; + } + + return _route_dump(L, &filter); +} + + +static bool diff_macaddr(struct ether_addr *mac1, struct ether_addr *mac2) +{ + struct ether_addr empty = { }; + + if (!memcmp(mac2, &empty, sizeof(empty))) + return false; + + if (!mac1 || memcmp(mac1, mac2, sizeof(empty))) + return true; + + return false; +} + +static int cb_dump_neigh(struct nl_msg *msg, void *arg) +{ + char buf[32]; + struct ether_addr *mac; + struct in6_addr *dst; + struct dump_state *s = arg; + struct dump_filter *f = s->filter; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ndmsg *nd = NLMSG_DATA(hdr); + struct nlattr *tb[NDA_MAX+1]; + int bitlen; + + if (hdr->nlmsg_type != RTM_NEWNEIGH || + (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6)) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL); + + mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL; + dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL; + + bitlen = (nd->ndm_family == AF_INET) ? 32 : 128; + + if ((f->family && nd->ndm_family != f->family) || + (f->iif && nd->ndm_ifindex != f->iif) || + (f->type && !(f->type & nd->ndm_state)) || + diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) || + diff_macaddr(mac, &f->mac)) + goto out; + + if (s->callback) + lua_pushvalue(s->L, 2); + + lua_newtable(s->L); + + L_setint(s->L, "family", (nd->ndm_family == AF_INET) ? 4 : 6); + L_setstr(s->L, "dev", if_indextoname(nd->ndm_ifindex, buf)); + + L_setbool(s->L, "router", (nd->ndm_flags & NTF_ROUTER)); + L_setbool(s->L, "proxy", (nd->ndm_flags & NTF_PROXY)); + + L_setbool(s->L, "incomplete", (nd->ndm_state & NUD_INCOMPLETE)); + L_setbool(s->L, "reachable", (nd->ndm_state & NUD_REACHABLE)); + L_setbool(s->L, "stale", (nd->ndm_state & NUD_STALE)); + L_setbool(s->L, "delay", (nd->ndm_state & NUD_DELAY)); + L_setbool(s->L, "probe", (nd->ndm_state & NUD_PROBE)); + L_setbool(s->L, "failed", (nd->ndm_state & NUD_FAILED)); + L_setbool(s->L, "noarp", (nd->ndm_state & NUD_NOARP)); + L_setbool(s->L, "permanent", (nd->ndm_state & NUD_PERMANENT)); + + if (dst) + L_setaddr(s->L, "dest", nd->ndm_family, dst, -1); + + if (mac) + { + snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac->ether_addr_octet[0], mac->ether_addr_octet[1], + mac->ether_addr_octet[2], mac->ether_addr_octet[3], + mac->ether_addr_octet[4], mac->ether_addr_octet[5]); + + lua_pushstring(s->L, buf); + lua_setfield(s->L, -2, "mac"); + } + + s->index++; + + if (s->callback) + lua_call(s->L, 1, 0); + else if (hdr->nlmsg_flags & NLM_F_MULTI) + lua_rawseti(s->L, -2, s->index); + +out: + s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI); + return NL_SKIP; +} + +static int neighbor_dump(lua_State *L) +{ + cidr_t p = { }; + const char *s; + struct ether_addr *mac; + struct dump_filter filter = { .type = 0xFF & ~NUD_NOARP }; + struct dump_state st = { + .callback = lua_isfunction(L, 2), + .pending = 1, + .filter = &filter, + .L = L + }; + + if (lua_type(L, 1) == LUA_TTABLE) + { + filter.family = L_getint(L, 1, "family"); + + if (filter.family == 4) + filter.family = AF_INET; + else if (filter.family == 6) + filter.family = AF_INET6; + else + filter.family = 0; + + if ((s = L_getstr(L, 1, "dev")) != NULL) + filter.iif = if_nametoindex(s); + + if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p)) + filter.dst = p; + + if ((s = L_getstr(L, 1, "mac")) != NULL && + (mac = ether_aton(s)) != NULL) + filter.mac = *mac; + } + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg; + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct ndmsg ndm = { + .ndm_family = filter.family + }; + + msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) + goto out; + + nlmsg_append(msg, &ndm, sizeof(ndm), 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_neigh, &st); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st); + + nl_send_auto_complete(sock, msg); + + if (!st.callback) + lua_newtable(L); + + while (st.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + +out: + nl_cb_put(cb); + return (st.callback == 0); +} + + +static int cb_dump_link(struct nl_msg *msg, void *arg) +{ + char *p, *addr, buf[48]; + struct dump_state *s = arg; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ifinfomsg *ifm = NLMSG_DATA(hdr); + struct nlattr *tb[IFLA_MAX+1]; + int i, len; + + if (hdr->nlmsg_type != RTM_NEWLINK) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*ifm), tb, IFLA_MAX, NULL); + + L_setbool(s->L, "up", (ifm->ifi_flags & IFF_RUNNING)); + L_setint(s->L, "type", ifm->ifi_type); + L_setstr(s->L, "name", if_indextoname(ifm->ifi_index, buf)); + + if (tb[IFLA_MTU]) + L_setint(s->L, "mtu", RTA_U32(tb[IFLA_MTU])); + + if (tb[IFLA_TXQLEN]) + L_setint(s->L, "qlen", RTA_U32(tb[IFLA_TXQLEN])); + + if (tb[IFLA_MASTER]) + L_setdev(s->L, "master", tb[IFLA_MASTER]); + + if (tb[IFLA_ADDRESS]) + { + len = nla_len(tb[IFLA_ADDRESS]); + addr = nla_get_string(tb[IFLA_ADDRESS]); + + if ((len * 3) <= sizeof(buf)) + { + for (p = buf, i = 0; i < len; i++) + p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++); + + L_setstr(s->L, "mac", buf); + } + } + + s->pending = 0; + return NL_SKIP; +} + +static int link_get(lua_State *L) +{ + const char *dev = luaL_checkstring(L, 1); + struct dump_state st = { + .pending = 1, + .L = L + }; + + if (!sock) + { + sock = nl_socket_alloc(); + if (!sock) + return _error(L, -1, "Out of memory"); + + if (nl_connect(sock, NETLINK_ROUTE)) + return _error(L, 0, NULL); + } + + struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST); + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct ifinfomsg ifm = { .ifi_index = if_nametoindex(dev) }; + + if (!msg || !cb) + return 0; + + nlmsg_append(msg, &ifm, sizeof(ifm), 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_link, &st); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st); + + lua_newtable(L); + + nl_send_auto_complete(sock, msg); + + while (st.pending > 0) + nl_recvmsgs(sock, cb); + + nlmsg_free(msg); + nl_cb_put(cb); + + return 1; +} + + +static const luaL_reg ip_methods[] = { + { "new", cidr_new }, + { "IPv4", cidr_ipv4 }, + { "IPv6", cidr_ipv6 }, + + { "route", route_get }, + { "routes", route_dump }, + + { "neighbors", neighbor_dump }, + + { "link", link_get }, + + { } +}; + +static const luaL_reg ip_cidr_methods[] = { + { "is4", cidr_is4 }, + { "is4rfc1918", cidr_is4rfc1918 }, + { "is4linklocal", cidr_is4linklocal }, + { "is6", cidr_is6 }, + { "is6linklocal", cidr_is6linklocal }, + { "is6mapped4", cidr_is6mapped4 }, + { "lower", cidr_lower }, + { "higher", cidr_higher }, + { "equal", cidr_equal }, + { "prefix", cidr_prefix }, + { "network", cidr_network }, + { "host", cidr_host }, + { "mask", cidr_mask }, + { "broadcast", cidr_broadcast }, + { "mapped4", cidr_mapped4 }, + { "contains", cidr_contains }, + { "add", cidr_add }, + { "sub", cidr_sub }, + { "minhost", cidr_minhost }, + { "maxhost", cidr_maxhost }, + { "string", cidr_tostring }, + + { "__lt", cidr_lower }, + { "__le", cidr_lower_equal }, + { "__eq", cidr_equal }, + { "__add", cidr_add }, + { "__sub", cidr_sub }, + { "__gc", cidr_gc }, + { "__tostring", cidr_tostring }, + + { } +}; + +int luaopen_luci_ip(lua_State *L) +{ + luaL_register(L, LUCI_IP, ip_methods); + + luaL_newmetatable(L, LUCI_IP_CIDR); + luaL_register(L, NULL, ip_cidr_methods); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + return 1; +} diff --git a/libs/luci-lib-ip/src/ip.luadoc b/libs/luci-lib-ip/src/ip.luadoc new file mode 100644 index 000000000..00738832c --- /dev/null +++ b/libs/luci-lib-ip/src/ip.luadoc @@ -0,0 +1,831 @@ +--- LuCI IP calculation and netlink access library. +module "luci.ip" + +---[[ +Construct a new luci.ip.cidr instance and autodetect the address family. +Throws an error if the given strings do not represent a valid address or +if the given optional netmask is of a different family. +@class function +@sort 1 +@name new +@param address String containing a valid IPv4 or IPv6 address, optionally +with prefix size (CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 or IPv6 netmask or number +containing a prefix size in bits (`0..32` for IPv4, +`0..128` for IPv6). Overrides mask embedded in the first argument +if specified. (optional) +@return A `luci.ip.cidr` object representing the given +address/mask range. +@usage `addr = luci.ip.new("10.24.0.1/24") +addr = luci.ip.new("10.24.0.1/255.255.255.0") +addr = luci.ip.new("10.24.0.1", "255.255.255.0") -- separate netmask +addr = luci.ip.new("10.24.0.1/24", 16) -- override netmask + +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` +@see IPv4 +@see IPv6 +]] + +---[[ +Construct a new IPv4 luci.ip.cidr instance. +Throws an error if the given string does not represent a valid IPv4 address or +if the given optional netmask is of a different family. +@class function +@sort 2 +@name IPv4 +@param address String containing a valid IPv4, optionally with prefix size +(CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 netmask or number +containing a prefix size between `0` and `32` bit. +Overrides mask embedded in the first argument if specified. (optional) +@return A `luci.ip.cidr` object representing the given IPv4 range. +@usage `addr = luci.ip.new("10.24.0.1/24") +addr = luci.ip.new("10.24.0.1/255.255.255.0") +addr = luci.ip.new("10.24.0.1", "255.255.255.0") -- separate netmask +addr = luci.ip.new("10.24.0.1/24", 16) -- override netmask` +@see IPv6 +]] + +---[[ +Construct a new IPv6 luci.ip.cidr instance. +Throws an error if the given string does not represent a valid IPv6 address or +if the given optional netmask is of a different family. +@class function +@sort 3 +@name IPv6 +@param address String containing a valid IPv6, optionally with prefix size +(CIDR notation) or netmask separated by slash. +@param netmask String containing a valid IPv4 netmask or number +containing a prefix size between `0` and `128` bit. +Overrides mask embedded in the first argument if specified. (optional) +@return A `luci.ip.cidr` object representing the given IPv6 range. +@usage `addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") +addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` +@see IPv4 +]] + +---[[ +Determine the route leading to the given destination. +@class function +@sort 4 +@name route +@param address A `luci.ip.cidr` instance or a string containing +a valid IPv4 or IPv6 range as specified by `luci.ip.new()`. +@return

    Table containing the fields described below.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    `type` +

    Route type with one of the following numeric values:

    + + + + + + + + + + + + + + + + + + + + + +
    `1``RTN_UNICAST` - Gateway or direct route
    `2``RTN_LOCAL` - Accept locally
    `3``RTN_BROADCAST` - + Accept locally as broadcast send as broadcast
    `4``RTN_ANYCAST` - + Accept locally as broadcast but send as unicast
    `5``RTN_MULTICAST` - Multicast route
    +
    `family`Number containing the route family, `4` for IPv4 or + `6` for IPv6
    `dest`Destination `luci.ip.cidr` instance
    `gw`Gateway `luci.ip.cidr` instance (optional)
    `from`Source address `luci.ip.cidr` instance (optional)
    `src`Preferred source `luci.ip.cidr` instance (optional)
    `dev`String containing the name of the outgoing interface
    `iif`String containing the name of the incoming interface (optional)
    `table`Number of the associated routing table (`0..65535`)
    `proto`Number of the associated routing protocol
    `scope`Number describing the scope of the route, most commonly + `0` for global or `253` for on-link
    `metric`Number describing the route metric (optional)
    `expires`Number of seconds the prefix is valid (IPv6 only, optional)
    `error`Route destination error code (optional)
    +@usage
      +
    • 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`
    • +
    • Determine IPv6 upstream interface `rt = luci.ip.route("2001::/7") +if rt ~= nil then + print("ipv6 upstream device is", rt.dev) +end`
    • +
    +@see routes +]] + +---[[ +Fetch all routes, optionally matching the given criteria. +@class function +@sort 5 +@name routes +@param filter

    Table containing one or more of the possible filter +critera described below (optional)

    + + + + + + + + + + + + + + +
    FieldDescription
    `family` + Number describing the address family to return - `4` selects + IPv4 routes, `6` IPv6 ones. Any other value selects both. +
    `iif` + String containing the incoming route interface to match. +
    `oif` + String containing the outgoing route interface to match. +
    `type` + Numeric type to match, e.g. `1` for unicast. +
    `scope` + Numeric scope to match, e.g. `253` for onlink. +
    `proto` + Numeric protocol to match, e.g. `2` for boot. +
    `table` + Numeric routing table to match (`0..65535`). +
    `gw` + 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`. +
    `dest` + String containing the destination to match. Prefix matching is performed. +
    `from` + String containing the source address to match. Prefix matching is performed. +
    `src` + String containing the preferred source address to match. + Prefix matching is performed. +
    `dest_exact` + String containing the destination to match. Exact matching is performed, + e.g. `dest = "0.0.0.0/0"` would match any IPv4 route + while `dest_exact = "0.0.0.0/0"` will only match the + default route. +
    `from_exact` + String containing the source address to match. Exact matching is performed. +
    +@param callback

    Callback function to invoke for each found route +instead of returning one table of route objects (optional)

    +@return If no callback function is provided, a table of routes +as specified by `luci.ip.route()` +is returned. If a callback function is given, it is invoked for each route +and nothing is returned. +@see route +@usage
      +
    • 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)`
    • +
    • Find all global IPv6 prefixes on the current system: +`luci.ip.routes({ from = "2001::/7" }, function(rt) + print(rt.from) +end)`
    • +
    • Fetch all IPv4 routes: +`routes = luci.ip.routes({ family = 4 }) +for _, rt in ipairs(routes) do + print(rt.dest, rt.gw, rt.dev) +end`
    • +
    +]] + +---[[ +Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table +@class function +@sort 6 +@name neighbors +@param filter

    Table containing one or more of the possible filter +critera described below (optional)

    + + + + + +
    FieldDescription
    `family` + Number describing the address family to return - `4` selects + IPv4 ARP, `6` select IPv6 neighbour entries. Any other value + selects both. +
    `dev` + String containing the associated interface to match. +
    `dest` + 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`. +
    `mac` + String containing MAC address to match. +
    +@param callback

    Callback function to invoke for each found neighbour +entry instead of returning one table of neighbour entries (optional)

    +@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: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    `family`Number containing the neighbour entry family, `4` for IPv4 + ARP or `6` for IPv6 NDP
    `dev`String containing the associated device of the neighbour entry
    `dest`IP address `luci.ip.cidr` instance
    `mac`String containing the associated MAC address
    `router`Boolean "true" if the neighbour entry is a router (IPv6, optional)
    `proxy`Boolean "true" if this is a proxy entry (optional)
    `incomplete`Boolean "true" if the entry is in incomplete state (optional)
    `reachable`Boolean "true" if the entry is in reachable state (optional)
    `stale`Boolean "true" if the entry is stale (optional)
    `delay`Boolean "true" if the entry is delayed (optional)
    `probe`Boolean "true" if the entry is in probe state (optional)
    `failed`Boolean "true" if the entry is in failed state (optional)
    `noarp`Boolean "true" if the entry is not caused by NDP or + ARP (optional)
    `permanent`Boolean "true" if the entry was statically configured from + userspace (optional)
    +@usage
      +
    • Find all ARP neighbours in the LAN: +`luci.ip.neighbors({ dest = "192.168.0.0/16" }, function(n) + print(n.dest, n.mac) +end)`
    • +
    • 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)`
    • +
    +]] + +---[[ +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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    `up`Boolean indicating whether the device is in IFF_RUNNING state
    `type`Numeric value indicating the type of the device, e.g. `1` + for ethernet.
    `name`String containing the name of the device
    `master`If queried device is a bridge port, string containing the name of + parent bridge device (optional)
    `mtu`Number containing the current MTU of the device
    `qlen`Number containing the TX queue length of the device
    `mac`String containing the link local address of the device in + dotted hex notation
    +@usage
      +
    • Test whether device br-lan exists: +`print(luci.ip.link("br-lan").name ~= nil) +`
    • +
    • Query MAC address of eth0: +`print(luci.ip.link("eth0").mac) +`
    • +
    +]] + + +--- 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: +
    • An IPv4 address is always lower than an IPv6 address
    • +
    • Prefix sizes are ignored
    + +@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: +
    • An IPv4 address is always lower than an IPv6 address
    • +
    • Prefix sizes are ignored
    + +@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
      +
    • When adding inplace: Return `true` if the addition succeded + or `false` when the addition overflowed.
    • +
    • 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.
    +@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
      +
    • When substracting inplace: Return `true` if the substraction + succeded or `false` when the substraction underflowed.
    • +
    • 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.
    +@usage `local addr = luci.ip.new("192.168.1.1/24") +print(addr:sub(256)) -- "192.168.0.1/24" +print(addr:sub("0.168.0.0")) -- "192.0.1.1/24" + +addr:sub(256, true) -- true +print(addr) -- "192.168.0.1/24 + +addr:sub("255.0.0.0", true) -- false (underflow) +print(addr) -- "0.0.0.0/24 + +local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64") +print(addr6:sub(256)) -- "fe80::221:63f:fe75:a917/64" +print(addr6:sub("::ffff:0")) -- "fe80::221:63e:fe76:aa17/64" + +addr:sub(256, true) -- true +print(addr) -- "fe80::221:63f:fe75:a917/64" + +addr:sub("ffff::", true) -- false (underflow) +print(addr) -- "::/64"` +]] + +---[[ +Calculate the lowest possible host address within this CIDR instance. + +@class function +@sort 19 +@name cidr.minhost +@return Returns a new CIDR instance representing the lowest host address + within this range. +@usage `local addr = luci.ip.new("192.168.123.56/24") +print(addr:minhost()) -- "192.168.123.1" + +local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") +print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"` +]] + +---[[ +Calculate the highest possible host address within this CIDR instance. + +@class function +@sort 20 +@name cidr.maxhost +@return Returns a new CIDR instance representing the highest host address + within this range. +@usage `local addr = luci.ip.new("192.168.123.56/24") +print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast) + +local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") +print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"` +]] + +---[[ +Convert CIDR instance into string representation. + +If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the +address is returned in the form "address/prefix" otherwise just "address". + +It is usually not required to call this function directly as CIDR objects +define it as __tostring function in the associated metatable. + +@class function +@sort 21 +@name cidr.string +@return Returns a string representing the range or address of this CIDR instance +]] diff --git a/libs/luci-lib-json/luasrc/json.lua b/libs/luci-lib-json/luasrc/json.lua index 57b12c940..416b25faa 100644 --- a/libs/luci-lib-json/luasrc/json.lua +++ b/libs/luci-lib-json/luasrc/json.lua @@ -25,14 +25,9 @@ local char = string.char local getmetatable = getmetatable ---- LuCI JSON-Library --- @cstyle instance module "luci.json" ---- Directly decode a JSON string --- @param json JSON-String --- @return Lua object function decode(json, ...) local a = ActiveDecoder(function() return nil end, ...) a.chunk = json @@ -41,9 +36,6 @@ function decode(json, ...) end ---- Direcly encode a Lua object into a JSON string. --- @param obj Lua Object --- @return JSON string function encode(obj, ...) local out = {} local e = Encoder(obj, 1, ...):source() @@ -56,19 +48,10 @@ function encode(obj, ...) end ---- Null replacement function --- @return null function null() return null end ---- Create a new JSON-Encoder. --- @class function --- @name Encoder --- @param data Lua-Object to be encoded. --- @param buffersize Blocksize of returned data source. --- @param fastescape Use non-standard escaping (don't escape control chars) --- @return JSON-Encoder Encoder = util.class() function Encoder.__init__(self, data, buffersize, fastescape) @@ -80,8 +63,6 @@ function Encoder.__init__(self, data, buffersize, fastescape) getmetatable(self).__call = Encoder.source end ---- Create an LTN12 source providing the encoded JSON-Data. --- @return LTN12 source function Encoder.source(self) local source = coroutine.create(self.dispatch) return function() @@ -208,11 +189,6 @@ Encoder.parsers = { } ---- Create a new JSON-Decoder. --- @class function --- @name Decoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return JSON-Decoder Decoder = util.class() function Decoder.__init__(self, customnull) @@ -220,8 +196,6 @@ function Decoder.__init__(self, customnull) getmetatable(self).__call = Decoder.sink end ---- Create an LTN12 sink from the decoder object which accepts the JSON-Data. --- @return LTN12 sink function Decoder.sink(self) local sink = coroutine.create(self.dispatch) return function(...) @@ -230,8 +204,6 @@ function Decoder.sink(self) end ---- Get the decoded data packets after the rawdata has been sent to the sink. --- @return Decoded data function Decoder.get(self) return self.data end @@ -526,11 +498,6 @@ Decoder.parsers = { } ---- Create a new Active JSON-Decoder. --- @class function --- @name ActiveDecoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return Active JSON-Decoder ActiveDecoder = util.class(Decoder) function ActiveDecoder.__init__(self, source, customnull) @@ -541,8 +508,6 @@ function ActiveDecoder.__init__(self, source, customnull) end ---- Fetches one JSON-object from given source --- @return Decoded object function ActiveDecoder.get(self) local chunk, src_err, object if not self.chunk then diff --git a/libs/luci-lib-json/luasrc/json.luadoc b/libs/luci-lib-json/luasrc/json.luadoc new file mode 100644 index 000000000..d48dba13b --- /dev/null +++ b/libs/luci-lib-json/luasrc/json.luadoc @@ -0,0 +1,94 @@ +---[[ +LuCI JSON-Library + +@cstyle instance +module "luci.json" +]] + +---[[ +Directly decode a JSON string + +@class function +@name decode +@param json JSON-String +@return Lua object +]] + +---[[ +Direcly encode a Lua object into a JSON string. + +@class function +@name encode +@param obj Lua Object +@return JSON string +]] + +---[[ +Null replacement function + +@class function +@name null +@return null +]] + +---[[ +Create a new JSON-Encoder. + +@class function +@name Encoder +@param data Lua-Object to be encoded. +@param buffersize Blocksize of returned data source. +@param fastescape Use non-standard escaping (don't escape control chars) +@return JSON-Encoder +]] + +---[[ +Create an LTN12 source providing the encoded JSON-Data. + +@class function +@name Encoder.source +@return LTN12 source +]] + +---[[ +Create a new JSON-Decoder. + +@class function +@name Decoder +@param customnull Use luci.json.null instead of nil for decoding null +@return JSON-Decoder +]] + +---[[ +Create an LTN12 sink from the decoder object which accepts the JSON-Data. + +@class function +@name Decoder.sink +@return LTN12 sink +]] + +---[[ +Get the decoded data packets after the rawdata has been sent to the sink. + +@class function +@name Decoder.get +@return Decoded data +]] + +---[[ +Create a new Active JSON-Decoder. + +@class function +@name ActiveDecoder +@param customnull Use luci.json.null instead of nil for decoding null +@return Active JSON-Decoder +]] + +---[[ +Fetches one JSON-object from given source + +@class function +@name ActiveDecoder.get +@return Decoded object +]] + diff --git a/libs/luci-lib-jsonc/Makefile b/libs/luci-lib-jsonc/Makefile new file mode 100644 index 000000000..6a63dab5e --- /dev/null +++ b/libs/luci-lib-jsonc/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2015 LuCI Team +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Lua binding for JSON-C +LUCI_DEPENDS:=+liblua +libjson-c + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-jsonc/src/Makefile b/libs/luci-lib-jsonc/src/Makefile new file mode 100644 index 000000000..e15fbac38 --- /dev/null +++ b/libs/luci-lib-jsonc/src/Makefile @@ -0,0 +1,17 @@ +JSONC_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/json-c/ +JSONC_LDFLAGS = -llua -lm -ljson-c +JSONC_OBJ = jsonc.o +JSONC_LIB = jsonc.so + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(JSONC_CFLAGS) $(FPIC) -c -o $@ $< + +compile: $(JSONC_OBJ) + $(CC) $(LDFLAGS) -shared -o $(JSONC_LIB) $(JSONC_OBJ) $(JSONC_LDFLAGS) + +install: compile + mkdir -p $(DESTDIR)/usr/lib/lua/luci + cp $(JSONC_LIB) $(DESTDIR)/usr/lib/lua/luci/$(JSONC_LIB) + +clean: + rm -f *.o *.so diff --git a/libs/luci-lib-jsonc/src/jsonc.c b/libs/luci-lib-jsonc/src/jsonc.c new file mode 100644 index 000000000..49cb21f5b --- /dev/null +++ b/libs/luci-lib-jsonc/src/jsonc.c @@ -0,0 +1,388 @@ +/* +Copyright 2015 Jo-Philipp Wich + +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 +#include +#include + +#include +#include +#include + +#define LUCI_JSONC "luci.jsonc" +#define LUCI_JSONC_PARSER "luci.jsonc.parser" + +struct json_state { + struct json_object *obj; + struct json_tokener *tok; + enum json_tokener_error err; +}; + +static void _json_to_lua(lua_State *L, struct json_object *obj); +static struct json_object * _lua_to_json(lua_State *L, int index); + +static int json_new(lua_State *L) +{ + struct json_state *s; + struct json_tokener *tok = json_tokener_new(); + + if (!tok) + return 0; + + s = lua_newuserdata(L, sizeof(*s)); + + if (!s) + { + json_tokener_free(tok); + return 0; + } + + s->tok = tok; + s->obj = NULL; + s->err = json_tokener_continue; + + luaL_getmetatable(L, LUCI_JSONC_PARSER); + lua_setmetatable(L, -2); + + return 1; +} + +static int json_parse(lua_State *L) +{ + size_t len; + const char *json = luaL_checklstring(L, 1, &len); + struct json_state s = { + .tok = json_tokener_new() + }; + + if (!s.tok) + return 0; + + s.obj = json_tokener_parse_ex(s.tok, json, len); + s.err = json_tokener_get_error(s.tok); + + if (s.obj) + { + _json_to_lua(L, s.obj); + json_object_put(s.obj); + } + else + { + lua_pushnil(L); + } + + if (s.err == json_tokener_continue) + s.err = json_tokener_error_parse_eof; + + if (s.err) + lua_pushstring(L, json_tokener_error_desc(s.err)); + + json_tokener_free(s.tok); + return (1 + !!s.err); +} + +static int json_stringify(lua_State *L) +{ + struct json_object *obj = _lua_to_json(L, 1); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(obj, flags)); + return 1; +} + + +static int json_parse_chunk(lua_State *L) +{ + size_t len; + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + const char *chunk = luaL_checklstring(L, 2, &len); + + s->obj = json_tokener_parse_ex(s->tok, chunk, len); + s->err = json_tokener_get_error(s->tok); + + if (!s->err) + { + lua_pushboolean(L, true); + return 1; + } + else if (s->err == json_tokener_continue) + { + lua_pushboolean(L, false); + return 1; + } + + lua_pushnil(L); + lua_pushstring(L, json_tokener_error_desc(s->err)); + return 2; +} + +static void _json_to_lua(lua_State *L, struct json_object *obj) +{ + int n; + + switch (json_object_get_type(obj)) + { + case json_type_object: + lua_newtable(L); + json_object_object_foreach(obj, key, val) + { + _json_to_lua(L, val); + lua_setfield(L, -2, key); + } + break; + + case json_type_array: + lua_newtable(L); + for (n = 0; n < json_object_array_length(obj); n++) + { + _json_to_lua(L, json_object_array_get_idx(obj, n)); + lua_rawseti(L, -2, n + 1); + } + break; + + case json_type_boolean: + lua_pushboolean(L, json_object_get_boolean(obj)); + break; + + case json_type_int: + lua_pushinteger(L, json_object_get_int(obj)); + break; + + case json_type_double: + lua_pushnumber(L, json_object_get_double(obj)); + break; + + case json_type_string: + lua_pushstring(L, json_object_get_string(obj)); + break; + + case json_type_null: + lua_pushnil(L); + break; + } +} + +static int json_parse_get(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (!s->obj || s->err) + lua_pushnil(L); + else + _json_to_lua(L, s->obj); + + return 1; +} + +static int _lua_test_array(lua_State *L, int index) +{ + int max = 0; + lua_Number idx; + + lua_pushnil(L); + + /* check for non-integer keys */ + while (lua_next(L, index)) + { + if (lua_type(L, -2) != LUA_TNUMBER) + goto out; + + idx = lua_tonumber(L, -2); + + if (idx != (lua_Number)(lua_Integer)idx) + goto out; + + if (idx <= 0) + goto out; + + if (idx > max) + max = idx; + + lua_pop(L, 1); + continue; + +out: + lua_pop(L, 2); + return 0; + } + + /* check for holes */ + //for (i = 1; i <= max; i++) + //{ + // lua_rawgeti(L, index, i); + // + // if (lua_isnil(L, -1)) + // { + // lua_pop(L, 1); + // return 0; + // } + // + // lua_pop(L, 1); + //} + + return max; +} + +static struct json_object * _lua_to_json(lua_State *L, int index) +{ + lua_Number nd, ni; + struct json_object *obj; + const char *key; + int i, max; + + switch (lua_type(L, index)) + { + case LUA_TTABLE: + max = _lua_test_array(L, index); + + if (max > 0) + { + obj = json_object_new_array(); + + if (!obj) + return NULL; + + for (i = 1; i <= max; i++) + { + lua_rawgeti(L, index, i); + + json_object_array_put_idx(obj, i - 1, + _lua_to_json(L, lua_gettop(L))); + + lua_pop(L, 1); + } + + return obj; + } + + obj = json_object_new_object(); + + if (!obj) + return NULL; + + lua_pushnil(L); + + while (lua_next(L, index)) + { + lua_pushvalue(L, -2); + key = lua_tostring(L, -1); + + json_object_object_add(obj, key, + _lua_to_json(L, lua_gettop(L) - 1)); + + lua_pop(L, 2); + } + + return obj; + + case LUA_TNIL: + return NULL; + + case LUA_TBOOLEAN: + return json_object_new_boolean(lua_toboolean(L, index)); + + case LUA_TNUMBER: + nd = lua_tonumber(L, index); + ni = lua_tointeger(L, index); + + if (nd == ni) + return json_object_new_int(nd); + + return json_object_new_double(nd); + + case LUA_TSTRING: + return json_object_new_string(lua_tostring(L, index)); + } + + return NULL; +} + +static int json_parse_set(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + s->err = 0; + s->obj = _lua_to_json(L, 2); + + return 0; +} + +static int json_tostring(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + bool pretty = lua_toboolean(L, 2); + int flags = 0; + + if (pretty) + flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED; + + lua_pushstring(L, json_object_to_json_string_ext(s->obj, flags)); + return 1; +} + +static int json_gc(lua_State *L) +{ + struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER); + + if (s->obj) + json_object_put(s->obj); + + if (s->tok) + json_tokener_free(s->tok); + + return 0; +} + + +static const luaL_reg jsonc_methods[] = { + { "new", json_new }, + { "parse", json_parse }, + { "stringify", json_stringify }, + + { } +}; + +static const luaL_reg jsonc_parser_methods[] = { + { "parse", json_parse_chunk }, + { "get", json_parse_get }, + { "set", json_parse_set }, + { "stringify", json_tostring }, + + { "__gc", json_gc }, + { "__tostring", json_tostring }, + + { } +}; + + +int luaopen_luci_jsonc(lua_State *L) +{ + luaL_register(L, LUCI_JSONC, jsonc_methods); + + luaL_newmetatable(L, LUCI_JSONC_PARSER); + luaL_register(L, NULL, jsonc_parser_methods); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + return 1; +} diff --git a/libs/luci-lib-jsonc/src/jsonc.luadoc b/libs/luci-lib-jsonc/src/jsonc.luadoc new file mode 100644 index 000000000..2ee9cebdc --- /dev/null +++ b/libs/luci-lib-jsonc/src/jsonc.luadoc @@ -0,0 +1,134 @@ +--- LuCI JSON parsing and serialization library. +-- The luci.jsonc class is a high level Lua binding to the JSON-C library to +-- allow reading and writing JSON data with minimal overhead. +module "luci.jsonc" + +---[[ +Construct a new luci.jsonc.parser instance. +@class function +@sort 1 +@name new +@return A `luci.jsonc.parser` object representing a JSON-C tokener. +@usage `parser = luci.jsonc.new()` +]] + +---[[ +Parse a complete JSON string and convert it into a Lua data structure. +@class function +@sort 2 +@name parse +@param json A string containing the JSON data to parse, must be either a + JSON array or a JSON object. +@return On success, a table containing the parsed JSON data is returned, on + failure the function returns `nil` and a string containing the reason of + the parse error. +@usage `data = luci.jsonc.parse('{ "name": "John", "age": 34 }') +print(data.name) -- "John"` +@see stringify +]] + +---[[ +Convert given Lua data into a JSON string. + +This function recursively converts the given Lua data into a JSON string, +ignoring any unsupported data. Lua tables are converted into JSON arrays if they +only contain integer keys, mixed tables are turned into JSON objects with any +existing numeric keys converted into strings. + +Lua functions, coroutines and userdata objects are ignored and Lua numbers are +converted to integers if they do not contain fractional values. + +@class function +@sort 3 +@name stringify +@param data The Lua data to convert, can be a table, string, boolean or number. +@param pretty A boolean value indicating whether the resulting JSON should be + pretty printed. +@return Returns a string containing the JSON representation of the given Lua + data. +@usage `json = luci.jsonc.stringify({ item = true, values = { 1, 2, 3 } }) +print(json) -- '{"item":true,"values":[1,2,3]}'` +@see parse +]] + + +--- LuCI JSON parser instance. +-- A JSON parser instance is useful to parse JSON data chunk by chunk, without +-- the need to assemble all data in advance. +-- @cstyle instance +module "luci.jsonc.parser" + +---[[ +Parses one chunk of JSON data. + +@class function +@sort 1 +@name parser.parse +@see parser.get +@param json String containing the JSON fragment to parse +@return
      +
    • `true` if a complete JSON object has been parsed and no further input is + expected.
    • +
    • `false` if further input is required
    • +
    • `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.
    +@usage `parser = luci.jsonc.new() + +while true do + chunk = ... -- fetch a cunk of data, e.g. from a socket + finish, errmsg = parser.parse(chunk) + + if finish == nil then + error("Cannot parse JSON: " .. errmsg) + end + + if finish == true then + break + end +end` +]] + +---[[ +Convert parsed JSON data into Lua table. + +@class function +@sort 2 +@name parser.get +@see parser.parse +@return Parsed JSON object converted into a Lua table or `nil` if the parser + didn't finish or encountered an error. +@usage `parser = luci.jsonc.new() +parser:parse('{ "example": "test" }') + +data = parser:get() +print(data.example) -- "test"` +]] + +---[[ +Put Lua data into the parser. + +@class function +@sort 3 +@name parser.set +@see parser.stringify +@param data Lua data to put into the parser object. The data is converted to an + internal JSON representation that can be dumped with `stringify()`. + The conversion follows the rules described in `luci.jsonc.stringify`. +@return Nothing is returned. +@usage `parser = luci.jsonc.new() +parser:set({ "some", "data" })` +]] + +---[[ +Serialize current parser state as JSON. + +@class function +@sort 4 +@name parser.stringify +@param pretty A boolean value indicating whether the resulting JSON should be pretty printed. +@return Returns the serialized JSON data of this parser instance. +@usage `parser = luci.jsonc.new() +parser:parse('{ "example": "test" }') +print(parser:serialize()) -- '{"example":"test"}'` +]] diff --git a/libs/luci-lib-nixio/docsrc/CHANGELOG.lua b/libs/luci-lib-nixio/docsrc/CHANGELOG.lua index fb1cf160b..aa3184140 100644 --- a/libs/luci-lib-nixio/docsrc/CHANGELOG.lua +++ b/libs/luci-lib-nixio/docsrc/CHANGELOG.lua @@ -1,5 +1,5 @@ --- Changes and improvements. -module "CHANGELOG" +module "nixio.CHANGELOG" --- Service Release. --
      @@ -26,4 +26,4 @@ module "CHANGELOG" --
    -- @class table -- @name 0.2 --- @return ! \ No newline at end of file +-- @return ! diff --git a/libs/luci-lib-nixio/docsrc/README.lua b/libs/luci-lib-nixio/docsrc/README.lua index b957a6990..ee3e3a216 100644 --- a/libs/luci-lib-nixio/docsrc/README.lua +++ b/libs/luci-lib-nixio/docsrc/README.lua @@ -1,5 +1,5 @@ --- General Information. -module "README" +module "nixio.README" --- General error handling information. --
      @@ -92,4 +92,4 @@ module "README" -- @usage Tes -- @class table -- @name TLS-Crypto --- @return ! \ No newline at end of file +-- @return ! diff --git a/libs/luci-lib-nixio/src/Makefile b/libs/luci-lib-nixio/src/Makefile index aaa4292bd..a7e9a77d9 100644 --- a/libs/luci-lib-nixio/src/Makefile +++ b/libs/luci-lib-nixio/src/Makefile @@ -1,4 +1,4 @@ -OS = $(shell uname) +OS = Linux AXTLS_VERSION = 1.2.1 AXTLS_DIR = axTLS AXTLS_FILE = $(AXTLS_DIR)-$(AXTLS_VERSION).tar.gz diff --git a/libs/luci-lib-rpcc/luasrc/rpcc.lua b/libs/luci-lib-rpcc/luasrc/rpcc.lua index fc52b7eb0..5558910bf 100644 --- a/libs/luci-lib-rpcc/luasrc/rpcc.lua +++ b/libs/luci-lib-rpcc/luasrc/rpcc.lua @@ -9,17 +9,10 @@ local nixio = require "nixio", require "nixio.util" local tostring, assert, setmetatable = tostring, assert, setmetatable local error = error ---- LuCI RPC Client. --- @cstyle instance module "luci.rpcc" RQLIMIT = 32 * nixio.const.buffersize ---- Create a new JSON-RPC stream client. --- @class function --- @param fd File descriptor --- @param v1 Use protocol version 1.0 --- @return RPC Client Client = util.class() function Client.__init__(self, fd, v1) @@ -29,11 +22,6 @@ function Client.__init__(self, fd, v1) self.v1 = v1 end ---- Request an RP call and get the response. --- @param method Remote method --- @param params Parameters --- @param notification Notification only? --- @return response function Client.request(self, method, params, notification) local oldchunk = self.decoder and self.decoder.chunk self.decoder = json.ActiveDecoder(self.fd:blocksource(nil, RQLIMIT)) @@ -58,9 +46,6 @@ function Client.request(self, method, params, notification) end end ---- Create a transparent RPC proxy. --- @param prefix Method prefix --- @return RPC Proxy object function Client.proxy(self, prefix) prefix = prefix or "" return setmetatable({}, { diff --git a/libs/luci-lib-rpcc/luasrc/rpcc.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc.luadoc new file mode 100644 index 000000000..8c90ab524 --- /dev/null +++ b/libs/luci-lib-rpcc/luasrc/rpcc.luadoc @@ -0,0 +1,36 @@ +---[[ +LuCI RPC Client. + +@cstyle instance +module "luci.rpcc" +]] + +---[[ +Create a new JSON-RPC stream client. + +@class function +@param fd File descriptor +@param v1 Use protocol version 1.0 +@return RPC Client +]] + +---[[ +Request an RP call and get the response. + +@class function +@name Client.request +@param method Remote method +@param params Parameters +@param notification Notification only? +@return response +]] + +---[[ +Create a transparent RPC proxy. + +@class function +@name Client.proxy +@param prefix Method prefix +@return RPC Proxy object +]] + diff --git a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua index 666d58585..275c39699 100644 --- a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua +++ b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua @@ -5,16 +5,11 @@ local util = require "luci.util" local rawget, setmetatable = rawget, setmetatable local ipairs = ipairs ---- Transparent UCI over RPC client. --- @cstyle instance module "luci.rpcc.ruci" local Proxy = util.class() ---- Create a new UCI over RPC proxy. --- @param rpccl RPC client --- @return Network transparent UCI module function factory(rpccl) return { cursor = function(...) diff --git a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc new file mode 100644 index 000000000..980ef46d9 --- /dev/null +++ b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc @@ -0,0 +1,16 @@ +---[[ +Transparent UCI over RPC client. + +@cstyle instance +module "luci.rpcc.ruci" +]] + +---[[ +Create a new UCI over RPC proxy. + +@class function +@name factory +@param rpccl RPC client +@return Network transparent UCI module +]] + diff --git a/luci.mk b/luci.mk index 0aba5c5ae..ab09f0f4f 100644 --- a/luci.mk +++ b/luci.mk @@ -147,6 +147,7 @@ define Package/$(PKG_NAME)/install if [ -d $(PKG_BUILD_DIR)/luasrc ]; then \ $(INSTALL_DIR) $(1)$(LUCI_LIBRARYDIR); \ cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)$(LUCI_LIBRARYDIR)/; \ + $(FIND) $(1)$(LUCI_LIBRARYDIR)/ -type f -name '*.luadoc' | $(XARGS) rm; \ $(if $(CONFIG_LUCI_SRCDIET),$(call SrcDiet,$(1)$(LUCI_LIBRARYDIR)/),true); \ else true; fi if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \ diff --git a/modules/luci-base/Makefile b/modules/luci-base/Makefile index 8337fea71..80bbda107 100644 --- a/modules/luci-base/Makefile +++ b/modules/luci-base/Makefile @@ -12,7 +12,7 @@ LUCI_TYPE:=mod LUCI_BASENAME:=base LUCI_TITLE:=LuCI core libraries -LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +rpcd +LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2 PKG_SOURCE_URL:=https://luasrcdiet.googlecode.com/files diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua index 155d31b10..795b62bed 100644 --- a/modules/luci-base/luasrc/dispatcher.lua +++ b/modules/luci-base/luasrc/dispatcher.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Steven Barth -- 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 000000000..743463c74 --- /dev/null +++ b/modules/luci-base/luasrc/dispatcher.luadoc @@ -0,0 +1,220 @@ +---[[ +LuCI web dispatcher. +]] +module "luci.dispatcher" + +---[[ +Build the URL relative to the server webroot from given virtual path. + +@class function +@name build_url +@param ... Virtual path +@return Relative URL +]] + +---[[ +Check whether a dispatch node shall be visible + +@class function +@name node_visible +@param node Dispatch node +@return Boolean indicating whether the node should be visible +]] + +---[[ +Return a sorted table of visible childs within a given node + +@class function +@name node_childs +@param node Dispatch node +@return Ordered table of child node names +]] + +---[[ +Send a 404 error code and render the "error404" template if available. + +@class function +@name error404 +@param message Custom error message (optional) +@return false +]] + +---[[ +Send a 500 error code and render the "error500" template if available. + +@class function +@name error500 +@param message Custom error message (optional)# +@return false +]] + +---[[ +Dispatch an HTTP request. + +@class function +@name httpdispatch +@param request LuCI HTTP Request object +]] + +---[[ +Dispatches a LuCI virtual path. + +@class function +@name dispatch +@param request Virtual path +]] + +---[[ +Generate the dispatching index using the native file-cache based strategy. + + +@class function +@name createindex +]] + +---[[ +Create the dispatching tree from the index. + +Build the index before if it does not exist yet. + +@class function +@name createtree +]] + +---[[ +Register a tree modifier. + +@class function +@name modifier +@param func Modifier function +@param order Modifier order value (optional) +]] + +---[[ +Clone a node of the dispatching tree to another position. + +@class function +@name assign +@param path Virtual path destination +@param clone Virtual path source +@param title Destination node title (optional) +@param order Destination node order value (optional) +@return Dispatching tree node +]] + +---[[ +Create a new dispatching node and define common parameters. + +@class function +@name entry +@param path Virtual path +@param target Target function to call when dispatched. +@param title Destination node title +@param order Destination node order value (optional) +@return Dispatching tree node +]] + +---[[ +Fetch or create a dispatching node without setting the target module or + +enabling the node. +@class function +@name get +@param ... Virtual path +@return Dispatching tree node +]] + +---[[ +Fetch or create a new dispatching node. + +@class function +@name node +@param ... Virtual path +@return Dispatching tree node +]] + +---[[ +Alias the first (lowest order) page automatically + + +@class function +@name firstchild +]] + +---[[ +Create a redirect to another dispatching node. + +@class function +@name alias +@param ... Virtual path destination +]] + +---[[ +Rewrite the first x path values of the request. + +@class function +@name rewrite +@param n Number of path values to replace +@param ... Virtual path to replace removed path values with +]] + +---[[ +Create a function-call dispatching target. + +@class function +@name call +@param name Target function of local controller +@param ... Additional parameters passed to the function +]] + +---[[ +Create a template render dispatching target. + +@class function +@name template +@param name Template to be rendered +]] + +---[[ +Create a CBI model dispatching target. + +@class function +@name cbi +@param model CBI model to be rendered +]] + +---[[ +Create a combined dispatching target for non argv and argv requests. + +@class function +@name arcombine +@param trg1 Overview Target +@param trg2 Detail Target +]] + +---[[ +Create a CBI form model dispatching target. + +@class function +@name form +@param model CBI form model tpo be rendered +]] + +---[[ +Access the luci.i18n translate() api. + +@class function +@name translate +@param text Text to translate +]] + +---[[ +No-op function used to mark translation entries for menu labels. + +This function does not actually translate the given argument but +is used by build/i18n-scan.pl to find translatable entries. + +@class function +@name _ +]] + diff --git a/modules/luci-base/luasrc/http.lua b/modules/luci-base/luasrc/http.lua index a5329e51d..a92d8affb 100644 --- a/modules/luci-base/luasrc/http.lua +++ b/modules/luci-base/luasrc/http.lua @@ -11,7 +11,6 @@ local table = require "table" local ipairs, pairs, next, type, tostring, error = ipairs, pairs, next, type, tostring, error ---- LuCI Web Framework high-level HTTP functions. module "luci.http" context = util.threadlocal() @@ -101,7 +100,6 @@ function Request._parse_input(self) self.parsed_input = true end ---- Close the HTTP-Connection. function close() if not context.eoh then context.eoh = true @@ -114,52 +112,31 @@ function close() end end ---- Return the request content if the request was of unknown type. --- @return HTTP request body --- @return HTTP request body length function content() return context.request:content() end ---- Get a certain HTTP input value or a table of all input values. --- @param name Name of the GET or POST variable to fetch --- @param noparse Don't parse POST data before getting the value --- @return HTTP input value or table of all input value function formvalue(name, noparse) return context.request:formvalue(name, noparse) end ---- Get a table of all HTTP input values with a certain prefix. --- @param prefix Prefix --- @return Table of all HTTP input values with given prefix function formvaluetable(prefix) return context.request:formvaluetable(prefix) end ---- Get the value of a certain HTTP-Cookie. --- @param name Cookie Name --- @return String containing cookie data function getcookie(name) return context.request:getcookie(name) end ---- Get the value of a certain HTTP environment variable -- or the environment table itself. --- @param name Environment variable --- @return HTTP environment value or environment table function getenv(name) return context.request:getenv(name) end ---- Set a handler function for incoming user file uploads. --- @param callback Handler function function setfilehandler(callback) return context.request:setfilehandler(callback) end ---- Send a HTTP-Header. --- @param key Header key --- @param value Header value function header(key, value) if not context.headers then context.headers = {} @@ -168,8 +145,6 @@ function header(key, value) coroutine.yield(2, key, value) end ---- Set the mime type of following content data. --- @param mime Mimetype of following content function prepare_content(mime) if not context.headers or not context.headers["content-type"] then if mime == "application/xhtml+xml" then @@ -183,15 +158,10 @@ function prepare_content(mime) end end ---- Get the RAW HTTP input source --- @return HTTP LTN12 source function source() return context.request.input end ---- Set the HTTP status code and status message. --- @param code Status code --- @param message Status message function status(code, message) code = code or 200 message = message or "OK" @@ -199,12 +169,8 @@ function status(code, message) coroutine.yield(1, code, message) end ---- Send a chunk of content data to the client. -- This function is as a valid LTN12 sink. -- If the content chunk is nil this function will automatically invoke close. --- @param content Content chunk --- @param src_err Error object from source (optional) --- @see close function write(content, src_err) if not content then if src_err then @@ -237,24 +203,16 @@ function write(content, src_err) end end ---- Splice data from a filedescriptor to the client. --- @param fp File descriptor --- @param size Bytes to splice (optional) function splice(fd, size) coroutine.yield(6, fd, size) end ---- Redirects the client to a new URL and closes the connection. --- @param url Target URL function redirect(url) status(302, "Found") header("Location", url) close() end ---- Create a querystring out of a table of key - value pairs. --- @param table Query string source table --- @return Encoded HTTP query string function build_querystring(q) local s = { "?" } @@ -269,56 +227,10 @@ function build_querystring(q) return table.concat(s, "") end ---- Return the URL-decoded equivalent of a string. --- @param str URL-encoded string --- @param no_plus Don't decode + to " " --- @return URL-decoded string --- @see urlencode urldecode = protocol.urldecode ---- Return the URL-encoded equivalent of a string. --- @param str Source string --- @return URL-encoded string --- @see urldecode urlencode = protocol.urlencode ---- Send the given data as JSON encoded string. --- @param data Data to send function write_json(x) - if x == nil then - write("null") - elseif type(x) == "table" then - local k, v - if type(next(x)) == "number" then - write("[ ") - for k, v in ipairs(x) do - write_json(v) - if next(x, k) then - write(", ") - end - end - write(" ]") - else - write("{ ") - for k, v in pairs(x) do - write("%q: " % k) - write_json(v) - if next(x, k) then - write(", ") - end - end - write(" }") - end - elseif type(x) == "number" or type(x) == "boolean" then - if (x ~= x) then - -- NaN is the only value that doesn't equal to itself. - write("Number.NaN") - else - write(tostring(x)) - end - else - write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c) - return '\\u%04x' % c:byte(1) - end)) - end + util.serialize_json(x, write) end diff --git a/modules/luci-base/luasrc/http.luadoc b/modules/luci-base/luasrc/http.luadoc new file mode 100644 index 000000000..4e31216a1 --- /dev/null +++ b/modules/luci-base/luasrc/http.luadoc @@ -0,0 +1,166 @@ +---[[ +LuCI Web Framework high-level HTTP functions. + +module "luci.http" +]] + +---[[ +Close the HTTP-Connection. + + +@class function +@name close +]] + +---[[ +Return the request content if the request was of unknown type. + +@class function +@name content +@return HTTP request body +@return HTTP request body length +]] + +---[[ +Get a certain HTTP input value or a table of all input values. + +@class function +@name formvalue +@param name Name of the GET or POST variable to fetch +@param noparse Don't parse POST data before getting the value +@return HTTP input value or table of all input value +]] + +---[[ +Get a table of all HTTP input values with a certain prefix. + +@class function +@name formvaluetable +@param prefix Prefix +@return Table of all HTTP input values with given prefix +]] + +---[[ +Get the value of a certain HTTP-Cookie. + +@class function +@name getcookie +@param name Cookie Name +@return String containing cookie data +]] + +---[[ +Get the value of a certain HTTP environment variable + +or the environment table itself. +@class function +@name getenv +@param name Environment variable +@return HTTP environment value or environment table +]] + +---[[ +Set a handler function for incoming user file uploads. + +@class function +@name setfilehandler +@param callback Handler function +]] + +---[[ +Send a HTTP-Header. + +@class function +@name header +@param key Header key +@param value Header value +]] + +---[[ +Set the mime type of following content data. + +@class function +@name prepare_content +@param mime Mimetype of following content +]] + +---[[ +Get the RAW HTTP input source + +@class function +@name source +@return HTTP LTN12 source +]] + +---[[ +Set the HTTP status code and status message. + +@class function +@name status +@param code Status code +@param message Status message +]] + +---[[ +Send a chunk of content data to the client. + +This function is as a valid LTN12 sink. +If the content chunk is nil this function will automatically invoke close. +@class function +@name write +@param content Content chunk +@param src_err Error object from source (optional) +@see close +]] + +---[[ +Splice data from a filedescriptor to the client. + +@class function +@name splice +@param fp File descriptor +@param size Bytes to splice (optional) +]] + +---[[ +Redirects the client to a new URL and closes the connection. + +@class function +@name redirect +@param url Target URL +]] + +---[[ +Create a querystring out of a table of key - value pairs. + +@class function +@name build_querystring +@param table Query string source table +@return Encoded HTTP query string +]] + +---[[ +Return the URL-decoded equivalent of a string. + +@param str URL-encoded string +@param no_plus Don't decode + to " " +@return URL-decoded string +@see urlencode +]] + +---[[ +Return the URL-encoded equivalent of a string. + +@param str Source string +@return URL-encoded string +@see urldecode +]] + +---[[ +Send the given data as JSON encoded string. + +@class function +@name write_json +@param data Data to send +]] + diff --git a/modules/luci-base/luasrc/http/protocol.lua b/modules/luci-base/luasrc/http/protocol.lua index aeb7ea621..e9efb44cf 100644 --- a/modules/luci-base/luasrc/http/protocol.lua +++ b/modules/luci-base/luasrc/http/protocol.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol class. -- This class contains several functions useful for http message- and content -- decoding and to retrive form data from raw http messages. module("luci.http.protocol", package.seeall) @@ -10,12 +9,7 @@ local ltn12 = require("luci.ltn12") HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size ---- Decode an urlencoded string - optionally without decoding -- the "+" sign to " " - and return the decoded string. --- @param str Input string in x-www-urlencoded format --- @param no_plus Don't decode "+" signs to spaces --- @return The decoded string --- @see urlencode function urldecode( str, no_plus ) local function __chrdec( hex ) @@ -33,15 +27,10 @@ function urldecode( str, no_plus ) return str end ---- Extract and split urlencoded data pairs, separated bei either "&" or ";" -- from given url or string. Returns a table with urldecoded values. -- Simple parameters are stored as string values associated with the parameter -- name within the table. Parameters with multiple values are stored as array -- containing the corresponding values. --- @param url The url or string which contains x-www-urlencoded form data --- @param tbl Use the given table for storing values (optional) --- @return Table containing the urldecoded parameters --- @see urlencode_params function urldecode_params( url, tbl ) local params = tbl or { } @@ -73,10 +62,6 @@ function urldecode_params( url, tbl ) return params end ---- Encode given string to x-www-urlencoded format. --- @param str String to encode --- @return String containing the encoded data --- @see urldecode function urlencode( str ) local function __chrenc( chr ) @@ -95,12 +80,8 @@ function urlencode( str ) return str end ---- Encode each key-value-pair in given table to x-www-urlencoded format, -- separated by "&". Tables are encoded as parameters with multiple values by -- repeating the parameter name with each value. --- @param tbl Table with the values --- @return String containing encoded values --- @see urldecode_params function urlencode_params( tbl ) local enc = "" @@ -122,9 +103,6 @@ end -- (Internal function) -- Initialize given parameter and coerce string into table when the parameter -- already exists. --- @param tbl Table where parameter should be created --- @param key Parameter name --- @return Always nil local function __initval( tbl, key ) if tbl[key] == nil then tbl[key] = "" @@ -138,11 +116,6 @@ end -- (Internal function) -- Append given data to given parameter, either by extending the string value -- or by appending it to the last string in the parameter's value table. --- @param tbl Table containing the previously initialized parameter value --- @param key Parameter name --- @param chunk String containing the data to append --- @return Always nil --- @see __initval local function __appendval( tbl, key, chunk ) if type(tbl[key]) == "table" then tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk @@ -155,12 +128,6 @@ end -- Finish the value of given parameter, either by transforming the string value -- or - in the case of multi value parameters - the last element in the -- associated values table. --- @param tbl Table containing the previously initialized parameter value --- @param key Parameter name --- @param handler Function which transforms the parameter value --- @return Always nil --- @see __initval --- @see __appendval local function __finishval( tbl, key, handler ) if handler then if type(tbl[key]) == "table" then @@ -259,10 +226,7 @@ process_states['headers'] = function( msg, chunk ) end ---- Creates a ltn12 source from the given socket. The source will return it's -- data line by line with the trailing \r\n stripped of. --- @param sock Readable network socket --- @return Ltn12 source function function header_source( sock ) return ltn12.source.simplify( function() @@ -289,7 +253,6 @@ function header_source( sock ) end ) end ---- Decode a mime encoded http message body with multipart/form-data -- Content-Type. Stores all extracted data associated with its parameter name -- in the params table withing the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. @@ -300,12 +263,6 @@ end -- o Table containing decoded (name, file) and raw (headers) mime header data -- o String value containing a chunk of the file data -- o Boolean which indicates wheather the current chunk is the last one (eof) --- @param src Ltn12 source function --- @param msg HTTP message object --- @param filecb File callback function (optional) --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function mimedecode_message_body( src, msg, filecb ) if msg and msg.env.CONTENT_TYPE then @@ -449,15 +406,9 @@ function mimedecode_message_body( src, msg, filecb ) return ltn12.pump.all( src, snk ) end ---- Decode an urlencoded http message body with application/x-www-urlencoded -- Content-Type. Stores all extracted data associated with its parameter name -- in the params table withing the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. --- @param src Ltn12 source function --- @param msg HTTP message object --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function urldecode_message_body( src, msg ) local tlen = 0 @@ -507,12 +458,8 @@ function urldecode_message_body( src, msg ) return ltn12.pump.all( src, snk ) end ---- Try to extract an http message header including information like protocol -- version, message headers and resulting CGI environment variables from the -- given ltn12 source. --- @param src Ltn12 source function --- @return HTTP message object --- @see parse_message_body function parse_message_header( src ) local ok = true @@ -582,19 +529,12 @@ function parse_message_header( src ) return msg end ---- Try to extract and decode a http message body from the given ltn12 source. -- This function will examine the Content-Type within the given message object -- to select the appropriate content decoder. -- Currently the application/x-www-urlencoded and application/form-data -- mime types are supported. If the encountered content encoding can't be -- handled then the whole message body will be stored unaltered as "content" -- property within the given message object. --- @param src Ltn12 source function --- @param msg HTTP message object --- @param filecb File data callback (optional, see mimedecode_message_body()) --- @return Value indicating successful operation (not nil means "ok") --- @return String containing the error if unsuccessful --- @see parse_message_header function parse_message_body( src, msg, filecb ) -- Is it multipart/mime ? if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and @@ -655,8 +595,6 @@ function parse_message_body( src, msg, filecb ) end end ---- Table containing human readable messages for several http status codes. --- @class table statusmsg = { [200] = "OK", [206] = "Partial Content", diff --git a/modules/luci-base/luasrc/http/protocol.luadoc b/modules/luci-base/luasrc/http/protocol.luadoc new file mode 100644 index 000000000..67a60d9e7 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol.luadoc @@ -0,0 +1,142 @@ +---[[ +LuCI http protocol class. + +This class contains several functions useful for http message- and content +decoding and to retrive form data from raw http messages. +]] +module "luci.http.protocol" + +---[[ +Decode an urlencoded string - optionally without decoding + +the "+" sign to " " - and return the decoded string. +@class function +@name urldecode +@param str Input string in x-www-urlencoded format +@param no_plus Don't decode "+" signs to spaces +@return The decoded string +@see urlencode +]] + +---[[ +Extract and split urlencoded data pairs, separated bei either "&" or ";" + +from given url or string. Returns a table with urldecoded values. +Simple parameters are stored as string values associated with the parameter +name within the table. Parameters with multiple values are stored as array +containing the corresponding values. +@class function +@name urldecode_params +@param url The url or string which contains x-www-urlencoded form data +@param tbl Use the given table for storing values (optional) +@return Table containing the urldecoded parameters +@see urlencode_params +]] + +---[[ +Encode given string to x-www-urlencoded format. + +@class function +@name urlencode +@param str String to encode +@return String containing the encoded data +@see urldecode +]] + +---[[ +Encode each key-value-pair in given table to x-www-urlencoded format, + +separated by "&". Tables are encoded as parameters with multiple values by +repeating the parameter name with each value. +@class function +@name urlencode_params +@param tbl Table with the values +@return String containing encoded values +@see urldecode_params +]] + +---[[ +Creates a ltn12 source from the given socket. The source will return it's + +data line by line with the trailing \r\n stripped of. +@class function +@name header_source +@param sock Readable network socket +@return Ltn12 source function +]] + +---[[ +Decode a mime encoded http message body with multipart/form-data + +Content-Type. Stores all extracted data associated with its parameter name +in the params table withing the given message object. Multiple parameter +values are stored as tables, ordinary ones as strings. +If an optional file callback function is given then it is feeded with the +file contents chunk by chunk and only the extracted file name is stored +within the params table. The callback function will be called subsequently +with three arguments: + o Table containing decoded (name, file) and raw (headers) mime header data + o String value containing a chunk of the file data + o Boolean which indicates wheather the current chunk is the last one (eof) +@class function +@name mimedecode_message_body +@param src Ltn12 source function +@param msg HTTP message object +@param filecb File callback function (optional) +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Decode an urlencoded http message body with application/x-www-urlencoded + +Content-Type. Stores all extracted data associated with its parameter name +in the params table withing the given message object. Multiple parameter +values are stored as tables, ordinary ones as strings. +@class function +@name urldecode_message_body +@param src Ltn12 source function +@param msg HTTP message object +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Try to extract an http message header including information like protocol + +version, message headers and resulting CGI environment variables from the +given ltn12 source. +@class function +@name parse_message_header +@param src Ltn12 source function +@return HTTP message object +@see parse_message_body +]] + +---[[ +Try to extract and decode a http message body from the given ltn12 source. + +This function will examine the Content-Type within the given message object +to select the appropriate content decoder. +Currently the application/x-www-urlencoded and application/form-data +mime types are supported. If the encountered content encoding can't be +handled then the whole message body will be stored unaltered as "content" +property within the given message object. +@class function +@name parse_message_body +@param src Ltn12 source function +@param msg HTTP message object +@param filecb File data callback (optional, see mimedecode_message_body()) +@return Value indicating successful operation (not nil means "ok") +@return String containing the error if unsuccessful +@see parse_message_header +]] + +---[[ +Table containing human readable messages for several http status codes. + +@class table +]] + diff --git a/modules/luci-base/luasrc/http/protocol/conditionals.lua b/modules/luci-base/luasrc/http/protocol/conditionals.lua index 1d40425ff..d31a4e38a 100644 --- a/modules/luci-base/luasrc/http/protocol/conditionals.lua +++ b/modules/luci-base/luasrc/http/protocol/conditionals.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - HTTP/1.1 bits. -- This class provides basic ETag handling and implements most of the -- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . module("luci.http.protocol.conditionals", package.seeall) @@ -9,22 +8,14 @@ module("luci.http.protocol.conditionals", package.seeall) local date = require("luci.http.protocol.date") ---- Implement 14.19 / ETag. --- @param stat A file.stat structure --- @return String containing the generated tag suitable for ETag headers function mk_etag( stat ) if stat ~= nil then return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime ) end end ---- 14.24 / If-Match -- Test whether the given message object contains an "If-Match" header and -- compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_match( req, stat ) local h = req.headers local etag = mk_etag( stat ) @@ -43,14 +34,8 @@ function if_match( req, stat ) return true end ---- 14.25 / If-Modified-Since -- Test whether the given message object contains an "If-Modified-Since" header -- and compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed --- @return Table containing extra HTTP headers if the precondition failed function if_modified_since( req, stat ) local h = req.headers @@ -72,14 +57,8 @@ function if_modified_since( req, stat ) return true end ---- 14.26 / If-None-Match -- Test whether the given message object contains an "If-None-Match" header and -- compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed --- @return Table containing extra HTTP headers if the precondition failed function if_none_match( req, stat ) local h = req.headers local etag = mk_etag( stat ) @@ -105,26 +84,16 @@ function if_none_match( req, stat ) return true end ---- 14.27 / If-Range -- The If-Range header is currently not implemented due to the lack of general -- byte range stuff in luci.http.protocol . This function will always return -- false, 412 to indicate a failed precondition. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_range( req, stat ) -- Sorry, no subranges (yet) return false, 412 end ---- 14.28 / If-Unmodified-Since -- Test whether the given message object contains an "If-Unmodified-Since" -- header and compare it against the given stat object. --- @param req HTTP request message object --- @param stat A file.stat object --- @return Boolean indicating whether the precondition is ok --- @return Alternative status code if the precondition failed function if_unmodified_since( req, stat ) local h = req.headers diff --git a/modules/luci-base/luasrc/http/protocol/conditionals.luadoc b/modules/luci-base/luasrc/http/protocol/conditionals.luadoc new file mode 100644 index 000000000..9cfe02dd5 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/conditionals.luadoc @@ -0,0 +1,85 @@ +---[[ +LuCI http protocol implementation - HTTP/1.1 bits. + +This class provides basic ETag handling and implements most of the +conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . +]] +module "luci.http.protocol.conditionals" + +---[[ +Implement 14.19 / ETag. + +@class function +@name mk_etag +@param stat A file.stat structure +@return String containing the generated tag suitable for ETag headers +]] + +---[[ +14.24 / If-Match + +Test whether the given message object contains an "If-Match" header and +compare it against the given stat object. +@class function +@name if_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.25 / If-Modified-Since + +Test whether the given message object contains an "If-Modified-Since" header +and compare it against the given stat object. +@class function +@name if_modified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.26 / If-None-Match + +Test whether the given message object contains an "If-None-Match" header and +compare it against the given stat object. +@class function +@name if_none_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.27 / If-Range + +The If-Range header is currently not implemented due to the lack of general +byte range stuff in luci.http.protocol . This function will always return +false, 412 to indicate a failed precondition. +@class function +@name if_range +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.28 / If-Unmodified-Since + +Test whether the given message object contains an "If-Unmodified-Since" +header and compare it against the given stat object. +@class function +@name if_unmodified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + diff --git a/modules/luci-base/luasrc/http/protocol/date.lua b/modules/luci-base/luasrc/http/protocol/date.lua index 3105f37f6..e440219a9 100644 --- a/modules/luci-base/luasrc/http/protocol/date.lua +++ b/modules/luci-base/luasrc/http/protocol/date.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - date helper class. -- This class contains functions to parse, compare and format http dates. module("luci.http.protocol.date", package.seeall) @@ -13,9 +12,6 @@ MONTHS = { "Sep", "Oct", "Nov", "Dec" } ---- Return the time offset in seconds between the UTC and given time zone. --- @param tz Symbolic or numeric timezone specifier --- @return Time offset to UTC in seconds function tz_offset(tz) if type(tz) == "string" then @@ -39,9 +35,6 @@ function tz_offset(tz) return 0 end ---- Parse given HTTP date string and convert it to unix epoch time. --- @param data String containing the date --- @return Unix epoch time function to_unix(date) local wd, day, mon, yr, hr, min, sec, tz = date:match( @@ -75,19 +68,10 @@ function to_unix(date) return 0 end ---- Convert the given unix epoch time to valid HTTP date string. --- @param time Unix epoch time --- @return String containing the formatted date function to_http(time) return os.date( "%a, %d %b %Y %H:%M:%S GMT", time ) end ---- Compare two dates which can either be unix epoch times or HTTP date strings. --- @param d1 The first date or epoch time to compare --- @param d2 The first date or epoch time to compare --- @return -1 - if d1 is lower then d2 --- @return 0 - if both dates are equal --- @return 1 - if d1 is higher then d2 function compare(d1, d2) if d1:match("[^0-9]") then d1 = to_unix(d1) end diff --git a/modules/luci-base/luasrc/http/protocol/date.luadoc b/modules/luci-base/luasrc/http/protocol/date.luadoc new file mode 100644 index 000000000..d6f1c8d65 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/date.luadoc @@ -0,0 +1,46 @@ +---[[ +LuCI http protocol implementation - date helper class. + +This class contains functions to parse, compare and format http dates. +]] +module "luci.http.protocol.date" + +---[[ +Return the time offset in seconds between the UTC and given time zone. + +@class function +@name tz_offset +@param tz Symbolic or numeric timezone specifier +@return Time offset to UTC in seconds +]] + +---[[ +Parse given HTTP date string and convert it to unix epoch time. + +@class function +@name to_unix +@param data String containing the date +@return Unix epoch time +]] + +---[[ +Convert the given unix epoch time to valid HTTP date string. + +@class function +@name to_http +@param time Unix epoch time +@return String containing the formatted date +]] + +---[[ +Compare two dates which can either be unix epoch times or HTTP date strings. + +@class function +@name compare +@param d1 The first date or epoch time to compare +@param d2 The first date or epoch time to compare +@return -1 - if d1 is lower then d2 +@return 0 - if both dates are equal +@return 1 - if d1 is higher then d2 +]] + diff --git a/modules/luci-base/luasrc/http/protocol/mime.lua b/modules/luci-base/luasrc/http/protocol/mime.lua index 15da15cf8..2b99d8e74 100644 --- a/modules/luci-base/luasrc/http/protocol/mime.lua +++ b/modules/luci-base/luasrc/http/protocol/mime.lua @@ -1,15 +1,12 @@ -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. ---- LuCI http protocol implementation - mime helper class. -- This class provides functions to guess mime types from file extensions and -- vice versa. module("luci.http.protocol.mime", package.seeall) require("luci.util") ---- MIME mapping table containg extension - mimetype relations. --- @class table MIME_TYPES = { ["txt"] = "text/plain"; ["js"] = "text/javascript"; @@ -54,10 +51,7 @@ MIME_TYPES = { ["avi"] = "video/x-msvideo"; } ---- Extract extension from a filename and return corresponding mime-type or -- "application/octet-stream" if the extension is unknown. --- @param filename The filename for which the mime type is guessed --- @return String containign the determined mime type function to_mime(filename) if type(filename) == "string" then local ext = filename:match("[^%.]+$") @@ -70,10 +64,7 @@ function to_mime(filename) return "application/octet-stream" end ---- Return corresponding extension for a given mime type or nil if the -- given mime-type is unknown. --- @param mimetype The mimetype to retrieve the extension from --- @return String with the extension or nil for unknown type function to_ext(mimetype) if type(mimetype) == "string" then for ext, type in luci.util.kspairs( MIME_TYPES ) do diff --git a/modules/luci-base/luasrc/http/protocol/mime.luadoc b/modules/luci-base/luasrc/http/protocol/mime.luadoc new file mode 100644 index 000000000..195b5fcc8 --- /dev/null +++ b/modules/luci-base/luasrc/http/protocol/mime.luadoc @@ -0,0 +1,34 @@ +---[[ +LuCI http protocol implementation - mime helper class. + +This class provides functions to guess mime types from file extensions and +vice versa. +]] +module "luci.http.protocol.mime" + +---[[ +MIME mapping table containg extension - mimetype relations. + +@class table +]] + +---[[ +Extract extension from a filename and return corresponding mime-type or + +"application/octet-stream" if the extension is unknown. +@class function +@name to_mime +@param filename The filename for which the mime type is guessed +@return String containign the determined mime type +]] + +---[[ +Return corresponding extension for a given mime type or nil if the + +given mime-type is unknown. +@class function +@name to_ext +@param mimetype The mimetype to retrieve the extension from +@return String with the extension or nil for unknown type +]] + diff --git a/modules/luci-base/luasrc/i18n.lua b/modules/luci-base/luasrc/i18n.lua index dd84a59f8..bcb16d5c0 100644 --- a/modules/luci-base/luasrc/i18n.lua +++ b/modules/luci-base/luasrc/i18n.lua @@ -1,7 +1,6 @@ -- Copyright 2008 Steven Barth -- 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 tostring(translate(...)) --- @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 tostring(translatef(...)) --- @param key Default translation text --- @param ... Format parameters --- @return Translated and formatted string function stringf(key, ...) return tostring(translate(key)):format(...) end diff --git a/modules/luci-base/luasrc/i18n.luadoc b/modules/luci-base/luasrc/i18n.luadoc new file mode 100644 index 000000000..aa38841e1 --- /dev/null +++ b/modules/luci-base/luasrc/i18n.luadoc @@ -0,0 +1,84 @@ +---[[ +LuCI translation library. +]] +module "luci.i18n" + +---[[ +Clear the translation table. + + +@class function +@name clear +]] + +---[[ +Load a translation and copy its data into the translation table. + +@class function +@name load +@param file Language file +@param lang Two-letter language code +@param force Force reload even if already loaded (optional) +@return Success status +]] + +---[[ +Load a translation file using the default translation language. + +Alternatively load the translation of the fallback language. +@class function +@name loadc +@param file Language file +@param force Force reload even if already loaded (optional) +]] + +---[[ +Set the context default translation language. + +@class function +@name setlanguage +@param lang Two-letter language code +]] + +---[[ +Return the translated value for a specific translation key. + +@class function +@name translate +@param key Default translation text +@return Translated string +]] + +---[[ +Return the translated value for a specific translation key and use it as sprintf pattern. + +@class function +@name translatef +@param key Default translation text +@param ... Format parameters +@return Translated and formatted string +]] + +---[[ +Return the translated value for a specific translation key + +and ensure that the returned value is a Lua string value. +This is the same as calling tostring(translate(...)) +@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 tostring(translatef(...)) +@class function +@name stringf +@param key Default translation text +@param ... Format parameters +@return Translated and formatted string +]] + diff --git a/modules/luci-base/luasrc/ip.lua b/modules/luci-base/luasrc/ip.lua deleted file mode 100644 index d8aaea91d..000000000 --- a/modules/luci-base/luasrc/ip.lua +++ /dev/null @@ -1,661 +0,0 @@ --- Copyright 2008 Jo-Philipp Wich --- Copyright 2008 Steven Barth --- Licensed to the public under the Apache License 2.0. - ---- LuCI IP calculation library. -module( "luci.ip", package.seeall ) - -require "nixio" -local bit = nixio.bit -local util = require "luci.util" - ---- Boolean; true if system is little endian -LITTLE_ENDIAN = not util.bigendian() - ---- Boolean; true if system is big endian -BIG_ENDIAN = not LITTLE_ENDIAN - ---- Specifier for IPv4 address family -FAMILY_INET4 = 0x04 - ---- Specifier for IPv6 address family -FAMILY_INET6 = 0x06 - - -local function __bless(x) - return setmetatable( x, { - __index = luci.ip.cidr, - __add = luci.ip.cidr.add, - __sub = luci.ip.cidr.sub, - __lt = luci.ip.cidr.lower, - __eq = luci.ip.cidr.equal, - __le = - function(...) - return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...) - end - } ) -end - -local function __array16( x, family ) - local list - - if type(x) == "number" then - list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) } - - elseif type(x) == "string" then - if x:find(":") then x = IPv6(x) else x = IPv4(x) end - if x then - assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" ) - list = { unpack(x[2]) } - end - - elseif type(x) == "table" and type(x[2]) == "table" then - assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" ) - list = { unpack(x[2]) } - - elseif type(x) == "table" then - list = { unpack(x) } - end - - assert( list, "Invalid operand" ) - - return list -end - -local function __mask16(bits) - return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 ) -end - -local function __not16(bits) - return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF ) -end - -local function __maxlen(family) - return ( family == FAMILY_INET4 ) and 32 or 128 -end - -local function __sublen(family) - return ( family == FAMILY_INET4 ) and 30 or 127 -end - - ---- Convert given short value to network byte order on little endian hosts --- @param x Unsigned integer value between 0x0000 and 0xFFFF --- @return Byte-swapped value --- @see htonl --- @see ntohs -function htons(x) - if LITTLE_ENDIAN then - return bit.bor( - bit.rshift( x, 8 ), - bit.band( bit.lshift( x, 8 ), 0xFF00 ) - ) - else - return x - end -end - ---- Convert given long value to network byte order on little endian hosts --- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF --- @return Byte-swapped value --- @see htons --- @see ntohl -function htonl(x) - if LITTLE_ENDIAN then - return bit.bor( - bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ), - htons( bit.rshift( x, 16 ) ) - ) - else - return x - end -end - ---- Convert given short value to host byte order on little endian hosts --- @class function --- @name ntohs --- @param x Unsigned integer value between 0x0000 and 0xFFFF --- @return Byte-swapped value --- @see htonl --- @see ntohs -ntohs = htons - ---- Convert given short value to host byte order on little endian hosts --- @class function --- @name ntohl --- @param x Unsigned integer value between 0x00000000 and 0xFFFFFFFF --- @return Byte-swapped value --- @see htons --- @see ntohl -ntohl = htonl - - ---- Parse given IPv4 address in dotted quad or CIDR notation. If an optional --- netmask is given as second argument and the IP address is encoded in CIDR --- notation then the netmask parameter takes precedence. If neither a CIDR --- encoded prefix nor a netmask parameter is given, then a prefix length of --- 32 bit is assumed. --- @param address IPv4 address in dotted quad or CIDR notation --- @param netmask IPv4 netmask in dotted quad notation (optional) --- @return luci.ip.cidr instance or nil if given address was invalid --- @see IPv6 --- @see Hex -function IPv4(address, netmask) - address = address or "0.0.0.0/0" - - local obj = __bless({ FAMILY_INET4 }) - - local data = {} - local prefix = address:match("/(.+)") - address = address:gsub("/.+","") - address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "") - - if netmask then - prefix = obj:prefix(netmask) - elseif prefix then - prefix = tonumber(prefix) - if not prefix or prefix < 0 or prefix > 32 then return nil end - else - prefix = 32 - end - - local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$") - - b1 = tonumber(b1) - b2 = tonumber(b2) - b3 = tonumber(b3) - b4 = tonumber(b4) - - if b1 and b1 <= 255 and - b2 and b2 <= 255 and - b3 and b3 <= 255 and - b4 and b4 <= 255 and - prefix - then - table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 }) - table.insert(obj, prefix) - return obj - end -end - ---- Parse given IPv6 address in full, compressed, mixed or CIDR notation. --- If an optional netmask is given as second argument and the IP address is --- encoded in CIDR notation then the netmask parameter takes precedence. --- If neither a CIDR encoded prefix nor a netmask parameter is given, then a --- prefix length of 128 bit is assumed. --- @param address IPv6 address in full/compressed/mixed or CIDR notation --- @param netmask IPv6 netmask in full/compressed/mixed notation (optional) --- @return luci.ip.cidr instance or nil if given address was invalid --- @see IPv4 --- @see Hex -function IPv6(address, netmask) - address = address or "::/0" - - local obj = __bless({ FAMILY_INET6 }) - - local data = {} - local prefix = address:match("/(.+)") - address = address:gsub("/.+","") - address = address:gsub("^%[(.*)%]$", "%1") - - if netmask then - prefix = obj:prefix(netmask) - elseif prefix then - prefix = tonumber(prefix) - if not prefix or prefix < 0 or prefix > 128 then return nil end - else - prefix = 128 - end - - local borderl = address:sub(1, 1) == ":" and 2 or 1 - local borderh, zeroh, chunk, block, i - - if #address > 45 then return nil end - - repeat - borderh = address:find(":", borderl, true) - if not borderh then break end - - block = tonumber(address:sub(borderl, borderh - 1), 16) - if block and block <= 0xFFFF then - data[#data+1] = block - else - if zeroh or borderh - borderl > 1 then return nil end - zeroh = #data + 1 - end - - borderl = borderh + 1 - until #data == 7 - - chunk = address:sub(borderl) - if #chunk > 0 and #chunk <= 4 then - block = tonumber(chunk, 16) - if not block or block > 0xFFFF then return nil end - - data[#data+1] = block - elseif #chunk > 4 then - if #data == 7 or #chunk > 15 then return nil end - borderl = 1 - for i=1, 4 do - borderh = chunk:find(".", borderl, true) - if not borderh and i < 4 then return nil end - borderh = borderh and borderh - 1 - - block = tonumber(chunk:sub(borderl, borderh)) - if not block or block > 255 then return nil end - - if i == 1 or i == 3 then - data[#data+1] = block * 256 - else - data[#data] = data[#data] + block - end - - borderl = borderh and borderh + 2 - end - end - - if zeroh then - if #data == 8 then return nil end - while #data < 8 do - table.insert(data, zeroh, 0) - end - end - - if #data == 8 and prefix then - table.insert(obj, data) - table.insert(obj, prefix) - return obj - end -end - ---- Transform given hex-encoded value to luci.ip.cidr instance of specified --- address family. --- @param hex String containing hex encoded value --- @param prefix Prefix length of CIDR instance (optional, default is 32/128) --- @param family Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6 --- @param swap Bool indicating whether to swap byteorder on low endian host --- @return luci.ip.cidr instance or nil if given value was invalid --- @see IPv4 --- @see IPv6 -function Hex( hex, prefix, family, swap ) - family = ( family ~= nil ) and family or FAMILY_INET4 - swap = ( swap == nil ) and true or swap - prefix = prefix or __maxlen(family) - - local len = __maxlen(family) - local tmp = "" - local data = { } - local i - - for i = 1, (len/4) - #hex do tmp = tmp .. '0' end - - if swap and LITTLE_ENDIAN then - for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end - else - tmp = tmp .. hex - end - - hex = tmp - - for i = 1, ( len / 4 ), 4 do - local n = tonumber( hex:sub( i, i+3 ), 16 ) - if n then - data[#data+1] = n - else - return nil - end - end - - return __bless({ family, data, prefix }) -end - - ---- LuCI IP Library / CIDR instances --- @class module --- @cstyle instance --- @name luci.ip.cidr -cidr = util.class() - ---- Test whether the instance is a IPv4 address. --- @return Boolean indicating a IPv4 address type --- @see cidr.is6 -function cidr.is4( self ) - return self[1] == FAMILY_INET4 -end - ---- Test whether this instance is an IPv4 RFC1918 private address --- @return Boolean indicating whether this instance is an RFC1918 address -function cidr.is4rfc1918( self ) - if self[1] == FAMILY_INET4 then - return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or - ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or - (self[2][1] == 0xC0A8) - end - return false -end - ---- Test whether this instance is an IPv4 link-local address (Zeroconf) --- @return Boolean indicating whether this instance is IPv4 link-local -function cidr.is4linklocal( self ) - if self[1] == FAMILY_INET4 then - return (self[2][1] == 0xA9FE) - end - return false -end - ---- Test whether the instance is a IPv6 address. --- @return Boolean indicating a IPv6 address type --- @see cidr.is4 -function cidr.is6( self ) - return self[1] == FAMILY_INET6 -end - ---- Test whether this instance is an IPv6 link-local address --- @return Boolean indicating whether this instance is IPv6 link-local -function cidr.is6linklocal( self ) - if self[1] == FAMILY_INET6 then - return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF) - end - return false -end - ---- Return a corresponding string representation of the instance. --- If the prefix length is lower then the maximum possible prefix length for the --- corresponding address type then the address is returned in CIDR notation, --- otherwise the prefix will be left out. -function cidr.string( self ) - local str - if self:is4() then - str = string.format( - "%d.%d.%d.%d", - bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF), - bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF) - ) - if self[3] < 32 then - str = str .. "/" .. self[3] - end - elseif self:is6() then - str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) ) - if self[3] < 128 then - str = str .. "/" .. self[3] - end - end - return str -end - ---- Test whether the value of the instance is lower then the given address. --- This function will throw an exception if the given address has a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is lower --- @see cidr.higher --- @see cidr.equal -function cidr.lower( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return self[2][i] < addr[2][i] - end - end - return false -end - ---- Test whether the value of the instance is higher then the given address. --- This function will throw an exception if the given address has a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is higher --- @see cidr.lower --- @see cidr.equal -function cidr.higher( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return self[2][i] > addr[2][i] - end - end - return false -end - ---- Test whether the value of the instance is equal to the given address. --- This function will throw an exception if the given address is a different --- family than this instance. --- @param addr A luci.ip.cidr instance to compare against --- @return Boolean indicating whether this instance is equal --- @see cidr.lower --- @see cidr.higher -function cidr.equal( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - local i - for i = 1, #self[2] do - if self[2][i] ~= addr[2][i] then - return false - end - end - return true -end - ---- Return the prefix length of this CIDR instance. --- @param mask Override instance prefix with given netmask (optional) --- @return Prefix length in bit -function cidr.prefix( self, mask ) - local prefix = self[3] - - if mask then - prefix = 0 - - local stop = false - local obj = type(mask) ~= "table" - and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask - - if not obj then return nil end - - local _, word - for _, word in ipairs(obj[2]) do - if word == 0xFFFF then - prefix = prefix + 16 - else - local bitmask = bit.lshift(1, 15) - while bit.band(word, bitmask) == bitmask do - prefix = prefix + 1 - bitmask = bit.lshift(1, 15 - (prefix % 16)) - end - - break - end - end - end - - return prefix -end - ---- Return a corresponding CIDR representing the network address of this --- instance. --- @param bits Override prefix length of this instance (optional) --- @return CIDR instance containing the network address --- @see cidr.host --- @see cidr.broadcast --- @see cidr.mask -function cidr.network( self, bits ) - local data = { } - bits = bits or self[3] - - local i - for i = 1, math.floor( bits / 16 ) do - data[#data+1] = self[2][i] - end - - if #data < #self[2] then - data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) ) - - for i = #data + 1, #self[2] do - data[#data+1] = 0 - end - end - - return __bless({ self[1], data, __maxlen(self[1]) }) -end - ---- Return a corresponding CIDR representing the host address of this --- instance. This is intended to extract the host address from larger subnet. --- @return CIDR instance containing the network address --- @see cidr.network --- @see cidr.broadcast --- @see cidr.mask -function cidr.host( self ) - return __bless({ self[1], self[2], __maxlen(self[1]) }) -end - ---- Return a corresponding CIDR representing the netmask of this instance. --- @param bits Override prefix length of this instance (optional) --- @return CIDR instance containing the netmask --- @see cidr.network --- @see cidr.host --- @see cidr.broadcast -function cidr.mask( self, bits ) - local data = { } - bits = bits or self[3] - - for i = 1, math.floor( bits / 16 ) do - data[#data+1] = 0xFFFF - end - - if #data < #self[2] then - data[#data+1] = __mask16(bits) - - for i = #data + 1, #self[2] do - data[#data+1] = 0 - end - end - - return __bless({ self[1], data, __maxlen(self[1]) }) -end - ---- Return CIDR containing the broadcast address of this instance. --- @return CIDR instance containing the netmask, always nil for IPv6 --- @see cidr.network --- @see cidr.host --- @see cidr.mask -function cidr.broadcast( self ) - -- IPv6 has no broadcast addresses (XXX: assert() instead?) - if self[1] == FAMILY_INET4 then - local data = { unpack(self[2]) } - local offset = math.floor( self[3] / 16 ) + 1 - - if offset <= #data then - data[offset] = bit.bor( data[offset], __not16(self[3]) ) - for i = offset + 1, #data do data[i] = 0xFFFF end - - return __bless({ self[1], data, __maxlen(self[1]) }) - end - end -end - ---- Test whether this instance fully contains the given CIDR instance. --- @param addr CIDR instance to test against --- @return Boolean indicating whether this instance contains the given CIDR -function cidr.contains( self, addr ) - assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" ) - - if self:prefix() <= addr:prefix() then - return self:network() == addr:network(self:prefix()) - end - - return false -end - ---- Add specified amount of hosts to this instance. --- @param amount Number of hosts to add to this instance --- @param inplace Boolen indicating whether to alter values inplace (optional) --- @return CIDR representing the new address or nil on overflow error --- @see cidr.sub -function cidr.add( self, amount, inplace ) - local pos - local data = { unpack(self[2]) } - local shorts = __array16( amount, self[1] ) - - for pos = #data, 1, -1 do - local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 - if ( data[pos] + add ) > 0xFFFF then - data[pos] = ( data[pos] + add ) % 0xFFFF - if pos > 1 then - data[pos-1] = data[pos-1] + ( add - data[pos] ) - else - return nil - end - else - data[pos] = data[pos] + add - end - end - - if inplace then - self[2] = data - return self - else - return __bless({ self[1], data, self[3] }) - end -end - ---- Substract specified amount of hosts from this instance. --- @param amount Number of hosts to substract from this instance --- @param inplace Boolen indicating whether to alter values inplace (optional) --- @return CIDR representing the new address or nil on underflow error --- @see cidr.add -function cidr.sub( self, amount, inplace ) - local pos - local data = { unpack(self[2]) } - local shorts = __array16( amount, self[1] ) - - for pos = #data, 1, -1 do - local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 - if ( data[pos] - sub ) < 0 then - data[pos] = ( sub - data[pos] ) % 0xFFFF - if pos > 1 then - data[pos-1] = data[pos-1] - ( sub + data[pos] ) - else - return nil - end - else - data[pos] = data[pos] - sub - end - end - - if inplace then - self[2] = data - return self - else - return __bless({ self[1], data, self[3] }) - end -end - ---- Return CIDR containing the lowest available host address within this subnet. --- @return CIDR containing the host address, nil if subnet is too small --- @see cidr.maxhost -function cidr.minhost( self ) - if self[3] <= __sublen(self[1]) then - -- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6 - return self:network():add(1, true) - end -end - ---- Return CIDR containing the highest available host address within the subnet. --- @return CIDR containing the host address, nil if subnet is too small --- @see cidr.minhost -function cidr.maxhost( self ) - if self[3] <= __sublen(self[1]) then - local i - local data = { unpack(self[2]) } - local offset = math.floor( self[3] / 16 ) + 1 - - data[offset] = bit.bor( data[offset], __not16(self[3]) ) - for i = offset + 1, #data do data[i] = 0xFFFF end - data = __bless({ self[1], data, __maxlen(self[1]) }) - - -- Last address in reserved for Broadcast Address in IPv4 - if data[1] == FAMILY_INET4 then data:sub(1, true) end - - return data - end -end diff --git a/modules/luci-base/luasrc/ltn12.lua b/modules/luci-base/luasrc/ltn12.lua index b59fb8c48..3a7268cca 100644 --- a/modules/luci-base/luasrc/ltn12.lua +++ b/modules/luci-base/luasrc/ltn12.lua @@ -39,7 +39,6 @@ local string = require("string") local table = require("table") local base = _G ---- Diego Nehab's LTN12 - Filters, sources, sinks and pumps. -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts module("luci.ltn12") @@ -56,16 +55,8 @@ _VERSION = "LTN12 1.0.1" -- Filter stuff ----------------------------------------------------------------------------- ---- LTN12 Filter constructors --- @class module --- @name luci.ltn12.filter ---- Return a high level filter that cycles a low-level filter -- by passing it each chunk and updating a context between calls. --- @param low Low-level filter --- @param ctx Context --- @param extra Extra argument passed to the low-level filter --- @return LTN12 filter function filter.cycle(low, ctx, extra) base.assert(low) return function(chunk) @@ -75,10 +66,7 @@ function filter.cycle(low, ctx, extra) end end ---- Chain a bunch of filters together. -- (thanks to Wim Couwenberg) --- @param ... filters to be chained --- @return LTN12 filter function filter.chain(...) local n = table.getn(arg) local top, index = 1, 1 @@ -112,34 +100,22 @@ end -- Source stuff ----------------------------------------------------------------------------- ---- LTN12 Source constructors --- @class module --- @name luci.ltn12.source -- create an empty source local function empty() return nil end ---- Create an empty source. --- @return LTN12 source function source.empty() return empty end ---- Return a source that just outputs an error. --- @param err Error object --- @return LTN12 source function source.error(err) return function() return nil, err end end ---- Create a file source. --- @param handle File handle ready for reading --- @param io_err IO error object --- @return LTN12 source function source.file(handle, io_err) if handle then return function() @@ -151,9 +127,6 @@ function source.file(handle, io_err) else return source.error(io_err or "unable to open file") end end ---- Turn a fancy source into a simple source. --- @param src fancy source --- @return LTN12 source function source.simplify(src) base.assert(src) return function() @@ -164,9 +137,6 @@ function source.simplify(src) end end ---- Create a string source. --- @param s Data --- @return LTN12 source function source.string(s) if s then local i = 1 @@ -179,9 +149,6 @@ function source.string(s) else return source.empty() end end ---- Creates rewindable source. --- @param src LTN12 source to be made rewindable --- @return LTN12 source function source.rewind(src) base.assert(src) local t = {} @@ -196,10 +163,6 @@ function source.rewind(src) end end ---- Chain a source and a filter together. --- @param src LTN12 source --- @param f LTN12 filter --- @return LTN12 source function source.chain(src, f) base.assert(src and f) local last_in, last_out = "", "" @@ -247,11 +210,8 @@ function source.chain(src, f) end end ---- Create a source that produces contents of several sources. -- Sources will be used one after the other, as if they were concatenated -- (thanks to Wim Couwenberg) --- @param ... LTN12 sources --- @return LTN12 source function source.cat(...) local src = table.remove(arg, 1) return function() @@ -268,13 +228,7 @@ end -- Sink stuff ----------------------------------------------------------------------------- ---- LTN12 sink constructors --- @class module --- @name luci.ltn12.sink ---- Create a sink that stores into a table. --- @param t output table to store into --- @return LTN12 sink function sink.table(t) t = t or {} local f = function(chunk, err) @@ -284,9 +238,6 @@ function sink.table(t) return f, t end ---- Turn a fancy sink into a simple sink. --- @param snk fancy sink --- @return LTN12 sink function sink.simplify(snk) base.assert(snk) return function(chunk, err) @@ -297,10 +248,6 @@ function sink.simplify(snk) end end ---- Create a file sink. --- @param handle file handle to write to --- @param io_err IO error --- @return LTN12 sink function sink.file(handle, io_err) if handle then return function(chunk, err) @@ -317,25 +264,16 @@ local function null() return 1 end ---- Create a sink that discards data. --- @return LTN12 sink function sink.null() return null end ---- Create a sink that just returns an error. --- @param err Error object --- @return LTN12 sink function sink.error(err) return function() return nil, err end end ---- Chain a sink with a filter. --- @param f LTN12 filter --- @param snk LTN12 sink --- @return LTN12 sink function sink.chain(f, snk) base.assert(f and snk) return function(chunk, err) @@ -356,15 +294,7 @@ end -- Pump stuff ----------------------------------------------------------------------------- ---- LTN12 pump functions --- @class module --- @name luci.ltn12.pump ---- Pump one chunk from the source to the sink. --- @param src LTN12 source --- @param snk LTN12 sink --- @return Chunk of data or nil if an error occured --- @return Error object function pump.step(src, snk) local chunk, src_err = src() local ret, snk_err = snk(chunk, src_err) @@ -372,12 +302,6 @@ function pump.step(src, snk) else return nil, src_err or snk_err end end ---- Pump all data from a source to a sink, using a step function. --- @param src LTN12 source --- @param snk LTN12 sink --- @param step step function (optional) --- @return 1 if the operation succeeded otherwise nil --- @return Error object function pump.all(src, snk, step) base.assert(src and snk) step = step or pump.step diff --git a/modules/luci-base/luasrc/model/ipkg.lua b/modules/luci-base/luasrc/model/ipkg.lua index 216caa5cc..587637272 100644 --- a/modules/luci-base/luasrc/model/ipkg.lua +++ b/modules/luci-base/luasrc/model/ipkg.lua @@ -15,7 +15,6 @@ local table = table local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase" local icfg = "/etc/opkg.conf" ---- LuCI OPKG call abstraction library module "luci.model.ipkg" @@ -93,54 +92,31 @@ local function _lookup(act, pkg) end ---- Return information about installed and available packages. --- @param pkg Limit output to a (set of) packages --- @return Table containing package information function info(pkg) return _lookup("info", pkg) end ---- Return the package status of one or more packages. --- @param pkg Limit output to a (set of) packages --- @return Table containing package status information function status(pkg) return _lookup("status", pkg) end ---- Install one or more packages. --- @param ... List of packages to install --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function install(...) return _action("install", ...) end ---- Determine whether a given package is installed. --- @param pkg Package --- @return Boolean function installed(pkg) local p = status(pkg)[pkg] return (p and p.Status and p.Status.installed) end ---- Remove one or more packages. --- @param ... List of packages to install --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function remove(...) return _action("remove", ...) end ---- Update package lists. --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function update() return _action("update") end ---- Upgrades all installed packages. --- @return Boolean indicating the status of the action --- @return OPKG return code, STDOUT and STDERR function upgrade() return _action("upgrade") end @@ -174,33 +150,19 @@ function _list(action, pat, cb) end end ---- List all packages known to opkg. --- @param pat Only find packages matching this pattern, nil lists all packages --- @param cb Callback function invoked for each package, receives name, version and description as arguments --- @return nothing function list_all(pat, cb) _list("list", pat, cb) end ---- List installed packages. --- @param pat Only find packages matching this pattern, nil lists all packages --- @param cb Callback function invoked for each package, receives name, version and description as arguments --- @return nothing function list_installed(pat, cb) _list("list_installed", pat, cb) end ---- Find packages that match the given pattern. --- @param pat Find packages whose names or descriptions match this pattern, nil results in zero results --- @param cb Callback function invoked for each patckage, receives name, version and description as arguments --- @return nothing function find(pat, cb) _list("find", pat, cb) end ---- Determines the overlay root used by opkg. --- @return String containing the directory path of the overlay root. function overlay_root() local od = "/" local fd = io.open(icfg, "r") diff --git a/modules/luci-base/luasrc/model/ipkg.luadoc b/modules/luci-base/luasrc/model/ipkg.luadoc new file mode 100644 index 000000000..cf0985f94 --- /dev/null +++ b/modules/luci-base/luasrc/model/ipkg.luadoc @@ -0,0 +1,109 @@ +---[[ +LuCI OPKG call abstraction library + +module "luci.model.ipkg" +]] + +---[[ +Return information about installed and available packages. + +@class function +@name info +@param pkg Limit output to a (set of) packages +@return Table containing package information +]] + +---[[ +Return the package status of one or more packages. + +@class function +@name status +@param pkg Limit output to a (set of) packages +@return Table containing package status information +]] + +---[[ +Install one or more packages. + +@class function +@name install +@param ... List of packages to install +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Determine whether a given package is installed. + +@class function +@name installed +@param pkg Package +@return Boolean +]] + +---[[ +Remove one or more packages. + +@class function +@name remove +@param ... List of packages to install +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Update package lists. + +@class function +@name update +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +Upgrades all installed packages. + +@class function +@name upgrade +@return Boolean indicating the status of the action +@return OPKG return code, STDOUT and STDERR +]] + +---[[ +List all packages known to opkg. + +@class function +@name list_all +@param pat Only find packages matching this pattern, nil lists all packages +@param cb Callback function invoked for each package, receives name, version and description as arguments +@return nothing +]] + +---[[ +List installed packages. + +@class function +@name list_installed +@param pat Only find packages matching this pattern, nil lists all packages +@param cb Callback function invoked for each package, receives name, version and description as arguments +@return nothing +]] + +---[[ +Find packages that match the given pattern. + +@class function +@name find +@param pat Find packages whose names or descriptions match this pattern, nil results in zero results +@param cb Callback function invoked for each patckage, receives name, version and description as arguments +@return nothing +]] + +---[[ +Determines the overlay root used by opkg. + +@class function +@name overlay_root +@return String containing the directory path of the overlay root. +]] + diff --git a/modules/luci-base/luasrc/model/uci.lua b/modules/luci-base/luasrc/model/uci.lua index 8ac82773f..165913774 100644 --- a/modules/luci-base/luasrc/model/uci.lua +++ b/modules/luci-base/luasrc/model/uci.lua @@ -12,26 +12,18 @@ local require, getmetatable = require, getmetatable local error, pairs, ipairs = error, pairs, ipairs local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack ---- LuCI UCI model library. -- The typical workflow for UCI is: Get a cursor instance from the -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), -- save the changes to the staging area via Cursor.save and finally -- Cursor.commit the data to the actual config files. -- LuCI then needs to Cursor.apply the changes so deamons etc. are -- reloaded. --- @cstyle instance module "luci.model.uci" ---- Create a new UCI-Cursor. --- @class function --- @name cursor --- @return UCI-Cursor cursor = uci.cursor APIVERSION = uci.APIVERSION ---- Create a new Cursor initialized to the state directory. --- @return UCI cursor function cursor_state() return cursor(nil, "/var/state") end @@ -42,9 +34,6 @@ inst_state = cursor_state() local Cursor = getmetatable(inst) ---- Applies UCI configuration changes --- @param configlist List of UCI configurations --- @param command Don't apply only return the command function Cursor.apply(self, configlist, command) configlist = self:_affected(configlist) if command then @@ -56,10 +45,6 @@ function Cursor.apply(self, configlist, command) end ---- Delete all sections of a given type that match certain criteria. --- @param config UCI config --- @param type UCI section type --- @param comparator Function that will be called for each section and -- returns a boolean whether to delete the current section (optional) function Cursor.delete_all(self, config, stype, comparator) local del = {} @@ -90,12 +75,6 @@ function Cursor.delete_all(self, config, stype, comparator) end end ---- Create a new section and initialize it with data. --- @param config UCI config --- @param type UCI section type --- @param name UCI section name (optional) --- @param values Table of key - value pairs to initialize the section with --- @return Name of created section function Cursor.section(self, config, type, name, values) local stat = true if name then @@ -112,10 +91,6 @@ function Cursor.section(self, config, type, name, values) return stat and name end ---- Updated the data of a section using data from a table. --- @param config UCI config --- @param section UCI section name (optional) --- @param values Table of key - value pairs to update the section with function Cursor.tset(self, config, section, values) local stat = true for k, v in pairs(values) do @@ -126,21 +101,11 @@ function Cursor.tset(self, config, section, values) return stat end ---- Get a boolean option and return it's value as true or false. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return Boolean function Cursor.get_bool(self, ...) local val = self:get(...) return ( val == "1" or val == "true" or val == "yes" or val == "on" ) end ---- Get an option or list and return values as table. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @return UCI value function Cursor.get_list(self, config, section, option) if config and section and option then local val = self:get(config, section, option) @@ -149,12 +114,6 @@ function Cursor.get_list(self, config, section, option) return nil end ---- Get the given option from the first section with the given type. --- @param config UCI config --- @param type UCI section type --- @param option UCI option (optional) --- @param default Default value (optional) --- @return UCI value function Cursor.get_first(self, conf, stype, opt, def) local rv = def @@ -178,12 +137,6 @@ function Cursor.get_first(self, conf, stype, opt, def) return rv end ---- Set given values as list. --- @param config UCI config --- @param section UCI section name --- @param option UCI option --- @param value UCI value --- @return Boolean whether operation succeeded function Cursor.set_list(self, config, section, option, value) if config and section and option then return self:set( @@ -238,10 +191,8 @@ function Cursor._affected(self, configlist) return reloadlist end ---- Create a sub-state of this cursor. The sub-state is tied to the parent -- curser, means it the parent unloads or loads configs, the sub state will -- do so as well. --- @return UCI state cursor tied to the parent cursor function Cursor.substate(self) Cursor._substates = Cursor._substates or { } Cursor._substates[self] = Cursor._substates[self] or cursor_state() @@ -265,118 +216,18 @@ function Cursor.unload(self, ...) end ---- Add an anonymous section. --- @class function --- @name Cursor.add --- @param config UCI config --- @param type UCI section type --- @return Name of created section - ---- Get a table of saved but uncommitted changes. --- @class function --- @name Cursor.changes --- @param config UCI config --- @return Table of changes --- @see Cursor.save - ---- Commit saved changes. --- @class function --- @name Cursor.commit --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.revert --- @see Cursor.save - ---- Deletes a section or an option. --- @class function --- @name Cursor.delete --- @param config UCI config --- @param section UCI section name --- @param option UCI option (optional) --- @return Boolean whether operation succeeded - ---- Call a function for every section of a certain type. --- @class function --- @name Cursor.foreach --- @param config UCI config --- @param type UCI section type --- @param callback Function to be called --- @return Boolean whether operation succeeded - ---- Get a section type or an option --- @class function --- @name Cursor.get --- @param config UCI config --- @param section UCI section name --- @param option UCI option (optional) --- @return UCI value - ---- Get all sections of a config or all values of a section. --- @class function --- @name Cursor.get_all --- @param config UCI config --- @param section UCI section name (optional) --- @return Table of UCI sections or table of UCI values - ---- Manually load a config. --- @class function --- @name Cursor.load --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.save --- @see Cursor.unload - ---- Revert saved but uncommitted changes. --- @class function --- @name Cursor.revert --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.commit --- @see Cursor.save - ---- Saves changes made to a config to make them committable. --- @class function --- @name Cursor.save --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.load --- @see Cursor.unload - ---- Set a value or create a named section. --- @class function --- @name Cursor.set --- @param config UCI config --- @param section UCI section name --- @param option UCI option or UCI section type --- @param value UCI value or nil if you want to create a section --- @return Boolean whether operation succeeded - ---- Get the configuration directory. --- @class function --- @name Cursor.get_confdir --- @return Configuration directory - ---- Get the directory for uncomitted changes. --- @class function --- @name Cursor.get_savedir --- @return Save directory - ---- Set the configuration directory. --- @class function --- @name Cursor.set_confdir --- @param directory UCI configuration directory --- @return Boolean whether operation succeeded - ---- Set the directory for uncommited changes. --- @class function --- @name Cursor.set_savedir --- @param directory UCI changes directory --- @return Boolean whether operation succeeded - ---- Discard changes made to a config. --- @class function --- @name Cursor.unload --- @param config UCI config --- @return Boolean whether operation succeeded --- @see Cursor.load --- @see Cursor.save + + + + + + + + + + + + + + + diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc new file mode 100644 index 000000000..1c208669d --- /dev/null +++ b/modules/luci-base/luasrc/model/uci.luadoc @@ -0,0 +1,291 @@ +---[[ +LuCI UCI model library. + +The typical workflow for UCI is: Get a cursor instance from the +cursor factory, modify data (via Cursor.add, Cursor.delete, etc.), +save the changes to the staging area via Cursor.save and finally +Cursor.commit the data to the actual config files. +LuCI then needs to Cursor.apply the changes so deamons etc. are +reloaded. +@cstyle instance +module "luci.model.uci" +]] + +---[[ +Create a new UCI-Cursor. + +@class function +@name cursor +@return UCI-Cursor +]] + +---[[ +Create a new Cursor initialized to the state directory. + +@class function +@name cursor_state +@return UCI cursor +]] + +---[[ +Applies UCI configuration changes + +@class function +@name Cursor.apply +@param configlist List of UCI configurations +@param command Don't apply only return the command +]] + +---[[ +Delete all sections of a given type that match certain criteria. + +@class function +@name Cursor.delete_all +@param config UCI config +@param type UCI section type +@param comparator Function that will be called for each section and +returns a boolean whether to delete the current section (optional) +]] + +---[[ +Create a new section and initialize it with data. + +@class function +@name Cursor.section +@param config UCI config +@param type UCI section type +@param name UCI section name (optional) +@param values Table of key - value pairs to initialize the section with +@return Name of created section +]] + +---[[ +Updated the data of a section using data from a table. + +@class function +@name Cursor.tset +@param config UCI config +@param section UCI section name (optional) +@param values Table of key - value pairs to update the section with +]] + +---[[ +Get a boolean option and return it's value as true or false. + +@class function +@name Cursor.get_bool +@param config UCI config +@param section UCI section name +@param option UCI option +@return Boolean +]] + +---[[ +Get an option or list and return values as table. + +@class function +@name Cursor.get_list +@param config UCI config +@param section UCI section name +@param option UCI option +@return UCI value +]] + +---[[ +Get the given option from the first section with the given type. + +@class function +@name Cursor.get_first +@param config UCI config +@param type UCI section type +@param option UCI option (optional) +@param default Default value (optional) +@return UCI value +]] + +---[[ +Set given values as list. + +@class function +@name Cursor.set_list +@param config UCI config +@param section UCI section name +@param option UCI option +@param value UCI value +@return Boolean whether operation succeeded +]] + +---[[ +Create a sub-state of this cursor. The sub-state is tied to the parent + +curser, means it the parent unloads or loads configs, the sub state will +do so as well. +@class function +@name Cursor.substate +@return UCI state cursor tied to the parent cursor +]] + +---[[ +Add an anonymous section. + +@class function +@name Cursor.add +@param config UCI config +@param type UCI section type +@return Name of created section +]] + +---[[ +Get a table of saved but uncommitted changes. + +@class function +@name Cursor.changes +@param config UCI config +@return Table of changes +@see Cursor.save +]] + +---[[ +Commit saved changes. + +@class function +@name Cursor.commit +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.revert +@see Cursor.save +]] + +---[[ +Deletes a section or an option. + +@class function +@name Cursor.delete +@param config UCI config +@param section UCI section name +@param option UCI option (optional) +@return Boolean whether operation succeeded +]] + +---[[ +Call a function for every section of a certain type. + +@class function +@name Cursor.foreach +@param config UCI config +@param type UCI section type +@param callback Function to be called +@return Boolean whether operation succeeded +]] + +---[[ +Get a section type or an option + +@class function +@name Cursor.get +@param config UCI config +@param section UCI section name +@param option UCI option (optional) +@return UCI value +]] + +---[[ +Get all sections of a config or all values of a section. + +@class function +@name Cursor.get_all +@param config UCI config +@param section UCI section name (optional) +@return Table of UCI sections or table of UCI values +]] + +---[[ +Manually load a config. + +@class function +@name Cursor.load +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.save +@see Cursor.unload +]] + +---[[ +Revert saved but uncommitted changes. + +@class function +@name Cursor.revert +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.commit +@see Cursor.save +]] + +---[[ +Saves changes made to a config to make them committable. + +@class function +@name Cursor.save +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.load +@see Cursor.unload +]] + +---[[ +Set a value or create a named section. + +@class function +@name Cursor.set +@param config UCI config +@param section UCI section name +@param option UCI option or UCI section type +@param value UCI value or nil if you want to create a section +@return Boolean whether operation succeeded +]] + +---[[ +Get the configuration directory. + +@class function +@name Cursor.get_confdir +@return Configuration directory +]] + +---[[ +Get the directory for uncomitted changes. + +@class function +@name Cursor.get_savedir +@return Save directory +]] + +---[[ +Set the configuration directory. + +@class function +@name Cursor.set_confdir +@param directory UCI configuration directory +@return Boolean whether operation succeeded +]] + +---[[ +Set the directory for uncommited changes. + +@class function +@name Cursor.set_savedir +@param directory UCI changes directory +@return Boolean whether operation succeeded +]] + +---[[ +Discard changes made to a config. + +@class function +@name Cursor.unload +@param config UCI config +@return Boolean whether operation succeeded +@see Cursor.load +@see Cursor.save +]] + diff --git a/modules/luci-base/luasrc/sys.lua b/modules/luci-base/luasrc/sys.lua index 1e594e1c8..3977da3ed 100644 --- a/modules/luci-base/luasrc/sys.lua +++ b/modules/luci-base/luasrc/sys.lua @@ -16,27 +16,14 @@ local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select ---- LuCI Linux and POSIX system utilities. module "luci.sys" ---- Execute a given shell command and return the error code --- @class function --- @name call --- @param ... Command to call --- @return Error code of the command function call(...) return os.execute(...) / 256 end ---- Execute a given shell command and capture its standard output --- @class function --- @name exec --- @param command Command to call --- @return String containg the return the output of the command exec = luci.util.exec ---- Retrieve information about currently mounted file systems. --- @return Table containing mount information function mounts() local data = {} local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"} @@ -82,20 +69,11 @@ function mounts() return data end ---- Retrieve environment variables. If no variable is given then a table -- containing the whole environment is returned otherwise this function returns -- the corresponding string value for the given name or nil if no such variable -- exists. --- @class function --- @name getenv --- @param var Name of the environment variable to retrieve (optional) --- @return String containg the value of the specified variable --- @return Table containing all variables if no variable name is given getenv = nixio.getenv ---- Get or set the current hostname. --- @param String containing a new hostname to set (optional) --- @return String containing the system hostname function hostname(newname) if type(newname) == "string" and #newname > 0 then fs.writefile( "/proc/sys/kernel/hostname", newname ) @@ -105,11 +83,6 @@ function hostname(newname) end end ---- Returns the contents of a documented referred by an URL. --- @param url The URL to retrieve --- @param stream Return a stream instead of a buffer --- @param target Directly write to target file name --- @return String containing the contents of given the URL function httpget(url, stream, target) if not target then local source = stream and io.popen or luci.util.exec @@ -120,46 +93,30 @@ function httpget(url, stream, target) end end ---- Initiate a system reboot. --- @return Return value of os.execute() function reboot() return os.execute("reboot >/dev/null 2>&1") end ---- Retrieves the output of the "logread" command. --- @return String containing the current log buffer function syslog() return luci.util.exec("logread") end ---- Retrieves the output of the "dmesg" command. --- @return String containing the current log buffer function dmesg() return luci.util.exec("dmesg") end ---- Generates a random id with specified length. --- @param bytes Number of bytes for the unique id --- @return String containing hex encoded id function uniqueid(bytes) local rand = fs.readfile("/dev/urandom", bytes) return rand and nixio.bin.hexlify(rand) end ---- Returns the current system uptime stats. --- @return String containing total uptime in seconds function uptime() return nixio.sysinfo().uptime end ---- LuCI system utilities / network related functions. --- @class module --- @name luci.sys.net net = {} ---- Returns the current arp-table entries as two-dimensional table. --- @return Table of table containing the current arp entries. -- The following fields are defined for arp entry objects: -- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } function net.arptable(callback) @@ -269,8 +226,6 @@ local function _nethints(what, callback) end end ---- Returns a two-dimensional table of mac address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "mac", "name" ] function net.mac_hints(callback) @@ -293,8 +248,6 @@ function net.mac_hints(callback) end end ---- Returns a two-dimensional table of IPv4 address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "ip", "name" ] function net.ipv4_hints(callback) @@ -317,8 +270,6 @@ function net.ipv4_hints(callback) end end ---- Returns a two-dimensional table of IPv6 address hints. --- @return Table of table containing known hosts from various sources. -- Each entry contains the values in the following order: -- [ "ip", "name" ] function net.ipv6_hints(callback) @@ -341,8 +292,6 @@ function net.ipv6_hints(callback) end end ---- Returns conntrack information --- @return Table with the currently tracked IP connections function net.conntrack(callback) local connt = {} if fs.access("/proc/net/nf_conntrack", "r") then @@ -387,57 +336,6 @@ function net.conntrack(callback) return connt end ---- Determine the current IPv4 default route. If multiple default routes exist, --- return the one with the lowest metric. --- @return Table with the properties of the current default route. --- The following fields are defined: --- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", --- "flags", "device" } -function net.defaultroute() - local route - - net.routes(function(rt) - if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then - route = rt - end - end) - - return route -end - ---- Determine the current IPv6 default route. If multiple default routes exist, --- return the one with the lowest metric. --- @return Table with the properties of the current default route. --- The following fields are defined: --- { "source", "dest", "nexthop", "metric", "refcount", "usecount", --- "flags", "device" } -function net.defaultroute6() - local route - - net.routes6(function(rt) - if rt.dest:prefix() == 0 and rt.device ~= "lo" and - (not route or route.metric > rt.metric) - then - route = rt - end - end) - - if not route then - local global_unicast = luci.ip.IPv6("2000::/3") - net.routes6(function(rt) - if rt.dest:equal(global_unicast) and - (not route or route.metric > rt.metric) - then - route = rt - end - end) - end - - return route -end - ---- Determine the names of available network interfaces. --- @return Table containing all current interface names function net.devices() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -449,8 +347,6 @@ function net.devices() end ---- Return information about available network interfaces. --- @return Table containing all current interface names and their information function net.deviceinfo() local devs = {} for k, v in ipairs(nixio.getifaddrs()) do @@ -479,21 +375,6 @@ function net.deviceinfo() end --- Determine the MAC address belonging to the given IP address. --- @param ip IPv4 address --- @return String containing the MAC address or nil if it cannot be found -function net.ip4mac(ip) - local mac = nil - net.arptable(function(e) - if e["IP address"] == ip then - mac = e["HW address"] - end - end) - return mac -end - ---- Returns the current kernel routing table entries. --- @return Table of tables with properties of the corresponding routes. -- The following fields are defined for route entry tables: -- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", -- "flags", "device" } @@ -539,8 +420,6 @@ function net.routes(callback) return routes end ---- Returns the current ipv6 kernel routing table entries. --- @return Table of tables with properties of the corresponding routes. -- The following fields are defined for route entry tables: -- { "source", "dest", "nexthop", "metric", "refcount", "usecount", -- "flags", "device" } @@ -602,30 +481,18 @@ function net.routes6(callback) end end ---- Tests whether the given host responds to ping probes. --- @param host String containing a hostname or IPv4 address --- @return Number containing 0 on success and >= 1 on error function net.pingtest(host) return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") end ---- LuCI system utilities / process related functions. --- @class module --- @name luci.sys.process process = {} ---- Get the current process id. --- @class function --- @name process.info --- @return Number containing the current pid function process.info(key) local s = {uid = nixio.getuid(), gid = nixio.getgid()} return not key and s or s[key] end ---- Retrieve information about currently running processes. --- @return Table containing process information function process.list() local data = {} local k @@ -658,51 +525,22 @@ function process.list() return data end ---- Set the gid of a process identified by given pid. --- @param gid Number containing the Unix group id --- @return Boolean indicating successful operation --- @return String containing the error message if failed --- @return Number containing the error code if failed function process.setgroup(gid) return nixio.setgid(gid) end ---- Set the uid of a process identified by given pid. --- @param uid Number containing the Unix user id --- @return Boolean indicating successful operation --- @return String containing the error message if failed --- @return Number containing the error code if failed function process.setuser(uid) return nixio.setuid(uid) end ---- Send a signal to a process identified by given pid. --- @class function --- @name process.signal --- @param pid Number containing the process id --- @param sig Signal to send (default: 15 [SIGTERM]) --- @return Boolean indicating successful operation --- @return Number containing the error code if failed process.signal = nixio.kill ---- LuCI system utilities / user related functions. --- @class module --- @name luci.sys.user user = {} ---- Retrieve user informations for given uid. --- @class function --- @name getuser --- @param uid Number containing the Unix user id --- @return Table containing the following fields: -- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" } user.getuser = nixio.getpw ---- Retrieve the current user password hash. --- @param username String containing the username to retrieve the password for --- @return String containing the hash or nil if no password is set. --- @return Password database entry function user.getpasswd(username) local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username) local pwh = pwe and (pwe.pwdp or pwe.passwd) @@ -713,10 +551,6 @@ function user.getpasswd(username) end end ---- Test whether given string matches the password of a given system user. --- @param username String containing the Unix user name --- @param pass String containing the password to compare --- @return Boolean indicating wheather the passwords are equal function user.checkpasswd(username, pass) local pwh, pwe = user.getpasswd(username) if pwe then @@ -725,10 +559,6 @@ function user.checkpasswd(username, pass) return false end ---- Change the password of given user. --- @param username String containing the Unix user name --- @param password String containing the password to compare --- @return Number containing 0 on success and >= 1 on error function user.setpasswd(username, password) if password then password = password:gsub("'", [['"'"']]) @@ -745,14 +575,8 @@ function user.setpasswd(username, password) end ---- LuCI system utilities / wifi related functions. --- @class module --- @name luci.sys.wifi wifi = {} ---- Get wireless information for given interface. --- @param ifname String containing the interface name --- @return A wrapped iwinfo object instance function wifi.getiwinfo(ifname) local stat, iwinfo = pcall(require, "iwinfo") @@ -798,14 +622,9 @@ function wifi.getiwinfo(ifname) end ---- LuCI system utilities / init related functions. --- @class module --- @name luci.sys.init init = {} init.dir = "/etc/init.d/" ---- Get the names of all installed init scripts --- @return Table containing the names of all inistalled init scripts function init.names() local names = { } for name in fs.glob(init.dir.."*") do @@ -814,9 +633,6 @@ function init.names() return names end ---- Get the index of he given init script --- @param name Name of the init script --- @return Numeric index value function init.index(name) if fs.access(init.dir..name) then return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null" @@ -830,37 +646,22 @@ local function init_action(action, name) end end ---- Test whether the given init script is enabled --- @param name Name of the init script --- @return Boolean indicating whether init is enabled function init.enabled(name) return (init_action("enabled", name) == 0) end ---- Enable the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.enable(name) return (init_action("enable", name) == 1) end ---- Disable the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.disable(name) return (init_action("disable", name) == 0) end ---- Start the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.start(name) return (init_action("start", name) == 0) end ---- Stop the given init script --- @param name Name of the init script --- @return Boolean indicating success function init.stop(name) return (init_action("stop", name) == 0) end diff --git a/modules/luci-base/luasrc/sys.luadoc b/modules/luci-base/luasrc/sys.luadoc new file mode 100644 index 000000000..72a16a1ab --- /dev/null +++ b/modules/luci-base/luasrc/sys.luadoc @@ -0,0 +1,396 @@ +---[[ +LuCI Linux and POSIX system utilities. + +module "luci.sys" +]] + +---[[ +Execute a given shell command and return the error code + +@class function +@name call +@param ... Command to call +@return Error code of the command +]] + +---[[ +Execute a given shell command and capture its standard output + +@class function +@name exec +@param command Command to call +@return String containg the return the output of the command +]] + +---[[ +Retrieve information about currently mounted file systems. + +@class function +@name mounts +@return Table containing mount information +]] + +---[[ +Retrieve environment variables. If no variable is given then a table + +containing the whole environment is returned otherwise this function returns +the corresponding string value for the given name or nil if no such variable +exists. +@class function +@name getenv +@param var Name of the environment variable to retrieve (optional) +@return String containg the value of the specified variable +@return Table containing all variables if no variable name is given +]] + +---[[ +Get or set the current hostname. + +@class function +@name hostname +@param String containing a new hostname to set (optional) +@return String containing the system hostname +]] + +---[[ +Returns the contents of a documented referred by an URL. + +@class function +@name httpget +@param url The URL to retrieve +@param stream Return a stream instead of a buffer +@param target Directly write to target file name +@return String containing the contents of given the URL +]] + +---[[ +Initiate a system reboot. + +@class function +@name reboot +@return Return value of os.execute() +]] + +---[[ +Retrieves the output of the "logread" command. + +@class function +@name syslog +@return String containing the current log buffer +]] + +---[[ +Retrieves the output of the "dmesg" command. + +@class function +@name dmesg +@return String containing the current log buffer +]] + +---[[ +Generates a random id with specified length. + +@class function +@name uniqueid +@param bytes Number of bytes for the unique id +@return String containing hex encoded id +]] + +---[[ +Returns the current system uptime stats. + +@class function +@name uptime +@return String containing total uptime in seconds +]] + +---[[ +LuCI system utilities / network related functions. + +@class module +@name luci.sys.net +]] + +---[[ +Returns the current arp-table entries as two-dimensional table. + +@class function +@name net.arptable +@return Table of table containing the current arp entries. +-- The following fields are defined for arp entry objects: +-- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } +]] + +---[[ +Returns a two-dimensional table of mac address hints. + +@class function +@name net.mac_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "mac", "name" ] +]] + +---[[ +Returns a two-dimensional table of IPv4 address hints. + +@class function +@name net.ipv4_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "ip", "name" ] +]] + +---[[ +Returns a two-dimensional table of IPv6 address hints. + +@class function +@name net.ipv6_hints +@return Table of table containing known hosts from various sources. + Each entry contains the values in the following order: + [ "ip", "name" ] +]] + +---[[ +Returns conntrack information + +@class function +@name net.conntrack +@return Table with the currently tracked IP connections +]] + +---[[ +Determine the names of available network interfaces. + +@class function +@name net.devices +@return Table containing all current interface names +]] + +---[[ +Return information about available network interfaces. + +@class function +@name net.deviceinfo +@return Table containing all current interface names and their information +]] + +---[[ +Returns the current kernel routing table entries. + +@class function +@name net.routes +@return Table of tables with properties of the corresponding routes. +-- The following fields are defined for route entry tables: +-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", +-- "flags", "device" } +]] + +---[[ +Returns the current ipv6 kernel routing table entries. + +@class function +@name net.routes6 +@return Table of tables with properties of the corresponding routes. +-- The following fields are defined for route entry tables: +-- { "source", "dest", "nexthop", "metric", "refcount", "usecount", +-- "flags", "device" } +]] + +---[[ +Tests whether the given host responds to ping probes. + +@class function +@name net.pingtest +@param host String containing a hostname or IPv4 address +@return Number containing 0 on success and >= 1 on error +]] + +---[[ +LuCI system utilities / process related functions. + +@class module +@name luci.sys.process +]] + +---[[ +Get the current process id. + +@class function +@name process.info +@return Number containing the current pid +]] + +---[[ +Retrieve information about currently running processes. + +@class function +@name process.list +@return Table containing process information +]] + +---[[ +Set the gid of a process identified by given pid. + +@class function +@name process.setgroup +@param gid Number containing the Unix group id +@return Boolean indicating successful operation +@return String containing the error message if failed +@return Number containing the error code if failed +]] + +---[[ +Set the uid of a process identified by given pid. + +@class function +@name process.setuser +@param uid Number containing the Unix user id +@return Boolean indicating successful operation +@return String containing the error message if failed +@return Number containing the error code if failed +]] + +---[[ +Send a signal to a process identified by given pid. + +@class function +@name process.signal +@param pid Number containing the process id +@param sig Signal to send (default: 15 [SIGTERM]) +@return Boolean indicating successful operation +@return Number containing the error code if failed +]] + +---[[ +LuCI system utilities / user related functions. + +@class module +@name luci.sys.user +]] + +---[[ +Retrieve user informations for given uid. + +@class function +@name getuser +@param uid Number containing the Unix user id +@return Table containing the following fields: +-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" } +]] + +---[[ +Retrieve the current user password hash. + +@class function +@name user.getpasswd +@param username String containing the username to retrieve the password for +@return String containing the hash or nil if no password is set. +@return Password database entry +]] + +---[[ +Test whether given string matches the password of a given system user. + +@class function +@name user.checkpasswd +@param username String containing the Unix user name +@param pass String containing the password to compare +@return Boolean indicating wheather the passwords are equal +]] + +---[[ +Change the password of given user. + +@class function +@name user.setpasswd +@param username String containing the Unix user name +@param password String containing the password to compare +@return Number containing 0 on success and >= 1 on error +]] + +---[[ +LuCI system utilities / wifi related functions. + +@class module +@name luci.sys.wifi +]] + +---[[ +Get wireless information for given interface. + +@class function +@name wifi.getiwinfo +@param ifname String containing the interface name +@return A wrapped iwinfo object instance +]] + +---[[ +LuCI system utilities / init related functions. + +@class module +@name luci.sys.init +]] + +---[[ +Get the names of all installed init scripts + +@class function +@name init.names +@return Table containing the names of all inistalled init scripts +]] + +---[[ +Get the index of he given init script + +@class function +@name init.index +@param name Name of the init script +@return Numeric index value +]] + +---[[ +Test whether the given init script is enabled + +@class function +@name init.enabled +@param name Name of the init script +@return Boolean indicating whether init is enabled +]] + +---[[ +Enable the given init script + +@class function +@name init.enable +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Disable the given init script + +@class function +@name init.disable +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Start the given init script + +@class function +@name init.start +@param name Name of the init script +@return Boolean indicating success +]] + +---[[ +Stop the given init script + +@class function +@name init.stop +@param name Name of the init script +@return Boolean indicating success +]] + diff --git a/modules/luci-base/luasrc/sys/iptparser.lua b/modules/luci-base/luasrc/sys/iptparser.lua index 6715937c6..2b81e0ee3 100644 --- a/modules/luci-base/luasrc/sys/iptparser.lua +++ b/modules/luci-base/luasrc/sys/iptparser.lua @@ -21,15 +21,8 @@ luci.ip = require "luci.ip" local tonumber, ipairs, table = tonumber, ipairs, table ---- LuCI iptables parser and query library --- @cstyle instance module("luci.sys.iptparser") ---- Create a new iptables parser object. --- @class function --- @name IptParser --- @param family Number specifying the address family. 4 for IPv4, 6 for IPv6 --- @return IptParser instance IptParser = luci.util.class() function IptParser.__init__( self, family ) @@ -50,7 +43,6 @@ function IptParser.__init__( self, family ) self:_parse_rules() end ---- Find all firewall rules that match the given criteria. Expects a table with -- search criteria as only argument. If args is nil or an empty table then all -- rules will be returned. -- @@ -108,8 +100,6 @@ end -- This will match all rules with target "-j REJECT", -- protocol "-p tcp" (or "-p all") -- and the option "--reject-with tcp-reset". --- @params args Table containing the search arguments (optional) --- @return Table of matching rule tables function IptParser.find( self, args ) local args = args or { } @@ -205,9 +195,7 @@ function IptParser.find( self, args ) end ---- Rebuild the internal lookup table, for example when rules have changed -- through external commands. --- @return nothing function IptParser.resync( self ) self._rules = { } self._chain = nil @@ -215,16 +203,11 @@ function IptParser.resync( self ) end ---- Find the names of all tables. --- @return Table of table names. function IptParser.tables( self ) return self._tables end ---- Find the names of all chains within the given table name. --- @param table String containing the table name --- @return Table of chain names in the order they occur. function IptParser.chains( self, table ) local lookup = { } local chains = { } @@ -238,19 +221,12 @@ function IptParser.chains( self, table ) end ---- Return the given firewall chain within the given table name. --- @param table String containing the table name --- @param chain String containing the chain name --- @return Table containing the fields "policy", "packets", "bytes" -- and "rules". The "rules" field is a table of rule tables. function IptParser.chain( self, table, chain ) return self._chains[table:lower()] and self._chains[table:lower()][chain] end ---- Test whether the given target points to a custom chain. --- @param target String containing the target action --- @return Boolean indicating whether target is a custom chain. function IptParser.is_custom_target( self, target ) for _, r in ipairs(self._rules) do if r.chain == target then diff --git a/modules/luci-base/luasrc/sys/iptparser.luadoc b/modules/luci-base/luasrc/sys/iptparser.luadoc new file mode 100644 index 000000000..071e7d52e --- /dev/null +++ b/modules/luci-base/luasrc/sys/iptparser.luadoc @@ -0,0 +1,69 @@ +---[[ +LuCI iptables parser and query library + +@cstyle instance +]] +module "luci.sys.iptparser" + +---[[ +Create a new iptables parser object. + +@class function +@name IptParser +@param family Number specifying the address family. 4 for IPv4, 6 for IPv6 +@return IptParser instance +]] + +---[[ +Find all firewall rules that match the given criteria. Expects a table with + +search criteria as only argument. If args is nil or an empty table then all +rules will be returned. +]] + +---[[ +Rebuild the internal lookup table, for example when rules have changed + +through external commands. +@class function +@name IptParser.resync +@return nothing +]] + +---[[ +Find the names of all tables. + +@class function +@name IptParser.tables +@return Table of table names. +]] + +---[[ +Find the names of all chains within the given table name. + +@class function +@name IptParser.chains +@param table String containing the table name +@return Table of chain names in the order they occur. +]] + +---[[ +Return the given firewall chain within the given table name. + +@class function +@name IptParser.chain +@param table String containing the table name +@param chain String containing the chain name +@return Table containing the fields "policy", "packets", "bytes" +-- and "rules". The "rules" field is a table of rule tables. +]] + +---[[ +Test whether the given target points to a custom chain. + +@class function +@name IptParser.is_custom_target +@param target String containing the target action +@return Boolean indicating whether target is a custom chain. +]] + diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua index 633749669..97a3608ea 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua @@ -86,7 +86,7 @@ TZ = { { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' }, { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' }, { 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' }, - { 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' }, + { 'America/Cancun', 'EST5' }, { 'America/Caracas', 'VET4:30' }, { 'America/Cayenne', 'GFT3' }, { 'America/Cayman', 'EST5' }, @@ -178,7 +178,7 @@ TZ = { { 'America/Rio Branco', 'ACT5' }, { 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' }, { 'America/Santarem', 'BRT3' }, - { 'America/Santiago', 'CLT4CLST,M9.1.6/24,M4.4.6/24' }, + { 'America/Santiago', 'CLT3' }, { 'America/Santo Domingo', 'AST4' }, { 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' }, { 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' }, @@ -207,7 +207,7 @@ TZ = { { 'Antarctica/Macquarie', 'MIST-11' }, { 'Antarctica/Mawson', 'MAWT-5' }, { 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' }, - { 'Antarctica/Palmer', 'CLT4CLST,M9.1.6/24,M4.4.6/24' }, + { 'Antarctica/Palmer', 'CLT3' }, { 'Antarctica/Rothera', 'ROTT3' }, { 'Antarctica/Syowa', 'SYOT-3' }, { 'Antarctica/Troll', 'UTC0CEST-2,M3.5.0/1,M10.5.0/3' }, @@ -384,7 +384,7 @@ TZ = { { 'Pacific/Bougainville', 'BST-11' }, { 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' }, { 'Pacific/Chuuk', 'CHUT-10' }, - { 'Pacific/Easter', 'EAST6EASST,M9.1.6/22,M4.4.6/22' }, + { 'Pacific/Easter', 'EAST5' }, { 'Pacific/Efate', 'VUT-11' }, { 'Pacific/Enderbury', 'PHOT-13' }, { 'Pacific/Fakaofo', 'TKT-13' }, diff --git a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua index facfd0c8b..c8c908b6e 100644 --- a/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua +++ b/modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua @@ -45,8 +45,7 @@ OFFSET = { uyst = -7200, -- UYST fnt = -7200, -- FNT srt = -10800, -- SRT - clt = -14400, -- CLT - clst = -10800, -- CLST + clt = -10800, -- CLT egt = -3600, -- EGT egst = 0, -- EGST nst = -12600, -- NST @@ -135,8 +134,7 @@ OFFSET = { chast = 45900, -- CHAST chadt = 49500, -- CHADT chut = 36000, -- CHUT - east = -21600, -- EAST - easst = -18000, -- EASST + east = -18000, -- EAST vut = 39600, -- VUT phot = 46800, -- PHOT tkt = 46800, -- TKT diff --git a/modules/luci-base/luasrc/tools/webadmin.lua b/modules/luci-base/luasrc/tools/webadmin.lua index 9adac29bf..8273175de 100644 --- a/modules/luci-base/luasrc/tools/webadmin.lua +++ b/modules/luci-base/luasrc/tools/webadmin.lua @@ -1,11 +1,12 @@ -- Copyright 2008 Steven Barth --- Copyright 2008 Jo-Philipp Wich +-- Copyright 2008-2015 Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. module("luci.tools.webadmin", package.seeall) -local uci = require("luci.model.uci") -require("luci.sys") -require("luci.ip") + +local util = require "luci.util" +local uci = require "luci.model.uci" +local ip = require "luci.ip" function byte_format(byte) local suff = {"B", "KB", "MB", "GB", "TB"} @@ -47,49 +48,6 @@ function date_format(secs) end end -function network_get_addresses(net) - local state = uci.cursor_state() - state:load("network") - local addr = {} - local ipv4 = state:get("network", net, "ipaddr") - local mav4 = state:get("network", net, "netmask") - local ipv6 = state:get("network", net, "ip6addr") - - if ipv4 and #ipv4 > 0 then - if mav4 and #mav4 == 0 then mav4 = nil end - - ipv4 = luci.ip.IPv4(ipv4, mav4) - - if ipv4 then - table.insert(addr, ipv4:string()) - end - end - - if ipv6 then - table.insert(addr, ipv6) - end - - state:foreach("network", "alias", - function (section) - if section.interface == net then - if section.ipaddr and section.netmask then - local ipv4 = luci.ip.IPv4(section.ipaddr, section.netmask) - - if ipv4 then - table.insert(addr, ipv4:string()) - end - end - - if section.ip6addr then - table.insert(addr, section.ip6addr) - end - end - end - ) - - return addr -end - function cbi_add_networks(field) uci.cursor():foreach("network", "interface", function (section) @@ -102,29 +60,12 @@ function cbi_add_networks(field) end function cbi_add_knownips(field) - for i, dataset in ipairs(luci.sys.net.arptable()) do - field:value(dataset["IP address"]) - end -end - -function network_get_zones(net) - local state = uci.cursor_state() - if not state:load("firewall") then - return nil - end - - local zones = {} - - state:foreach("firewall", "zone", - function (section) - local znet = section.network or section.name - if luci.util.contains(luci.util.split(znet, " "), net) then - table.insert(zones, section.name) - end + local _, n + for _, n in ipairs(ip.neighbors({ family = 4 })) do + if n.dest then + field:value(n.dest:string()) end - ) - - return zones + end end function firewall_find_zone(name) @@ -142,21 +83,23 @@ function firewall_find_zone(name) end function iface_get_network(iface) - local state = uci.cursor_state() - state:load("network") - local net - - state:foreach("network", "interface", - function (section) - local ifname = state:get( - "network", section[".name"], "ifname" - ) - - if iface == ifname then - net = section[".name"] + local link = ip.link(tostring(iface)) + if link.master then + iface = link.master + end + + local cur = uci.cursor() + local dump = util.ubus("network.interface", "dump", { }) + if dump then + local _, net + for _, net in ipairs(dump.interface) do + if net.l3_device == iface or net.device == iface then + -- cross check with uci to filter out @name style aliases + local uciname = cur:get("network", net.interface, "ifname") + if not uciname or uciname:sub(1, 1) ~= "@" then + return net.interface + end end end - ) - - return net + end end diff --git a/modules/luci-base/luasrc/util.lua b/modules/luci-base/luasrc/util.lua index 42de3dd72..8b28b1752 100644 --- a/modules/luci-base/luasrc/util.lua +++ b/modules/luci-base/luasrc/util.lua @@ -20,7 +20,6 @@ local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring local require, pcall, xpcall = require, pcall, xpcall local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit ---- LuCI utility functions. module "luci.util" -- @@ -54,7 +53,6 @@ local function _instantiate(class, ...) return inst end ---- Create a Class object (Python-style object model). -- The class object can be instantiated by calling itself. -- Any class functions or shared parameters can be attached to this object. -- Attaching a table to the class object makes this table shared between @@ -64,10 +62,6 @@ end -- to the __init__ function of this class - if such a function exists. -- The __init__ function must be used to set any object parameters that are not shared -- with other objects of this class. Any return values will be ignored. --- @param base The base class to inherit from (optional) --- @return A class object --- @see instanceof --- @see clone function class(base) return setmetatable({}, { __call = _instantiate, @@ -75,12 +69,6 @@ function class(base) }) end ---- Test whether the given object is an instance of the given class. --- @param object Object instance --- @param class Class object to test against --- @return Boolean indicating whether the object is an instance --- @see class --- @see clone function instanceof(object, class) local meta = getmetatable(object) while meta and meta.__index do @@ -117,10 +105,8 @@ local tl_meta = { end } ---- Create a new or get an already existing thread local store associated with -- the current active coroutine. A thread local store is private a table object -- whose values can't be accessed from outside of the running coroutine. --- @return Table value representing the corresponding thread local store function threadlocal(tbl) return setmetatable(tbl or {}, tl_meta) end @@ -130,17 +116,10 @@ end -- Debugging routines -- ---- Write given object to stderr. --- @param obj Value to write to stderr --- @return Boolean indicating whether the write operation was successful function perror(obj) return io.stderr:write(tostring(obj) .. "\n") end ---- Recursively dumps a table to stdout, useful for testing and debugging. --- @param t Table value to dump --- @param maxdepth Maximum depth --- @return Always nil function dumptable(t, maxdepth, i, seen) i = i or 0 seen = seen or setmetatable({}, {__mode="k"}) @@ -163,31 +142,19 @@ end -- String and data manipulation routines -- ---- Create valid XML PCDATA from given string. --- @param value String value containing the data to escape --- @return String value containing the escaped data function pcdata(value) return value and tparser.pcdata(tostring(value)) end ---- Strip HTML tags from given string. --- @param value String containing the HTML text --- @return String with HTML tags stripped of function striptags(value) return value and tparser.striptags(tostring(value)) end ---- Splits given string on a defined separator sequence and return a table -- containing the resulting substrings. The optional max parameter specifies -- the number of bytes to process, regardless of the actual length of the given -- string. The optional last parameter, regex, specifies whether the separator -- sequence is interpreted as regular expression. --- @param str String value containing the data to split up --- @param pat String with separator pattern (optional, defaults to "\n") --- @param max Maximum times to split (optional) --- @param regex Boolean indicating whether to interpret the separator -- pattern as regular expression (optional, default is false) --- @return Table containing the resulting substrings function split(str, pat, max, regex) pat = pat or "\n" max = max or #str @@ -221,29 +188,19 @@ function split(str, pat, max, regex) return t end ---- Remove leading and trailing whitespace from given string value. --- @param str String value containing whitespace padded data --- @return String value with leading and trailing space removed function trim(str) return (str:gsub("^%s*(.-)%s*$", "%1")) end ---- Count the occurences of given substring in given string. --- @param str String to search in --- @param pattern String containing pattern to find --- @return Number of found occurences function cmatch(str, pat) local count = 0 for _ in str:gmatch(pat) do count = count + 1 end return count end ---- Return a matching iterator for the given value. The iterator will return -- one token per invocation, the tokens are separated by whitespace. If the -- input value is a table, it is transformed into a string first. A nil value -- will result in a valid interator which aborts with the first invocation. --- @param val The value to scan (table, string or nil) --- @return Iterator which returns one token per call function imatch(v) if type(v) == "table" then local k = nil @@ -268,7 +225,6 @@ function imatch(v) return function() end end ---- Parse certain units from the given string and return the canonical integer -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant. -- Recognized units are: -- o "y" - one year (60*60*24*366) @@ -283,8 +239,6 @@ end -- o "kib" - one si kilobyte (1000) -- o "mib" - one si megabyte (1000*1000) -- o "gib" - one si gigabyte (1000*1000*1000) --- @param ustr String containing a numerical value with trailing unit --- @return Number containing the canonical value function parse_units(ustr) local val = 0 @@ -336,10 +290,6 @@ string.cmatch = cmatch string.parse_units = parse_units ---- Appends numerically indexed tables or single objects to a given table. --- @param src Target table --- @param ... Objects to insert --- @return Target table function append(src, ...) for i, a in ipairs({...}) do if type(a) == "table" then @@ -353,19 +303,10 @@ function append(src, ...) return src end ---- Combines two or more numerically indexed tables and single objects into one table. --- @param tbl1 Table value to combine --- @param tbl2 Table value to combine --- @param ... More tables to combine --- @return Table value containing all values of given tables function combine(...) return append({}, ...) end ---- Checks whether the given table contains the given value. --- @param table Table value --- @param value Value to search within the given table --- @return Boolean indicating whether the given value occurs within table function contains(table, value) for k, v in pairs(table) do if value == v then @@ -375,20 +316,13 @@ function contains(table, value) return false end ---- Update values in given table with the values from the second given table. -- Both table are - in fact - merged together. --- @param t Table which should be updated --- @param updates Table containing the values to update --- @return Always nil function update(t, updates) for k, v in pairs(updates) do t[k] = v end end ---- Retrieve all keys of given associative table. --- @param t Table to extract keys from --- @return Sorted table containing the keys function keys(t) local keys = { } if t then @@ -399,10 +333,6 @@ function keys(t) return keys end ---- Clones the given object and return it's copy. --- @param object Table value to clone --- @param deep Boolean indicating whether to do recursive cloning --- @return Cloned table value function clone(object, deep) local copy = {} @@ -417,8 +347,6 @@ function clone(object, deep) end ---- Create a dynamic table which automatically creates subtables. --- @return Dynamic Table function dtable() return setmetatable({}, { __index = function(tbl, key) @@ -457,12 +385,7 @@ function _serialize_table(t, seen) return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data end ---- Recursively serialize given data to lua code, suitable for restoring -- with loadstring(). --- @param val Value containing the data to serialize --- @return String value containing the serialized code --- @see restore_data --- @see get_bytecode function serialize_data(val, seen) seen = seen or setmetatable({}, {__mode="k"}) @@ -483,11 +406,6 @@ function serialize_data(val, seen) end end ---- Restore data previously serialized with serialize_data(). --- @param str String containing the data to restore --- @return Value containing the restored data structure --- @see serialize_data --- @see get_bytecode function restore_data(str) return loadstring("return " .. str)() end @@ -497,10 +415,7 @@ end -- Byte code manipulation routines -- ---- Return the current runtime bytecode of the given data. The byte code -- will be stripped before it is returned. --- @param val Value to return as bytecode --- @return String value containing the bytecode of the given data function get_bytecode(val) local code @@ -513,11 +428,8 @@ function get_bytecode(val) return code -- and strip_bytecode(code) end ---- Strips unnescessary lua bytecode from given string. Information like line -- numbers and debugging numbers will be discarded. Original version by -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html) --- @param code String value containing the original lua byte code --- @return String value containing the stripped lua byte code function strip_bytecode(code) local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12) local subint @@ -607,27 +519,17 @@ function _sortiter( t, f ) end end ---- Return a key, value iterator which returns the values sorted according to -- the provided callback function. --- @param t The table to iterate --- @param f A callback function to decide the order of elements --- @return Function value containing the corresponding iterator function spairs(t,f) return _sortiter( t, f ) end ---- Return a key, value iterator for the given table. -- The table pairs are sorted by key. --- @param t The table to iterate --- @return Function value containing the corresponding iterator function kspairs(t) return _sortiter( t ) end ---- Return a key, value iterator for the given table. -- The table pairs are sorted by value. --- @param t The table to iterate --- @return Function value containing the corresponding iterator function vspairs(t) return _sortiter( t, function (a,b) return t[a] < t[b] end ) end @@ -637,15 +539,10 @@ end -- System utility functions -- ---- Test whether the current system is operating in big endian mode. --- @return Boolean value indicating whether system is big endian function bigendian() return string.byte(string.dump(function() end), 7) == 0 end ---- Execute given commandline and gather stdout. --- @param command String containing command to execute --- @return String containing the command's stdout function exec(command) local pp = io.popen(command) local data = pp:read("*a") @@ -654,9 +551,6 @@ function exec(command) return data end ---- Return a line-buffered iterator over the output of given command. --- @param command String containing the command to execute --- @return Iterator function execi(command) local pp = io.popen(command) @@ -687,11 +581,6 @@ function execl(command) return data end ---- Issue an ubus call. --- @param object String containing the ubus object to call --- @param method String containing the ubus method to call --- @param values Table containing the values to pass --- @return Table containin the ubus result function ubus(object, method, data) if not _ubus_connection then _ubus_connection = _ubus.connect() @@ -710,8 +599,60 @@ function ubus(object, method, data) end end ---- Returns the absolute path to LuCI base directory. --- @return String containing the directory path +function serialize_json(x, cb) + local rv, push = nil, cb + if not push then + rv = { } + push = function(tok) rv[#rv+1] = tok end + end + + if x == nil then + push("null") + elseif type(x) == "table" then + -- test if table is array like + local k, v + local n1, n2 = 0, 0 + for k in pairs(x) do n1 = n1 + 1 end + for k in ipairs(x) do n2 = n2 + 1 end + + if n1 == n2 and n1 > 0 then + push("[") + for k = 1, n2 do + if k > 1 then + push(",") + end + serialize_json(x[k], push) + end + push("]") + else + push("{") + for k, v in pairs(x) do + push("%q:" % tostring(k)) + serialize_json(v, push) + if next(x, k) then + push(",") + end + end + push("}") + end + elseif type(x) == "number" or type(x) == "boolean" then + if (x ~= x) then + -- NaN is the only value that doesn't equal to itself. + push("Number.NaN") + else + push(tostring(x)) + end + else + push('"%s"' % tostring(x):gsub('["%z\1-\31]', + function(c) return '\\u%04x' % c:byte(1) end)) + end + + if not cb then + return table.concat(rv, "") + end +end + + function libpath() return require "nixio.fs".dirname(ldebug.__file__) end @@ -751,11 +692,6 @@ local function copcall_id(trace, ...) return ... end ---- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function --- @param f Lua function to be called protected --- @param err Custom error handler --- @param ... Parameters passed to the function --- @return A boolean whether the function call succeeded and the return -- values of either the function or the error handler function coxpcall(f, err, ...) local res, co = oldpcall(coroutine.create, f) @@ -770,10 +706,6 @@ function coxpcall(f, err, ...) return performResume(err, co, ...) end ---- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function --- @param f Lua function to be called protected --- @param ... Parameters passed to the function --- @return A boolean whether the function call succeeded and the returns -- values of the function or the error object function copcall(f, ...) return coxpcall(f, copcall_id, ...) diff --git a/modules/luci-base/luasrc/util.luadoc b/modules/luci-base/luasrc/util.luadoc new file mode 100644 index 000000000..1c09b7a9a --- /dev/null +++ b/modules/luci-base/luasrc/util.luadoc @@ -0,0 +1,378 @@ +---[[ +LuCI utility functions. + +module "luci.util" +]] + +---[[ +Create a Class object (Python-style object model). + +The class object can be instantiated by calling itself. +Any class functions or shared parameters can be attached to this object. +Attaching a table to the class object makes this table shared between +all instances of this class. For object parameters use the __init__ function. +Classes can inherit member functions and values from a base class. +Class can be instantiated by calling them. All parameters will be passed +to the __init__ function of this class - if such a function exists. +The __init__ function must be used to set any object parameters that are not shared +with other objects of this class. Any return values will be ignored. +@class function +@name class +@param base The base class to inherit from (optional) +@return A class object +@see instanceof +@see clone +]] + +---[[ +Test whether the given object is an instance of the given class. + +@class function +@name instanceof +@param object Object instance +@param class Class object to test against +@return Boolean indicating whether the object is an instance +@see class +@see clone +]] + +---[[ +Create a new or get an already existing thread local store associated with + +the current active coroutine. A thread local store is private a table object +whose values can't be accessed from outside of the running coroutine. +@class function +@name threadlocal +@return Table value representing the corresponding thread local store +]] + +---[[ +Write given object to stderr. + +@class function +@name perror +@param obj Value to write to stderr +@return Boolean indicating whether the write operation was successful +]] + +---[[ +Recursively dumps a table to stdout, useful for testing and debugging. + +@class function +@name dumptable +@param t Table value to dump +@param maxdepth Maximum depth +@return Always nil +]] + +---[[ +Create valid XML PCDATA from given string. + +@class function +@name pcdata +@param value String value containing the data to escape +@return String value containing the escaped data +]] + +---[[ +Strip HTML tags from given string. + +@class function +@name striptags +@param value String containing the HTML text +@return String with HTML tags stripped of +]] + +---[[ +Splits given string on a defined separator sequence and return a table + +containing the resulting substrings. The optional max parameter specifies +the number of bytes to process, regardless of the actual length of the given +string. The optional last parameter, regex, specifies whether the separator +sequence is interpreted as regular expression. +@class function +@name split +@param str String value containing the data to split up +@param pat String with separator pattern (optional, defaults to "\n") +@param max Maximum times to split (optional) +@param regex Boolean indicating whether to interpret the separator +-- pattern as regular expression (optional, default is false) +@return Table containing the resulting substrings +]] + +---[[ +Remove leading and trailing whitespace from given string value. + +@class function +@name trim +@param str String value containing whitespace padded data +@return String value with leading and trailing space removed +]] + +---[[ +Count the occurences of given substring in given string. + +@class function +@name cmatch +@param str String to search in +@param pattern String containing pattern to find +@return Number of found occurences +]] + +---[[ +Return a matching iterator for the given value. The iterator will return + +one token per invocation, the tokens are separated by whitespace. If the +input value is a table, it is transformed into a string first. A nil value +will result in a valid interator which aborts with the first invocation. +@class function +@name imatch +@param val The value to scan (table, string or nil) +@return Iterator which returns one token per call +]] + +---[[ +Parse certain units from the given string and return the canonical integer + +value or 0 if the unit is unknown. Upper- or lower case is irrelevant. +Recognized units are: +-- o "y" - one year (60*60*24*366) + o "m" - one month (60*60*24*31) + o "w" - one week (60*60*24*7) + o "d" - one day (60*60*24) + o "h" - one hour (60*60) + o "min" - one minute (60) + o "kb" - one kilobyte (1024) + o "mb" - one megabyte (1024*1024) + o "gb" - one gigabyte (1024*1024*1024) + o "kib" - one si kilobyte (1000) + o "mib" - one si megabyte (1000*1000) + o "gib" - one si gigabyte (1000*1000*1000) +@class function +@name parse_units +@param ustr String containing a numerical value with trailing unit +@return Number containing the canonical value +]] + +---[[ +Appends numerically indexed tables or single objects to a given table. + +@class function +@name append +@param src Target table +@param ... Objects to insert +@return Target table +]] + +---[[ +Combines two or more numerically indexed tables and single objects into one table. + +@class function +@name combine +@param tbl1 Table value to combine +@param tbl2 Table value to combine +@param ... More tables to combine +@return Table value containing all values of given tables +]] + +---[[ +Checks whether the given table contains the given value. + +@class function +@name contains +@param table Table value +@param value Value to search within the given table +@return Boolean indicating whether the given value occurs within table +]] + +---[[ +Update values in given table with the values from the second given table. + +Both table are - in fact - merged together. +@class function +@name update +@param t Table which should be updated +@param updates Table containing the values to update +@return Always nil +]] + +---[[ +Retrieve all keys of given associative table. + +@class function +@name keys +@param t Table to extract keys from +@return Sorted table containing the keys +]] + +---[[ +Clones the given object and return it's copy. + +@class function +@name clone +@param object Table value to clone +@param deep Boolean indicating whether to do recursive cloning +@return Cloned table value +]] + +---[[ +Create a dynamic table which automatically creates subtables. + +@class function +@name dtable +@return Dynamic Table +]] + +---[[ +Recursively serialize given data to lua code, suitable for restoring + +with loadstring(). +@class function +@name serialize_data +@param val Value containing the data to serialize +@return String value containing the serialized code +@see restore_data +@see get_bytecode +]] + +---[[ +Restore data previously serialized with serialize_data(). + +@class function +@name restore_data +@param str String containing the data to restore +@return Value containing the restored data structure +@see serialize_data +@see get_bytecode +]] + +---[[ +Return the current runtime bytecode of the given data. The byte code + +will be stripped before it is returned. +@class function +@name get_bytecode +@param val Value to return as bytecode +@return String value containing the bytecode of the given data +]] + +---[[ +Strips unnescessary lua bytecode from given string. Information like line + +numbers and debugging numbers will be discarded. Original version by +Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html) +@class function +@name strip_bytecode +@param code String value containing the original lua byte code +@return String value containing the stripped lua byte code +]] + +---[[ +Return a key, value iterator which returns the values sorted according to + +the provided callback function. +@class function +@name spairs +@param t The table to iterate +@param f A callback function to decide the order of elements +@return Function value containing the corresponding iterator +]] + +---[[ +Return a key, value iterator for the given table. + +The table pairs are sorted by key. +@class function +@name kspairs +@param t The table to iterate +@return Function value containing the corresponding iterator +]] + +---[[ +Return a key, value iterator for the given table. + +The table pairs are sorted by value. +@class function +@name vspairs +@param t The table to iterate +@return Function value containing the corresponding iterator +]] + +---[[ +Test whether the current system is operating in big endian mode. + +@class function +@name bigendian +@return Boolean value indicating whether system is big endian +]] + +---[[ +Execute given commandline and gather stdout. + +@class function +@name exec +@param command String containing command to execute +@return String containing the command's stdout +]] + +---[[ +Return a line-buffered iterator over the output of given command. + +@class function +@name execi +@param command String containing the command to execute +@return Iterator +]] + +---[[ +Issue an ubus call. + +@class function +@name ubus +@param object String containing the ubus object to call +@param method String containing the ubus method to call +@param values Table containing the values to pass +@return Table containin the ubus result +]] + +---[[ +Convert data structure to JSON + +@class function +@name serialize_json +@param data The data to serialize +@param writer A function to write a chunk of JSON data (optional) +@return String containing the JSON if called without write callback +]] + +---[[ +Returns the absolute path to LuCI base directory. + +@class function +@name libpath +@return String containing the directory path +]] + +---[[ +This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function + +@class function +@name coxpcall +@param f Lua function to be called protected +@param err Custom error handler +@param ... Parameters passed to the function +@return A boolean whether the function call succeeded and the return +-- values of either the function or the error handler +]] + +---[[ +This is a coroutine-safe drop-in replacement for Lua's "pcall"-function + +@class function +@name copcall +@param f Lua function to be called protected +@param ... Parameters passed to the function +@return A boolean whether the function call succeeded and the returns +-- values of the function or the error object +]] + diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua index 055142b53..52e347d07 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/system.lua @@ -178,13 +178,7 @@ function action_flashops() local image_tmp = "/tmp/firmware.img" local function image_supported() - -- XXX: yay... - return ( 0 == os.execute( - ". /lib/functions.sh; " .. - "include /lib/upgrade; " .. - "platform_check_image %q >/dev/null" - % image_tmp - ) ) + return (os.execute("sysupgrade -T %q >/dev/null" % image_tmp) == 0) end local function image_checksum() diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua index 88e81bb18..997a9274d 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua @@ -1,7 +1,7 @@ -- Copyright 2008 Steven Barth -- 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 IPv6-Suffix (hex)")) -sys.net.arptable(function(entry) - ip:value(entry["IP address"]) - mac:value( - entry["HW address"], - entry["HW address"] .. " (" .. entry["IP address"] .. ")" - ) +ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then + ip:value(n.dest:string()) + mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() }) + end end) function ip.validate(self, value, section) diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua index da7c1181f..fafacf35c 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua @@ -1,9 +1,9 @@ -- Copyright 2008 Steven Barth --- Copyright 2010 Jo-Philipp Wich +-- Copyright 2010-2015 Jo-Philipp Wich -- Licensed to the public under the Apache License 2.0. -require("luci.sys") -require("luci.util") +local ipc = require "luci.ip" + m = Map("dhcp", translate("Hostnames")) s = m:section(TypedSection, "domain", translate("Host entries")) @@ -19,12 +19,10 @@ ip = s:option(Value, "ip", translate("IP address")) ip.datatype = "ipaddr" ip.rmempty = true -local arptable = luci.sys.net.arptable() or {} -for i, dataset in ipairs(arptable) do - ip:value( - dataset["IP address"], - "%s (%s)" %{ dataset["IP address"], dataset["HW address"] } - ) -end +ipc.neighbors({ }, function(n) + if n.mac and n.dest and not n.dest:is6linklocal() then + ip:value(n.dest:string(), "%s (%s)" %{ n.dest:string(), n.mac }) + end +end) return m diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua index eabb25768..c2e5c7228 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua @@ -14,6 +14,7 @@ local has_dnsmasq = fs.access("/etc/config/dhcp") local has_firewall = fs.access("/etc/config/firewall") m = Map("network", translate("Interfaces") .. " - " .. arg[1]:upper(), translate("On this page you can configure the network interfaces. You can bridge several interfaces by ticking the \"bridge interfaces\" field and enter the names of several network interfaces separated by spaces. You can also use VLAN notation INTERFACE.VLANNR (e.g.: eth0.1).")) +m.redirect = luci.dispatcher.build_url("admin", "network", "network") m:chain("wireless") if has_firewall then diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua index 01580f101..ac02b156e 100644 --- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua +++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua @@ -1,14 +1,14 @@ -- Copyright 2008 Steven Barth -- 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-IP 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("IPv6-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 ea60a7f07..b7c44f907 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm @@ -1,12 +1,12 @@ <%# Copyright 2008-2009 Steven Barth - Copyright 2008-2013 Jo-Philipp Wich + Copyright 2008-2015 Jo-Philipp Wich Licensed to the public under the Apache License 2.0. -%> <%- - local sys = require "luci.sys" + local ip = require "luci.ip" local fs = require "nixio.fs" local utl = require "luci.util" local uci = require "luci.model.uci".cursor() @@ -90,7 +90,9 @@ local devices = ntm:get_wifidevs() local arpcache = { } - sys.net.arptable(function(e) arpcache[e["HW address"]:upper()] = e["IP address"] end) + ip.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then arpcache[n.mac:upper()] = n.dest:string() end + end) local netlist = { } local netdevs = { } diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm b/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm index 2d9a4a3dc..82dd3a7df 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm @@ -1,15 +1,33 @@ <%# Copyright 2008-2009 Steven Barth - Copyright 2008-2009 Jo-Philipp Wich + Copyright 2008-2015 Jo-Philipp Wich 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 @@

      <%:Routes%>

      <%:The following rules are currently active on this system.%>
      -
      +
      ARP
      @@ -28,19 +46,26 @@ - <% luci.sys.net.arptable(function(e) %> + <% + for _, v in ipairs(ip.neighbors({ family = 4 })) do + if v.mac then + %> - - - + + + - <% style = not style; end) %> + <% + style = not style + end + end + %>
      <%:Interface%>
      <%=e["IP address"]%><%=e["HW address"]%><%=e["Device"]%><%=v.dest%><%=v.mac%><%=v.dev%>

      -
      +
      <%_Active IPv4-Routes%>
      @@ -50,25 +75,27 @@ <%:Target%> <%_IPv4-Gateway%> <%:Metric%> + <%:Table%> - <% luci.sys.net.routes(function(rt) %> + <% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %> - <%=luci.tools.webadmin.iface_get_network(rt.device) or rt.device%> - <%=rt.dest:string()%> - <%=rt.gateway:string()%> - <%=rt.metric%> + <%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%> + <%=v.dest%> + <%=v.gw%> + <%=v.metric or 0%> + <%=rtn[v.table] or v.table%> - <% style = not style; end) %> + <% style = not style end %>

      - <% 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 %> -
      +
      <%_Active IPv6-Routes%>
      @@ -76,17 +103,55 @@ <%:Network%> <%:Target%> - <%_IPv6-Gateway%> + <%:Source%> <%:Metric%> + <%:Table%> + + <% + for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do + if v.dest and not v.dest:is6linklocal() then + %> + + <%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%> + <%=v.dest%> + <%=v.from%> + <%=v.metric or 0%> + <%=rtn[v.table] or v.table%> + + <% + style = not style + end + end + %> + +
      +
      +
      + +
      + <%:IPv6 Neighbours%> + +
      + + + + + - <% 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 + %> - - - - + + + - <% style = not style; end) %> + <% + style = not style + end + end + %>
      <%:IPv6-Address%><%:MAC-Address%><%:Interface%>
      <%=luci.tools.webadmin.iface_get_network(rt.device) or '(' .. rt.device .. ')'%><%=rt.dest:string()%><%=rt.source:string()%><%=rt.metric_raw:upper()%><%=v.dest%><%=v.mac%><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%>
      diff --git a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua index 5d3d8ad22..9a1c1fea4 100644 --- a/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua +++ b/modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua @@ -3,7 +3,7 @@ -- Licensed to the public under the Apache License 2.0. local uci = require "luci.model.uci".cursor() -local sys = require "luci.sys" +local ipc = require "luci.ip" local wa = require "luci.tools.webadmin" local fs = require "nixio.fs" @@ -86,12 +86,12 @@ s2.template = "cbi/tblsection" name = s2:option(Value, "name", translate("Hostname")) mac = s2:option(Value, "mac", translate("MAC-Address")) ip = s2:option(Value, "ip", translate("IPv4-Address")) -sys.net.arptable(function(entry) - ip:value(entry["IP address"]) - mac:value( - entry["HW address"], - entry["HW address"] .. " (" .. entry["IP address"] .. ")" - ) + +ipc.neighbors({ family = 4 }, function(n) + if n.mac and n.dest then + ip:value(n.dest:string()) + mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() }) + end end) return m diff --git a/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm b/modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm index 715ac756f..72bd136c5 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-ipv6/Makefile b/protocols/luci-proto-ipv6/Makefile index f0ca605f8..e749bc9e8 100644 --- a/protocols/luci-proto-ipv6/Makefile +++ b/protocols/luci-proto-ipv6/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite +LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite/aiccu LUCI_DEPENDS:= include ../../luci.mk diff --git a/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua new file mode 100644 index 000000000..e3ae020f5 --- /dev/null +++ b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua @@ -0,0 +1,136 @@ +-- Copyright 2015 Paul Oranje +-- Licensed to the public under the Apache License 2.0. + +local map, section, net = ... + +-- config read by /lib/netifd/proto/aiccu.sh +local username, password, protocol, server, tunnelid, ip6prefix, requiretls, nat, heartbeat, + verbose, ntpsynctimeout, ip6addr, sourcerouting, defaultroute + +-- generic parameters +local metric, ttl, mtu + + +username = section:taboption("general", Value, "username", + translate("Server username"), + translate("SIXXS-handle[/Tunnel-ID]")) +username.datatype = "string" + +password = section:taboption("general", Value, "password", + translate("Server password"), + translate("Server password, enter the specific password of the tunnel when the username contains the tunnel ID")) +password.datatype = "string" +password.password = true + + +--[[ SIXXS supports only TIC as tunnel broker protocol, no use setting it. +protocol = section:taboption("general", ListValue, "protocol", + translate("Tunnel broker protocol"), + translate("SIXXS supports TIC only, for static tunnels using IP protocol 41 (RFC4213) use 6in4 instead")) + +protocol:value("tic", "TIC") +protocol:value("tsp", "TSP") +protocol:value("l2tp", "L2TP") +protocol.default = "tic" +protocol.optional = true +--]] + + +server = section:taboption("general", Value, "server", + translate("Tunnel setup server"), + translate("Optional, specify to override default server (tic.sixxs.net)")) +server.datatype = "host" +server.optional = true + + +tunnelid = section:taboption("general", Value, "tunnelid", + translate("Tunnel ID"), + translate("Optional, use when the SIXXS account has more than one tunnel")) +tunnelid.datatype = "string" +tunnelid.optional = true + + +local ip6prefix = section:taboption("general", Value, "ip6prefix", + translate("IPv6 prefix"), + translate("Routed IPv6 prefix for downstream interfaces")) +ip6prefix.datatype = "ip6addr" +ip6prefix.optional = true + + +heartbeat = s:taboption("general", ListValue, "heartbeat", + translate("Tunnel type"), + translate("Also see Tunneling Comparison on SIXXS")) +heartbeat:value("0", translate("AYIYA")) +heartbeat:value("1", translate("Heartbeat")) +heartbeat.default = "0" + + +nat = section:taboption("general", Flag, "nat", + translate("Behind NAT"), + translate("The tunnel end-point is behind NAT, defaults to disabled and only applies to AYIYA")) +nat.optional = true +nat.default = disabled + + +requiretls = section:taboption("general", Flag, "requiretls", + translate("Require TLS"), + translate("Connection to server fails when TLS cannot be used")) +requiretls.optional = true +requiretls.default = requiretls.enabled + + +verbose = section:taboption("advanced", Flag, "verbose", + translate("Verbose"), + translate("Verbose logging by aiccu daemon")) +verbose.optional = true +verbose.default = disabled + + +ntpsynctimeout = section:taboption("advanced", Value, "ntpsynctimeout", + translate("NTP sync time-out"), + translate("Wait for NTP sync that many seconds, seting to 0 disables waiting (optional)")) +ntpsynctimeout.datatype = "uinteger" +ntpsynctimeout.placeholder = "90" +ntpsynctimeout.optional = true + + +ip6addr = section:taboption("advanced", Value, "ip6addr", + translate("Local IPv6 address"), + translate("IPv6 address delegated to the local tunnel endpoint (optional)")) +ip6addr.datatype = "ip6addr" +ip6addr.optional = true + + +defaultroute = section:taboption("advanced", Flag, "defaultroute", + translate("Default route"), + translate("Whether to create an IPv6 default route over the tunnel")) +defaultroute.default = defaultroute.enabled +defaultroute.optional = true + + +sourcerouting = section:taboption("advanced", Flag, "sourcerouting", + translate("Source routing"), + translate("Whether to route only packets from delegated prefixes")) +sourcerouting.default = sourcerouting.enabled +sourcerouting.optional = true + + +metric = section:taboption("advanced", Value, "metric", + translate("Use gateway metric")) +metric.datatype = "uinteger" +metric.placeholder = "0" +metric:depends("defaultroute", defaultroute.enabled) + + +ttl = section:taboption("advanced", Value, "ttl", + translate("Use TTL on tunnel interface")) +ttl.datatype = "range(1,255)" +ttl.placeholder = "64" + + +mtu = section:taboption("advanced", Value, "mtu", + translate("Use MTU on tunnel interface"), + translate("minimum 1280, maximum 1480")) +mtu.datatype = "range(1280,1480)" +mtu.placeholder = "1280" + diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua new file mode 100644 index 000000000..5896a278c --- /dev/null +++ b/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua @@ -0,0 +1,49 @@ +-- Copyright 2015 Paul Oranje +-- Licensed to the public under GPLv2 + +local netmod = luci.model.network +local interface = luci.model.network.interface + +local proto = netmod:register_protocol("aiccu") + +function proto.get_i18n(self) + return luci.i18n.translate("AICCU (SIXXS)") +end + +function proto.ifname(self) + return "aiccu-" .. self.sid +end + +function proto.get_interface(self) + return interface(self:ifname(), self) +end + +function proto.is_installed(self) + return nixio.fs.access("/lib/netifd/proto/aiccu.sh") +end + +function proto.opkg_package(self) + return "aiccu" +end + +function proto.is_floating(self) + return true +end + +function proto.is_virtual(self) + return true +end + +function proto.get_interfaces(self) + return nil +end + +function proto.contains_interface(self, ifname) + if self:is_floating() then + return (netmod:ifnameof(ifc) == self:ifname()) + else + return netmod.protocol.contains_interface(self, ifc) + end +end + +netmod:register_pattern_virtual("^aiccu-%%w") diff --git a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua index e0e63d863..7b213127f 100644 --- a/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua +++ b/protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua @@ -19,6 +19,9 @@ port = section:taboption("general", Value, "port", translate("VPN Server port")) port.placeholder = "443" port.datatype = "port" +ifname = section:taboption("general", Value, "interface", translate("Output Interface")) +ifname.template = "cbi/network_netlist" + section:taboption("general", Value, "serverhash", translate("VPN Server's certificate SHA1 hash")) section:taboption("general", Value, "authgroup", translate("AuthGroup"))