luci-0.11: merge outstanding trunk changes

This commit is contained in:
Jo-Philipp Wich 2012-12-18 13:58:22 +00:00
parent 31d76dbb64
commit 839dcdc012
387 changed files with 8383 additions and 4520 deletions

View file

@ -14,7 +14,7 @@ all: build
build: gccbuild luabuild
gccbuild:
make -C libs/lmo CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
make -C libs/web CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
for i in $(MODULES); do \
make -C$$i SDK="$(shell test -f .running-sdk && echo 1)" compile || { \
echo "*** Compilation of $$i failed!"; \

View file

@ -19,7 +19,7 @@ function index()
return
end
entry({"admin", "network", "ahcpd"}, cbi("ahcp"), _("AHCP Server"), 90).i18n = "ahcp"
entry({"admin", "network", "ahcpd"}, cbi("ahcp"), _("AHCP Server"), 90)
entry({"admin", "network", "ahcpd", "status"}, call("ahcp_status"))
end

View file

@ -17,7 +17,7 @@ module("luci.controller.asterisk", package.seeall)
function index()
entry({"admin", "services", "asterisk"}, cbi("asterisk"), "Asterisk", 80).i18n = "asterisk"
entry({"admin", "services", "asterisk"}, cbi("asterisk"), "Asterisk", 80)
entry({"admin", "services", "asterisk", "voice"}, cbi("asterisk-voice"), "Voice Functions", 1)
entry({"admin", "services", "asterisk", "meetme"}, cbi("asterisk-meetme"), "Meetme Conferences", 2)

View file

@ -0,0 +1,4 @@
PO = commands
include ../../build/config.mk
include ../../build/module.mk

View file

@ -0,0 +1,237 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
module("luci.controller.commands", package.seeall)
function index()
entry({"admin", "system", "commands"}, firstchild(), _("Custom Commands"), 80)
entry({"admin", "system", "commands", "dashboard"}, template("commands"), _("Dashboard"), 1)
entry({"admin", "system", "commands", "config"}, cbi("commands"), _("Configure"), 2)
entry({"admin", "system", "commands", "run"}, call("action_run"), nil, 3).leaf = true
entry({"admin", "system", "commands", "download"}, call("action_download"), nil, 3).leaf = true
entry({"command"}, call("action_public"), nil, 1).leaf = true
end
--- Decode a given string into arguments following shell quoting rules
--- [[abc \def "foo\"bar" abc'def']] -> [[abc def]] [[foo"bar]] [[abcdef]]
local function parse_args(str)
local args = { }
local function isspace(c)
if c == 9 or c == 10 or c == 11 or c == 12 or c == 13 or c == 32 then
return c
end
end
local function isquote(c)
if c == 34 or c == 39 or c == 96 then
return c
end
end
local function isescape(c)
if c == 92 then
return c
end
end
local function ismeta(c)
if c == 36 or c == 92 or c == 96 then
return c
end
end
--- Convert given table of byte values into a Lua string and append it to
--- the "args" table. Segment byte value sequence into chunks of 256 values
--- to not trip over the parameter limit for string.char()
local function putstr(bytes)
local chunks = { }
local csz = 256
local upk = unpack
local chr = string.char
local min = math.min
local len = #bytes
local off
for off = 1, len, csz do
chunks[#chunks+1] = chr(upk(bytes, off, min(off + csz - 1, len)))
end
args[#args+1] = table.concat(chunks)
end
--- Scan substring defined by the indexes [s, e] of the string "str",
--- perform unquoting and de-escaping on the fly and store the result in
--- a table of byte values which is passed to putstr()
local function unquote(s, e)
local off, esc, quote
local res = { }
for off = s, e do
local byte = str:byte(off)
local q = isquote(byte)
local e = isescape(byte)
local m = ismeta(byte)
if e then
esc = true
elseif esc then
if m then res[#res+1] = 92 end
res[#res+1] = byte
esc = false
elseif q and quote and q == quote then
quote = nil
elseif q and not quote then
quote = q
else
if m then res[#res+1] = 92 end
res[#res+1] = byte
end
end
putstr(res)
end
--- Find substring boundaries in "str". Ignore escaped or quoted
--- whitespace, pass found start- and end-index for each substring
--- to unquote()
local off, esc, start, quote
for off = 1, #str + 1 do
local byte = str:byte(off)
local q = isquote(byte)
local s = isspace(byte) or (off > #str)
local e = isescape(byte)
if esc then
esc = false
elseif e then
esc = true
elseif q and quote and q == quote then
quote = nil
elseif q and not quote then
start = start or off
quote = q
elseif s and not quote then
if start then
unquote(start, off - 1)
start = nil
end
else
start = start or off
end
end
--- If the "quote" is still set we encountered an unfinished string
if quote then
unquote(start, #str)
end
return args
end
local function parse_cmdline(cmdid, args)
local uci = require "luci.model.uci".cursor()
if uci:get("luci", cmdid) == "command" then
local cmd = uci:get_all("luci", cmdid)
local argv = parse_args(cmd.command)
local i, v
if cmd.param == "1" and args then
for i, v in ipairs(parse_args(luci.http.urldecode(args))) do
argv[#argv+1] = v
end
end
for i, v in ipairs(argv) do
if v:match("[^%w%.%-i/]") then
argv[i] = '"%s"' % v:gsub('"', '\\"')
end
end
return argv
end
end
function action_run(...)
local fs = require "nixio.fs"
local argv = parse_cmdline(...)
if argv then
local outfile = os.tmpname()
local errfile = os.tmpname()
local rv = os.execute(table.concat(argv, " ") .. " >%s 2>%s" %{ outfile, errfile })
local stdout = fs.readfile(outfile, 1024 * 512) or ""
local stderr = fs.readfile(errfile, 1024 * 512) or ""
fs.unlink(outfile)
fs.unlink(errfile)
local binary = not not (stdout:match("[%z\1-\8\14-\31]"))
luci.http.prepare_content("application/json")
luci.http.write_json({
command = table.concat(argv, " "),
stdout = not binary and stdout,
stderr = stderr,
exitcode = rv,
binary = binary
})
else
luci.http.status(404, "No such command")
end
end
function action_download(...)
local fs = require "nixio.fs"
local argv = parse_cmdline(...)
if argv then
local fd = io.popen(table.concat(argv, " ") .. " 2>/dev/null")
if fd then
local chunk = fd:read(4096) or ""
local name
if chunk:match("[%z\1-\8\14-\31]") then
luci.http.header("Content-Disposition", "attachment; filename=%s"
% fs.basename(argv[1]):gsub("%W+", ".") .. ".bin")
luci.http.prepare_content("application/octet-stream")
else
luci.http.header("Content-Disposition", "attachment; filename=%s"
% fs.basename(argv[1]):gsub("%W+", ".") .. ".txt")
luci.http.prepare_content("text/plain")
end
while chunk do
luci.http.write(chunk)
chunk = fd:read(4096)
end
fd:close()
else
luci.http.status(500, "Failed to execute command")
end
else
luci.http.status(404, "No such command")
end
end
function action_public(cmdid, args)
local uci = require "luci.model.uci".cursor()
if cmdid and
uci:get("luci", cmdid) == "command" and
uci:get("luci", cmdid, "public") == "1"
then
action_download(cmdid, args)
else
luci.http.status(403, "Access to command denied")
end
end

View file

@ -0,0 +1,37 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local m, s
m = Map("luci", translate("Custom Commands"),
translate("This page allows you to configure custom shell commands which can be easily invoked from the web interface."))
s = m:section(TypedSection, "command", "")
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s:option(Value, "name", translate("Description"),
translate("A short textual description of the configured command"))
s:option(Value, "command", translate("Command"),
translate("Command line to execute"))
s:option(Flag, "param", translate("Custom arguments"),
translate("Allow the user to provide additional command line arguments"))
s:option(Flag, "public", translate("Public access"),
translate("Allow executing the command and downloading its output without prior authentication"))
return m

View file

@ -0,0 +1,176 @@
<%#
LuCI - Lua Configuration Interface
Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
-%>
<% css = [[
.commandbox {
height: 12em;
width: 30%;
float: left;
height: 12em;
margin: 5px;
position: relative;
}
.commandbox h3 {
font-size: 1.5em !important;
line-height: 2em !important;
margin: 0 !important;
}
.commandbox input[type="text"] {
width: 50% !important;
}
.commandbox div {
position: absolute;
left: 0;
bottom: 1.5em;
}
]] -%>
<%+header%>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<script type="text/javascript">//<![CDATA[
var stxhr = new XHR();
function command_run(id)
{
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
var legend = document.getElementById('command-rc-legend');
var output = document.getElementById('command-rc-output');
if (legend && output)
{
output.innerHTML =
'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' +
'<%:Waiting for command to complete...%>'
;
legend.parentNode.style.display = 'block';
legend.style.display = 'inline';
stxhr.get('<%=luci.dispatcher.build_url("admin", "system", "commands", "run")%>/' + id + (args ? '/' + args : ''), null,
function(x, st)
{
if (st)
{
if (st.binary)
st.stdout = '[<%:Binary data not displayed, download instead.%>]';
legend.style.display = 'none';
output.innerHTML = String.format(
'<pre><strong># %h\n</strong>%h<span style="color:red">%h</span></pre>' +
'<div class="alert-message warning">%s (<%:Code:%> %d)</div>',
st.command, st.stdout, st.stderr,
(st.exitcode == 0) ? '<%:Command successful%>' : '<%:Command failed%>',
st.exitcode);
}
else
{
legend.style.display = 'none';
output.innerHTML = '<span class="error"><%:Failed to execute command!%></span>';
}
location.hash = '#output';
}
);
}
}
function command_download(id)
{
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
location.href = '<%=luci.dispatcher.build_url("admin", "system", "commands", "download")%>/' + id + (args ? '/' + args : '');
}
function command_link(id)
{
var legend = document.getElementById('command-rc-legend');
var output = document.getElementById('command-rc-output');
var args;
var field = document.getElementById(id);
if (field)
args = encodeURIComponent(field.value);
if (legend && output)
{
var link = location.protocol + '//' + location.hostname +
(location.port ? ':' + location.port : '') +
location.pathname.split(';')[0] + 'command/' +
id + (args ? '/' + args : '');
legend.style.display = 'none';
output.parentNode.style.display = 'block';
output.innerHTML = String.format(
'<div class="alert-message"><%:Access command with%> <a href="%s">%s</a></div>',
link, link
);
location.hash = '#output';
}
}
//]]></script>
<%
local uci = require "luci.model.uci".cursor()
local commands = { }
uci:foreach("luci", "command", function(s) commands[#commands+1] = s end)
%>
<form method="get" action="<%=pcdata(luci.http.getenv("REQUEST_URI"))%>">
<div class="cbi-map">
<h2><a id="content" name="content"><%:Custom Commands%></a></h2>
<fieldset class="cbi-section">
<% local _, command; for _, command in ipairs(commands) do %>
<div class="commandbox">
<h3><%=pcdata(command.name)%></h3>
<p><%:Command:%> <code><%=pcdata(command.command)%></code></p>
<% if command.param == "1" then %>
<p><%:Arguments:%> <input type="text" id="<%=command['.name']%>" /></p>
<% end %>
<div>
<input type="button" value="<%:Run%>" class="cbi-button cbi-button-apply" onclick="command_run('<%=command['.name']%>')" />
<input type="button" value="<%:Download%>" class="cbi-button cbi-button-download" onclick="command_download('<%=command['.name']%>')" />
<% if command.public == "1" then %>
<input type="button" value="<%:Link%>" class="cbi-button cbi-button-link" onclick="command_link('<%=command['.name']%>')" />
<% end %>
</div>
</div>
<% end %>
<br style="clear:both" /><br />
<a name="output"></a>
</fieldset>
</div>
<fieldset class="cbi-section" style="display:none">
<legend id="command-rc-legend"><%:Collecting data...%></legend>
<span id="command-rc-output"></span>
</fieldset>
</form>
<%+footer%>

View file

@ -18,7 +18,6 @@ function index()
local cc
cc = entry( { "admin", "services", "coovachilli" }, cbi("coovachilli"), _("CoovaChilli"), 90)
cc.i18n = "coovachilli"
cc.subindex = true
entry( { "admin", "services", "coovachilli", "network" }, cbi("coovachilli_network"), _("Network Configuration"), 10)

View file

@ -23,10 +23,8 @@ function index()
local page
page = entry({"admin", "services", "ddns"}, cbi("ddns/ddns"), _("Dynamic DNS"), 60)
page.i18n = "ddns"
page.dependent = true
page = entry({"mini", "network", "ddns"}, cbi("ddns/ddns", {autoapply=true}), _("Dynamic DNS"), 60)
page.i18n = "ddns"
page.dependent = true
end

View file

@ -112,14 +112,19 @@ else
end
s:option(Value, "check_interval",
translate("Check for changed IP every")).default = 10
ci = s:option(Value, "check_interval", translate("Check for changed IP every"))
ci.datatype = "and(uinteger,min(1))"
ci.default = 10
unit = s:option(ListValue, "check_unit", translate("Check-time unit"))
unit.default = "minutes"
unit:value("minutes", translate("min"))
unit:value("hours", translate("h"))
s:option(Value, "force_interval", translate("Force update every")).default = 72
fi = s:option(Value, "force_interval", translate("Force update every"))
fi.datatype = "and(uinteger,min(1))"
fi.default = 72
unit = s:option(ListValue, "force_unit", translate("Force-time unit"))
unit.default = "hours"
unit:value("minutes", translate("min"))

View file

@ -11,8 +11,6 @@ You may obtain a copy of the License at
]]--
require("luci.i18n")
module("luci.controller.luci_diag", package.seeall)
function index()
@ -20,11 +18,9 @@ function index()
e = entry({"admin", "network", "diag_config"}, template("diag/network_config_index") , _("Configure Diagnostics"), 120)
e.index = true
e.i18n = "diag_core"
e.dependent = true
e = entry({"mini", "diag"}, template("diag/index"), _("Diagnostics"), 120)
e.index = true
e.i18n = "diag_core"
e.dependent = true
end

View file

@ -38,8 +38,6 @@ end
function parse_output(devmap, outnets, haslink, type, mini, debug)
local curnet = next(outnets, nil)
luci.i18n.loadc("diag_devinfo")
while (curnet) do
local output = outnets[curnet]["output"]
local subnet = outnets[curnet]["subnet"]

View file

@ -19,49 +19,38 @@ function index()
e = entry({"admin", "voice", "diag", "phones"}, arcombine(cbi("luci_diag/smap_devinfo"), cbi("luci_diag/smap_devinfo_config")), _("Phones"), 10)
e.leaf = true
e.subindex = true
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"admin", "voice", "diag", "phones", "config"}, cbi("luci_diag/smap_devinfo_config"), _("Configure"), 10)
e.i18n = "diag_devinfo"
e = entry({"admin", "status", "smap_devinfo"}, cbi("luci_diag/smap_devinfo"), _("SIP Devices on Network"), 120)
e.leaf = true
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"admin", "network", "diag_config", "netdiscover_devinfo_config"}, cbi("luci_diag/netdiscover_devinfo_config"), _("Network Device Scan"), 100)
e.leaf = true
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"admin", "network", "diag_config", "smap_devinfo_config"}, cbi("luci_diag/smap_devinfo_config"), _("SIP Device Scan"))
e.leaf = true
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"admin", "status", "netdiscover_devinfo"}, cbi("luci_diag/netdiscover_devinfo"), _("Devices on Network"), 90)
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"admin", "network", "mactodevinfo"}, cbi("luci_diag/mactodevinfo"), _("MAC Device Info Overrides"), 190)
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"mini", "diag", "phone_scan"}, cbi("luci_diag/smap_devinfo_mini"), _("Phone Scan"), 100)
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"mini", "voice", "phones", "phone_scan_config"}, cbi("luci_diag/smap_devinfo_config_mini"), _("Config Phone Scan"), 90)
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"mini", "diag", "netdiscover_devinfo"}, cbi("luci_diag/netdiscover_devinfo_mini"), _("Network Device Scan"), 10)
e.i18n = "diag_devinfo"
e.dependent = true
e = entry({"mini", "network", "netdiscover_devinfo_config"}, cbi("luci_diag/netdiscover_devinfo_config_mini"), _("Device Scan Config"))
e.i18n = "diag_devinfo"
e.dependent = true
end

View file

@ -83,7 +83,6 @@ function command_function(outnets, i)
end
function action_links(netdiscovermap, mini)
luci.i18n.loadc("diag_devinfo")
s = netdiscovermap:section(SimpleSection, "", translate("Actions"))
b = s:option(DummyValue, "_config", translate("Configure Scans"))
b.value = ""

View file

@ -94,7 +94,6 @@ function command_function(outnets, i)
end
function action_links(smapmap, mini)
luci.i18n.loadc("diag_devinfo")
s = smapmap:section(SimpleSection, "", translate("Actions"))
b = s:option(DummyValue, "_config", translate("Configure Scans"))
b.value = ""

View file

@ -3,7 +3,7 @@ module("luci.controller.firewall", package.seeall)
function index()
entry({"admin", "network", "firewall"},
alias("admin", "network", "firewall", "zones"),
_("Firewall"), 60).i18n = "firewall"
_("Firewall"), 60)
entry({"admin", "network", "firewall", "zones"},
arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),

View file

@ -14,5 +14,5 @@ module "luci.controller.freifunk.policy-routing"
function index()
entry({"admin", "freifunk", "policyrouting"}, cbi("freifunk/policyrouting"),
_("Policy Routing"), 60).i18n = "freifunk-policyrouting"
_("Policy Routing"), 60)
end

View file

@ -10,7 +10,6 @@ You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
luci.i18n.loadc("freifunk")
local uci = require "luci.model.uci".cursor()
m = Map("freifunk-policyrouting", translate("Policy Routing"), translate("These pages can be used to setup policy routing for certain firewall zones. "..

View file

@ -23,6 +23,5 @@ function index()
local page
page = entry({"admin", "services", "hd_idle"}, cbi("hd_idle"), _("hd-idle"), 60)
page.i18n = "hd_idle"
page.dependent = true
end

View file

@ -22,7 +22,6 @@ function index()
page.target = firstchild()
page.title = _("VoIP")
page.order = 90
page.i18n = "telephony"
entry({"admin", "telephony", "account"}, cbi("luci_ltqtapi/account") , _("Account"), 10)
entry({"admin", "telephony", "contact"}, cbi("luci_ltqtapi/contact") , _("Contacts"), 20)

View file

@ -22,7 +22,6 @@ function index()
local page
page = entry({"admin", "services", "minidlna"}, cbi("minidlna"), _("miniDLNA"))
page.i18n = "minidlna"
page.dependent = true
entry({"admin", "services", "minidlna_status"}, call("minidlna_status"))

View file

@ -1,5 +1,3 @@
<%- luci.i18n.loadc("minidlna") -%>
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=luci.dispatcher.build_url("admin/services/minidlna_status")%>', null,
function(x, st)

View file

@ -23,6 +23,5 @@ function index()
local page
page = entry({"admin", "system", "mmc_over_gpio"}, cbi("mmc_over_gpio"), _("MMC/SD driver configuration"), 60)
page.i18n = "mmc_over_gpio"
page.dependent = true
end

View file

@ -9,13 +9,11 @@ function index()
local page
page = entry({"admin", "network", "multiwan"}, cbi("multiwan/multiwan"), _("Multi-WAN"))
page.i18n = "multiwan"
page.dependent = true
entry({"admin", "network", "multiwan", "status"}, call("multiwan_status"))
page = entry({"mini", "network", "multiwan"}, cbi("multiwan/multiwanmini", {autoapply=true}), _("Multi-WAN"))
page.i18n = "multiwan"
page.dependent = true
end
function multiwan_status()

View file

@ -23,10 +23,8 @@ function index()
local page
page = entry({"admin", "system", "ntpc"}, cbi("ntpc/ntpc"), _("Time Synchronisation"), 50)
page.i18n = "ntpc"
page.dependent = true
page = entry({"mini", "system", "ntpc"}, cbi("ntpc/ntpcmini", {autoapply=true}), _("Time Synchronisation"), 50)
page.i18n = "ntpc"
page.dependent = true
end

View file

@ -20,14 +20,21 @@ s.addremove = false
s:option(DummyValue, "_time", translate("Current system time")).value = os.date("%c")
s:option(Value, "interval", translate("Update interval (in seconds)")).rmempty = true
s:option(Value, "count", translate("Count of time measurements"), translate("empty = infinite")).rmempty = true
interval = s:option(Value, "interval", translate("Update interval (in seconds)"))
interval.datatype = "and(uinteger,min(1))"
interval.rmempty = true
count = s:option(Value, "count", translate("Count of time measurements"), translate("empty = infinite"))
count.datatype = "and(uinteger,min(1))"
count.rmempty = true
s2 = m:section(TypedSection, "ntpdrift", translate("Clock Adjustment"))
s2.anonymous = true
s2.addremove = false
s2:option(Value, "freq", translate("Offset frequency")).rmempty = true
freq = s2:option(Value, "freq", translate("Offset frequency"))
freq.datatype = "integer"
freq.rmempty = true
s3 = m:section(TypedSection, "ntpserver", translate("Time Servers"))
s3.anonymous = true
@ -35,6 +42,8 @@ s3.addremove = true
s3.template = "cbi/tblsection"
s3:option(Value, "hostname", translate("Hostname"))
s3:option(Value, "port", translate("Port")).rmempty = true
port = s3:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = true
return m

View file

@ -21,8 +21,9 @@ s.addremove = false
s:option(DummyValue, "_time", translate("Current system time")).value = os.date("%c")
s:option(Value, "interval", translate("Update interval (in seconds)")).rmempty = true
interval = s:option(Value, "interval", translate("Update interval (in seconds)"))
interval.datatype = "and(uinteger,min(1))"
interval.rmempty = true
s3 = m:section(TypedSection, "ntpserver", translate("Time Server"))
s3.anonymous = true
@ -30,6 +31,8 @@ s3.addremove = true
s3.template = "cbi/tblsection"
s3:option(Value, "hostname", translate("Hostname"))
s3:option(Value, "port", translate("Port")).rmempty = true
port = s3:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = true
return m

View file

@ -8,7 +8,6 @@ function index()
local page = node("admin", "status", "olsr")
page.target = template("status-olsr/overview")
page.title = _("OLSR")
page.i18n = "olsr"
page.subindex = true
local page = node("admin", "status", "olsr", "neighbors")
@ -51,7 +50,6 @@ function index()
{"admin", "services", "olsrd"},
cbi("olsr/olsrd"), "OLSR"
)
ol.i18n = "olsr"
ol.subindex = true
entry(
@ -74,7 +72,6 @@ function index()
cbi("olsr/olsrddisplay"), _("Display")
)
oplg.i18n = "olsr"
oplg.leaf = true
oplg.subindex = true

View file

@ -16,11 +16,11 @@ function etx_color(etx)
local color = "#bb3333"
if etx == 0 then
color = "#bb3333"
elseif etx < 4 then
elseif etx < 2 then
color = "#00cc00"
elseif etx < 10 then
elseif etx < 4 then
color = "#ffcb05"
elseif etx < 100 then
elseif etx < 10 then
color = "#ff6600"
end
return color

View file

@ -147,6 +147,10 @@ end
<li><strong>LQ: </strong><%:Success rate of packages received from the neighbour%></li>
<li><strong>NLQ: </strong><%:Success rate of packages sent to the neighbour%></li>
<li><strong>ETX: </strong><%:Expected retransmission count%></li>
<li><strong><span style="color:#00cc00"><%:Green%></span></strong>:<%:Very good (ETX < 2)%></li>
<li><strong><span style="color:#ffcb05"><%:Yellow%></span></strong>:<%:Good (2 < ETX < 4)%></li>
<li><strong><span style="color:#ff6600"><%:Orange%></span></strong>:<%:Still usable (4 < ETX < 10)%></li>
<li><strong><span style="color:#bb3333"><%:Red%></span></strong>:<%:Bad (ETX > 10)%></li>
</ul>
</fieldset>
<%+footer%>

View file

@ -16,7 +16,7 @@ $Id$
module("luci.controller.openvpn", package.seeall)
function index()
entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") ).i18n = "openvpn"
entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") )
entry( {"admin", "services", "openvpn", "basic"}, cbi("openvpn-basic"), nil ).leaf = true
entry( {"admin", "services", "openvpn", "advanced"}, cbi("openvpn-advanced"), nil ).leaf = true
end

View file

@ -16,5 +16,5 @@ module("luci.controller.ff_p2pblock", package.seeall)
function index()
entry({"admin", "network", "firewall", "p2pblock"}, cbi("luci_fw/p2pblock"),
_("P2P-Block"), 40).i18n = "p2pblock"
_("P2P-Block"), 40)
end

View file

@ -23,6 +23,5 @@ function index()
local page
page = entry({"admin", "services", "p910nd"}, cbi("p910nd"), _("p910nd - Printer server"), 60)
page.i18n = "p910nd"
page.dependent = true
end

View file

@ -2,6 +2,7 @@
LuCI p910nd
(c) 2008 Yanira <forum-2008@email.de>
(c) 2012 Jo-Philipp Wich <jow@openwrt.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -9,15 +10,17 @@ You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local uci = luci.model.uci.cursor_state()
local net = require "luci.model.network"
local m, s, p, b
m = Map("p910nd", translate("p910nd - Printer server"),
translatef("First you have to install the packages to get support for USB (kmod-usb-printer) or parallel port (kmod-lp)."))
net = net.init(m.uci)
s = m:section(TypedSection, "p910nd", translate("Settings"))
s.addremove = true
s.anonymous = true
@ -26,6 +29,25 @@ s:option(Flag, "enabled", translate("enable"))
s:option(Value, "device", translate("Device")).rmempty = true
b = s:option(Value, "bind", translate("Interface"), translate("Specifies the interface to listen on."))
b.template = "cbi/network_netlist"
b.nocreate = true
b.unspecified = true
function b.cfgvalue(...)
local v = Value.cfgvalue(...)
if v then
return (net:get_status_by_address(v))
end
end
function b.write(self, section, value)
local n = net:get_network(value)
if n and n:ipaddr() then
Value.write(self, section, n:ipaddr())
end
end
p = s:option(ListValue, "port", translate("Port"), translate("TCP listener port."))
p.rmempty = true
for i=0,9 do

View file

@ -19,7 +19,7 @@ function index()
return
end
entry({"admin", "services", "polipo"}, alias("admin", "services", "polipo", "config"), _("Polipo")).i18n = "polipo"
entry({"admin", "services", "polipo"}, alias("admin", "services", "polipo", "config"), _("Polipo"))
entry({"admin", "services", "polipo", "status"}, template("polipo_status"), _("Status"))
entry({"admin", "services", "polipo", "config"}, cbi("polipo"), _("Configuration"))
end

View file

@ -22,10 +22,8 @@ function index()
local page
page = entry({"admin", "network", "qos"}, cbi("qos/qos"), _("QoS"))
page.i18n = "qos"
page.dependent = true
page = entry({"mini", "network", "qos"}, cbi("qos/qosmini", {autoapply=true}), _("QoS"))
page.i18n = "qos"
page.dependent = true
end

View file

@ -35,9 +35,11 @@ s:option(Flag, "overhead", translate("Calculate overhead"))
s:option(Flag, "halfduplex", translate("Half-duplex"))
s:option(Value, "download", translate("Download speed (kbit/s)"))
dl = s:option(Value, "download", translate("Download speed (kbit/s)"))
dl.datatype = "and(uinteger,min(1))"
s:option(Value, "upload", translate("Upload speed (kbit/s)"))
ul = s:option(Value, "upload", translate("Upload speed (kbit/s)"))
ul.datatype = "and(uinteger,min(1))"
s = m:section(TypedSection, "classify", translate("Classification Rules"))
s.template = "cbi/tblsection"

View file

@ -21,8 +21,12 @@ m = Map("qos")
s = m:section(NamedSection, "wan", "interface", translate("Internet Connection"))
s:option(Flag, "enabled", translate("Quality of Service"))
s:option(Value, "download", translate("Downlink"), "kbit/s")
s:option(Value, "upload", translate("Uplink"), "kbit/s")
dl = s:option(Value, "download", translate("Downlink"), "kbit/s")
dl.datatype = "and(uinteger,min(1))"
ul = s:option(Value, "upload", translate("Uplink"), "kbit/s")
ul.datatype = "and(uinteger,min(1))"
s = m:section(TypedSection, "classify")
s.template = "cbi/tblsection"

View file

@ -19,7 +19,7 @@ function index()
return
end
entry({"admin", "network", "radvd"}, cbi("radvd"), _("Radvd"), 61).i18n = "radvd"
entry({"admin", "network", "radvd"}, cbi("radvd"), _("Radvd"), 61)
entry({"admin", "network", "radvd", "interface"}, cbi("radvd/interface"), nil).leaf = true
entry({"admin", "network", "radvd", "prefix"}, cbi("radvd/prefix"), nil).leaf = true
entry({"admin", "network", "radvd", "route"}, cbi("radvd/route"), nil).leaf = true

View file

@ -23,6 +23,5 @@ function index()
local page
page = entry({"admin", "services", "samba"}, cbi("samba"), _("Network Shares"))
page.i18n = "samba"
page.dependent = true
end

View file

@ -1,28 +1,25 @@
module("luci.controller.splash.splash", package.seeall)
luci.i18n.loadc("splash")
local uci = luci.model.uci.cursor()
local util = require "luci.util"
function index()
entry({"admin", "services", "splash"}, cbi("splash/splash"), _("Client-Splash"), 90).i18n = "freifunk"
entry({"admin", "services", "splash"}, cbi("splash/splash"), _("Client-Splash"), 90)
entry({"admin", "services", "splash", "splashtext" }, form("splash/splashtext"), _("Splashtext"), 10)
local e
e = node("splash")
e.target = call("action_dispatch")
e.i18n = "freifunk"
node("splash", "activate").target = call("action_activate")
node("splash", "splash").target = template("splash_splash/splash")
node("splash", "blocked").target = template("splash/blocked")
entry({"admin", "status", "splash"}, call("action_status_admin"), _("Client-Splash")).i18n = "freifunk"
entry({"admin", "status", "splash"}, call("action_status_admin"), _("Client-Splash"))
local page = node("splash", "publicstatus")
page.target = call("action_status_public")
page.i18n = "freifunk"
page.leaf = true
end

View file

@ -9,7 +9,6 @@ You may obtain a copy of the License at
]]--
require("luci.model.uci")
luci.i18n.loadc("splash")
m = Map("luci_splash", translate("Client-Splash"), translate("Client-Splash is a hotspot authentification system for wireless mesh networks."))

View file

@ -13,7 +13,6 @@ You may obtain a copy of the License at
]]--
local fs = require "nixio.fs"
luci.i18n.loadc("splash")
local splashtextfile = "/usr/lib/luci-splash/splashtext.html"

View file

@ -19,7 +19,6 @@ local ipt = require "luci.sys.iptparser".IptParser()
local uci = require "luci.model.uci".cursor_state()
local wat = require "luci.tools.webadmin"
local fs = require "nixio.fs"
luci.i18n.loadc("splash")
local clients = { }
local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime") or 1) * 60 * 60

View file

@ -50,6 +50,7 @@ function index()
memory = _("Memory"),
netlink = _("Netlink"),
network = _("Network"),
nut = _("UPS"),
olsrd = _("OLSRd"),
ping = _("Ping"),
processes = _("Processes"),
@ -61,13 +62,12 @@ function index()
-- our collectd menu
local collectd_menu = {
output = { "csv", "network", "rrdtool", "unixsock" },
system = { "cpu", "df", "disk", "email", "exec", "irq", "load", "memory", "processes" },
system = { "cpu", "df", "disk", "email", "exec", "irq", "load", "memory", "nut", "processes" },
network = { "conntrack", "dns", "interface", "iptables", "netlink", "olsrd", "ping", "tcpconns", "iwinfo" }
}
-- create toplevel menu nodes
local st = entry({"admin", "statistics"}, template("admin_statistics/index"), _("Statistics"), 80)
st.i18n = "statistics"
st.index = true
entry({"admin", "statistics", "collectd"}, cbi("luci_statistics/collectd"), _("Collectd"), 10).subindex = true
@ -82,7 +82,6 @@ function index()
)
e.index = true
e.i18n = "rrdtool"
for j, plugin in luci.util.vspairs( plugins ) do
_entry(
@ -97,7 +96,6 @@ function index()
-- output views
local page = entry( { "admin", "statistics", "graph" }, template("admin_statistics/index"), _("Graphs"), 80)
page.i18n = "statistics"
page.setuser = "nobody"
page.setgroup = "nogroup"

View file

@ -19,7 +19,7 @@ require("luci.sys")
m = Map("luci_statistics",
translate("Collectd Settings"),
translate(
"Collectd is a small daeomon for collecting data from " ..
"Collectd is a small daemon for collecting data from " ..
"various sources through different plugins. On this page " ..
"you can change general settings for the collectd daemon."
))

View file

@ -0,0 +1,28 @@
--[[
LuCI - Lua Configuration Interface
Copyright © 2011 Manuel Munz <freifunk at somakoma dot de>
Copyright © 2012 David Woodhouse <dwmw2@infradead.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
m = Map("luci_statistics",
translate("UPS Plugin Configuration"),
translate("The NUT plugin reads information about Uninterruptible Power Supplies."))
s = m:section(NamedSection, "collectd_nut", "luci_statistics" )
enable = s:option(Flag, "enable", translate("Enable this plugin"))
enable.default = 0
host = s:option(Value, "UPS", translate("UPS"), translate("UPS name in NUT ups@host format"))
host.placeholder = "myupsname"
host.datatype = "string"
host.rmempty = true
return m

View file

@ -25,9 +25,6 @@ Instance = luci.util.class()
function Instance.__init__( self, graph )
self.i18n = luci.i18n
self.graph = graph
self.i18n.loadc("rrdtool")
self.i18n.loadc("statistics")
end
function Instance._subst( self, str, val )

View file

@ -156,11 +156,14 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
if not ds or ds:len() == 0 then ds = "value" end
_tif( _args, "DEF:%s_avg=%s:%s:AVERAGE", inst, rrd, ds )
_tif( _args, "DEF:%s_avg_raw=%s:%s:AVERAGE", inst, rrd, ds )
_tif( _args, "CDEF:%s_avg=%s_avg_raw,%s", inst, inst, source.transform_rpn )
if not self.opts.rrasingle then
_tif( _args, "DEF:%s_min=%s:%s:MIN", inst, rrd, ds )
_tif( _args, "DEF:%s_max=%s:%s:MAX", inst, rrd, ds )
_tif( _args, "DEF:%s_min_raw=%s:%s:MIN", inst, rrd, ds )
_tif( _args, "CDEF:%s_min=%s_min_raw,%s", inst, inst, source.transform_rpn )
_tif( _args, "DEF:%s_max_raw=%s:%s:MAX", inst, rrd, ds )
_tif( _args, "CDEF:%s_max=%s_max_raw,%s", inst, inst, source.transform_rpn )
end
_tif( _args, "CDEF:%s_nnl=%s_avg,UN,0,%s_avg,IF", inst, inst, inst )
@ -180,20 +183,23 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
-- is first source in stack or overlay source: source_stk = source_nnl
if not prev or source.overlay then
-- create cdef statement
-- create cdef statement for cumulative stack (no NaNs) and also
-- for display (preserving NaN where no points should be displayed)
_tif( _args, "CDEF:%s_stk=%s_nnl", source.sname, source.sname )
_tif( _args, "CDEF:%s_plot=%s_avg", source.sname, source.sname )
-- is subsequent source without overlay: source_stk = source_nnl + previous_stk
else
-- create cdef statement
_tif( _args, "CDEF:%s_stk=%s_nnl,%s_stk,+", source.sname, source.sname, prev )
_tif( _args, "CDEF:%s_plot=%s_avg,%s_stk,+", source.sname, source.sname, prev )
end
-- create multiply by minus one cdef if flip is enabled
if source.flip then
-- create cdef statement: source_stk = source_stk * -1
_tif( _args, "CDEF:%s_neg=%s_stk,-1,*", source.sname, source.sname )
_tif( _args, "CDEF:%s_neg=%s_plot,-1,*", source.sname, source.sname )
-- push to negative stack if overlay is disabled
if not source.overlay then
@ -253,11 +259,11 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
-- derive area background color from line color
area_color = self.colors:to_string( self.colors:faded( area_color ) )
-- choose source_stk or source_neg variable depending on flip state
-- choose source_plot or source_neg variable depending on flip state
if source.flip then
var = "neg"
else
var = "stk"
var = "plot"
end
-- create legend
@ -400,6 +406,7 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
flip = dopts.flip or false,
total = dopts.total or false,
overlay = dopts.overlay or false,
transform_rpn = dopts.transform_rpn or "0,+",
noarea = dopts.noarea or false,
title = dopts.title or nil,
ds = dsource,
@ -450,6 +457,18 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
_ti( _args, self.i18n:title( plugin, plugin_instance, _sources[1].type, instance, opts.title ) )
_ti( _args, "-v" )
_ti( _args, self.i18n:label( plugin, plugin_instance, _sources[1].type, instance, opts.vlabel ) )
if opts.y_max then
_ti ( _args, "-u" )
_ti ( _args, opts.y_max )
end
if opts.y_min then
_ti ( _args, "-l" )
_ti ( _args, opts.y_min )
end
if opts.units_exponent then
_ti ( _args, "-X" )
_ti ( _args, opts.units_exponent )
end
-- store additional rrd options
if opts.rrdopts then

View file

@ -19,6 +19,7 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Processor usage on core #%pi",
y_min = "0",
vlabel = "Percent",
number_format = "%5.1lf%%",
data = {

View file

@ -19,6 +19,8 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
return {
title = "%H: Load", vlabel = "Load",
y_min = "0",
units_exponent = "0",
number_format = "%5.2lf", data = {
sources = {
load = { "shortterm", "midterm", "longterm" }

View file

@ -0,0 +1,106 @@
--[[
Luci statistics - ups plugin diagram definition
Copyright © 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Copyright © 2012 David Woodhouse <dwmw2@infradead.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
module("luci.statistics.rrdtool.definitions.nut",package.seeall)
function rrdargs( graph, plugin, plugin_instance, dtype )
local voltages = {
title = "%H: Voltages on UPS \"%pi\"",
vlabel = "V",
number_format = "%5.1lfV",
data = {
instances = {
voltage = { "battery", "input", "output" }
},
options = {
voltage_output = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
voltage_battery = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true },
voltage_input = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
}
}
}
local currents = {
title = "%H: Current on UPS \"%pi\"",
vlabel = "A",
number_format = "%5.3lfA",
data = {
instances = {
current = { "battery", "output" }
},
options = {
current_output = { color = "00e000", title = "Output current", noarea=true, overlay=true },
current_battery = { color = "0000ff", title = "Battery current", noarea=true, overlay=true },
}
}
}
local percentage = {
title = "%H: Battery charge on UPS \"%pi\"",
vlabel = "Percent",
y_min = "0",
y_max = "100",
number_format = "%5.1lf%%",
data = {
sources = {
percent = { "percent" }
},
instances = {
percent = "charge"
},
options = {
percent_charge = { color = "00ff00", title = "Charge level" }
}
}
}
-- Note: This is in ISO8859-1 for rrdtool. Welcome to the 20th century.
local temperature = {
title = "%H: Battery temperature on UPS \"%pi\"",
vlabel = "\176C",
number_format = "%5.1lf\176C",
data = {
instances = {
temperature = "battery"
},
options = {
temperature_battery = { color = "ffb000", title = "Battery temperature" }
}
}
}
local timeleft = {
title = "%H: Time left on UPS \"%pi\"",
vlabel = "Minutes",
number_format = "%.1lfm",
data = {
sources = {
timeleft = { "timeleft" }
},
instances = {
timeleft = { "battery" }
},
options = {
timeleft_battery = { color = "0000ff", title = "Time left", transform_rpn = "60,/" }
}
}
}
return { voltages, currents, percentage, temperature, timeleft }
end

View file

@ -131,3 +131,7 @@ config 'statistics' 'collectd_olsrd'
config 'statistics' 'collectd_iwinfo'
option 'enable' '1'
config 'statistics' 'collectd_nut'
option 'enable' '0'
option 'UPS' 'myupsname'

View file

@ -357,6 +357,12 @@ plugins = {
network = config_network,
nut = {
{ "UPS" },
{ },
{ }
},
olsrd = {
{ "Host", "Port", "CollectLinks","CollectRoutes","CollectTopology"},
{ },

View file

@ -20,7 +20,7 @@ function index()
return
end
entry({"admin", "services", "tinyproxy"}, alias("admin", "services", "tinyproxy", "config"), _("Tinyproxy")).i18n = "tinyproxy"
entry({"admin", "services", "tinyproxy"}, alias("admin", "services", "tinyproxy", "config"), _("Tinyproxy"))
entry({"admin", "services", "tinyproxy", "status"}, template("tinyproxy_status"), _("Status"))
entry({"admin", "services", "tinyproxy", "config"}, cbi("tinyproxy"), _("Configuration"))
end

View file

@ -15,14 +15,11 @@ $Id$
module("luci.controller.transmission", package.seeall)
function index()
require("luci.i18n")
luci.i18n.loadc("transmission")
if not nixio.fs.access("/etc/config/transmission") then
return
end
local page = entry({"admin", "services", "transmission"}, cbi("transmission"), _("Transmission"))
page.i18n = "transmission"
page.dependent = true
end

View file

@ -23,11 +23,9 @@ function index()
local page
page = entry({"admin", "services", "upnp"}, cbi("upnp/upnp"), _("UPNP"))
page.i18n = "upnp"
page.dependent = true
page = entry({"mini", "network", "upnp"}, cbi("upnp/upnpmini", {autoapply=true}), _("UPNP"))
page.i18n = "upnp"
page.dependent = true
entry({"admin", "services", "upnp", "status"}, call("act_status")).leaf = true
@ -69,10 +67,8 @@ function act_status()
end
end
function act_delete()
local path = luci.dispatcher.context.requestpath
local idx = tonumber(path[#path])
function act_delete(idx)
idx = tonumber(idx)
if idx and idx > 0 then
luci.sys.call("iptables -t filter -D MINIUPNPD %d 2>/dev/null" % idx)
luci.sys.call("iptables -t nat -D MINIUPNPD %d 2>/dev/null" % idx)

View file

@ -1,5 +1,3 @@
<%- luci.i18n.loadc("upnp") -%>
<script type="text/javascript">//<![CDATA[
function upnp_delete_fwd(idx) {
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "upnp", "delete")%>/' + idx, null,

View file

@ -23,6 +23,5 @@ function index()
local page
page = entry({"admin", "services", "ushare"}, cbi("ushare"), _("uShare"), 60)
page.i18n = "ushare"
page.dependent = true
end

View file

@ -1,11 +1,11 @@
module("luci.controller.vnstat", package.seeall)
function index()
entry({"admin", "status", "vnstat"}, alias("admin", "status", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90).i18n = "vnstat"
entry({"admin", "status", "vnstat"}, alias("admin", "status", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90)
entry({"admin", "status", "vnstat", "graphs"}, template("vnstat"), _("Graphs"), 1)
entry({"admin", "status", "vnstat", "config"}, cbi("vnstat"), _("Configuration"), 2)
entry({"mini", "network", "vnstat"}, alias("mini", "network", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90).i18n = "vnstat"
entry({"mini", "network", "vnstat"}, alias("mini", "network", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90)
entry({"mini", "network", "vnstat", "graphs"}, template("vnstat"), _("Graphs"), 1)
entry({"mini", "network", "vnstat", "config"}, cbi("vnstat"), _("Configuration"), 2)
end

View file

@ -18,18 +18,14 @@ function index()
e = entry({"admin", "voice"}, template("luci_voice/index") , _("Voice"), 90)
e.index = true
e.i18n = "voice_core"
e = entry({"mini", "voice"}, template("luci_voice/index"), _("Voice"), 90)
e.index = true
e.i18n = "voice_core"
e = entry({"mini", "voice", "phones"}, template("luci_voice/phone_index"), _("Phones"), 90)
e.index = true
e.i18n = "voice_core"
e = entry({"admin", "voice", "phones"}, template("luci_voice/phone_index"), _("Phones"), 90)
e.index = true
e.i18n = "voice_core"
end

View file

@ -18,6 +18,5 @@ function index()
e = entry({"admin", "voice", "diag"}, template("luci_voice/diag_index"), _("Diagnostics"), 90)
e.index = true
e.i18n = "voice_diag"
e.dependent = true
end

View file

@ -1,6 +1,6 @@
module("luci.controller.wol", package.seeall)
function index()
entry({"admin", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90).i18n = "wol"
entry({"mini", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90).i18n = "wol"
entry({"admin", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90)
entry({"mini", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90)
end

View file

@ -4,7 +4,8 @@ PATTERN=$1
SCM=
[ -d .svn ] && SCM="svn"
[ -d .git ] && SCM="git"
git=$( which git 2>/dev/null )
[ "$git" ] && "$git" status >/dev/null && SCM="git"
[ -z "$SCM" ] && {
echo "Unsupported SCM tool" >&2

View file

@ -1,15 +0,0 @@
config 'community' 'profile'
option 'name' 'Freifunk Hamburg'
option 'homepage' 'http://hamburg.piratenpartei.de'
option 'ssid' 'hamburg.freifunk.net'
option 'mesh_network' '10.112.0.0/12'
option 'splash_network' '10.104.0.0/16'
option 'splash_prefix' '27'
option 'latitude' '53.56262'
option 'longitude' '10.01069'
config 'defaults' 'interface'
option 'netmask' '255.240.0.0'
config 'defaults' 'wifi_device'
option 'channel' '1'

View file

@ -1,5 +1,6 @@
#
# Copyright (C) 2009 Andreas Seidler <tetzlav@subsignal.org>
# Copyright (C) 2012 Jo-Philipp Wich <xm@subsignal.org>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
@ -8,7 +9,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=freifunk-p2pblock
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)

View file

@ -26,6 +26,7 @@ start() {
config_get ipp2p p2pblock ipp2p
config_get portrange p2pblock portrange
config_get blocktime p2pblock blocktime
config_get whitelist p2pblock whitelist
# load modules
insmod ipt_ipp2p 2>&-

View file

@ -177,14 +177,13 @@ $(eval $(call library,fastindex,Fastindex indexing module,+PACKAGE_luci-lib-fast
$(eval $(call library,httpclient,HTTP(S) client library,+luci-lib-web +luci-lib-nixio))
$(eval $(call library,ipkg,LuCI IPKG/OPKG call abstraction library))
$(eval $(call library,json,LuCI JSON library))
$(eval $(call library,lmo,LuCI LMO I18N library))
$(eval $(call library,lucid,LuCId Full-Stack Webserver,+luci-lib-nixio +luci-lib-web +luci-lib-px5g))
$(eval $(call library,lucid-http,LuCId HTTP Backend,+luci-lib-lucid))
$(eval $(call library,lucid-rpc,LuCId RPC Backend,+luci-lib-lucid))
$(eval $(call library,nixio,NIXIO POSIX library,+PACKAGE_luci-lib-nixio_openssl:libopenssl +PACKAGE_luci-lib-nixio_cyassl:libcyassl))
$(eval $(call library,px5g,RSA/X.509 Key Generator (required for LuCId SSL support),+luci-lib-nixio))
$(eval $(call library,sys,LuCI Linux/POSIX system library))
$(eval $(call library,web,MVC Webframework,+luci-lib-sys +luci-lib-nixio +luci-lib-core +luci-sgi-cgi +luci-lib-lmo))
$(eval $(call library,web,MVC Webframework,+luci-lib-sys +luci-lib-nixio +luci-lib-core +luci-sgi-cgi))
### Protocols ###
@ -327,7 +326,7 @@ endef
$(eval $(call application,siitwizard,SIIT IPv4-over-IPv6 configuration wizard,\
+PACKAGE_luci-app-siitwizard:kmod-siit))
$(eval $(call application,firewall,Firmware and Portforwarding application,\
$(eval $(call application,firewall,Firewall and Portforwarding application,\
+PACKAGE_luci-app-firewall:firewall))
$(eval $(call application,freifunk-policyrouting,Policy routing for mesh traffic,\
@ -380,6 +379,8 @@ $(eval $(call application,voice-core,LuCI Voice Software (Core)))
$(eval $(call application,voice-diag,LuCI Voice Software (Diagnostics),\
luci-app-diag-devinfo))
$(eval $(call application,commands,LuCI Shell Command Module))
$(eval $(call application,upnp,Universal Plug & Play configuration module,\
+PACKAGE_luci-app-upnp:miniupnpd))

View file

@ -1,5 +1,5 @@
#!/bin/sh
uci batch <<-EOF
set luci.languages.pl='Język polski'
set luci.languages.pl='Polski'
commit luci
EOF

View file

@ -20,7 +20,7 @@ limitations under the License.
local type, pairs, ipairs, table, luci, math
= type, pairs, ipairs, table, luci, math
local lmo = require "lmo"
local tpl = require "luci.template.parser"
local utl = require "luci.util"
local uci = require "luci.model.uci"
@ -478,7 +478,7 @@ function zone.get_color(self)
elseif self and self:name() == "wan" then
return "#f09090"
elseif self then
math.randomseed(lmo.hash(self:name()))
math.randomseed(tpl.hash(self:name()))
local r = math.random(128)
local g = math.random(128)

View file

@ -623,6 +623,32 @@ function get_status_by_route(self, addr, mask)
end
end
function get_status_by_address(self, addr)
local _, object
for _, object in ipairs(_ubus:objects()) do
local net = object:match("^network%.interface%.(.+)")
if net then
local s = _ubus:call(object, "status", {})
if s and s['ipv4-address'] then
local a
for _, a in ipairs(s['ipv4-address']) do
if a.address == addr then
return net, s
end
end
end
if s and s['ipv6-address'] then
local a
for _, a in ipairs(s['ipv6-address']) do
if a.address == addr then
return net, s
end
end
end
end
end
end
function get_wannet(self)
local net = self:get_status_by_route("0.0.0.0", 0)
return net and network(net)

View file

@ -182,27 +182,18 @@ end
-- String and data manipulation routines
--
--- Escapes all occurrences of the given character in given string.
-- @param s String value containing unescaped characters
-- @param c String value with character to escape (optional, defaults to "\")
-- @return String value with each occurrence of character escaped with "\"
function escape(s, c)
c = c or "\\"
return s:gsub(c, "\\" .. c)
end
--- 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.sanitize_pcdata(tostring(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(s)
return pcdata(tostring(s):gsub("</?[A-Za-z][A-Za-z0-9:_%-]*[^>]*>", " "):gsub("%s+", " "))
function striptags(value)
return value and tparser.striptags(tostring(value))
end
--- Splits given string on a defined separator sequence and return a table
@ -356,7 +347,6 @@ function parse_units(ustr)
end
-- also register functions above in the central string class for convenience
string.escape = escape
string.pcdata = pcdata
string.striptags = striptags
string.split = split

View file

@ -10,7 +10,6 @@ You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local os = require "os"
@ -23,7 +22,7 @@ local pairs = pairs
local error = error
local table = table
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite"
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
local icfg = "/etc/opkg.conf"
--- LuCI OPKG call abstraction library
@ -159,7 +158,7 @@ end
-- List helper
function _list(action, pat, cb)
local fd = io.popen(ipkg .. " " .. action ..
(pat and (" '%s'" % pat:gsub("'", "")) or "")) -- .. " | grep -vE '^ '")
(pat and (" '%s'" % pat:gsub("'", "")) or ""))
if fd then
local name, version, desc
@ -167,20 +166,18 @@ function _list(action, pat, cb)
local line = fd:read("*l")
if not line then break end
if line:sub(1,1) ~= " " then
name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
if not name then
name, version = line:match("^(.-) %- (.+)")
desc = ""
end
cb(name, version, desc)
name = nil
version = nil
desc = nil
if not name then
name, version = line:match("^(.-) %- (.+)")
desc = ""
end
cb(name, version, desc)
name = nil
version = nil
desc = nil
end
fd:close()
@ -203,6 +200,15 @@ 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()

View file

@ -1,46 +0,0 @@
ifneq (,$(wildcard ../../build/config.mk))
include ../../build/config.mk
include ../../build/module.mk
include ../../build/gccconfig.mk
else
include standalone.mk
endif
LMO_LDFLAGS =
LMO_CFLAGS =
LMO_SO = lmo.so
LMO_PO2LMO = po2lmo
LMO_LOOKUP = lookup
LMO_COMMON_OBJ = src/lmo_core.o src/lmo_hash.o
LMO_PO2LMO_OBJ = src/lmo_po2lmo.o
LMO_LOOKUP_OBJ = src/lmo_lookup.o
LMO_LUALIB_OBJ = src/lmo_lualib.o
%.o: %.c
$(COMPILE) $(LMO_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
compile: build-clean $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ) $(LMO_LOOKUP_OBJ) $(LMO_LUALIB_OBJ)
$(LINK) $(SHLIB_FLAGS) $(LMO_LDFLAGS) -o src/$(LMO_SO) \
$(LMO_COMMON_OBJ) $(LMO_LUALIB_OBJ)
$(LINK) $(LMO_LDFLAGS) -o src/$(LMO_PO2LMO) $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
$(LINK) $(LMO_LDFLAGS) -o src/$(LMO_LOOKUP) $(LMO_COMMON_OBJ) $(LMO_LOOKUP_OBJ)
mkdir -p dist$(LUA_LIBRARYDIR)
cp src/$(LMO_SO) dist$(LUA_LIBRARYDIR)/$(LMO_SO)
install: build
cp -pR dist$(LUA_LIBRARYDIR)/* $(LUA_LIBRARYDIR)
clean: build-clean
build-clean:
rm -f src/*.o src/lookup src/po2lmo src/lmo.so
host-compile: build-clean host-clean $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
$(LINK) $(LMO_LDFLAGS) -o src/$(LMO_PO2LMO) $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
host-install: host-compile
cp src/$(LMO_PO2LMO) ../../build/$(LMO_PO2LMO)
host-clean:
rm -f ../../build/$(LMO_PO2LMO)

View file

@ -1,234 +0,0 @@
/*
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lmo.h"
extern char _lmo_error[1024];
static int lmo_read32( int fd, uint32_t *val )
{
if( read(fd, val, 4) < 4 )
return -1;
*val = ntohl(*val);
return 4;
}
static char * error(const char *message, int add_errno)
{
memset(_lmo_error, 0, sizeof(_lmo_error));
if( add_errno )
snprintf(_lmo_error, sizeof(_lmo_error),
"%s: %s", message, strerror(errno));
else
snprintf(_lmo_error, sizeof(_lmo_error), "%s", message);
return NULL;
}
const char * lmo_error(void)
{
return _lmo_error;
}
lmo_archive_t * lmo_open(const char *file)
{
int in = -1;
uint32_t idx_offset = 0;
uint32_t i;
struct stat s;
lmo_archive_t *ar = NULL;
lmo_entry_t *head = NULL;
lmo_entry_t *entry = NULL;
if( stat(file, &s) == -1 )
{
error("Can not stat file", 1);
goto cleanup;
}
if( (in = open(file, O_RDONLY)) == -1 )
{
error("Can not open file", 1);
goto cleanup;
}
if( lseek(in, -sizeof(uint32_t), SEEK_END) == -1 )
{
error("Can not seek to eof", 1);
goto cleanup;
}
if( lmo_read32(in, &idx_offset) != 4 )
{
error("Unexpected EOF while reading index offset", 0);
goto cleanup;
}
if( lseek(in, (off_t)idx_offset, SEEK_SET) == -1 )
{
error("Can not seek to index offset", 1);
goto cleanup;
}
if( (ar = (lmo_archive_t *) malloc(sizeof(lmo_archive_t))) != NULL )
{
ar->fd = in;
ar->length = idx_offset;
fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
for( i = idx_offset;
i < (s.st_size - sizeof(uint32_t));
i += (4 * sizeof(uint32_t))
) {
if( (entry = (lmo_entry_t *) malloc(sizeof(lmo_entry_t))) != NULL )
{
if( (lmo_read32(ar->fd, &entry->key_id) == 4) &&
(lmo_read32(ar->fd, &entry->val_id) == 4) &&
(lmo_read32(ar->fd, &entry->offset) == 4) &&
(lmo_read32(ar->fd, &entry->length) == 4)
) {
entry->next = head;
head = entry;
}
else
{
error("Unexpected EOF while reading index entry", 0);
goto cleanup;
}
}
else
{
error("Out of memory", 0);
goto cleanup;
}
}
ar->index = head;
if( lseek(ar->fd, 0, SEEK_SET) == -1 )
{
error("Can not seek to start", 1);
goto cleanup;
}
if( (ar->mmap = mmap(NULL, ar->length, PROT_READ, MAP_PRIVATE, ar->fd, 0)) == MAP_FAILED )
{
error("Failed to memory map archive contents", 1);
goto cleanup;
}
return ar;
}
else
{
error("Out of memory", 0);
goto cleanup;
}
cleanup:
if( in > -1 )
close(in);
if( head != NULL )
{
entry = head;
while( entry != NULL )
{
head = entry->next;
free(entry);
entry = head;
}
head = entry = NULL;
}
if( ar != NULL )
{
if( (ar->mmap != NULL) && (ar->mmap != MAP_FAILED) )
munmap(ar->mmap, ar->length);
free(ar);
ar = NULL;
}
return NULL;
}
void lmo_close(lmo_archive_t *ar)
{
lmo_entry_t *head = NULL;
lmo_entry_t *entry = NULL;
if( ar != NULL )
{
entry = ar->index;
while( entry != NULL )
{
head = entry->next;
free(entry);
entry = head;
}
head = entry = NULL;
if( (ar->mmap != NULL) && (ar->mmap != MAP_FAILED) )
munmap(ar->mmap, ar->length);
close(ar->fd);
free(ar);
ar = NULL;
}
}
int lmo_lookup(lmo_archive_t *ar, const char *key, char *dest, int len)
{
uint32_t look_key = sfh_hash(key, strlen(key));
int copy_len = -1;
lmo_entry_t *entry;
if( !ar )
return copy_len;
entry = ar->index;
while( entry != NULL )
{
if( entry->key_id == look_key )
{
copy_len = ((len - 1) > entry->length) ? entry->length : (len - 1);
memcpy(dest, &ar->mmap[entry->offset], copy_len);
dest[copy_len] = '\0';
break;
}
entry = entry->next;
}
return copy_len;
}

View file

@ -1,53 +0,0 @@
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
* Copyright (C) 2004-2008 by Paul Hsieh
*/
#include "lmo.h"
uint32_t sfh_hash(const char * data, int len)
{
uint32_t hash = len, tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += sfh_get16(data);
tmp = (sfh_get16(data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof(uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += sfh_get16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}

View file

@ -1,58 +0,0 @@
/*
* lmo - Lua Machine Objects - Lookup utility
*
* Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lmo.h"
extern char _lmo_error[1024];
static void die(const char *msg)
{
printf("Error: %s\n", msg);
exit(1);
}
static void usage(const char *name)
{
printf("Usage: %s input.lmo key\n", name);
exit(1);
}
int main(int argc, char *argv[])
{
char val[4096];
lmo_archive_t *ar = NULL;
if( argc != 3 )
usage(argv[0]);
if( (ar = (lmo_archive_t *) lmo_open(argv[1])) != NULL )
{
if( lmo_lookup(ar, argv[2], val, sizeof(val)) > -1 )
{
printf("%s\n", val);
}
lmo_close(ar);
}
else
{
die(lmo_error());
}
return 0;
}

View file

@ -1,237 +0,0 @@
/*
* lmo - Lua Machine Objects - Lua binding
*
* Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lmo_lualib.h"
extern char _lmo_error[1024];
static int lmo_L_open(lua_State *L) {
const char *filename = luaL_checklstring(L, 1, NULL);
lmo_archive_t *ar, **udata;
if( (ar = lmo_open(filename)) != NULL )
{
if( (udata = lua_newuserdata(L, sizeof(lmo_archive_t *))) != NULL )
{
*udata = ar;
luaL_getmetatable(L, LMO_ARCHIVE_META);
lua_setmetatable(L, -2);
return 1;
}
lmo_close(ar);
lua_pushnil(L);
lua_pushstring(L, "out of memory");
return 2;
}
lua_pushnil(L);
lua_pushstring(L, lmo_error());
return 2;
}
static int lmo_L_hash(lua_State *L) {
const char *data = luaL_checkstring(L, 1);
uint32_t hash = sfh_hash(data, strlen(data));
lua_pushinteger(L, (lua_Integer)hash);
return 1;
}
static lmo_luaentry_t *_lmo_push_entry(lua_State *L) {
lmo_luaentry_t *le;
if( (le = lua_newuserdata(L, sizeof(lmo_luaentry_t))) != NULL )
{
luaL_getmetatable(L, LMO_ENTRY_META);
lua_setmetatable(L, -2);
return le;
}
return NULL;
}
static int _lmo_lookup(lua_State *L, lmo_archive_t *ar, uint32_t hash) {
lmo_entry_t *e = ar->index;
lmo_luaentry_t *le = NULL;
while( e != NULL )
{
if( e->key_id == hash )
{
if( (le = _lmo_push_entry(L)) != NULL )
{
le->archive = ar;
le->entry = e;
return 1;
}
else
{
lua_pushnil(L);
lua_pushstring(L, "out of memory");
return 2;
}
}
e = e->next;
}
lua_pushnil(L);
return 1;
}
static int lmo_L_get(lua_State *L) {
lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
uint32_t hash = (uint32_t) luaL_checkinteger(L, 2);
return _lmo_lookup(L, *ar, hash);
}
static int lmo_L_lookup(lua_State *L) {
lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
const char *key = luaL_checkstring(L, 2);
uint32_t hash = sfh_hash(key, strlen(key));
return _lmo_lookup(L, *ar, hash);
}
static int lmo_L_foreach(lua_State *L) {
lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
lmo_entry_t *e = (*ar)->index;
if( lua_isfunction(L, 2) )
{
while( e != NULL )
{
lua_pushvalue(L, 2);
lua_pushinteger(L, e->key_id);
lua_pushlstring(L, &(*ar)->mmap[e->offset], e->length);
lua_pcall(L, 2, 0, 0);
e = e->next;
}
}
return 0;
}
static int lmo_L__gc(lua_State *L) {
lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
if( (*ar) != NULL )
lmo_close(*ar);
*ar = NULL;
return 0;
}
static int lmo_L__tostring(lua_State *L) {
lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
lua_pushfstring(L, "LMO Archive (%d bytes)", (*ar)->length);
return 1;
}
static int _lmo_convert_entry(lua_State *L, int idx) {
lmo_luaentry_t *le = luaL_checkudata(L, idx, LMO_ENTRY_META);
lua_pushlstring(L,
&le->archive->mmap[le->entry->offset],
le->entry->length
);
return 1;
}
static int lmo_L_entry__tostring(lua_State *L) {
return _lmo_convert_entry(L, 1);
}
static int lmo_L_entry__concat(lua_State *L) {
if( lua_isuserdata(L, 1) )
_lmo_convert_entry(L, 1);
else
lua_pushstring(L, lua_tostring(L, 1));
if( lua_isuserdata(L, 2) )
_lmo_convert_entry(L, 2);
else
lua_pushstring(L, lua_tostring(L, 2));
lua_concat(L, 2);
return 1;
}
static int lmo_L_entry__len(lua_State *L) {
lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
lua_pushinteger(L, le->entry->length);
return 1;
}
static int lmo_L_entry__gc(lua_State *L) {
lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
le->archive = NULL;
le->entry = NULL;
return 0;
}
/* lmo method table */
static const luaL_reg M[] = {
{"close", lmo_L__gc},
{"get", lmo_L_get},
{"lookup", lmo_L_lookup},
{"foreach", lmo_L_foreach},
{"__tostring", lmo_L__tostring},
{"__gc", lmo_L__gc},
{NULL, NULL}
};
/* lmo.entry method table */
static const luaL_reg E[] = {
{"__tostring", lmo_L_entry__tostring},
{"__concat", lmo_L_entry__concat},
{"__len", lmo_L_entry__len},
{"__gc", lmo_L_entry__gc},
{NULL, NULL}
};
/* module table */
static const luaL_reg R[] = {
{"open", lmo_L_open},
{"hash", lmo_L_hash},
{NULL, NULL}
};
LUALIB_API int luaopen_lmo(lua_State *L) {
luaL_newmetatable(L, LMO_ARCHIVE_META);
luaL_register(L, NULL, M);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_setglobal(L, LMO_ARCHIVE_META);
luaL_newmetatable(L, LMO_ENTRY_META);
luaL_register(L, NULL, E);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_setglobal(L, LMO_ENTRY_META);
luaL_register(L, LMO_LUALIB_META, R);
return 1;
}

View file

@ -1,42 +0,0 @@
/*
* lmo - Lua Machine Objects - Lua library header
*
* Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LMO_LUALIB_H_
#define _LMO_LUALIB_H_
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "lmo.h"
#define LMO_LUALIB_META "lmo"
#define LMO_ARCHIVE_META "lmo.archive"
#define LMO_ENTRY_META "lmo.entry"
struct lmo_luaentry {
lmo_archive_t *archive;
lmo_entry_t *entry;
};
typedef struct lmo_luaentry lmo_luaentry_t;
LUALIB_API int luaopen_lmo(lua_State *L);
#endif

View file

@ -1,19 +1,28 @@
ifneq (,$(wildcard ../../build/config.mk))
include ../../build/config.mk
include ../../build/module.mk
include ../../build/gccconfig.mk
else
include standalone.mk
endif
TPL_LDFLAGS =
TPL_CFLAGS =
TPL_SO = parser.so
TPL_PO2LMO = po2lmo
TPL_PO2LMO_OBJ = src/po2lmo.o
TPL_LMO_OBJ = src/template_lmo.o
TPL_COMMON_OBJ = src/template_parser.o src/template_utils.o
TPL_LUALIB_OBJ = src/template_lualib.o
%.o: %.c
$(COMPILE) $(TPL_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ)
compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
$(LINK) $(SHLIB_FLAGS) $(TPL_LDFLAGS) -o src/$(TPL_SO) \
$(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ)
$(TPL_COMMON_OBJ) $(TPL_LMO_OBJ) $(TPL_LUALIB_OBJ)
$(LINK) -o src/$(TPL_PO2LMO) \
$(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
mkdir -p dist$(LUCI_LIBRARYDIR)/template
cp src/$(TPL_SO) dist$(LUCI_LIBRARYDIR)/template/$(TPL_SO)
@ -24,3 +33,12 @@ clean: build-clean
build-clean:
rm -f src/*.o src/$(TPL_SO)
host-compile: build-clean host-clean $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
$(LINK) -o src/$(TPL_PO2LMO) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
host-install: host-compile
cp src/$(TPL_PO2LMO) ../../build/$(TPL_PO2LMO)
host-clean:
rm -f ../../build/$(TPL_PO2LMO)

View file

@ -1097,216 +1097,243 @@ function cbi_tag_last(container)
}
}
if( ! String.serialize )
String.serialize = function(o)
String.prototype.serialize = function()
{
var o = this;
switch(typeof(o))
{
switch(typeof(o))
{
case 'object':
// null
if( o == null )
{
return 'null';
}
// array
else if( o.length )
{
var i, s = '';
for( var i = 0; i < o.length; i++ )
s += (s ? ', ' : '') + String.serialize(o[i]);
return '[ ' + s + ' ]';
}
// object
else
{
var k, s = '';
for( k in o )
s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
return '{ ' + s + ' }';
}
break;
case 'string':
// complex string
if( o.match(/[^a-zA-Z0-9_,.: -]/) )
return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
// simple string
else
return '"' + o + '"';
break;
default:
return o.toString();
}
}
if( ! String.format )
String.format = function()
{
if (!arguments || arguments.length < 1 || !RegExp)
return;
var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
function esc(s, r) {
for( var i = 0; i < r.length; i += 2 )
s = s.replace(r[i], r[i+1]);
return s;
}
var str = arguments[0];
var out = '';
var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
var a = b = [], numSubstitutions = 0, numMatches = 0;
while( a = re.exec(str) )
{
var m = a[1];
var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
var pPrecision = a[6], pType = a[7];
numMatches++;
if (pType == '%')
case 'object':
// null
if( o == null )
{
subst = '%';
return 'null';
}
// array
else if( o.length )
{
var i, s = '';
for( var i = 0; i < o.length; i++ )
s += (s ? ', ' : '') + String.serialize(o[i]);
return '[ ' + s + ' ]';
}
// object
else
{
if (numSubstitutions++ < arguments.length)
{
var param = arguments[numSubstitutions];
var k, s = '';
var pad = '';
if (pPad && pPad.substr(0,1) == "'")
pad = leftpart.substr(1,1);
else if (pPad)
pad = pPad;
for( k in o )
s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
var justifyRight = true;
if (pJustify && pJustify === "-")
justifyRight = false;
var minLength = -1;
if (pMinLength)
minLength = parseInt(pMinLength);
var precision = -1;
if (pPrecision && pType == 'f')
precision = parseInt(pPrecision.substring(1));
var subst = param;
switch(pType)
{
case 'b':
subst = (parseInt(param) || 0).toString(2);
break;
case 'c':
subst = String.fromCharCode(parseInt(param) || 0);
break;
case 'd':
subst = (parseInt(param) || 0);
break;
case 'u':
subst = Math.abs(parseInt(param) || 0);
break;
case 'f':
subst = (precision > -1)
? ((parseFloat(param) || 0.0)).toFixed(precision)
: (parseFloat(param) || 0.0);
break;
case 'o':
subst = (parseInt(param) || 0).toString(8);
break;
case 's':
subst = param;
break;
case 'x':
subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
break;
case 'X':
subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
break;
case 'h':
subst = esc(param, html_esc);
break;
case 'q':
subst = esc(param, quot_esc);
break;
case 'j':
subst = String.serialize(param);
break;
case 't':
var td = 0;
var th = 0;
var tm = 0;
var ts = (param || 0);
if (ts > 60) {
tm = Math.floor(ts / 60);
ts = (ts % 60);
}
if (tm > 60) {
th = Math.floor(tm / 60);
tm = (tm % 60);
}
if (th > 24) {
td = Math.floor(th / 24);
th = (th % 24);
}
subst = (td > 0)
? String.format('%dd %dh %dm %ds', td, th, tm, ts)
: String.format('%dh %dm %ds', th, tm, ts);
break;
case 'm':
var mf = pMinLength ? parseInt(pMinLength) : 1000;
var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
var i = 0;
var val = parseFloat(param || 0);
var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
for (i = 0; (i < units.length) && (val > mf); i++)
val /= mf;
subst = val.toFixed(pr) + ' ' + units[i];
break;
}
}
return '{ ' + s + ' }';
}
out += leftpart + subst;
str = str.substr(m.length);
break;
case 'string':
// complex string
if( o.match(/[^a-zA-Z0-9_,.: -]/) )
return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
// simple string
else
return '"' + o + '"';
break;
default:
return o.toString();
}
}
String.prototype.format = function()
{
if (!RegExp)
return;
var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
function esc(s, r) {
for( var i = 0; i < r.length; i += 2 )
s = s.replace(r[i], r[i+1]);
return s;
}
var str = this;
var out = '';
var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
var a = b = [], numSubstitutions = 0, numMatches = 0;
while( a = re.exec(str) )
{
var m = a[1];
var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
var pPrecision = a[6], pType = a[7];
numMatches++;
if (pType == '%')
{
subst = '%';
}
else
{
if (numSubstitutions < arguments.length)
{
var param = arguments[numSubstitutions++];
var pad = '';
if (pPad && pPad.substr(0,1) == "'")
pad = leftpart.substr(1,1);
else if (pPad)
pad = pPad;
var justifyRight = true;
if (pJustify && pJustify === "-")
justifyRight = false;
var minLength = -1;
if (pMinLength)
minLength = parseInt(pMinLength);
var precision = -1;
if (pPrecision && pType == 'f')
precision = parseInt(pPrecision.substring(1));
var subst = param;
switch(pType)
{
case 'b':
subst = (parseInt(param) || 0).toString(2);
break;
case 'c':
subst = String.fromCharCode(parseInt(param) || 0);
break;
case 'd':
subst = (parseInt(param) || 0);
break;
case 'u':
subst = Math.abs(parseInt(param) || 0);
break;
case 'f':
subst = (precision > -1)
? ((parseFloat(param) || 0.0)).toFixed(precision)
: (parseFloat(param) || 0.0);
break;
case 'o':
subst = (parseInt(param) || 0).toString(8);
break;
case 's':
subst = param;
break;
case 'x':
subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
break;
case 'X':
subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
break;
case 'h':
subst = esc(param, html_esc);
break;
case 'q':
subst = esc(param, quot_esc);
break;
case 'j':
subst = String.serialize(param);
break;
case 't':
var td = 0;
var th = 0;
var tm = 0;
var ts = (param || 0);
if (ts > 60) {
tm = Math.floor(ts / 60);
ts = (ts % 60);
}
if (tm > 60) {
th = Math.floor(tm / 60);
tm = (tm % 60);
}
if (th > 24) {
td = Math.floor(th / 24);
th = (th % 24);
}
subst = (td > 0)
? String.format('%dd %dh %dm %ds', td, th, tm, ts)
: String.format('%dh %dm %ds', th, tm, ts);
break;
case 'm':
var mf = pMinLength ? parseInt(pMinLength) : 1000;
var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
var i = 0;
var val = parseFloat(param || 0);
var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
for (i = 0; (i < units.length) && (val > mf); i++)
val /= mf;
subst = val.toFixed(pr) + ' ' + units[i];
break;
}
}
}
return out + str;
out += leftpart + subst;
str = str.substr(m.length);
}
return out + str;
}
String.prototype.nobr = function()
{
return this.replace(/[\s\n]+/g, '&#160;');
}
String.serialize = function()
{
var a = [ ];
for (var i = 1; i < arguments.length; i++)
a.push(arguments[i]);
return ''.serialize.apply(arguments[0], a);
}
String.format = function()
{
var a = [ ];
for (var i = 1; i < arguments.length; i++)
a.push(arguments[i]);
return ''.format.apply(arguments[0], a);
}
String.nobr = function()
{
var a = [ ];
for (var i = 1; i < arguments.length; i++)
a.push(arguments[i]);
return ''.nobr.apply(arguments[0], a);
}

View file

@ -74,8 +74,6 @@ function load(cbimap, ...)
assert(func, err)
luci.i18n.loadc("base")
local env = {
translate=i18n.translate,
translatef=i18n.translatef,

View file

@ -4,9 +4,6 @@ LuCI - HTTP-Interaction
Description:
HTTP-Header manipulator and form variable preprocessor
FileId:
$Id$
License:
Copyright 2008 Steven Barth <steven@midlink.org>
@ -334,12 +331,14 @@ function write_json(x)
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.
-- NaN is the only value that doesn't equal to itself.
write("Number.NaN")
else
write(tostring(x))
end
else
write("%q" % tostring(x))
write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
return '\\u%04x' % c:byte(1)
end))
end
end

View file

@ -27,7 +27,8 @@ limitations under the License.
--- LuCI translation library.
module("luci.i18n", package.seeall)
require("luci.util")
require("lmo")
local tparser = require "luci.template.parser"
table = {}
i18ndir = luci.util.libpath() .. "/i18n/"
@ -37,7 +38,6 @@ default = "en"
--- Clear the translation table.
function clear()
table = {}
end
--- Load a translation and copy its data into the translation table.
@ -46,33 +46,6 @@ end
-- @param force Force reload even if already loaded (optional)
-- @return Success status
function load(file, lang, force)
lang = lang and lang:gsub("_", "-") or ""
if force or not loaded[lang] or not loaded[lang][file] then
local f = lmo.open(i18ndir .. file .. "." .. lang .. ".lmo")
if f then
if not table[lang] then
table[lang] = { f }
setmetatable(table[lang], {
__index = function(tbl, key)
for i = 1, #tbl do
local s = rawget(tbl, i):lookup(key)
if s then return s end
end
end
})
else
table[lang][#table[lang]+1] = f
end
loaded[lang] = loaded[lang] or {}
loaded[lang][file] = true
return true
else
return false
end
else
return true
end
end
--- Load a translation file using the default translation language.
@ -80,9 +53,6 @@ end
-- @param file Language file
-- @param force Force reload even if already loaded (optional)
function loadc(file, force)
load(file, default, force)
if context.parent then load(file, context.parent, force) end
return load(file, context.lang, force)
end
--- Set the context default translation language.
@ -90,16 +60,20 @@ end
function setlanguage(lang)
context.lang = lang:gsub("_", "-")
context.parent = (context.lang:match("^([a-z][a-z])_"))
if not tparser.load_catalog(context.lang, i18ndir) then
if context.parent then
tparser.load_catalog(context.parent, i18ndir)
return context.parent
end
end
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 (table[context.lang] and table[context.lang][key])
or (table[context.parent] and table[context.parent][key])
or (table[default] and table[default][key])
or key
return tparser.translate(key) or key
end
--- Return the translated value for a specific translation key and use it as sprintf pattern.

View file

@ -79,9 +79,8 @@ function Template.__init__(self, name)
-- If we have no valid template throw error, otherwise cache the template
if not self.template then
error("Failed to load template '" .. name .. "'.\n" ..
"Error while parsing template '" .. sourcefile .. "'.\n" ..
"A syntax error occured near '" ..
(err or "(nil)"):gsub("\t", "\\t"):gsub("\n", "\\n") .. "'.")
"Error while parsing template '" .. sourcefile .. "':\n" ..
(err or "Unknown syntax error"))
else
self.cache[name] = self.template
end

View file

@ -38,6 +38,6 @@
//]]></script>
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" />
<span id="cbi-apply-<%=id%>-status"><%:Waiting for router...%></span>
<span id="cbi-apply-<%=id%>-status"><%:Waiting for changes to be applied...%></span>
</fieldset>
<%- end) %>

View file

@ -9,11 +9,15 @@
<% if flow.skip then %>
<input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
<% end %>
<% if not autoapply then %>
<% if not autoapply and not flow.hideapplybtn then %>
<input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
<% end %>
<input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
<input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
<% if not flow.hidesavebtn then %>
<input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
<% end %>
<% if not flow.hideresetbtn then %>
<input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
<% end %>
<script type="text/javascript">cbi_d_update();</script>
</div>

View file

@ -1,7 +1,7 @@
/*
* lmo - Lua Machine Objects - PO to LMO conversion tool
*
* Copyright (C) 2009-2011 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* limitations under the License.
*/
#include "lmo.h"
#include "template_lmo.h"
static void die(const char *msg)
{
@ -52,12 +52,19 @@ static int extract_string(const char *src, char *dest, int len)
{
if( esc == 1 )
{
switch (src[pos])
{
case '"':
case '\\':
off++;
break;
}
dest[pos-off] = src[pos];
esc = 0;
}
else if( src[pos] == '\\' )
{
off++;
dest[pos-off] = src[pos];
esc = 1;
}
else if( src[pos] != '"' )
@ -75,6 +82,34 @@ static int extract_string(const char *src, char *dest, int len)
return (off > -1) ? strlen(dest) : -1;
}
static int cmp_index(const void *a, const void *b)
{
uint32_t x = ntohl(((const lmo_entry_t *)a)->key_id);
uint32_t y = ntohl(((const lmo_entry_t *)b)->key_id);
if (x < y)
return -1;
else if (x > y)
return 1;
return 0;
}
static void print_index(void *array, int n, FILE *out)
{
lmo_entry_t *e;
qsort(array, n, sizeof(*e), cmp_index);
for (e = array; n > 0; n--, e++)
{
print(&e->key_id, sizeof(uint32_t), 1, out);
print(&e->val_id, sizeof(uint32_t), 1, out);
print(&e->offset, sizeof(uint32_t), 1, out);
print(&e->length, sizeof(uint32_t), 1, out);
}
}
int main(int argc, char *argv[])
{
char line[4096];
@ -84,14 +119,14 @@ int main(int argc, char *argv[])
int state = 0;
int offset = 0;
int length = 0;
int n_entries = 0;
void *array = NULL;
lmo_entry_t *entry = NULL;
uint32_t key_id, val_id;
FILE *in;
FILE *out;
lmo_entry_t *head = NULL;
lmo_entry_t *entry = NULL;
if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
usage(argv[0]);
@ -160,26 +195,22 @@ int main(int argc, char *argv[])
if( key_id != val_id )
{
if( (entry = (lmo_entry_t *) malloc(sizeof(lmo_entry_t))) != NULL )
{
memset(entry, 0, sizeof(entry));
length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
n_entries++;
array = realloc(array, n_entries * sizeof(lmo_entry_t));
entry = (lmo_entry_t *)array + n_entries - 1;
entry->key_id = htonl(key_id);
entry->val_id = htonl(val_id);
entry->offset = htonl(offset);
entry->length = htonl(strlen(val));
print(val, length, 1, out);
offset += length;
entry->next = head;
head = entry;
}
else
{
if (!array)
die("Out of memory");
}
entry->key_id = htonl(key_id);
entry->val_id = htonl(val_id);
entry->offset = htonl(offset);
entry->length = htonl(strlen(val));
length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
print(val, length, 1, out);
offset += length;
}
}
@ -191,15 +222,7 @@ int main(int argc, char *argv[])
memset(line, 0, sizeof(line));
}
entry = head;
while( entry != NULL )
{
print(&entry->key_id, sizeof(uint32_t), 1, out);
print(&entry->val_id, sizeof(uint32_t), 1, out);
print(&entry->offset, sizeof(uint32_t), 1, out);
print(&entry->length, sizeof(uint32_t), 1, out);
entry = entry->next;
}
print_index(array, n_entries, out);
if( offset > 0 )
{

325
libs/web/src/template_lmo.c Normal file
View file

@ -0,0 +1,325 @@
/*
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "template_lmo.h"
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
* Copyright (C) 2004-2008 by Paul Hsieh
*/
uint32_t sfh_hash(const char *data, int len)
{
uint32_t hash = len, tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += sfh_get16(data);
tmp = (sfh_get16(data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof(uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += sfh_get16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
uint32_t lmo_canon_hash(const char *str, int len)
{
char res[4096];
char *ptr, prev;
int off;
if (!str || len >= sizeof(res))
return 0;
for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
{
if (isspace(*str))
{
if (!isspace(prev))
*ptr++ = ' ';
}
else
{
*ptr++ = *str;
}
}
if ((ptr > res) && isspace(*(ptr-1)))
ptr--;
return sfh_hash(res, ptr - res);
}
lmo_archive_t * lmo_open(const char *file)
{
int in = -1;
uint32_t idx_offset = 0;
struct stat s;
lmo_archive_t *ar = NULL;
if (stat(file, &s) == -1)
goto err;
if ((in = open(file, O_RDONLY)) == -1)
goto err;
if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
{
memset(ar, 0, sizeof(*ar));
ar->fd = in;
ar->size = s.st_size;
fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
goto err;
idx_offset = *((const uint32_t *)
(ar->mmap + ar->size - sizeof(uint32_t)));
if (idx_offset >= ar->size)
goto err;
ar->index = (lmo_entry_t *)(ar->mmap + idx_offset);
ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
ar->end = ar->mmap + ar->size;
return ar;
}
err:
if (in > -1)
close(in);
if (ar != NULL)
{
if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
munmap(ar->mmap, ar->size);
free(ar);
}
return NULL;
}
void lmo_close(lmo_archive_t *ar)
{
if (ar != NULL)
{
if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
munmap(ar->mmap, ar->size);
close(ar->fd);
free(ar);
ar = NULL;
}
}
lmo_catalog_t *_lmo_catalogs = NULL;
lmo_catalog_t *_lmo_active_catalog = NULL;
int lmo_load_catalog(const char *lang, const char *dir)
{
DIR *dh = NULL;
char pattern[16];
char path[PATH_MAX];
struct dirent *de = NULL;
lmo_archive_t *ar = NULL;
lmo_catalog_t *cat = NULL;
if (!lmo_change_catalog(lang))
return 0;
if (!dir || !(dh = opendir(dir)))
goto err;
if (!(cat = malloc(sizeof(*cat))))
goto err;
memset(cat, 0, sizeof(*cat));
snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
while ((de = readdir(dh)) != NULL)
{
if (!fnmatch(pattern, de->d_name, 0))
{
snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
ar = lmo_open(path);
if (ar)
{
ar->next = cat->archives;
cat->archives = ar;
}
}
}
closedir(dh);
cat->next = _lmo_catalogs;
_lmo_catalogs = cat;
if (!_lmo_active_catalog)
_lmo_active_catalog = cat;
return 0;
err:
if (dh) closedir(dh);
if (cat) free(cat);
return -1;
}
int lmo_change_catalog(const char *lang)
{
lmo_catalog_t *cat;
for (cat = _lmo_catalogs; cat; cat = cat->next)
{
if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
{
_lmo_active_catalog = cat;
return 0;
}
}
return -1;
}
static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
{
unsigned int m, l, r;
l = 0;
r = ar->length - 1;
while (1)
{
m = l + ((r - l) / 2);
if (r < l)
break;
if (ar->index[m].key_id == hash)
return &ar->index[m];
if (ar->index[m].key_id > hash)
{
if (!m)
break;
r = m - 1;
}
else
{
l = m + 1;
}
}
return NULL;
}
int lmo_translate(const char *key, int keylen, char **out, int *outlen)
{
uint32_t hash;
lmo_entry_t *e;
lmo_archive_t *ar;
if (!key || !_lmo_active_catalog)
return -2;
hash = htonl(lmo_canon_hash(key, keylen));
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
{
if ((e = lmo_find_entry(ar, hash)) != NULL)
{
*out = ar->mmap + e->offset;
*outlen = e->length;
return 0;
}
}
return -1;
}
void lmo_close_catalog(const char *lang)
{
lmo_archive_t *ar, *next;
lmo_catalog_t *cat, *prev;
for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
{
if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
{
if (prev)
prev->next = cat->next;
else
_lmo_catalogs = cat->next;
for (ar = cat->archives; ar; ar = next)
{
next = ar->next;
lmo_close(ar);
}
free(cat);
break;
}
}
}

View file

@ -1,7 +1,7 @@
/*
* lmo - Lua Machine Objects - General header
*
* Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,8 @@
* limitations under the License.
*/
#ifndef _LMO_H_
#define _LMO_H_
#ifndef _TEMPLATE_LMO_H_
#define _TEMPLATE_LMO_H_
#include <stdlib.h>
#include <stdio.h>
@ -29,7 +29,9 @@
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fnmatch.h>
#include <dirent.h>
#include <ctype.h>
#if (defined(__GNUC__) && defined(__i386__))
#define sfh_get16(d) (*((const uint16_t *) (d)))
@ -44,7 +46,6 @@ struct lmo_entry {
uint32_t val_id;
uint32_t offset;
uint32_t length;
struct lmo_entry *next;
} __attribute__((packed));
typedef struct lmo_entry lmo_entry_t;
@ -52,21 +53,39 @@ typedef struct lmo_entry lmo_entry_t;
struct lmo_archive {
int fd;
uint32_t length;
int length;
uint32_t size;
lmo_entry_t *index;
char *mmap;
char *end;
struct lmo_archive *next;
};
typedef struct lmo_archive lmo_archive_t;
uint32_t sfh_hash(const char * data, int len);
struct lmo_catalog {
char lang[6];
struct lmo_archive *archives;
struct lmo_catalog *next;
};
char _lmo_error[1024];
const char * lmo_error(void);
typedef struct lmo_catalog lmo_catalog_t;
uint32_t sfh_hash(const char *data, int len);
uint32_t lmo_canon_hash(const char *data, int len);
lmo_archive_t * lmo_open(const char *file);
int lmo_lookup(lmo_archive_t *ar, const char *key, char *dest, int len);
void lmo_close(lmo_archive_t *ar);
extern lmo_catalog_t *_lmo_catalogs;
extern lmo_catalog_t *_lmo_active_catalog;
int lmo_load_catalog(const char *lang, const char *dir);
int lmo_change_catalog(const char *lang);
int lmo_translate(const char *key, int keylen, char **out, int *outlen);
void lmo_close_catalog(const char *lang);
#endif

View file

@ -21,79 +21,139 @@
int template_L_parse(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
struct template_parser parser;
int lua_status;
struct template_parser *parser = template_open(file);
int lua_status, rv;
if( (parser.fd = open(file, O_RDONLY)) > 0 )
if (!parser)
{
parser.flags = 0;
parser.bufsize = 0;
parser.state = T_STATE_TEXT_NEXT;
lua_pushnil(L);
lua_pushinteger(L, errno);
lua_pushstring(L, strerror(errno));
return 3;
}
lua_status = lua_load(L, template_reader, &parser, file);
lua_status = lua_load(L, template_reader, parser, file);
(void) close(parser.fd);
if (lua_status == 0)
rv = 1;
else
rv = template_error(L, parser);
template_close(parser);
if( lua_status == 0 )
{
return rv;
}
int template_L_utf8(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = utf8(str, len);
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
return 1;
}
return 0;
}
int template_L_pcdata(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = pcdata(str, len);
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
return 1;
}
return 0;
}
int template_L_striptags(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = striptags(str, len);
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
return 1;
}
return 0;
}
static int template_L_load_catalog(lua_State *L) {
const char *lang = luaL_optstring(L, 1, "en");
const char *dir = luaL_optstring(L, 2, NULL);
lua_pushboolean(L, !lmo_load_catalog(lang, dir));
return 1;
}
static int template_L_close_catalog(lua_State *L) {
const char *lang = luaL_optstring(L, 1, "en");
lmo_close_catalog(lang);
return 0;
}
static int template_L_change_catalog(lua_State *L) {
const char *lang = luaL_optstring(L, 1, "en");
lua_pushboolean(L, !lmo_change_catalog(lang));
return 1;
}
static int template_L_translate(lua_State *L) {
size_t len;
char *tr;
int trlen;
const char *key = luaL_checklstring(L, 1, &len);
switch (lmo_translate(key, len, &tr, &trlen))
{
case 0:
lua_pushlstring(L, tr, trlen);
return 1;
}
else
{
lua_pushnil(L);
lua_pushinteger(L, lua_status);
lua_pushlstring(L, parser.out, parser.outsize);
return 3;
}
case -1:
return 0;
}
lua_pushnil(L);
lua_pushinteger(L, 255);
lua_pushstring(L, "No such file or directory");
return 3;
lua_pushstring(L, "no catalog loaded");
return 2;
}
int template_L_sanitize_utf8(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = sanitize_utf8(str, len);
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
return 1;
}
return 0;
}
int template_L_sanitize_pcdata(lua_State *L)
{
size_t len = 0;
const char *str = luaL_checklstring(L, 1, &len);
char *res = sanitize_pcdata(str, len);
if (res != NULL)
{
lua_pushstring(L, res);
free(res);
return 1;
}
return 0;
static int template_L_hash(lua_State *L) {
size_t len;
const char *key = luaL_checklstring(L, 1, &len);
lua_pushinteger(L, sfh_hash(key, len));
return 1;
}
/* module table */
static const luaL_reg R[] = {
{ "parse", template_L_parse },
{ "sanitize_utf8", template_L_sanitize_utf8 },
{ "sanitize_pcdata", template_L_sanitize_pcdata },
{ "utf8", template_L_utf8 },
{ "pcdata", template_L_pcdata },
{ "striptags", template_L_striptags },
{ "load_catalog", template_L_load_catalog },
{ "close_catalog", template_L_close_catalog },
{ "change_catalog", template_L_change_catalog },
{ "translate", template_L_translate },
{ "hash", template_L_hash },
{ NULL, NULL }
};

View file

@ -21,6 +21,7 @@
#include "template_parser.h"
#include "template_utils.h"
#include "template_lmo.h"
#define TEMPLATE_LUALIB_META "template.parser"

View file

@ -1,7 +1,7 @@
/*
* LuCI Template - Parser implementation
*
* Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,17 +17,21 @@
*/
#include "template_parser.h"
#include "template_utils.h"
#include "template_lmo.h"
/* leading and trailing code for different types */
const char * gen_code[7][2] = {
const char *gen_code[9][2] = {
{ NULL, NULL },
{ "write(\"", "\")" },
{ NULL, NULL },
{ "write(tostring(", " or \"\"))" },
{ "include(\"", "\")" },
{ "write(pcdata(translate(\"", "\")))" },
{ "write(translate(\"", "\"))" },
{ NULL, " " }
{ "write(\"", "\")" },
{ "write(\"", "\")" },
{ NULL, " " },
{ NULL, NULL },
};
/* Simple strstr() like function that takes len arguments for both haystack and needle. */
@ -59,407 +63,324 @@ static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
return NULL;
}
/*
* Inspect current read buffer and find the number of "vague" characters at the end
* which could indicate an opening token. Returns the number of "vague" chars.
* The last continuous sequence of whitespace, optionally followed by a "<" is
* treated as "vague" because whitespace may be discarded if the upcoming opening
* token indicates pre-whitespace-removal ("<%-"). A single remaining "<" char
* can't be differentiated from an opening token ("<%"), so it's kept to be processed
* in the next cycle.
*/
static int stokscan(struct template_parser *data, int off, int no_whitespace)
struct template_parser * template_open(const char *file)
{
int i;
int skip = 0;
int tokoff = data->bufsize - 1;
struct stat s;
static struct template_parser *parser;
for( i = tokoff; i >= off; i-- )
if (!(parser = malloc(sizeof(*parser))))
goto err;
memset(parser, 0, sizeof(*parser));
parser->fd = -1;
parser->file = file;
if (stat(file, &s))
goto err;
if ((parser->fd = open(file, O_RDONLY)) < 0)
goto err;
parser->size = s.st_size;
parser->mmap = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
parser->fd, 0);
if (parser->mmap != MAP_FAILED)
{
if( data->buf[i] == T_TOK_START[0] )
{
skip = tokoff - i + 1;
tokoff = i - 1;
break;
}
parser->off = parser->mmap;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->mmap;
parser->cur_chunk.e = parser->mmap;
return parser;
}
if( !no_whitespace )
{
for( i = tokoff; i >= off; i-- )
{
if( isspace(data->buf[i]) )
skip++;
else
break;
}
}
return skip;
}
/*
* Similar to stokscan() but looking for closing token indicators.
* Matches "-", optionally followed by a "%" char.
*/
static int etokscan(struct template_parser *data)
{
int skip = 0;
if( (data->bufsize > 0) && (data->buf[data->bufsize-1] == T_TOK_END[0]) )
skip++;
if( (data->bufsize > skip) && (data->buf[data->bufsize-skip-1] == T_TOK_SKIPWS[0]) )
skip++;
return skip;
}
/*
* Generate Lua expressions from the given raw code, write it into the
* output buffer and set the lua_Reader specific size pointer.
* Takes parser-state, lua_Reader's size pointer and generator flags
* as parameter. The given flags indicate whether leading or trailing
* code should be added. Returns a pointer to the output buffer.
*/
static const char * generate_expression(struct template_parser *data, size_t *sz, int what)
{
char tmp[T_OUTBUFSZ];
int i;
int size = 0;
int start = 0;
int whitespace = 0;
memset(tmp, 0, T_OUTBUFSZ);
/* Inject leading expression code (if any) */
if( (what & T_GEN_START) && (gen_code[data->type][0] != NULL) )
{
memcpy(tmp, gen_code[data->type][0], strlen(gen_code[data->type][0]));
size += strlen(gen_code[data->type][0]);
}
/* Parse source buffer */
for( i = 0; i < data->outsize; i++ )
{
/* Skip leading whitespace for non-raw and non-expr chunks */
if( !start && isspace(data->out[i]) && (data->type == T_TYPE_I18N ||
data->type == T_TYPE_I18N_RAW || data->type == T_TYPE_INCLUDE) )
continue;
else if( !start )
start = 1;
/* Found whitespace after i18n key */
if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
{
/* Is initial whitespace, insert space */
if( !whitespace && isspace(data->out[i]) )
{
tmp[size++] = ' ';
whitespace = 1;
}
/* Suppress subsequent whitespace, escape special chars */
else if( !isspace(data->out[i]) )
{
if( data->out[i] == '\\' || data->out[i] == '"' )
tmp[size++] = '\\';
tmp[size++] = data->out[i];
whitespace = 0;
}
}
/* Escape quotes, backslashes and newlines for plain and include expressions */
else if( (data->type == T_TYPE_TEXT || data->type == T_TYPE_INCLUDE) &&
(data->out[i] == '\\' || data->out[i] == '"' || data->out[i] == '\n' || data->out[i] == '\t') )
{
tmp[size++] = '\\';
switch(data->out[i])
{
case '\n':
tmp[size++] = 'n';
break;
case '\t':
tmp[size++] = 't';
break;
default:
tmp[size++] = data->out[i];
}
}
/* Normal char */
else
{
tmp[size++] = data->out[i];
}
}
/* Inject trailing expression code (if any) */
if( (what & T_GEN_END) && (gen_code[data->type][1] != NULL) )
{
/* Strip trailing space for i18n expressions */
if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
if( (size > 0) && (tmp[size-1] == ' ') )
size--;
memcpy(&tmp[size], gen_code[data->type][1], strlen(gen_code[data->type][1]));
size += strlen(gen_code[data->type][1]);
}
*sz = data->outsize = size;
memset(data->out, 0, T_OUTBUFSZ);
memcpy(data->out, tmp, size);
//printf("<<<%i|%i|%i|%s>>>\n", what, data->type, *sz, data->out);
return data->out;
}
/*
* Move the number of bytes specified in data->bufsize from the
* given source pointer to the beginning of the read buffer.
*/
static void bufmove(struct template_parser *data, const char *src)
{
if( data->bufsize > 0 )
memmove(data->buf, src, data->bufsize);
else if( data->bufsize < 0 )
data->bufsize = 0;
data->buf[data->bufsize] = 0;
}
/*
* Move the given amount of bytes from the given source pointer
* to the output buffer and set data->outputsize.
*/
static void bufout(struct template_parser *data, const char *src, int len)
{
if( len >= 0 )
{
memset(data->out, 0, T_OUTBUFSZ);
memcpy(data->out, src, len);
data->outsize = len;
}
else
{
data->outsize = 0;
}
}
/*
* lua_Reader compatible function that parses template code on demand from
* the given file handle.
*/
const char *template_reader(lua_State *L, void *ud, size_t *sz)
{
struct template_parser *data = ud;
char *match = NULL;
int off = 0;
int ignore = 0;
int genflags = 0;
int readlen = 0;
int vague = 0;
while( !(data->flags & T_FLAG_EOF) || (data->bufsize > 0) )
{
/* Fill buffer */
if( !(data->flags & T_FLAG_EOF) && (data->bufsize < T_READBUFSZ) )
{
if( (readlen = read(data->fd, &data->buf[data->bufsize], T_READBUFSZ - data->bufsize)) > 0 )
data->bufsize += readlen;
else if( readlen == 0 )
data->flags |= T_FLAG_EOF;
else
return NULL;
}
/* Evaluate state */
switch(data->state)
{
/* Plain text chunk (before "<%") */
case T_STATE_TEXT_INIT:
case T_STATE_TEXT_NEXT:
off = 0; ignore = 0; *sz = 0;
data->type = T_TYPE_TEXT;
/* Skip leading whitespace if requested */
if( data->flags & T_FLAG_SKIPWS )
{
data->flags &= ~T_FLAG_SKIPWS;
while( (off < data->bufsize) && isspace(data->buf[off]) )
off++;
}
/* Found "<%" */
if( (match = strfind(&data->buf[off], data->bufsize - off - 1, T_TOK_START, strlen(T_TOK_START))) != NULL )
{
readlen = (int)(match - &data->buf[off]);
data->bufsize -= (readlen + strlen(T_TOK_START) + off);
match += strlen(T_TOK_START);
/* Check for leading '-' */
if( match[0] == T_TOK_SKIPWS[0] )
{
data->bufsize--;
match++;
while( (readlen > 1) && isspace(data->buf[off+readlen-1]) )
{
readlen--;
}
}
bufout(data, &data->buf[off], readlen);
bufmove(data, match);
data->state = T_STATE_CODE_INIT;
}
/* Maybe plain chunk */
else
{
/* Preserve trailing "<" or white space, maybe a start token */
vague = stokscan(data, off, 0);
/* We can process some bytes ... */
if( vague < data->bufsize )
{
readlen = data->bufsize - vague - off;
}
/* No bytes to process, so try to remove at least whitespace ... */
else
{
/* ... but try to preserve trailing "<" ... */
vague = stokscan(data, off, 1);
if( vague < data->bufsize )
{
readlen = data->bufsize - vague - off;
}
/* ... no chance, push out buffer */
else
{
readlen = vague - off;
vague = 0;
}
}
bufout(data, &data->buf[off], readlen);
data->state = T_STATE_TEXT_NEXT;
data->bufsize = vague;
bufmove(data, &data->buf[off+readlen]);
}
if( ignore || data->outsize == 0 )
continue;
else
return generate_expression(data, sz, T_GEN_START | T_GEN_END);
break;
/* Ignored chunk (inside "<%# ... %>") */
case T_STATE_SKIP:
ignore = 1;
/* Initial code chunk ("<% ...") */
case T_STATE_CODE_INIT:
off = 0;
/* Check for leading '-' */
if( data->buf[off] == T_TOK_SKIPWS[0] )
off++;
/* Determine code type */
switch(data->buf[off])
{
case '#':
ignore = 1;
off++;
data->type = T_TYPE_COMMENT;
break;
case '=':
off++;
data->type = T_TYPE_EXPR;
break;
case '+':
off++;
data->type = T_TYPE_INCLUDE;
break;
case ':':
off++;
data->type = T_TYPE_I18N;
break;
case '_':
off++;
data->type = T_TYPE_I18N_RAW;
break;
default:
data->type = T_TYPE_CODE;
break;
}
/* Subsequent code chunk ("..." or "... %>") */
case T_STATE_CODE_NEXT:
/* Found "%>" */
if( (match = strfind(&data->buf[off], data->bufsize - off, T_TOK_END, strlen(T_TOK_END))) != NULL )
{
genflags = ( data->state == T_STATE_CODE_INIT )
? (T_GEN_START | T_GEN_END) : T_GEN_END;
readlen = (int)(match - &data->buf[off]);
/* Check for trailing '-' */
if( (match > data->buf) && (*(match-1) == T_TOK_SKIPWS[0]) )
{
readlen--;
data->flags |= T_FLAG_SKIPWS;
}
bufout(data, &data->buf[off], readlen);
data->state = T_STATE_TEXT_INIT;
data->bufsize -= ((int)(match - &data->buf[off]) + strlen(T_TOK_END) + off);
bufmove(data, &match[strlen(T_TOK_END)]);
}
/* Code chunk */
else
{
genflags = ( data->state == T_STATE_CODE_INIT ) ? T_GEN_START : 0;
/* Preserve trailing "%" and "-", maybe an end token */
vague = etokscan(data);
readlen = data->bufsize - off - vague;
bufout(data, &data->buf[off], readlen);
data->state = T_STATE_CODE_NEXT;
data->bufsize = vague;
bufmove(data, &data->buf[readlen+off]);
}
if( ignore || (data->outsize == 0 && !genflags) )
continue;
else
return generate_expression(data, sz, genflags);
break;
}
}
*sz = 0;
err:
template_close(parser);
return NULL;
}
void template_close(struct template_parser *parser)
{
if (!parser)
return;
if (parser->gc != NULL)
free(parser->gc);
if ((parser->mmap != NULL) && (parser->mmap != MAP_FAILED))
munmap(parser->mmap, parser->size);
if (parser->fd >= 0)
close(parser->fd);
free(parser);
}
void template_text(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
if (s < (parser->mmap + parser->size))
{
if (parser->strip_after)
{
while ((s <= e) && isspace(*s))
s++;
}
parser->cur_chunk.type = T_TYPE_TEXT;
}
else
{
parser->cur_chunk.type = T_TYPE_EOF;
}
parser->cur_chunk.line = parser->line;
parser->cur_chunk.s = s;
parser->cur_chunk.e = e;
}
void template_code(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
parser->strip_before = 0;
parser->strip_after = 0;
if (*s == '-')
{
parser->strip_before = 1;
for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
}
if (*(e-1) == '-')
{
parser->strip_after = 1;
for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
}
switch (*s)
{
/* comment */
case '#':
s++;
parser->cur_chunk.type = T_TYPE_COMMENT;
break;
/* include */
case '+':
s++;
parser->cur_chunk.type = T_TYPE_INCLUDE;
break;
/* translate */
case ':':
s++;
parser->cur_chunk.type = T_TYPE_I18N;
break;
/* translate raw */
case '_':
s++;
parser->cur_chunk.type = T_TYPE_I18N_RAW;
break;
/* expr */
case '=':
s++;
parser->cur_chunk.type = T_TYPE_EXPR;
break;
/* code */
default:
parser->cur_chunk.type = T_TYPE_CODE;
break;
}
parser->cur_chunk.line = parser->line;
parser->cur_chunk.s = s;
parser->cur_chunk.e = e;
}
static const char *
template_format_chunk(struct template_parser *parser, size_t *sz)
{
const char *s, *p;
const char *head, *tail;
struct template_chunk *c = &parser->prv_chunk;
struct template_buffer *buf;
*sz = 0;
s = parser->gc = NULL;
if (parser->strip_before && c->type == T_TYPE_TEXT)
{
while ((c->e > c->s) && isspace(*(c->e - 1)))
c->e--;
}
/* empty chunk */
if (c->s == c->e)
{
if (c->type == T_TYPE_EOF)
{
*sz = 0;
s = NULL;
}
else
{
*sz = 1;
s = " ";
}
}
/* format chunk */
else if ((buf = buf_init(c->e - c->s)) != NULL)
{
if ((head = gen_code[c->type][0]) != NULL)
buf_append(buf, head, strlen(head));
switch (c->type)
{
case T_TYPE_TEXT:
luastr_escape(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_EXPR:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
case T_TYPE_INCLUDE:
luastr_escape(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_I18N:
luastr_translate(buf, c->s, c->e - c->s, 1);
break;
case T_TYPE_I18N_RAW:
luastr_translate(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_CODE:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
}
if ((tail = gen_code[c->type][1]) != NULL)
buf_append(buf, tail, strlen(tail));
*sz = buf_length(buf);
s = parser->gc = buf_destroy(buf);
if (!*sz)
{
*sz = 1;
s = " ";
}
}
return s;
}
const char *template_reader(lua_State *L, void *ud, size_t *sz)
{
struct template_parser *parser = ud;
int rem = parser->size - (parser->off - parser->mmap);
char *tag;
parser->prv_chunk = parser->cur_chunk;
/* free previous string */
if (parser->gc)
{
free(parser->gc);
parser->gc = NULL;
}
/* before tag */
if (!parser->in_expr)
{
if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL)
{
template_text(parser, tag);
parser->off = tag + 2;
parser->in_expr = 1;
}
else
{
template_text(parser, parser->mmap + parser->size);
parser->off = parser->mmap + parser->size;
}
}
/* inside tag */
else
{
if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL)
{
template_code(parser, tag);
parser->off = tag + 2;
parser->in_expr = 0;
}
else
{
/* unexpected EOF */
template_code(parser, parser->mmap + parser->size);
*sz = 1;
return "\033";
}
}
return template_format_chunk(parser, sz);
}
int template_error(lua_State *L, struct template_parser *parser)
{
const char *err = luaL_checkstring(L, -1);
const char *off = parser->prv_chunk.s;
const char *ptr;
char msg[1024];
int line = 0;
int chunkline = 0;
if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL)
{
chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
while (*ptr)
{
if (*ptr++ == ' ')
{
err = ptr;
break;
}
}
}
if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL)
{
off = parser->mmap + parser->size;
err = "'%>' expected before end of file";
chunkline = 0;
}
for (ptr = parser->mmap; ptr < off; ptr++)
if (*ptr == '\n')
line++;
snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
parser->file, line + chunkline, err ? err : "(unknown error)");
lua_pushnil(L);
lua_pushinteger(L, line + chunkline);
lua_pushstring(L, msg);
return 3;
}

View file

@ -21,61 +21,59 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#define T_READBUFSZ 1024
#define T_OUTBUFSZ T_READBUFSZ * 3
/* parser states */
#define T_STATE_TEXT_INIT 0
#define T_STATE_TEXT_NEXT 1
#define T_STATE_CODE_INIT 2
#define T_STATE_CODE_NEXT 3
#define T_STATE_SKIP 4
/* parser flags */
#define T_FLAG_EOF 0x01
#define T_FLAG_SKIPWS 0x02
/* tokens used in matching and expression generation */
#define T_TOK_START "<%"
#define T_TOK_END "%>"
#define T_TOK_SKIPWS "-"
/* generator flags */
#define T_GEN_START 0x01
#define T_GEN_END 0x02
/* code types */
#define T_TYPE_TEXT 0
#define T_TYPE_COMMENT 1
#define T_TYPE_EXPR 2
#define T_TYPE_INCLUDE 3
#define T_TYPE_I18N 4
#define T_TYPE_I18N_RAW 5
#define T_TYPE_CODE 6
#define T_TYPE_INIT 0
#define T_TYPE_TEXT 1
#define T_TYPE_COMMENT 2
#define T_TYPE_EXPR 3
#define T_TYPE_INCLUDE 4
#define T_TYPE_I18N 5
#define T_TYPE_I18N_RAW 6
#define T_TYPE_CODE 7
#define T_TYPE_EOF 8
struct template_chunk {
const char *s;
const char *e;
int type;
int line;
};
/* parser state */
struct template_parser {
int fd;
int bufsize;
int outsize;
int state;
int flags;
int type;
char buf[T_READBUFSZ];
char out[T_OUTBUFSZ];
uint32_t size;
char *mmap;
char *off;
char *gc;
int line;
int in_expr;
int strip_before;
int strip_after;
struct template_chunk prv_chunk;
struct template_chunk cur_chunk;
const char *file;
};
struct template_parser * template_open(const char *file);
void template_close(struct template_parser *parser);
const char *template_reader(lua_State *L, void *ud, size_t *sz);
int template_error(lua_State *L, struct template_parser *parser);
#endif

View file

@ -17,19 +17,23 @@
*/
#include "template_utils.h"
#include "template_lmo.h"
/* initialize a buffer object */
static struct template_buffer * buf_init(void)
struct template_buffer * buf_init(int size)
{
struct template_buffer *buf;
if (size <= 0)
size = 1024;
buf = (struct template_buffer *)malloc(sizeof(struct template_buffer));
if (buf != NULL)
{
buf->fill = 0;
buf->size = 1024;
buf->data = (unsigned char *)malloc(buf->size);
buf->size = size;
buf->data = malloc(buf->size);
if (buf->data != NULL)
{
@ -46,17 +50,21 @@ static struct template_buffer * buf_init(void)
}
/* grow buffer */
static int buf_grow(struct template_buffer *buf)
int buf_grow(struct template_buffer *buf, int size)
{
unsigned int off = (buf->dptr - buf->data);
unsigned char *data =
(unsigned char *)realloc(buf->data, buf->size + 1024);
char *data;
if (size <= 0)
size = 1024;
data = realloc(buf->data, buf->size + size);
if (data != NULL)
{
buf->data = data;
buf->dptr = data + off;
buf->size += 1024;
buf->size += size;
return buf->size;
}
@ -65,9 +73,9 @@ static int buf_grow(struct template_buffer *buf)
}
/* put one char into buffer object */
static int buf_putchar(struct template_buffer *buf, unsigned char c)
int buf_putchar(struct template_buffer *buf, char c)
{
if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf) )
if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) )
return 0;
*(buf->dptr++) = c;
@ -78,11 +86,11 @@ static int buf_putchar(struct template_buffer *buf, unsigned char c)
}
/* append data to buffer */
static int buf_append(struct template_buffer *buf, unsigned char *s, int len)
int buf_append(struct template_buffer *buf, const char *s, int len)
{
while ((buf->fill + len + 1) >= buf->size)
if ((buf->fill + len + 1) >= buf->size)
{
if (!buf_grow(buf))
if (!buf_grow(buf, len + 1))
return 0;
}
@ -95,13 +103,19 @@ static int buf_append(struct template_buffer *buf, unsigned char *s, int len)
return len;
}
/* destroy buffer object and return pointer to data */
static char * buf_destroy(struct template_buffer *buf)
/* read buffer length */
int buf_length(struct template_buffer *buf)
{
unsigned char *data = buf->data;
return buf->fill;
}
/* destroy buffer object and return pointer to data */
char * buf_destroy(struct template_buffer *buf)
{
char *data = buf->data;
free(buf);
return (char *)data;
return data;
}
@ -229,7 +243,7 @@ static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
!mb_is_surrogate(ptr, n) && !mb_is_illegal(ptr, n))
{
/* copy sequence */
if (!buf_append(buf, ptr, n))
if (!buf_append(buf, (char *)ptr, n))
return 0;
}
@ -264,9 +278,9 @@ static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
}
/* sanitize given string and replace all invalid UTF-8 sequences with "?" */
char * sanitize_utf8(const char *s, unsigned int l)
char * utf8(const char *s, unsigned int l)
{
struct template_buffer *buf = buf_init();
struct template_buffer *buf = buf_init(l);
unsigned char *ptr = (unsigned char *)s;
unsigned int v, o;
@ -278,7 +292,7 @@ char * sanitize_utf8(const char *s, unsigned int l)
/* ascii char */
if ((*ptr >= 0x01) && (*ptr <= 0x7F))
{
if (!buf_putchar(buf, *ptr++))
if (!buf_putchar(buf, (char)*ptr++))
break;
}
@ -298,9 +312,9 @@ char * sanitize_utf8(const char *s, unsigned int l)
/* Sanitize given string and strip all invalid XML bytes
* Validate UTF-8 sequences
* Escape XML control chars */
char * sanitize_pcdata(const char *s, unsigned int l)
char * pcdata(const char *s, unsigned int l)
{
struct template_buffer *buf = buf_init();
struct template_buffer *buf = buf_init(l);
unsigned char *ptr = (unsigned char *)s;
unsigned int o, v;
char esq[8];
@ -329,7 +343,7 @@ char * sanitize_pcdata(const char *s, unsigned int l)
{
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
if (!buf_append(buf, (unsigned char *)esq, esl))
if (!buf_append(buf, esq, esl))
break;
ptr++;
@ -338,7 +352,7 @@ char * sanitize_pcdata(const char *s, unsigned int l)
/* ascii char */
else if (*ptr <= 0x7F)
{
buf_putchar(buf, *ptr++);
buf_putchar(buf, (char)*ptr++);
}
/* multi byte sequence */
@ -353,3 +367,128 @@ char * sanitize_pcdata(const char *s, unsigned int l)
return buf_destroy(buf);
}
char * striptags(const char *s, unsigned int l)
{
struct template_buffer *buf = buf_init(l);
unsigned char *ptr = (unsigned char *)s;
unsigned char *end = ptr + l;
unsigned char *tag;
unsigned char prev;
char esq[8];
int esl;
for (prev = ' '; ptr < end; ptr++)
{
if ((*ptr == '<') && ((ptr + 2) < end) &&
((*(ptr + 1) == '/') || isalpha(*(ptr + 1))))
{
for (tag = ptr; tag < end; tag++)
{
if (*tag == '>')
{
if (!isspace(prev))
buf_putchar(buf, ' ');
ptr = tag;
prev = ' ';
break;
}
}
}
else if (isspace(*ptr))
{
if (!isspace(prev))
buf_putchar(buf, *ptr);
prev = *ptr;
}
else
{
switch(*ptr)
{
case '"':
case '\'':
case '<':
case '>':
case '&':
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
buf_append(buf, esq, esl);
break;
default:
buf_putchar(buf, *ptr);
break;
}
prev = *ptr;
}
}
return buf_destroy(buf);
}
void luastr_escape(struct template_buffer *out, const char *s, unsigned int l,
int escape_xml)
{
int esl;
char esq[8];
char *ptr;
for (ptr = (char *)s; ptr < (s + l); ptr++)
{
switch (*ptr)
{
case '\\':
buf_append(out, "\\\\", 2);
break;
case '"':
if (escape_xml)
buf_append(out, "&#34;", 5);
else
buf_append(out, "\\\"", 2);
break;
case '\n':
buf_append(out, "\\n", 2);
break;
case '\'':
case '&':
case '<':
case '>':
if (escape_xml)
{
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
buf_append(out, esq, esl);
break;
}
default:
buf_putchar(out, *ptr);
}
}
}
void luastr_translate(struct template_buffer *out, const char *s, unsigned int l,
int escape_xml)
{
char *tr;
int trlen;
switch (lmo_translate(s, l, &tr, &trlen))
{
case 0:
luastr_escape(out, tr, trlen, escape_xml);
break;
case -1:
luastr_escape(out, s, l, escape_xml);
break;
default:
/* no catalog loaded */
break;
}
}

View file

@ -1,7 +1,7 @@
/*
* LuCI Template - Utility header
*
* Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,13 +26,24 @@
/* buffer object */
struct template_buffer {
unsigned char *data;
unsigned char *dptr;
char *data;
char *dptr;
unsigned int size;
unsigned int fill;
};
char * sanitize_utf8(const char *s, unsigned int l);
char * sanitize_pcdata(const char *s, unsigned int l);
struct template_buffer * buf_init(int size);
int buf_grow(struct template_buffer *buf, int size);
int buf_putchar(struct template_buffer *buf, char c);
int buf_append(struct template_buffer *buf, const char *s, int len);
int buf_length(struct template_buffer *buf);
char * buf_destroy(struct template_buffer *buf);
char * utf8(const char *s, unsigned int l);
char * pcdata(const char *s, unsigned int l);
char * striptags(const char *s, unsigned int l);
void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
void luastr_translate(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
#endif

View file

@ -30,15 +30,13 @@ function action_status()
end
end
function action_restart()
function action_restart(args)
local uci = require "luci.model.uci".cursor()
local rqp = luci.dispatcher.context.requestpath
if rqp[3] then
if args then
local service
local services = { }
for service in rqp[3]:gmatch("[%w_-]+") do
for service in args:gmatch("[%w_-]+") do
services[#services+1] = service
end

View file

@ -153,35 +153,40 @@ function wifi_network(id)
return { }
end
function switch_status(dev)
local ports = { }
local swc = io.popen("swconfig dev %q show" % dev, "r")
if swc then
local l
repeat
l = swc:read("*l")
if l then
local port, up = l:match("port:(%d+) link:(%w+)")
if port then
local speed = l:match(" speed:(%d+)")
local duplex = l:match(" (%w+)-duplex")
local txflow = l:match(" (txflow)")
local rxflow = l:match(" (rxflow)")
local auto = l:match(" (auto)")
function switch_status(devs)
local dev
local switches = { }
for dev in devs:gmatch("[^%s,]+") do
local ports = { }
local swc = io.popen("swconfig dev %q show" % dev, "r")
if swc then
local l
repeat
l = swc:read("*l")
if l then
local port, up = l:match("port:(%d+) link:(%w+)")
if port then
local speed = l:match(" speed:(%d+)")
local duplex = l:match(" (%w+)-duplex")
local txflow = l:match(" (txflow)")
local rxflow = l:match(" (rxflow)")
local auto = l:match(" (auto)")
ports[#ports+1] = {
port = tonumber(port) or 0,
speed = tonumber(speed) or 0,
link = (up == "up"),
duplex = (duplex == "full"),
rxflow = (not not rxflow),
txflow = (not not txflow),
auto = (not not auto)
}
ports[#ports+1] = {
port = tonumber(port) or 0,
speed = tonumber(speed) or 0,
link = (up == "up"),
duplex = (duplex == "full"),
rxflow = (not not rxflow),
txflow = (not not txflow),
auto = (not not auto)
}
end
end
end
until not l
swc:close()
until not l
swc:close()
end
switches[dev] = ports
end
return ports
return switches
end

View file

@ -68,7 +68,7 @@ function index()
page = entry({"admin", "network", "wireless_reconnect"}, call("wifi_reconnect"), nil)
page.leaf = true
page = entry({"admin", "network", "wireless_shutdown"}, call("wifi_reconnect"), nil)
page = entry({"admin", "network", "wireless_shutdown"}, call("wifi_shutdown"), nil)
page.leaf = true
page = entry({"admin", "network", "wireless"}, arcombine(template("admin_network/wifi_overview"), cbi("admin_network/wifi")), _("Wifi"), 15)
@ -156,6 +156,12 @@ function index()
page = entry({"admin", "network", "diag_traceroute"}, call("diag_traceroute"), nil)
page.leaf = true
page = entry({"admin", "network", "diag_ping6"}, call("diag_ping6"), nil)
page.leaf = true
page = entry({"admin", "network", "diag_traceroute6"}, call("diag_traceroute6"), nil)
page.leaf = true
-- end
end
@ -235,13 +241,12 @@ function wifi_delete(network)
luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless"))
end
function iface_status()
local path = luci.dispatcher.context.requestpath
function iface_status(ifaces)
local netm = require "luci.model.network".init()
local rv = { }
local iface
for iface in path[#path]:gmatch("[%w%.%-_]+") do
for iface in ifaces:gmatch("[%w%.%-_]+") do
local net = netm:get_network(iface)
local device = net and net:get_interface()
if device then
@ -318,11 +323,8 @@ function iface_status()
luci.http.status(404, "No such device")
end
function iface_reconnect()
local path = luci.dispatcher.context.requestpath
local iface = path[#path]
function iface_reconnect(iface)
local netmd = require "luci.model.network".init()
local net = netmd:get_network(iface)
if net then
luci.sys.call("env -i /sbin/ifup %q >/dev/null 2>/dev/null" % iface)
@ -333,11 +335,8 @@ function iface_reconnect()
luci.http.status(404, "No such interface")
end
function iface_shutdown()
local path = luci.dispatcher.context.requestpath
local iface = path[#path]
function iface_shutdown(iface)
local netmd = require "luci.model.network".init()
local net = netmd:get_network(iface)
if net then
luci.sys.call("env -i /sbin/ifdown %q >/dev/null 2>/dev/null" % iface)
@ -348,11 +347,8 @@ function iface_shutdown()
luci.http.status(404, "No such interface")
end
function iface_delete()
local path = luci.dispatcher.context.requestpath
local iface = path[#path]
function iface_delete(iface)
local netmd = require "luci.model.network".init()
local net = netmd:del_network(iface)
if net then
luci.sys.call("env -i /sbin/ifdown %q >/dev/null 2>/dev/null" % iface)
@ -365,13 +361,12 @@ function iface_delete()
luci.http.status(404, "No such interface")
end
function wifi_status()
local path = luci.dispatcher.context.requestpath
function wifi_status(devs)
local s = require "luci.tools.status"
local rv = { }
local dev
for dev in path[#path]:gmatch("[%w%.%-]+") do
for dev in devs:gmatch("[%w%.%-]+") do
rv[#rv+1] = s.wifi_network(dev)
end
@ -384,23 +379,19 @@ function wifi_status()
luci.http.status(404, "No such device")
end
function wifi_reconnect()
local path = luci.dispatcher.context.requestpath
local mode = path[#path-1]
local wnet = path[#path]
local function wifi_reconnect_shutdown(shutdown, wnet)
local netmd = require "luci.model.network".init()
local net = netmd:get_wifinet(wnet)
local dev = net:get_device()
if dev and net then
luci.sys.call("env -i /sbin/wifi down >/dev/null 2>/dev/null")
dev:set("disabled", nil)
net:set("disabled", (mode == "wireless_shutdown") and 1 or nil)
net:set("disabled", shutdown and 1 or nil)
netmd:commit("wireless")
luci.sys.call("env -i /sbin/wifi up >/dev/null 2>/dev/null")
luci.http.status(200, (mode == "wireless_shutdown") and "Shutdown" or "Reconnected")
luci.http.status(200, shutdown and "Shutdown" or "Reconnected")
return
end
@ -408,6 +399,14 @@ function wifi_reconnect()
luci.http.status(404, "No such radio")
end
function wifi_reconnect(wnet)
wifi_reconnect_shutdown(false, wnet)
end
function wifi_shutdown(wnet)
wifi_reconnect_shutdown(true, wnet)
end
function lease_status()
local s = require "luci.tools.status"
@ -419,18 +418,14 @@ function lease_status()
luci.http.write(']')
end
function switch_status()
local path = luci.dispatcher.context.requestpath
function switch_status(switches)
local s = require "luci.tools.status"
luci.http.prepare_content("application/json")
luci.http.write_json(s.switch_status(path[#path]))
luci.http.write_json(s.switch_status(switches))
end
function diag_command(cmd)
local path = luci.dispatcher.context.requestpath
local addr = path[#path]
function diag_command(cmd, addr)
if addr and addr:match("^[a-zA-Z0-9%-%.:_]+$") then
luci.http.prepare_content("text/plain")
@ -452,14 +447,22 @@ function diag_command(cmd)
luci.http.status(500, "Bad address")
end
function diag_ping()
diag_command("ping -c 5 -W 1 %q 2>&1")
function diag_ping(addr)
diag_command("ping -c 5 -W 1 %q 2>&1", addr)
end
function diag_traceroute()
diag_command("traceroute -q 1 -w 1 -n %q 2>&1")
function diag_traceroute(addr)
diag_command("traceroute -q 1 -w 1 -n %q 2>&1", addr)
end
function diag_nslookup()
diag_command("nslookup %q 2>&1")
function diag_nslookup(addr)
diag_command("nslookup %q 2>&1", addr)
end
function diag_ping6(addr)
diag_command("ping6 -c 5 %q 2>&1", addr)
end
function diag_traceroute6(addr)
diag_command("traceroute6 -q 1 -w 2 -n %q 2>&1", addr)
end

Some files were not shown because too many files have changed in this diff Show more