From aa9ccf77c6648515ba58c37b9345cdbd561028db Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Thu, 8 May 2008 15:37:41 +0000 Subject: * Mördercommit ;-) * Major Repository Reorganisation * API 0.4 Softfreeze to come MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/Makefile | 41 +- core/contrib/ffluci | 4 - core/contrib/ffluci-upload | 4 - core/contrib/index.cgi | 3 - core/contrib/index.html | 10 - core/contrib/uci/freifunk | 26 -- core/contrib/uci/luci | 35 -- core/contrib/webuci_bootstrap.lua | 23 - core/root/etc/config/luci | 35 ++ core/src/bits.lua | 542 ++++++++++++++++++++++ core/src/cbi.lua | 749 +++++++++++++++++++++++++++++++ core/src/config.lua | 48 ++ core/src/debug.lua | 2 + core/src/dispatcher.lua | 300 +++++++++++++ core/src/ffluci/bits.lua | 542 ---------------------- core/src/ffluci/cbi.lua | 749 ------------------------------- core/src/ffluci/config.lua | 48 -- core/src/ffluci/debug.lua | 2 - core/src/ffluci/dispatcher.lua | 300 ------------- core/src/ffluci/fs.lua | 94 ---- core/src/ffluci/http.lua | 36 -- core/src/ffluci/i18n.lua | 58 --- core/src/ffluci/i18n/cbi.en | 4 - core/src/ffluci/i18n/default.en | 22 - core/src/ffluci/init.lua | 29 -- core/src/ffluci/menu.lua | 137 ------ core/src/ffluci/model/ipkg.lua | 140 ------ core/src/ffluci/model/uci.lua | 92 ---- core/src/ffluci/model/uci/libuci.lua | 193 -------- core/src/ffluci/model/uci/wrapper.lua | 171 ------- core/src/ffluci/sgi/haserl.lua | 76 ---- core/src/ffluci/sgi/webuci.lua | 83 ---- core/src/ffluci/sys.lua | 363 --------------- core/src/ffluci/template.lua | 227 ---------- core/src/ffluci/util.lua | 203 --------- core/src/ffluci/view/cbi/dvalue.htm | 12 - core/src/ffluci/view/cbi/footer.htm | 7 - core/src/ffluci/view/cbi/fvalue.htm | 3 - core/src/ffluci/view/cbi/header.htm | 7 - core/src/ffluci/view/cbi/lvalue.htm | 16 - core/src/ffluci/view/cbi/map.htm | 6 - core/src/ffluci/view/cbi/mvalue.htm | 19 - core/src/ffluci/view/cbi/nsection.htm | 17 - core/src/ffluci/view/cbi/tsection.htm | 22 - core/src/ffluci/view/cbi/ucisection.htm | 23 - core/src/ffluci/view/cbi/value.htm | 3 - core/src/ffluci/view/cbi/valuefooter.htm | 8 - core/src/ffluci/view/cbi/valueheader.htm | 3 - core/src/ffluci/view/error404.htm | 5 - core/src/ffluci/view/error500.htm | 5 - core/src/ffluci/view/footer.htm | 7 - core/src/ffluci/view/header.htm | 79 ---- core/src/fs.lua | 94 ++++ core/src/http.lua | 36 ++ core/src/i18n.lua | 58 +++ core/src/i18n/cbi.en | 4 + core/src/i18n/default.en | 22 + core/src/init.lua | 29 ++ core/src/menu.lua | 137 ++++++ core/src/model/ipkg.lua | 140 ++++++ core/src/model/uci.lua | 92 ++++ core/src/model/uci/libuci.lua | 193 ++++++++ core/src/model/uci/wrapper.lua | 171 +++++++ core/src/sys.lua | 363 +++++++++++++++ core/src/template.lua | 227 ++++++++++ core/src/util.lua | 203 +++++++++ core/src/view/cbi/dvalue.htm | 12 + core/src/view/cbi/footer.htm | 7 + core/src/view/cbi/fvalue.htm | 3 + core/src/view/cbi/header.htm | 7 + core/src/view/cbi/lvalue.htm | 16 + core/src/view/cbi/map.htm | 6 + core/src/view/cbi/mvalue.htm | 19 + core/src/view/cbi/nsection.htm | 17 + core/src/view/cbi/tsection.htm | 22 + core/src/view/cbi/ucisection.htm | 23 + core/src/view/cbi/value.htm | 3 + core/src/view/cbi/valuefooter.htm | 8 + core/src/view/cbi/valueheader.htm | 3 + core/src/view/error404.htm | 5 + core/src/view/error500.htm | 5 + core/src/view/footer.htm | 7 + core/src/view/header.htm | 79 ++++ 83 files changed, 3689 insertions(+), 3955 deletions(-) delete mode 100755 core/contrib/ffluci delete mode 100755 core/contrib/ffluci-upload delete mode 100755 core/contrib/index.cgi delete mode 100644 core/contrib/index.html delete mode 100644 core/contrib/uci/freifunk delete mode 100644 core/contrib/uci/luci delete mode 100644 core/contrib/webuci_bootstrap.lua create mode 100644 core/root/etc/config/luci create mode 100644 core/src/bits.lua create mode 100644 core/src/cbi.lua create mode 100644 core/src/config.lua create mode 100644 core/src/debug.lua create mode 100644 core/src/dispatcher.lua delete mode 100644 core/src/ffluci/bits.lua delete mode 100644 core/src/ffluci/cbi.lua delete mode 100644 core/src/ffluci/config.lua delete mode 100644 core/src/ffluci/debug.lua delete mode 100644 core/src/ffluci/dispatcher.lua delete mode 100644 core/src/ffluci/fs.lua delete mode 100644 core/src/ffluci/http.lua delete mode 100644 core/src/ffluci/i18n.lua delete mode 100644 core/src/ffluci/i18n/cbi.en delete mode 100644 core/src/ffluci/i18n/default.en delete mode 100644 core/src/ffluci/init.lua delete mode 100644 core/src/ffluci/menu.lua delete mode 100644 core/src/ffluci/model/ipkg.lua delete mode 100644 core/src/ffluci/model/uci.lua delete mode 100644 core/src/ffluci/model/uci/libuci.lua delete mode 100644 core/src/ffluci/model/uci/wrapper.lua delete mode 100644 core/src/ffluci/sgi/haserl.lua delete mode 100644 core/src/ffluci/sgi/webuci.lua delete mode 100644 core/src/ffluci/sys.lua delete mode 100644 core/src/ffluci/template.lua delete mode 100644 core/src/ffluci/util.lua delete mode 100644 core/src/ffluci/view/cbi/dvalue.htm delete mode 100644 core/src/ffluci/view/cbi/footer.htm delete mode 100644 core/src/ffluci/view/cbi/fvalue.htm delete mode 100644 core/src/ffluci/view/cbi/header.htm delete mode 100644 core/src/ffluci/view/cbi/lvalue.htm delete mode 100644 core/src/ffluci/view/cbi/map.htm delete mode 100644 core/src/ffluci/view/cbi/mvalue.htm delete mode 100644 core/src/ffluci/view/cbi/nsection.htm delete mode 100644 core/src/ffluci/view/cbi/tsection.htm delete mode 100644 core/src/ffluci/view/cbi/ucisection.htm delete mode 100644 core/src/ffluci/view/cbi/value.htm delete mode 100644 core/src/ffluci/view/cbi/valuefooter.htm delete mode 100644 core/src/ffluci/view/cbi/valueheader.htm delete mode 100644 core/src/ffluci/view/error404.htm delete mode 100644 core/src/ffluci/view/error500.htm delete mode 100644 core/src/ffluci/view/footer.htm delete mode 100644 core/src/ffluci/view/header.htm create mode 100644 core/src/fs.lua create mode 100644 core/src/http.lua create mode 100644 core/src/i18n.lua create mode 100644 core/src/i18n/cbi.en create mode 100644 core/src/i18n/default.en create mode 100644 core/src/init.lua create mode 100644 core/src/menu.lua create mode 100644 core/src/model/ipkg.lua create mode 100644 core/src/model/uci.lua create mode 100644 core/src/model/uci/libuci.lua create mode 100644 core/src/model/uci/wrapper.lua create mode 100644 core/src/sys.lua create mode 100644 core/src/template.lua create mode 100644 core/src/util.lua create mode 100644 core/src/view/cbi/dvalue.htm create mode 100644 core/src/view/cbi/footer.htm create mode 100644 core/src/view/cbi/fvalue.htm create mode 100644 core/src/view/cbi/header.htm create mode 100644 core/src/view/cbi/lvalue.htm create mode 100644 core/src/view/cbi/map.htm create mode 100644 core/src/view/cbi/mvalue.htm create mode 100644 core/src/view/cbi/nsection.htm create mode 100644 core/src/view/cbi/tsection.htm create mode 100644 core/src/view/cbi/ucisection.htm create mode 100644 core/src/view/cbi/value.htm create mode 100644 core/src/view/cbi/valuefooter.htm create mode 100644 core/src/view/cbi/valueheader.htm create mode 100644 core/src/view/error404.htm create mode 100644 core/src/view/error500.htm create mode 100644 core/src/view/footer.htm create mode 100644 core/src/view/header.htm (limited to 'core') diff --git a/core/Makefile b/core/Makefile index dbebb52d9..5e64bcd1f 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1,39 +1,2 @@ -LUAC = luac -LUAC_OPTIONS = -s - -FILES = ffluci/debug.lua ffluci/view/*.htm ffluci/view/cbi/*.htm ffluci/i18n/* - -CFILES = ffluci/bits.lua ffluci/util.lua \ -ffluci/sgi/haserl.lua ffluci/sgi/webuci.lua \ -ffluci/http.lua ffluci/fs.lua ffluci/sys.lua \ -ffluci/model/uci/wrapper.lua ffluci/model/uci/libuci.lua ffluci/model/uci.lua \ -ffluci/model/ipkg.lua ffluci/config.lua ffluci/i18n.lua ffluci/template.lua \ -ffluci/cbi.lua ffluci/dispatcher.lua ffluci/menu.lua ffluci/init.lua - -DIRECTORIES = ffluci/model/cbi ffluci/model/menu ffluci/controller ffluci/i18n ffluci/view/cbi ffluci/model/uci ffluci/sgi - -OUTDIRS = $(DIRECTORIES:%=dist/%) -INFILES = $(CFILES:%=src/%) -OUTFILE = ffluci/init.lua -CPFILES = $(FILES:%=src/%) - -.PHONY: all compile source depends clean - -all: compile - -depends: - mkdir -p $(OUTDIRS) - for i in $(CPFILES); do if [ -f "$$i" ]; then i=$$(echo $$i | cut -d/ -f2-); \ - mkdir -p dist/$$(dirname $$i); cp src/$$i dist/$$i; fi; done - -compile: depends - $(LUAC) $(LUAC_OPTIONS) -o dist/$(OUTFILE) $(INFILES) - for i in $(CFILES); do [ -f dist/$$i ] || ln -s `dirname $$i | cut -s -d / -f 2- | sed -e 's/[^/]*\/*/..\//g'``basename $(OUTFILE)` dist/$$i; done - - -source: depends - for i in $(CFILES); do cp src/$$i dist/$$i; done - - -clean: - rm dist -rf +include ../build/config.mk +include ../build/module.mk \ No newline at end of file diff --git a/core/contrib/ffluci b/core/contrib/ffluci deleted file mode 100755 index 183a6ad41..000000000 --- a/core/contrib/ffluci +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/haserl --shell=luac -package.path = "/usr/lib/lua/?.lua;/usr/lib/lua/?/init.lua;" .. package.path -package.cpath = "/usr/lib/lua/?.so;" .. package.cpath -require("ffluci.dispatcher").httpdispatch() \ No newline at end of file diff --git a/core/contrib/ffluci-upload b/core/contrib/ffluci-upload deleted file mode 100755 index 0128c2dd7..000000000 --- a/core/contrib/ffluci-upload +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/haserl --shell=luac --upload-limit=6144 --- This is a bit hacky: remove -upload from SCRIPT_NAME -ENV.SCRIPT_NAME = ENV.SCRIPT_NAME:sub(1, #ENV.SCRIPT_NAME - 7) -dofile("ffluci") \ No newline at end of file diff --git a/core/contrib/index.cgi b/core/contrib/index.cgi deleted file mode 100755 index 31705ccf2..000000000 --- a/core/contrib/index.cgi +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/haserl --shell=luac -print("Status: 302 Found") -print("Location: ffluci\n") diff --git a/core/contrib/index.html b/core/contrib/index.html deleted file mode 100644 index 58387a5fe..000000000 --- a/core/contrib/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - -FFLuCI - Freifunk Lua Configuration Interface - - \ No newline at end of file diff --git a/core/contrib/uci/freifunk b/core/contrib/uci/freifunk deleted file mode 100644 index aba12e636..000000000 --- a/core/contrib/uci/freifunk +++ /dev/null @@ -1,26 +0,0 @@ -config public contact - option nickname - option name - option mail - option phone - option location - option geo - option note - -config public community - option name "Freifunk Halle" - option homepage http://halle.freifunk.net - option essid halle.freifunk.net - option bssid 02:CA:FF:EE:BA:BE - option realm netz.freifunk-halle.net - option channel 1 - option net 104.0.0.0 - option mask 255.0.0.0 - option dhcp 10.0.0.0 - option dhcpmask 255.255.255.0 - option dns "88.198.178.18 141.54.1.1 212.204.49.83 208.67.220.220 208.67.222.222" - -config settings routing - option internal 0 - option internet 0 - \ No newline at end of file diff --git a/core/contrib/uci/luci b/core/contrib/uci/luci deleted file mode 100644 index a2b61f483..000000000 --- a/core/contrib/uci/luci +++ /dev/null @@ -1,35 +0,0 @@ -config core main - option lang de - option mediaurlbase /ffluci/media - -config core category_privileges - option public nobody:nogroup - -config extern flash_keep - option uci "/etc/config" - option dropbear "/etc/dropbear" - option openvpn "/etc/openvpn" - option passwd "/etc/passwd" - option ipkg "/etc/ipkg.conf" - option httpd "/etc/httpd.conf" - option firewall "/etc/firewall.user" - -config event uci_oncommit - option network "/etc/init.d/network restart" - option wireless "/etc/init.d/network restart" - option olsr "/sbin/reboot" - option dhcp "/etc/init.d/dnsmasq restart" - option luci_fw "/etc/init.d/luci_fw restart" - option dropbear "/etc/init.d/dropbear restart" - option httpd "/etc/init.d/httpd restart" - option fstab "/etc/init.d/fstab restart" - option qos "/etc/init.d/qos restart" - option luci_splash "/etc/init.d/luci_splash restart" - option freifunk "/etc/init.d/luci_freifunk restart" - -config internal languages - option de "Deutsch" - option en "English" - -config internal themes - option Fledermaus "/ffluci/media" \ No newline at end of file diff --git a/core/contrib/webuci_bootstrap.lua b/core/contrib/webuci_bootstrap.lua deleted file mode 100644 index 50ac8184b..000000000 --- a/core/contrib/webuci_bootstrap.lua +++ /dev/null @@ -1,23 +0,0 @@ -package.path = "/usr/lib/lua/?.lua;/usr/lib/lua/?/init.lua;" .. package.path -package.cpath = "/usr/lib/lua/?.so;" .. package.cpath -module("webuci", package.seeall) - -function prepare_req(uri) - env = {} - env.REQUEST_URI = uri - require("ffluci.menu").get() -end - -function init_req(context) - env.SERVER_PROTOCOL = context.server_proto - env.REMOTE_ADDR = context.remote_addr - env.REQUEST_METHOD = context.request_method - env.PATH_INFO = "/" .. context.uri - env.REMOTE_PORT = context.remote_port - env.SERVER_ADDR = context.server_addr - env.SCRIPT_NAME = env.REQUEST_URI:sub(1, #env.REQUEST_URI - #env.PATH_INFO) -end - -function handle_req(context) - require("ffluci.dispatcher").httpdispatch() -end \ No newline at end of file diff --git a/core/root/etc/config/luci b/core/root/etc/config/luci new file mode 100644 index 000000000..a2b61f483 --- /dev/null +++ b/core/root/etc/config/luci @@ -0,0 +1,35 @@ +config core main + option lang de + option mediaurlbase /ffluci/media + +config core category_privileges + option public nobody:nogroup + +config extern flash_keep + option uci "/etc/config" + option dropbear "/etc/dropbear" + option openvpn "/etc/openvpn" + option passwd "/etc/passwd" + option ipkg "/etc/ipkg.conf" + option httpd "/etc/httpd.conf" + option firewall "/etc/firewall.user" + +config event uci_oncommit + option network "/etc/init.d/network restart" + option wireless "/etc/init.d/network restart" + option olsr "/sbin/reboot" + option dhcp "/etc/init.d/dnsmasq restart" + option luci_fw "/etc/init.d/luci_fw restart" + option dropbear "/etc/init.d/dropbear restart" + option httpd "/etc/init.d/httpd restart" + option fstab "/etc/init.d/fstab restart" + option qos "/etc/init.d/qos restart" + option luci_splash "/etc/init.d/luci_splash restart" + option freifunk "/etc/init.d/luci_freifunk restart" + +config internal languages + option de "Deutsch" + option en "English" + +config internal themes + option Fledermaus "/ffluci/media" \ No newline at end of file diff --git a/core/src/bits.lua b/core/src/bits.lua new file mode 100644 index 000000000..f8434c335 --- /dev/null +++ b/core/src/bits.lua @@ -0,0 +1,542 @@ +--[[ +/* + * Copyright (c) 2007 Tim Kelly/Dialectronics + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +--]] + +--[[ +/* + * Copyright (c) 2007 Tim Kelly/Dialectronics + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +--]] + +module("ffluci.bits", package.seeall); + +local hex2bin = { + ["0"] = "0000", + ["1"] = "0001", + ["2"] = "0010", + ["3"] = "0011", + ["4"] = "0100", + ["5"] = "0101", + ["6"] = "0110", + ["7"] = "0111", + ["8"] = "1000", + ["9"] = "1001", + ["a"] = "1010", + ["b"] = "1011", + ["c"] = "1100", + ["d"] = "1101", + ["e"] = "1110", + ["f"] = "1111" + } + + + +local bin2hex = { + ["0000"] = "0", + ["0001"] = "1", + ["0010"] = "2", + ["0011"] = "3", + ["0100"] = "4", + ["0101"] = "5", + ["0110"] = "6", + ["0111"] = "7", + ["1000"] = "8", + ["1001"] = "9", + ["1010"] = "A", + ["1011"] = "B", + ["1100"] = "C", + ["1101"] = "D", + ["1110"] = "E", + ["1111"] = "F" + } + +--[[ +local dec2hex = { + ["0"] = "0", + ["1"] = "1", + ["2"] = "2", + ["3"] = "3", + ["4"] = "4", + ["5"] = "5", + ["6"] = "6", + ["7"] = "7", + ["8"] = "8", + ["9"] = "9", + ["10"] = "A", + ["11"] = "B", + ["12"] = "C", + ["13"] = "D", + ["14"] = "E", + ["15"] = "F" + } +--]] + + +-- These functions are big-endian and take up to 32 bits + +-- Hex2Bin +-- Bin2Hex +-- Hex2Dec +-- Dec2Hex +-- Bin2Dec +-- Dec2Bin + + +function Hex2Bin(s) + +-- s -> hexadecimal string + +local ret = "" +local i = 0 + + + for i in string.gfind(s, ".") do + i = string.lower(i) + + ret = ret..hex2bin[i] + + end + + return ret +end + + +function Bin2Hex(s) + +-- s -> binary string + +local l = 0 +local h = "" +local b = "" +local rem + +l = string.len(s) +rem = l % 4 +l = l-1 +h = "" + + -- need to prepend zeros to eliminate mod 4 + if (rem > 0) then + s = string.rep("0", 4 - rem)..s + end + + for i = 1, l, 4 do + b = string.sub(s, i, i+3) + h = h..bin2hex[b] + end + + return h + +end + + +function Bin2Dec(s) + +-- s -> binary string + +local num = 0 +local ex = string.len(s) - 1 +local l = 0 + + l = ex + 1 + for i = 1, l do + b = string.sub(s, i, i) + if b == "1" then + num = num + 2^ex + end + ex = ex - 1 + end + + return string.format("%u", num) + +end + + + +function Dec2Bin(s, num) + +-- s -> Base10 string +-- num -> string length to extend to + +local n + + if (num == nil) then + n = 0 + else + n = num + end + + s = string.format("%x", s) + + s = Hex2Bin(s) + + while string.len(s) < n do + s = "0"..s + end + + return s + +end + + + + +function Hex2Dec(s) + +-- s -> hexadecimal string + +local s = Hex2Bin(s) + + return Bin2Dec(s) + +end + + + +function Dec2Hex(s) + +-- s -> Base10 string + + s = string.format("%x", s) + + return s + +end + + + + +-- These functions are big-endian and will extend to 32 bits + +-- BMAnd +-- BMNAnd +-- BMOr +-- BMXOr +-- BMNot + + +function BMAnd(v, m) + +-- v -> hex string to be masked +-- m -> hex string mask + +-- s -> hex string as masked + +-- bv -> binary string of v +-- bm -> binary string mask + +local bv = Hex2Bin(v) +local bm = Hex2Bin(m) + +local i = 0 +local s = "" + + while (string.len(bv) < 32) do + bv = "0000"..bv + end + + while (string.len(bm) < 32) do + bm = "0000"..bm + end + + + for i = 1, 32 do + cv = string.sub(bv, i, i) + cm = string.sub(bm, i, i) + if cv == cm then + if cv == "1" then + s = s.."1" + else + s = s.."0" + end + else + s = s.."0" + + end + end + + return Bin2Hex(s) + +end + + +function BMNAnd(v, m) + +-- v -> hex string to be masked +-- m -> hex string mask + +-- s -> hex string as masked + +-- bv -> binary string of v +-- bm -> binary string mask + +local bv = Hex2Bin(v) +local bm = Hex2Bin(m) + +local i = 0 +local s = "" + + while (string.len(bv) < 32) do + bv = "0000"..bv + end + + while (string.len(bm) < 32) do + bm = "0000"..bm + end + + + for i = 1, 32 do + cv = string.sub(bv, i, i) + cm = string.sub(bm, i, i) + if cv == cm then + if cv == "1" then + s = s.."0" + else + s = s.."1" + end + else + s = s.."1" + + end + end + + return Bin2Hex(s) + +end + + + +function BMOr(v, m) + +-- v -> hex string to be masked +-- m -> hex string mask + +-- s -> hex string as masked + +-- bv -> binary string of v +-- bm -> binary string mask + +local bv = Hex2Bin(v) +local bm = Hex2Bin(m) + +local i = 0 +local s = "" + + while (string.len(bv) < 32) do + bv = "0000"..bv + end + + while (string.len(bm) < 32) do + bm = "0000"..bm + end + + + for i = 1, 32 do + cv = string.sub(bv, i, i) + cm = string.sub(bm, i, i) + if cv == "1" then + s = s.."1" + elseif cm == "1" then + s = s.."1" + else + s = s.."0" + end + end + + return Bin2Hex(s) + +end + +function BMXOr(v, m) + +-- v -> hex string to be masked +-- m -> hex string mask + +-- s -> hex string as masked + +-- bv -> binary string of v +-- bm -> binary string mask + +local bv = Hex2Bin(v) +local bm = Hex2Bin(m) + +local i = 0 +local s = "" + + while (string.len(bv) < 32) do + bv = "0000"..bv + end + + while (string.len(bm) < 32) do + bm = "0000"..bm + end + + + for i = 1, 32 do + cv = string.sub(bv, i, i) + cm = string.sub(bm, i, i) + if cv == "1" then + if cm == "0" then + s = s.."1" + else + s = s.."0" + end + elseif cm == "1" then + if cv == "0" then + s = s.."1" + else + s = s.."0" + end + else + -- cv and cm == "0" + s = s.."0" + end + end + + return Bin2Hex(s) + +end + + +function BMNot(v, m) + +-- v -> hex string to be masked +-- m -> hex string mask + +-- s -> hex string as masked + +-- bv -> binary string of v +-- bm -> binary string mask + +local bv = Hex2Bin(v) +local bm = Hex2Bin(m) + +local i = 0 +local s = "" + + while (string.len(bv) < 32) do + bv = "0000"..bv + end + + while (string.len(bm) < 32) do + bm = "0000"..bm + end + + + for i = 1, 32 do + cv = string.sub(bv, i, i) + cm = string.sub(bm, i, i) + if cm == "1" then + if cv == "1" then + -- turn off + s = s.."0" + else + -- turn on + s = s.."1" + end + else + -- leave untouched + s = s..cv + + end + end + + return Bin2Hex(s) + +end + + +-- these functions shift right and left, adding zeros to lost or gained bits +-- returned values are 32 bits long + +-- BShRight(v, nb) +-- BShLeft(v, nb) + + +function BShRight(v, nb) + +-- v -> hexstring value to be shifted +-- nb -> number of bits to shift to the right + +-- s -> binary string of v + +local s = Hex2Bin(v) + + while (string.len(s) < 32) do + s = "0000"..s + end + + s = string.sub(s, 1, 32 - nb) + + while (string.len(s) < 32) do + s = "0"..s + end + + return Bin2Hex(s) + +end + +function BShLeft(v, nb) + +-- v -> hexstring value to be shifted +-- nb -> number of bits to shift to the right + +-- s -> binary string of v + +local s = Hex2Bin(v) + + while (string.len(s) < 32) do + s = "0000"..s + end + + s = string.sub(s, nb + 1, 32) + + while (string.len(s) < 32) do + s = s.."0" + end + + return Bin2Hex(s) + +end \ No newline at end of file diff --git a/core/src/cbi.lua b/core/src/cbi.lua new file mode 100644 index 000000000..3384e5c38 --- /dev/null +++ b/core/src/cbi.lua @@ -0,0 +1,749 @@ +--[[ +FFLuCI - Configuration Bind Interface + +Description: +Offers an interface for binding confiugration values to certain +data types. Supports value and range validation and basic dependencies. + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci.cbi", package.seeall) + +require("ffluci.template") +require("ffluci.util") +require("ffluci.http") +require("ffluci.model.uci") + +local class = ffluci.util.class +local instanceof = ffluci.util.instanceof + +-- Loads a CBI map from given file, creating an environment and returns it +function load(cbimap) + require("ffluci.fs") + require("ffluci.i18n") + require("ffluci.config") + require("ffluci.sys") + + local cbidir = ffluci.sys.libpath() .. "/model/cbi/" + local func, err = loadfile(cbidir..cbimap..".lua") + + if not func then + return nil + end + + ffluci.i18n.loadc("cbi") + + ffluci.util.resfenv(func) + ffluci.util.updfenv(func, ffluci.cbi) + ffluci.util.extfenv(func, "translate", ffluci.i18n.translate) + + local map = func() + + if not instanceof(map, Map) then + error("CBI map returns no valid map object!") + return nil + end + + return map +end + +-- Node pseudo abstract class +Node = class() + +function Node.__init__(self, title, description) + self.children = {} + self.title = title or "" + self.description = description or "" + self.template = "cbi/node" +end + +-- Append child nodes +function Node.append(self, obj) + table.insert(self.children, obj) +end + +-- Parse this node and its children +function Node.parse(self, ...) + for k, child in ipairs(self.children) do + child:parse(...) + end +end + +-- Render this node +function Node.render(self) + ffluci.template.render(self.template, {self=self}) +end + +-- Render the children +function Node.render_children(self, ...) + for k, node in ipairs(self.children) do + node:render(...) + end +end + + +--[[ +A simple template element +]]-- +Template = class(Node) + +function Template.__init__(self, template) + Node.__init__(self) + self.template = template +end + + +--[[ +Map - A map describing a configuration file +]]-- +Map = class(Node) + +function Map.__init__(self, config, ...) + Node.__init__(self, ...) + self.config = config + self.template = "cbi/map" + self.uci = ffluci.model.uci.Session() + self.ucidata, self.uciorder = self.uci:sections(self.config) + if not self.ucidata or not self.uciorder then + error("Unable to read UCI data: " .. self.config) + end +end + +-- Use optimized UCI writing +function Map.parse(self, ...) + self.uci:t_load(self.config) + Node.parse(self, ...) + self.uci:t_save(self.config) +end + +-- Creates a child section +function Map.section(self, class, ...) + if instanceof(class, AbstractSection) then + local obj = class(self, ...) + self:append(obj) + return obj + else + error("class must be a descendent of AbstractSection") + end +end + +-- UCI add +function Map.add(self, sectiontype) + local name = self.uci:t_add(self.config, sectiontype) + if name then + self.ucidata[name] = {} + self.ucidata[name][".type"] = sectiontype + table.insert(self.uciorder, name) + end + return name +end + +-- UCI set +function Map.set(self, section, option, value) + local stat = self.uci:t_set(self.config, section, option, value) + if stat then + local val = self.uci:t_get(self.config, section, option) + if option then + self.ucidata[section][option] = val + else + if not self.ucidata[section] then + self.ucidata[section] = {} + end + self.ucidata[section][".type"] = val + table.insert(self.uciorder, section) + end + end + return stat +end + +-- UCI del +function Map.del(self, section, option) + local stat = self.uci:t_del(self.config, section, option) + if stat then + if option then + self.ucidata[section][option] = nil + else + self.ucidata[section] = nil + for i, k in ipairs(self.uciorder) do + if section == k then + table.remove(self.uciorder, i) + end + end + end + end + return stat +end + +-- UCI get (cached) +function Map.get(self, section, option) + if not section then + return self.ucidata, self.uciorder + elseif option and self.ucidata[section] then + return self.ucidata[section][option] + else + return self.ucidata[section] + end +end + + +--[[ +AbstractSection +]]-- +AbstractSection = class(Node) + +function AbstractSection.__init__(self, map, sectiontype, ...) + Node.__init__(self, ...) + self.sectiontype = sectiontype + self.map = map + self.config = map.config + self.optionals = {} + + self.optional = true + self.addremove = false + self.dynamic = false +end + +-- Appends a new option +function AbstractSection.option(self, class, ...) + if instanceof(class, AbstractValue) then + local obj = class(self.map, ...) + self:append(obj) + return obj + else + error("class must be a descendent of AbstractValue") + end +end + +-- Parse optional options +function AbstractSection.parse_optionals(self, section) + if not self.optional then + return + end + + self.optionals[section] = {} + + local field = ffluci.http.formvalue("cbi.opt."..self.config.."."..section) + for k,v in ipairs(self.children) do + if v.optional and not v:cfgvalue(section) then + if field == v.option then + field = nil + else + table.insert(self.optionals[section], v) + end + end + end + + if field and #field > 0 and self.dynamic then + self:add_dynamic(field) + end +end + +-- Add a dynamic option +function AbstractSection.add_dynamic(self, field, optional) + local o = self:option(Value, field, field) + o.optional = optional +end + +-- Parse all dynamic options +function AbstractSection.parse_dynamic(self, section) + if not self.dynamic then + return + end + + local arr = ffluci.util.clone(self:cfgvalue(section)) + local form = ffluci.http.formvaluetable("cbid."..self.config.."."..section) + for k, v in pairs(form) do + arr[k] = v + end + + for key,val in pairs(arr) do + local create = true + + for i,c in ipairs(self.children) do + if c.option == key then + create = false + end + end + + if create and key:sub(1, 1) ~= "." then + self:add_dynamic(key, true) + end + end +end + +-- Returns the section's UCI table +function AbstractSection.cfgvalue(self, section) + return self.map:get(section) +end + +-- Removes the section +function AbstractSection.remove(self, section) + return self.map:del(section) +end + +-- Creates the section +function AbstractSection.create(self, section) + return self.map:set(section, nil, self.sectiontype) +end + + + +--[[ +NamedSection - A fixed configuration section defined by its name +]]-- +NamedSection = class(AbstractSection) + +function NamedSection.__init__(self, map, section, ...) + AbstractSection.__init__(self, map, ...) + self.template = "cbi/nsection" + + self.section = section + self.addremove = false +end + +function NamedSection.parse(self) + local s = self.section + local active = self:cfgvalue(s) + + + if self.addremove then + local path = self.config.."."..s + if active then -- Remove the section + if ffluci.http.formvalue("cbi.rns."..path) and self:remove(s) then + return + end + else -- Create and apply default values + if ffluci.http.formvalue("cbi.cns."..path) and self:create(s) then + for k,v in pairs(self.children) do + v:write(s, v.default) + end + end + end + end + + if active then + AbstractSection.parse_dynamic(self, s) + if ffluci.http.formvalue("cbi.submit") then + Node.parse(self, s) + end + AbstractSection.parse_optionals(self, s) + end +end + + +--[[ +TypedSection - A (set of) configuration section(s) defined by the type + addremove: Defines whether the user can add/remove sections of this type + anonymous: Allow creating anonymous sections + validate: a validation function returning nil if the section is invalid +]]-- +TypedSection = class(AbstractSection) + +function TypedSection.__init__(self, ...) + AbstractSection.__init__(self, ...) + self.template = "cbi/tsection" + self.deps = {} + self.excludes = {} + + self.anonymous = false +end + +-- Return all matching UCI sections for this TypedSection +function TypedSection.cfgsections(self) + local sections = {} + local map, order = self.map:get() + + for i, k in ipairs(order) do + if map[k][".type"] == self.sectiontype then + if self:checkscope(k) then + table.insert(sections, k) + end + end + end + + return sections +end + +-- Creates a new section of this type with the given name (or anonymous) +function TypedSection.create(self, name) + if name then + self.map:set(name, nil, self.sectiontype) + else + name = self.map:add(self.sectiontype) + end + + for k,v in pairs(self.children) do + if v.default then + self.map:set(name, v.option, v.default) + end + end +end + +-- Limits scope to sections that have certain option => value pairs +function TypedSection.depends(self, option, value) + table.insert(self.deps, {option=option, value=value}) +end + +-- Excludes several sections by name +function TypedSection.exclude(self, field) + self.excludes[field] = true +end + +function TypedSection.parse(self) + if self.addremove then + -- Create + local crval = "cbi.cts." .. self.config .. "." .. self.sectiontype + local name = ffluci.http.formvalue(crval) + if self.anonymous then + if name then + self:create() + end + else + if name then + -- Ignore if it already exists + if self:cfgvalue(name) then + name = nil; + end + + name = self:checkscope(name) + + if not name then + self.err_invalid = true + end + + if name and name:len() > 0 then + self:create(name) + end + end + end + + -- Remove + crval = "cbi.rts." .. self.config + name = ffluci.http.formvaluetable(crval) + for k,v in pairs(name) do + if self:cfgvalue(k) and self:checkscope(k) then + self:remove(k) + end + end + end + + for i, k in ipairs(self:cfgsections()) do + AbstractSection.parse_dynamic(self, k) + if ffluci.http.formvalue("cbi.submit") then + Node.parse(self, k) + end + AbstractSection.parse_optionals(self, k) + end +end + +-- Render the children +function TypedSection.render_children(self, section) + for k, node in ipairs(self.children) do + node:render(section) + end +end + +-- Verifies scope of sections +function TypedSection.checkscope(self, section) + -- Check if we are not excluded + if self.excludes[section] then + return nil + end + + -- Check if at least one dependency is met + if #self.deps > 0 and self:cfgvalue(section) then + local stat = false + + for k, v in ipairs(self.deps) do + if self:cfgvalue(section)[v.option] == v.value then + stat = true + end + end + + if not stat then + return nil + end + end + + return self:validate(section) +end + + +-- Dummy validate function +function TypedSection.validate(self, section) + return section +end + + +--[[ +AbstractValue - An abstract Value Type + null: Value can be empty + valid: A function returning the value if it is valid otherwise nil + depends: A table of option => value pairs of which one must be true + default: The default value + size: The size of the input fields + rmempty: Unset value if empty + optional: This value is optional (see AbstractSection.optionals) +]]-- +AbstractValue = class(Node) + +function AbstractValue.__init__(self, map, option, ...) + Node.__init__(self, ...) + self.option = option + self.map = map + self.config = map.config + self.tag_invalid = {} + self.deps = {} + + self.rmempty = false + self.default = nil + self.size = nil + self.optional = false +end + +-- Add a dependencie to another section field +function AbstractValue.depends(self, field, value) + table.insert(self.deps, {field=field, value=value}) +end + +-- Return whether this object should be created +function AbstractValue.formcreated(self, section) + local key = "cbi.opt."..self.config.."."..section + return (ffluci.http.formvalue(key) == self.option) +end + +-- Returns the formvalue for this object +function AbstractValue.formvalue(self, section) + local key = "cbid."..self.map.config.."."..section.."."..self.option + return ffluci.http.formvalue(key) +end + +function AbstractValue.parse(self, section) + local fvalue = self:formvalue(section) + + if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI + fvalue = self:validate(fvalue) + if not fvalue then + self.tag_invalid[section] = true + end + if fvalue and not (fvalue == self:cfgvalue(section)) then + self:write(section, fvalue) + end + else -- Unset the UCI or error + if self.rmempty or self.optional then + self:remove(section) + end + end +end + +-- Render if this value exists or if it is mandatory +function AbstractValue.render(self, s) + if not self.optional or self:cfgvalue(s) or self:formcreated(s) then + ffluci.template.render(self.template, {self=self, section=s}) + end +end + +-- Return the UCI value of this object +function AbstractValue.cfgvalue(self, section) + return self.map:get(section, self.option) +end + +-- Validate the form value +function AbstractValue.validate(self, value) + return value +end + +-- Write to UCI +function AbstractValue.write(self, section, value) + return self.map:set(section, self.option, value) +end + +-- Remove from UCI +function AbstractValue.remove(self, section) + return self.map:del(section, self.option) +end + + + + +--[[ +Value - A one-line value + maxlength: The maximum length + isnumber: The value must be a valid (floating point) number + isinteger: The value must be a valid integer + ispositive: The value must be positive (and a number) +]]-- +Value = class(AbstractValue) + +function Value.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/value" + + self.maxlength = nil + self.isnumber = false + self.isinteger = false +end + +-- This validation is a bit more complex +function Value.validate(self, val) + if self.maxlength and tostring(val):len() > self.maxlength then + val = nil + end + + return ffluci.util.validate(val, self.isnumber, self.isinteger) +end + + +-- DummyValue - This does nothing except being there +DummyValue = class(AbstractValue) + +function DummyValue.__init__(self, map, ...) + AbstractValue.__init__(self, map, ...) + self.template = "cbi/dvalue" + self.value = nil +end + +function DummyValue.parse(self) + +end + +function DummyValue.render(self, s) + ffluci.template.render(self.template, {self=self, section=s}) +end + + +--[[ +Flag - A flag being enabled or disabled +]]-- +Flag = class(AbstractValue) + +function Flag.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/fvalue" + + self.enabled = "1" + self.disabled = "0" +end + +-- A flag can only have two states: set or unset +function Flag.parse(self, section) + local fvalue = self:formvalue(section) + + if fvalue then + fvalue = self.enabled + else + fvalue = self.disabled + end + + if fvalue == self.enabled or (not self.optional and not self.rmempty) then + if not(fvalue == self:cfgvalue(section)) then + self:write(section, fvalue) + end + else + self:remove(section) + end +end + + + +--[[ +ListValue - A one-line value predefined in a list + widget: The widget that will be used (select, radio) +]]-- +ListValue = class(AbstractValue) + +function ListValue.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/lvalue" + self.keylist = {} + self.vallist = {} + + self.size = 1 + self.widget = "select" +end + +function ListValue.value(self, key, val) + val = val or key + table.insert(self.keylist, tostring(key)) + table.insert(self.vallist, tostring(val)) +end + +function ListValue.validate(self, val) + if ffluci.util.contains(self.keylist, val) then + return val + else + return nil + end +end + + + +--[[ +MultiValue - Multiple delimited values + widget: The widget that will be used (select, checkbox) + delimiter: The delimiter that will separate the values (default: " ") +]]-- +MultiValue = class(AbstractValue) + +function MultiValue.__init__(self, ...) + AbstractValue.__init__(self, ...) + self.template = "cbi/mvalue" + self.keylist = {} + self.vallist = {} + + self.widget = "checkbox" + self.delimiter = " " +end + +function MultiValue.value(self, key, val) + val = val or key + table.insert(self.keylist, tostring(key)) + table.insert(self.vallist, tostring(val)) +end + +function MultiValue.valuelist(self, section) + local val = self:cfgvalue(section) + + if not(type(val) == "string") then + return {} + end + + return ffluci.util.split(val, self.delimiter) +end + +function MultiValue.validate(self, val) + if not(type(val) == "string") then + return nil + end + + local result = "" + + for value in val:gmatch("[^\n]+") do + if ffluci.util.contains(self.keylist, value) then + result = result .. self.delimiter .. value + end + end + + if result:len() > 0 then + return result:sub(self.delimiter:len() + 1) + else + return nil + end +end \ No newline at end of file diff --git a/core/src/config.lua b/core/src/config.lua new file mode 100644 index 000000000..0db45ac89 --- /dev/null +++ b/core/src/config.lua @@ -0,0 +1,48 @@ +--[[ +FFLuCI - Configuration + +Description: +Some FFLuCI configuration values read from uci file "luci" + + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.config", package.seeall) +require("ffluci.model.uci") +require("ffluci.util") +require("ffluci.sys") + +-- Warning! This is only for fallback and compatibility purporses! -- +main = {} + +-- This is where stylesheets and images go +main.mediaurlbase = "/ffluci/media" + +-- Does anybody think about browser autodetect here? +-- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE +main.lang = "de" + + +-- Now overwrite with UCI values +local ucidata = ffluci.model.uci.sections("luci") +if ucidata then + ffluci.util.update(ffluci.config, ucidata) +end \ No newline at end of file diff --git a/core/src/debug.lua b/core/src/debug.lua new file mode 100644 index 000000000..1be40348e --- /dev/null +++ b/core/src/debug.lua @@ -0,0 +1,2 @@ +module("ffluci.debug", package.seeall) +__file__ = debug.getinfo(1, 'S').source:sub(2) \ No newline at end of file diff --git a/core/src/dispatcher.lua b/core/src/dispatcher.lua new file mode 100644 index 000000000..c60e5dcd1 --- /dev/null +++ b/core/src/dispatcher.lua @@ -0,0 +1,300 @@ +--[[ +FFLuCI - Dispatcher + +Description: +The request dispatcher and module dispatcher generators + + +The dispatching process: + For a detailed explanation of the dispatching process we assume: + You have installed the FFLuCI CGI-Dispatcher in /cgi-bin/ffluci + + To enforce a higher level of security only the CGI-Dispatcher + resides inside the web server's document root, everything else + stays inside an external directory, we assume this is /lua/ffluci + for this explanation. + + All controllers and action are reachable as sub-objects of /cgi-bin/ffluci + as if they were virtual folders and files + e.g.: /cgi-bin/ffluci/public/info/about + /cgi-bin/ffluci/admin/network/interfaces + and so on. + + The PATH_INFO variable holds the dispatch path and + will be split into three parts: /category/module/action + + Category: This is the category in which modules are stored in + By default there are two categories: + "public" - which is the default public category + "admin" - which is the default protected category + + As FFLuCI itself does not implement authentication + you should make sure that "admin" and other sensitive + categories are protected by the webserver. + + E.g. for busybox add a line like: + /cgi-bin/ffluci/admin:root:$p$root + to /etc/httpd.conf to protect the "admin" category + + + Module: This is the controller which will handle the request further + It is always a submodule of ffluci.controller, so a module + called "helloworld" will be stored in + /lua/ffluci/controller/helloworld.lua + You are free to submodule your controllers any further. + + Action: This is action that will be invoked after loading the module. + The kind of how the action will be dispatched depends on + the module dispatcher that is defined in the controller. + See the description of the default module dispatcher down + on this page for some examples. + + + The main dispatcher at first searches for the module by trying to + include ffluci.controller.category.module + (where "category" is the category name and "module" is the module name) + If this fails a 404 status code will be send to the client and FFLuCI exits + + Then the main dispatcher calls the module dispatcher + ffluci.controller.category.module.dispatcher with the request object + as the only argument. The module dispatcher is then responsible + for the further dispatching process. + + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.dispatcher", package.seeall) +require("ffluci.http") +require("ffluci.template") +require("ffluci.config") +require("ffluci.sys") + +-- Sets privilege for given category +function assign_privileges(category) + local cp = ffluci.config.category_privileges + if cp and cp[category] then + local u, g = cp[category]:match("([^:]+):([^:]+)") + ffluci.sys.process.setuser(u) + ffluci.sys.process.setgroup(g) + end +end + + +-- Builds a URL from a triple of category, module and action +function build_url(category, module, action) + category = category or "public" + module = module or "index" + action = action or "index" + + local pattern = ffluci.http.env.SCRIPT_NAME .. "/%s/%s/%s" + return pattern:format(category, module, action) +end + + +-- Dispatches the "request" +function dispatch(req) + request = req + local m = "ffluci.controller." .. request.category .. "." .. request.module + local stat, module = pcall(require, m) + if not stat then + return error404() + else + module.request = request + module.dispatcher = module.dispatcher or dynamic + setfenv(module.dispatcher, module) + return module.dispatcher(request) + end +end + +-- Sends a 404 error code and renders the "error404" template if available +function error404(message) + ffluci.http.status(404, "Not Found") + message = message or "Not Found" + + if not pcall(ffluci.template.render, "error404") then + ffluci.http.prepare_content("text/plain") + print(message) + end + return false +end + +-- Sends a 500 error code and renders the "error500" template if available +function error500(message) + ffluci.http.status(500, "Internal Server Error") + + if not pcall(ffluci.template.render, "error500", {message=message}) then + ffluci.http.prepare_content("text/plain") + print(message) + end + return false +end + + +-- Dispatches a request depending on the PATH_INFO variable +function httpdispatch() + local pathinfo = ffluci.http.env.PATH_INFO or "" + local parts = pathinfo:gmatch("/[%w-]+") + + local sanitize = function(s, default) + return s and s:sub(2) or default + end + + local cat = sanitize(parts(), "public") + local mod = sanitize(parts(), "index") + local act = sanitize(parts(), "index") + + assign_privileges(cat) + dispatch({category=cat, module=mod, action=act}) +end + + +-- Dispatchers -- + + +-- The Action Dispatcher searches the module for any function called +-- action_"request.action" and calls it +function action(...) + local disp = require("ffluci.dispatcher") + if not disp._action(...) then + disp.error404() + end +end + +-- The CBI dispatcher directly parses and renders the CBI map which is +-- placed in ffluci/modles/cbi/"request.module"/"request.action" +function cbi(...) + local disp = require("ffluci.dispatcher") + if not disp._cbi(...) then + disp.error404() + end +end + +-- The dynamic dispatcher chains the action, submodule, simpleview and CBI dispatcher +-- in this particular order. It is the default dispatcher. +function dynamic(...) + local disp = require("ffluci.dispatcher") + if not disp._action(...) + and not disp._submodule(...) + and not disp._simpleview(...) + and not disp._cbi(...) then + disp.error404() + end +end + +-- The Simple View Dispatcher directly renders the template +-- which is placed in ffluci/views/"request.module"/"request.action" +function simpleview(...) + local disp = require("ffluci.dispatcher") + if not disp._simpleview(...) then + disp.error404() + end +end + + +-- The submodule dispatcher tries to load a submodule of the controller +-- and calls its "action"-function +function submodule(...) + local disp = require("ffluci.dispatcher") + if not disp._submodule(...) then + disp.error404() + end +end + + +-- Internal Dispatcher Functions -- + +function _action(request) + local action = getfenv(2)["action_" .. request.action:gsub("-", "_")] + local i18n = require("ffluci.i18n") + + if action then + i18n.loadc(request.category .. "_" .. request.module) + i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) + action() + return true + else + return false + end +end + + +function _cbi(request) + local disp = require("ffluci.dispatcher") + local tmpl = require("ffluci.template") + local cbi = require("ffluci.cbi") + local i18n = require("ffluci.i18n") + + local path = request.category.."_"..request.module.."/"..request.action + + local stat, map = pcall(cbi.load, path) + if stat and map then + local stat, err = pcall(map.parse, map) + if not stat then + disp.error500(err) + return true + end + i18n.loadc(request.category .. "_" .. request.module) + i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) + tmpl.render("cbi/header") + map:render() + tmpl.render("cbi/footer") + return true + elseif not stat then + disp.error500(map) + return true + else + return false + end +end + + +function _simpleview(request) + local i18n = require("ffluci.i18n") + local tmpl = require("ffluci.template") + + local path = request.category.."_"..request.module.."/"..request.action + + local stat, t = pcall(tmpl.Template, path) + if stat then + i18n.loadc(request.category .. "_" .. request.module) + i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) + t:render() + return true + else + return false + end +end + + +function _submodule(request) + local i18n = require("ffluci.i18n") + local m = "ffluci.controller." .. request.category .. "." .. + request.module .. "." .. request.action + local stat, module = pcall(require, m) + + if stat and module.action then + i18n.loadc(request.category .. "_" .. request.module) + i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) + return pcall(module.action) + end + + return false +end \ No newline at end of file diff --git a/core/src/ffluci/bits.lua b/core/src/ffluci/bits.lua deleted file mode 100644 index f8434c335..000000000 --- a/core/src/ffluci/bits.lua +++ /dev/null @@ -1,542 +0,0 @@ ---[[ -/* - * Copyright (c) 2007 Tim Kelly/Dialectronics - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT - * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR - * THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - ---]] - ---[[ -/* - * Copyright (c) 2007 Tim Kelly/Dialectronics - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT - * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR - * THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - ---]] - -module("ffluci.bits", package.seeall); - -local hex2bin = { - ["0"] = "0000", - ["1"] = "0001", - ["2"] = "0010", - ["3"] = "0011", - ["4"] = "0100", - ["5"] = "0101", - ["6"] = "0110", - ["7"] = "0111", - ["8"] = "1000", - ["9"] = "1001", - ["a"] = "1010", - ["b"] = "1011", - ["c"] = "1100", - ["d"] = "1101", - ["e"] = "1110", - ["f"] = "1111" - } - - - -local bin2hex = { - ["0000"] = "0", - ["0001"] = "1", - ["0010"] = "2", - ["0011"] = "3", - ["0100"] = "4", - ["0101"] = "5", - ["0110"] = "6", - ["0111"] = "7", - ["1000"] = "8", - ["1001"] = "9", - ["1010"] = "A", - ["1011"] = "B", - ["1100"] = "C", - ["1101"] = "D", - ["1110"] = "E", - ["1111"] = "F" - } - ---[[ -local dec2hex = { - ["0"] = "0", - ["1"] = "1", - ["2"] = "2", - ["3"] = "3", - ["4"] = "4", - ["5"] = "5", - ["6"] = "6", - ["7"] = "7", - ["8"] = "8", - ["9"] = "9", - ["10"] = "A", - ["11"] = "B", - ["12"] = "C", - ["13"] = "D", - ["14"] = "E", - ["15"] = "F" - } ---]] - - --- These functions are big-endian and take up to 32 bits - --- Hex2Bin --- Bin2Hex --- Hex2Dec --- Dec2Hex --- Bin2Dec --- Dec2Bin - - -function Hex2Bin(s) - --- s -> hexadecimal string - -local ret = "" -local i = 0 - - - for i in string.gfind(s, ".") do - i = string.lower(i) - - ret = ret..hex2bin[i] - - end - - return ret -end - - -function Bin2Hex(s) - --- s -> binary string - -local l = 0 -local h = "" -local b = "" -local rem - -l = string.len(s) -rem = l % 4 -l = l-1 -h = "" - - -- need to prepend zeros to eliminate mod 4 - if (rem > 0) then - s = string.rep("0", 4 - rem)..s - end - - for i = 1, l, 4 do - b = string.sub(s, i, i+3) - h = h..bin2hex[b] - end - - return h - -end - - -function Bin2Dec(s) - --- s -> binary string - -local num = 0 -local ex = string.len(s) - 1 -local l = 0 - - l = ex + 1 - for i = 1, l do - b = string.sub(s, i, i) - if b == "1" then - num = num + 2^ex - end - ex = ex - 1 - end - - return string.format("%u", num) - -end - - - -function Dec2Bin(s, num) - --- s -> Base10 string --- num -> string length to extend to - -local n - - if (num == nil) then - n = 0 - else - n = num - end - - s = string.format("%x", s) - - s = Hex2Bin(s) - - while string.len(s) < n do - s = "0"..s - end - - return s - -end - - - - -function Hex2Dec(s) - --- s -> hexadecimal string - -local s = Hex2Bin(s) - - return Bin2Dec(s) - -end - - - -function Dec2Hex(s) - --- s -> Base10 string - - s = string.format("%x", s) - - return s - -end - - - - --- These functions are big-endian and will extend to 32 bits - --- BMAnd --- BMNAnd --- BMOr --- BMXOr --- BMNot - - -function BMAnd(v, m) - --- v -> hex string to be masked --- m -> hex string mask - --- s -> hex string as masked - --- bv -> binary string of v --- bm -> binary string mask - -local bv = Hex2Bin(v) -local bm = Hex2Bin(m) - -local i = 0 -local s = "" - - while (string.len(bv) < 32) do - bv = "0000"..bv - end - - while (string.len(bm) < 32) do - bm = "0000"..bm - end - - - for i = 1, 32 do - cv = string.sub(bv, i, i) - cm = string.sub(bm, i, i) - if cv == cm then - if cv == "1" then - s = s.."1" - else - s = s.."0" - end - else - s = s.."0" - - end - end - - return Bin2Hex(s) - -end - - -function BMNAnd(v, m) - --- v -> hex string to be masked --- m -> hex string mask - --- s -> hex string as masked - --- bv -> binary string of v --- bm -> binary string mask - -local bv = Hex2Bin(v) -local bm = Hex2Bin(m) - -local i = 0 -local s = "" - - while (string.len(bv) < 32) do - bv = "0000"..bv - end - - while (string.len(bm) < 32) do - bm = "0000"..bm - end - - - for i = 1, 32 do - cv = string.sub(bv, i, i) - cm = string.sub(bm, i, i) - if cv == cm then - if cv == "1" then - s = s.."0" - else - s = s.."1" - end - else - s = s.."1" - - end - end - - return Bin2Hex(s) - -end - - - -function BMOr(v, m) - --- v -> hex string to be masked --- m -> hex string mask - --- s -> hex string as masked - --- bv -> binary string of v --- bm -> binary string mask - -local bv = Hex2Bin(v) -local bm = Hex2Bin(m) - -local i = 0 -local s = "" - - while (string.len(bv) < 32) do - bv = "0000"..bv - end - - while (string.len(bm) < 32) do - bm = "0000"..bm - end - - - for i = 1, 32 do - cv = string.sub(bv, i, i) - cm = string.sub(bm, i, i) - if cv == "1" then - s = s.."1" - elseif cm == "1" then - s = s.."1" - else - s = s.."0" - end - end - - return Bin2Hex(s) - -end - -function BMXOr(v, m) - --- v -> hex string to be masked --- m -> hex string mask - --- s -> hex string as masked - --- bv -> binary string of v --- bm -> binary string mask - -local bv = Hex2Bin(v) -local bm = Hex2Bin(m) - -local i = 0 -local s = "" - - while (string.len(bv) < 32) do - bv = "0000"..bv - end - - while (string.len(bm) < 32) do - bm = "0000"..bm - end - - - for i = 1, 32 do - cv = string.sub(bv, i, i) - cm = string.sub(bm, i, i) - if cv == "1" then - if cm == "0" then - s = s.."1" - else - s = s.."0" - end - elseif cm == "1" then - if cv == "0" then - s = s.."1" - else - s = s.."0" - end - else - -- cv and cm == "0" - s = s.."0" - end - end - - return Bin2Hex(s) - -end - - -function BMNot(v, m) - --- v -> hex string to be masked --- m -> hex string mask - --- s -> hex string as masked - --- bv -> binary string of v --- bm -> binary string mask - -local bv = Hex2Bin(v) -local bm = Hex2Bin(m) - -local i = 0 -local s = "" - - while (string.len(bv) < 32) do - bv = "0000"..bv - end - - while (string.len(bm) < 32) do - bm = "0000"..bm - end - - - for i = 1, 32 do - cv = string.sub(bv, i, i) - cm = string.sub(bm, i, i) - if cm == "1" then - if cv == "1" then - -- turn off - s = s.."0" - else - -- turn on - s = s.."1" - end - else - -- leave untouched - s = s..cv - - end - end - - return Bin2Hex(s) - -end - - --- these functions shift right and left, adding zeros to lost or gained bits --- returned values are 32 bits long - --- BShRight(v, nb) --- BShLeft(v, nb) - - -function BShRight(v, nb) - --- v -> hexstring value to be shifted --- nb -> number of bits to shift to the right - --- s -> binary string of v - -local s = Hex2Bin(v) - - while (string.len(s) < 32) do - s = "0000"..s - end - - s = string.sub(s, 1, 32 - nb) - - while (string.len(s) < 32) do - s = "0"..s - end - - return Bin2Hex(s) - -end - -function BShLeft(v, nb) - --- v -> hexstring value to be shifted --- nb -> number of bits to shift to the right - --- s -> binary string of v - -local s = Hex2Bin(v) - - while (string.len(s) < 32) do - s = "0000"..s - end - - s = string.sub(s, nb + 1, 32) - - while (string.len(s) < 32) do - s = s.."0" - end - - return Bin2Hex(s) - -end \ No newline at end of file diff --git a/core/src/ffluci/cbi.lua b/core/src/ffluci/cbi.lua deleted file mode 100644 index 3384e5c38..000000000 --- a/core/src/ffluci/cbi.lua +++ /dev/null @@ -1,749 +0,0 @@ ---[[ -FFLuCI - Configuration Bind Interface - -Description: -Offers an interface for binding confiugration values to certain -data types. Supports value and range validation and basic dependencies. - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.cbi", package.seeall) - -require("ffluci.template") -require("ffluci.util") -require("ffluci.http") -require("ffluci.model.uci") - -local class = ffluci.util.class -local instanceof = ffluci.util.instanceof - --- Loads a CBI map from given file, creating an environment and returns it -function load(cbimap) - require("ffluci.fs") - require("ffluci.i18n") - require("ffluci.config") - require("ffluci.sys") - - local cbidir = ffluci.sys.libpath() .. "/model/cbi/" - local func, err = loadfile(cbidir..cbimap..".lua") - - if not func then - return nil - end - - ffluci.i18n.loadc("cbi") - - ffluci.util.resfenv(func) - ffluci.util.updfenv(func, ffluci.cbi) - ffluci.util.extfenv(func, "translate", ffluci.i18n.translate) - - local map = func() - - if not instanceof(map, Map) then - error("CBI map returns no valid map object!") - return nil - end - - return map -end - --- Node pseudo abstract class -Node = class() - -function Node.__init__(self, title, description) - self.children = {} - self.title = title or "" - self.description = description or "" - self.template = "cbi/node" -end - --- Append child nodes -function Node.append(self, obj) - table.insert(self.children, obj) -end - --- Parse this node and its children -function Node.parse(self, ...) - for k, child in ipairs(self.children) do - child:parse(...) - end -end - --- Render this node -function Node.render(self) - ffluci.template.render(self.template, {self=self}) -end - --- Render the children -function Node.render_children(self, ...) - for k, node in ipairs(self.children) do - node:render(...) - end -end - - ---[[ -A simple template element -]]-- -Template = class(Node) - -function Template.__init__(self, template) - Node.__init__(self) - self.template = template -end - - ---[[ -Map - A map describing a configuration file -]]-- -Map = class(Node) - -function Map.__init__(self, config, ...) - Node.__init__(self, ...) - self.config = config - self.template = "cbi/map" - self.uci = ffluci.model.uci.Session() - self.ucidata, self.uciorder = self.uci:sections(self.config) - if not self.ucidata or not self.uciorder then - error("Unable to read UCI data: " .. self.config) - end -end - --- Use optimized UCI writing -function Map.parse(self, ...) - self.uci:t_load(self.config) - Node.parse(self, ...) - self.uci:t_save(self.config) -end - --- Creates a child section -function Map.section(self, class, ...) - if instanceof(class, AbstractSection) then - local obj = class(self, ...) - self:append(obj) - return obj - else - error("class must be a descendent of AbstractSection") - end -end - --- UCI add -function Map.add(self, sectiontype) - local name = self.uci:t_add(self.config, sectiontype) - if name then - self.ucidata[name] = {} - self.ucidata[name][".type"] = sectiontype - table.insert(self.uciorder, name) - end - return name -end - --- UCI set -function Map.set(self, section, option, value) - local stat = self.uci:t_set(self.config, section, option, value) - if stat then - local val = self.uci:t_get(self.config, section, option) - if option then - self.ucidata[section][option] = val - else - if not self.ucidata[section] then - self.ucidata[section] = {} - end - self.ucidata[section][".type"] = val - table.insert(self.uciorder, section) - end - end - return stat -end - --- UCI del -function Map.del(self, section, option) - local stat = self.uci:t_del(self.config, section, option) - if stat then - if option then - self.ucidata[section][option] = nil - else - self.ucidata[section] = nil - for i, k in ipairs(self.uciorder) do - if section == k then - table.remove(self.uciorder, i) - end - end - end - end - return stat -end - --- UCI get (cached) -function Map.get(self, section, option) - if not section then - return self.ucidata, self.uciorder - elseif option and self.ucidata[section] then - return self.ucidata[section][option] - else - return self.ucidata[section] - end -end - - ---[[ -AbstractSection -]]-- -AbstractSection = class(Node) - -function AbstractSection.__init__(self, map, sectiontype, ...) - Node.__init__(self, ...) - self.sectiontype = sectiontype - self.map = map - self.config = map.config - self.optionals = {} - - self.optional = true - self.addremove = false - self.dynamic = false -end - --- Appends a new option -function AbstractSection.option(self, class, ...) - if instanceof(class, AbstractValue) then - local obj = class(self.map, ...) - self:append(obj) - return obj - else - error("class must be a descendent of AbstractValue") - end -end - --- Parse optional options -function AbstractSection.parse_optionals(self, section) - if not self.optional then - return - end - - self.optionals[section] = {} - - local field = ffluci.http.formvalue("cbi.opt."..self.config.."."..section) - for k,v in ipairs(self.children) do - if v.optional and not v:cfgvalue(section) then - if field == v.option then - field = nil - else - table.insert(self.optionals[section], v) - end - end - end - - if field and #field > 0 and self.dynamic then - self:add_dynamic(field) - end -end - --- Add a dynamic option -function AbstractSection.add_dynamic(self, field, optional) - local o = self:option(Value, field, field) - o.optional = optional -end - --- Parse all dynamic options -function AbstractSection.parse_dynamic(self, section) - if not self.dynamic then - return - end - - local arr = ffluci.util.clone(self:cfgvalue(section)) - local form = ffluci.http.formvaluetable("cbid."..self.config.."."..section) - for k, v in pairs(form) do - arr[k] = v - end - - for key,val in pairs(arr) do - local create = true - - for i,c in ipairs(self.children) do - if c.option == key then - create = false - end - end - - if create and key:sub(1, 1) ~= "." then - self:add_dynamic(key, true) - end - end -end - --- Returns the section's UCI table -function AbstractSection.cfgvalue(self, section) - return self.map:get(section) -end - --- Removes the section -function AbstractSection.remove(self, section) - return self.map:del(section) -end - --- Creates the section -function AbstractSection.create(self, section) - return self.map:set(section, nil, self.sectiontype) -end - - - ---[[ -NamedSection - A fixed configuration section defined by its name -]]-- -NamedSection = class(AbstractSection) - -function NamedSection.__init__(self, map, section, ...) - AbstractSection.__init__(self, map, ...) - self.template = "cbi/nsection" - - self.section = section - self.addremove = false -end - -function NamedSection.parse(self) - local s = self.section - local active = self:cfgvalue(s) - - - if self.addremove then - local path = self.config.."."..s - if active then -- Remove the section - if ffluci.http.formvalue("cbi.rns."..path) and self:remove(s) then - return - end - else -- Create and apply default values - if ffluci.http.formvalue("cbi.cns."..path) and self:create(s) then - for k,v in pairs(self.children) do - v:write(s, v.default) - end - end - end - end - - if active then - AbstractSection.parse_dynamic(self, s) - if ffluci.http.formvalue("cbi.submit") then - Node.parse(self, s) - end - AbstractSection.parse_optionals(self, s) - end -end - - ---[[ -TypedSection - A (set of) configuration section(s) defined by the type - addremove: Defines whether the user can add/remove sections of this type - anonymous: Allow creating anonymous sections - validate: a validation function returning nil if the section is invalid -]]-- -TypedSection = class(AbstractSection) - -function TypedSection.__init__(self, ...) - AbstractSection.__init__(self, ...) - self.template = "cbi/tsection" - self.deps = {} - self.excludes = {} - - self.anonymous = false -end - --- Return all matching UCI sections for this TypedSection -function TypedSection.cfgsections(self) - local sections = {} - local map, order = self.map:get() - - for i, k in ipairs(order) do - if map[k][".type"] == self.sectiontype then - if self:checkscope(k) then - table.insert(sections, k) - end - end - end - - return sections -end - --- Creates a new section of this type with the given name (or anonymous) -function TypedSection.create(self, name) - if name then - self.map:set(name, nil, self.sectiontype) - else - name = self.map:add(self.sectiontype) - end - - for k,v in pairs(self.children) do - if v.default then - self.map:set(name, v.option, v.default) - end - end -end - --- Limits scope to sections that have certain option => value pairs -function TypedSection.depends(self, option, value) - table.insert(self.deps, {option=option, value=value}) -end - --- Excludes several sections by name -function TypedSection.exclude(self, field) - self.excludes[field] = true -end - -function TypedSection.parse(self) - if self.addremove then - -- Create - local crval = "cbi.cts." .. self.config .. "." .. self.sectiontype - local name = ffluci.http.formvalue(crval) - if self.anonymous then - if name then - self:create() - end - else - if name then - -- Ignore if it already exists - if self:cfgvalue(name) then - name = nil; - end - - name = self:checkscope(name) - - if not name then - self.err_invalid = true - end - - if name and name:len() > 0 then - self:create(name) - end - end - end - - -- Remove - crval = "cbi.rts." .. self.config - name = ffluci.http.formvaluetable(crval) - for k,v in pairs(name) do - if self:cfgvalue(k) and self:checkscope(k) then - self:remove(k) - end - end - end - - for i, k in ipairs(self:cfgsections()) do - AbstractSection.parse_dynamic(self, k) - if ffluci.http.formvalue("cbi.submit") then - Node.parse(self, k) - end - AbstractSection.parse_optionals(self, k) - end -end - --- Render the children -function TypedSection.render_children(self, section) - for k, node in ipairs(self.children) do - node:render(section) - end -end - --- Verifies scope of sections -function TypedSection.checkscope(self, section) - -- Check if we are not excluded - if self.excludes[section] then - return nil - end - - -- Check if at least one dependency is met - if #self.deps > 0 and self:cfgvalue(section) then - local stat = false - - for k, v in ipairs(self.deps) do - if self:cfgvalue(section)[v.option] == v.value then - stat = true - end - end - - if not stat then - return nil - end - end - - return self:validate(section) -end - - --- Dummy validate function -function TypedSection.validate(self, section) - return section -end - - ---[[ -AbstractValue - An abstract Value Type - null: Value can be empty - valid: A function returning the value if it is valid otherwise nil - depends: A table of option => value pairs of which one must be true - default: The default value - size: The size of the input fields - rmempty: Unset value if empty - optional: This value is optional (see AbstractSection.optionals) -]]-- -AbstractValue = class(Node) - -function AbstractValue.__init__(self, map, option, ...) - Node.__init__(self, ...) - self.option = option - self.map = map - self.config = map.config - self.tag_invalid = {} - self.deps = {} - - self.rmempty = false - self.default = nil - self.size = nil - self.optional = false -end - --- Add a dependencie to another section field -function AbstractValue.depends(self, field, value) - table.insert(self.deps, {field=field, value=value}) -end - --- Return whether this object should be created -function AbstractValue.formcreated(self, section) - local key = "cbi.opt."..self.config.."."..section - return (ffluci.http.formvalue(key) == self.option) -end - --- Returns the formvalue for this object -function AbstractValue.formvalue(self, section) - local key = "cbid."..self.map.config.."."..section.."."..self.option - return ffluci.http.formvalue(key) -end - -function AbstractValue.parse(self, section) - local fvalue = self:formvalue(section) - - if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI - fvalue = self:validate(fvalue) - if not fvalue then - self.tag_invalid[section] = true - end - if fvalue and not (fvalue == self:cfgvalue(section)) then - self:write(section, fvalue) - end - else -- Unset the UCI or error - if self.rmempty or self.optional then - self:remove(section) - end - end -end - --- Render if this value exists or if it is mandatory -function AbstractValue.render(self, s) - if not self.optional or self:cfgvalue(s) or self:formcreated(s) then - ffluci.template.render(self.template, {self=self, section=s}) - end -end - --- Return the UCI value of this object -function AbstractValue.cfgvalue(self, section) - return self.map:get(section, self.option) -end - --- Validate the form value -function AbstractValue.validate(self, value) - return value -end - --- Write to UCI -function AbstractValue.write(self, section, value) - return self.map:set(section, self.option, value) -end - --- Remove from UCI -function AbstractValue.remove(self, section) - return self.map:del(section, self.option) -end - - - - ---[[ -Value - A one-line value - maxlength: The maximum length - isnumber: The value must be a valid (floating point) number - isinteger: The value must be a valid integer - ispositive: The value must be positive (and a number) -]]-- -Value = class(AbstractValue) - -function Value.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/value" - - self.maxlength = nil - self.isnumber = false - self.isinteger = false -end - --- This validation is a bit more complex -function Value.validate(self, val) - if self.maxlength and tostring(val):len() > self.maxlength then - val = nil - end - - return ffluci.util.validate(val, self.isnumber, self.isinteger) -end - - --- DummyValue - This does nothing except being there -DummyValue = class(AbstractValue) - -function DummyValue.__init__(self, map, ...) - AbstractValue.__init__(self, map, ...) - self.template = "cbi/dvalue" - self.value = nil -end - -function DummyValue.parse(self) - -end - -function DummyValue.render(self, s) - ffluci.template.render(self.template, {self=self, section=s}) -end - - ---[[ -Flag - A flag being enabled or disabled -]]-- -Flag = class(AbstractValue) - -function Flag.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/fvalue" - - self.enabled = "1" - self.disabled = "0" -end - --- A flag can only have two states: set or unset -function Flag.parse(self, section) - local fvalue = self:formvalue(section) - - if fvalue then - fvalue = self.enabled - else - fvalue = self.disabled - end - - if fvalue == self.enabled or (not self.optional and not self.rmempty) then - if not(fvalue == self:cfgvalue(section)) then - self:write(section, fvalue) - end - else - self:remove(section) - end -end - - - ---[[ -ListValue - A one-line value predefined in a list - widget: The widget that will be used (select, radio) -]]-- -ListValue = class(AbstractValue) - -function ListValue.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/lvalue" - self.keylist = {} - self.vallist = {} - - self.size = 1 - self.widget = "select" -end - -function ListValue.value(self, key, val) - val = val or key - table.insert(self.keylist, tostring(key)) - table.insert(self.vallist, tostring(val)) -end - -function ListValue.validate(self, val) - if ffluci.util.contains(self.keylist, val) then - return val - else - return nil - end -end - - - ---[[ -MultiValue - Multiple delimited values - widget: The widget that will be used (select, checkbox) - delimiter: The delimiter that will separate the values (default: " ") -]]-- -MultiValue = class(AbstractValue) - -function MultiValue.__init__(self, ...) - AbstractValue.__init__(self, ...) - self.template = "cbi/mvalue" - self.keylist = {} - self.vallist = {} - - self.widget = "checkbox" - self.delimiter = " " -end - -function MultiValue.value(self, key, val) - val = val or key - table.insert(self.keylist, tostring(key)) - table.insert(self.vallist, tostring(val)) -end - -function MultiValue.valuelist(self, section) - local val = self:cfgvalue(section) - - if not(type(val) == "string") then - return {} - end - - return ffluci.util.split(val, self.delimiter) -end - -function MultiValue.validate(self, val) - if not(type(val) == "string") then - return nil - end - - local result = "" - - for value in val:gmatch("[^\n]+") do - if ffluci.util.contains(self.keylist, value) then - result = result .. self.delimiter .. value - end - end - - if result:len() > 0 then - return result:sub(self.delimiter:len() + 1) - else - return nil - end -end \ No newline at end of file diff --git a/core/src/ffluci/config.lua b/core/src/ffluci/config.lua deleted file mode 100644 index 0db45ac89..000000000 --- a/core/src/ffluci/config.lua +++ /dev/null @@ -1,48 +0,0 @@ ---[[ -FFLuCI - Configuration - -Description: -Some FFLuCI configuration values read from uci file "luci" - - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.config", package.seeall) -require("ffluci.model.uci") -require("ffluci.util") -require("ffluci.sys") - --- Warning! This is only for fallback and compatibility purporses! -- -main = {} - --- This is where stylesheets and images go -main.mediaurlbase = "/ffluci/media" - --- Does anybody think about browser autodetect here? --- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE -main.lang = "de" - - --- Now overwrite with UCI values -local ucidata = ffluci.model.uci.sections("luci") -if ucidata then - ffluci.util.update(ffluci.config, ucidata) -end \ No newline at end of file diff --git a/core/src/ffluci/debug.lua b/core/src/ffluci/debug.lua deleted file mode 100644 index 1be40348e..000000000 --- a/core/src/ffluci/debug.lua +++ /dev/null @@ -1,2 +0,0 @@ -module("ffluci.debug", package.seeall) -__file__ = debug.getinfo(1, 'S').source:sub(2) \ No newline at end of file diff --git a/core/src/ffluci/dispatcher.lua b/core/src/ffluci/dispatcher.lua deleted file mode 100644 index c60e5dcd1..000000000 --- a/core/src/ffluci/dispatcher.lua +++ /dev/null @@ -1,300 +0,0 @@ ---[[ -FFLuCI - Dispatcher - -Description: -The request dispatcher and module dispatcher generators - - -The dispatching process: - For a detailed explanation of the dispatching process we assume: - You have installed the FFLuCI CGI-Dispatcher in /cgi-bin/ffluci - - To enforce a higher level of security only the CGI-Dispatcher - resides inside the web server's document root, everything else - stays inside an external directory, we assume this is /lua/ffluci - for this explanation. - - All controllers and action are reachable as sub-objects of /cgi-bin/ffluci - as if they were virtual folders and files - e.g.: /cgi-bin/ffluci/public/info/about - /cgi-bin/ffluci/admin/network/interfaces - and so on. - - The PATH_INFO variable holds the dispatch path and - will be split into three parts: /category/module/action - - Category: This is the category in which modules are stored in - By default there are two categories: - "public" - which is the default public category - "admin" - which is the default protected category - - As FFLuCI itself does not implement authentication - you should make sure that "admin" and other sensitive - categories are protected by the webserver. - - E.g. for busybox add a line like: - /cgi-bin/ffluci/admin:root:$p$root - to /etc/httpd.conf to protect the "admin" category - - - Module: This is the controller which will handle the request further - It is always a submodule of ffluci.controller, so a module - called "helloworld" will be stored in - /lua/ffluci/controller/helloworld.lua - You are free to submodule your controllers any further. - - Action: This is action that will be invoked after loading the module. - The kind of how the action will be dispatched depends on - the module dispatcher that is defined in the controller. - See the description of the default module dispatcher down - on this page for some examples. - - - The main dispatcher at first searches for the module by trying to - include ffluci.controller.category.module - (where "category" is the category name and "module" is the module name) - If this fails a 404 status code will be send to the client and FFLuCI exits - - Then the main dispatcher calls the module dispatcher - ffluci.controller.category.module.dispatcher with the request object - as the only argument. The module dispatcher is then responsible - for the further dispatching process. - - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.dispatcher", package.seeall) -require("ffluci.http") -require("ffluci.template") -require("ffluci.config") -require("ffluci.sys") - --- Sets privilege for given category -function assign_privileges(category) - local cp = ffluci.config.category_privileges - if cp and cp[category] then - local u, g = cp[category]:match("([^:]+):([^:]+)") - ffluci.sys.process.setuser(u) - ffluci.sys.process.setgroup(g) - end -end - - --- Builds a URL from a triple of category, module and action -function build_url(category, module, action) - category = category or "public" - module = module or "index" - action = action or "index" - - local pattern = ffluci.http.env.SCRIPT_NAME .. "/%s/%s/%s" - return pattern:format(category, module, action) -end - - --- Dispatches the "request" -function dispatch(req) - request = req - local m = "ffluci.controller." .. request.category .. "." .. request.module - local stat, module = pcall(require, m) - if not stat then - return error404() - else - module.request = request - module.dispatcher = module.dispatcher or dynamic - setfenv(module.dispatcher, module) - return module.dispatcher(request) - end -end - --- Sends a 404 error code and renders the "error404" template if available -function error404(message) - ffluci.http.status(404, "Not Found") - message = message or "Not Found" - - if not pcall(ffluci.template.render, "error404") then - ffluci.http.prepare_content("text/plain") - print(message) - end - return false -end - --- Sends a 500 error code and renders the "error500" template if available -function error500(message) - ffluci.http.status(500, "Internal Server Error") - - if not pcall(ffluci.template.render, "error500", {message=message}) then - ffluci.http.prepare_content("text/plain") - print(message) - end - return false -end - - --- Dispatches a request depending on the PATH_INFO variable -function httpdispatch() - local pathinfo = ffluci.http.env.PATH_INFO or "" - local parts = pathinfo:gmatch("/[%w-]+") - - local sanitize = function(s, default) - return s and s:sub(2) or default - end - - local cat = sanitize(parts(), "public") - local mod = sanitize(parts(), "index") - local act = sanitize(parts(), "index") - - assign_privileges(cat) - dispatch({category=cat, module=mod, action=act}) -end - - --- Dispatchers -- - - --- The Action Dispatcher searches the module for any function called --- action_"request.action" and calls it -function action(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) then - disp.error404() - end -end - --- The CBI dispatcher directly parses and renders the CBI map which is --- placed in ffluci/modles/cbi/"request.module"/"request.action" -function cbi(...) - local disp = require("ffluci.dispatcher") - if not disp._cbi(...) then - disp.error404() - end -end - --- The dynamic dispatcher chains the action, submodule, simpleview and CBI dispatcher --- in this particular order. It is the default dispatcher. -function dynamic(...) - local disp = require("ffluci.dispatcher") - if not disp._action(...) - and not disp._submodule(...) - and not disp._simpleview(...) - and not disp._cbi(...) then - disp.error404() - end -end - --- The Simple View Dispatcher directly renders the template --- which is placed in ffluci/views/"request.module"/"request.action" -function simpleview(...) - local disp = require("ffluci.dispatcher") - if not disp._simpleview(...) then - disp.error404() - end -end - - --- The submodule dispatcher tries to load a submodule of the controller --- and calls its "action"-function -function submodule(...) - local disp = require("ffluci.dispatcher") - if not disp._submodule(...) then - disp.error404() - end -end - - --- Internal Dispatcher Functions -- - -function _action(request) - local action = getfenv(2)["action_" .. request.action:gsub("-", "_")] - local i18n = require("ffluci.i18n") - - if action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - action() - return true - else - return false - end -end - - -function _cbi(request) - local disp = require("ffluci.dispatcher") - local tmpl = require("ffluci.template") - local cbi = require("ffluci.cbi") - local i18n = require("ffluci.i18n") - - local path = request.category.."_"..request.module.."/"..request.action - - local stat, map = pcall(cbi.load, path) - if stat and map then - local stat, err = pcall(map.parse, map) - if not stat then - disp.error500(err) - return true - end - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - tmpl.render("cbi/header") - map:render() - tmpl.render("cbi/footer") - return true - elseif not stat then - disp.error500(map) - return true - else - return false - end -end - - -function _simpleview(request) - local i18n = require("ffluci.i18n") - local tmpl = require("ffluci.template") - - local path = request.category.."_"..request.module.."/"..request.action - - local stat, t = pcall(tmpl.Template, path) - if stat then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - t:render() - return true - else - return false - end -end - - -function _submodule(request) - local i18n = require("ffluci.i18n") - local m = "ffluci.controller." .. request.category .. "." .. - request.module .. "." .. request.action - local stat, module = pcall(require, m) - - if stat and module.action then - i18n.loadc(request.category .. "_" .. request.module) - i18n.loadc(request.category .. "_" .. request.module .. "_" .. request.action) - return pcall(module.action) - end - - return false -end \ No newline at end of file diff --git a/core/src/ffluci/fs.lua b/core/src/ffluci/fs.lua deleted file mode 100644 index 0ca191be9..000000000 --- a/core/src/ffluci/fs.lua +++ /dev/null @@ -1,94 +0,0 @@ ---[[ -FFLuCI - Filesystem tools - -Description: -A module offering often needed filesystem manipulation functions - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.fs", package.seeall) - -require("posix") - --- Glob -function glob(pattern) - return posix.glob(pattern) -end - --- Checks whether a file exists -function isfile(filename) - local fp = io.open(filename, "r") - if fp then fp:close() end - return fp ~= nil -end - --- Returns the content of file -function readfile(filename) - local fp, err = io.open(filename) - - if fp == nil then - return nil, err - end - - local data = fp:read("*a") - fp:close() - return data -end - --- Writes given data to a file -function writefile(filename, data) - local fp, err = io.open(filename, "w") - - if fp == nil then - return nil, err - end - - fp:write(data) - fp:close() - - return true -end - --- Returns the file modification date/time of "path" -function mtime(path) - return posix.stat(path, "mtime") -end - --- basename wrapper -basename = posix.basename - --- dirname wrapper -dirname = posix.dirname - --- dir wrapper -function dir(path) - local dir = {} - for node in posix.files(path) do - table.insert(dir, 1, node) - end - return dir -end - --- Alias for posix.mkdir -mkdir = posix.mkdir - --- Alias for posix.rmdir -rmdir = posix.rmdir \ No newline at end of file diff --git a/core/src/ffluci/http.lua b/core/src/ffluci/http.lua deleted file mode 100644 index f4ba57094..000000000 --- a/core/src/ffluci/http.lua +++ /dev/null @@ -1,36 +0,0 @@ ---[[ -FFLuCI - HTTP-Interaction - -Description: -HTTP-Header manipulator and form variable preprocessor - -FileId: -$Id$ - -ToDo: -- Cookie handling - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.http", package.seeall) - -if ENV and ENV.HASERLVER then - require("ffluci.sgi.haserl") -elseif webuci then - require("ffluci.sgi.webuci") -end \ No newline at end of file diff --git a/core/src/ffluci/i18n.lua b/core/src/ffluci/i18n.lua deleted file mode 100644 index 88381dde7..000000000 --- a/core/src/ffluci/i18n.lua +++ /dev/null @@ -1,58 +0,0 @@ ---[[ -FFLuCI - Internationalisation - -Description: -A very minimalistic but yet effective internationalisation module - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.i18n", package.seeall) -require("ffluci.sys") - -table = {} -i18ndir = ffluci.sys.libpath() .. "/i18n/" - --- Clears the translation table -function clear() - table = {} -end - --- Loads a translation and copies its data into the global translation table -function load(file) - local f = loadfile(i18ndir .. file) - if f then - setfenv(f, table) - f() - return true - else - return false - end -end - --- Same as load but autocompletes the filename with .LANG from config.lang -function loadc(file) - return load(file .. "." .. require("ffluci.config").main.lang) -end - --- Returns the i18n-value defined by "key" or if there is no such: "default" -function translate(key, default) - return table[key] or default -end \ No newline at end of file diff --git a/core/src/ffluci/i18n/cbi.en b/core/src/ffluci/i18n/cbi.en deleted file mode 100644 index 6069cec45..000000000 --- a/core/src/ffluci/i18n/cbi.en +++ /dev/null @@ -1,4 +0,0 @@ -cbi_add = "Add entry" -cbi_del = "Remove entry" -cbi_invalid = "Error: Invalid input value" -cbi_addopt = "-- Field --" \ No newline at end of file diff --git a/core/src/ffluci/i18n/default.en b/core/src/ffluci/i18n/default.en deleted file mode 100644 index 726095ae7..000000000 --- a/core/src/ffluci/i18n/default.en +++ /dev/null @@ -1,22 +0,0 @@ -add = "Add" -save = "Save" -reset = "Reset" - -load = "Load" -webif = "Webinterface" -public = "Public" -admin = "Administration" -apply = "Apply" -changes = "Changes" -revert = "Revert" - -index = "Overview" -system = "System" -services = "Services" -network = "Network" -wifi = "Wifi" -status = "Status" -statistic = "Statistic" - -config = "Configuration" -path = "Path" \ No newline at end of file diff --git a/core/src/ffluci/init.lua b/core/src/ffluci/init.lua deleted file mode 100644 index 8d4cea2e9..000000000 --- a/core/src/ffluci/init.lua +++ /dev/null @@ -1,29 +0,0 @@ ---[[ -FFLuCI - Freifunk Lua Configuration Interface - -Description: -This is the init file - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci", package.seeall) - -__version__ = "0.4" -__appname__ = "FFLuCI" diff --git a/core/src/ffluci/menu.lua b/core/src/ffluci/menu.lua deleted file mode 100644 index 5724b2cb9..000000000 --- a/core/src/ffluci/menu.lua +++ /dev/null @@ -1,137 +0,0 @@ ---[[ -FFLuCI - Menu Builder - -Description: -Collects menu building information from controllers - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.menu", package.seeall) - -require("ffluci.fs") -require("ffluci.util") -require("ffluci.sys") - --- Default modelpath -modelpath = ffluci.sys.libpath() .. "/model/menu/" - --- Menu definition extra scope -scope = { - translate = function(...) return require("ffluci.i18n").translate(...) end, - loadtrans = function(...) return require("ffluci.i18n").loadc(...) end, - isfile = ffluci.fs.isfile -} - --- Local menu database -local menu = nil - --- The current pointer -local menuc = {} - --- Adds a menu category to the current menu and selects it -function add(cat, controller, title, order) - order = order or 100 - if not menu[cat] then - menu[cat] = {} - end - - local entry = {} - entry[".descr"] = title - entry[".order"] = order - entry[".contr"] = controller - - menuc = entry - - local i = 0 - for k,v in ipairs(menu[cat]) do - if v[".order"] > entry[".order"] then - break - end - i = k - end - table.insert(menu[cat], i+1, entry) - - return true -end - --- Adds an action to the current menu -function act(action, title) - table.insert(menuc, {action = action, descr = title}) - return true -end - --- Selects a menu category -function sel(cat, controller) - if not menu[cat] then - return nil - end - menuc = menu[cat] - - local stat = nil - for k,v in ipairs(menuc) do - if v[".contr"] == controller then - menuc = v - stat = true - end - end - - return stat -end - - --- Collect all menu information provided in the model dir -function collect() - local generators = {} - - for k, menu in pairs(ffluci.fs.dir(modelpath)) do - if menu:sub(1, 1) ~= "." then - local f = loadfile(modelpath.."/"..menu) - if f then - table.insert(generators, f) - end - end - end - - return generators -end - --- Parse the collected information -function parse(generators) - menu = {} - for i, f in pairs(generators) do - local env = ffluci.util.clone(scope) - - env.add = add - env.sel = sel - env.act = act - - setfenv(f, env) - f() - end - return menu -end - --- Returns the menu information -function get() - if not menu then - menu = parse(collect()) - end - return menu -end \ No newline at end of file diff --git a/core/src/ffluci/model/ipkg.lua b/core/src/ffluci/model/ipkg.lua deleted file mode 100644 index 3b149fb16..000000000 --- a/core/src/ffluci/model/ipkg.lua +++ /dev/null @@ -1,140 +0,0 @@ ---[[ -FFLuCI - IPKG wrapper library - -Description: -Wrapper for the ipkg Package manager - -Any return value of false or nil can be interpreted as an error - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.model.ipkg", package.seeall) -require("ffluci.sys") -require("ffluci.util") - -ipkg = "ipkg" - --- Returns repository information -function info(pkg) - return _lookup("info", pkg) -end - --- Returns a table with status information -function status(pkg) - return _lookup("status", pkg) -end - --- Installs packages -function install(...) - return _action("install", ...) -end - --- Returns whether a package is installed -function installed(pkg, ...) - local p = status(...)[pkg] - return (p and p.Status and p.Status.installed) -end - --- Removes packages -function remove(...) - return _action("remove", ...) -end - --- Updates package lists -function update() - return _action("update") -end - --- Upgrades installed packages -function upgrade() - return _action("upgrade") -end - - --- Internal action function -function _action(cmd, ...) - local pkg = "" - arg.n = nil - for k, v in pairs(arg) do - pkg = pkg .. " '" .. v:gsub("'", "") .. "'" - end - - local c = ipkg.." "..cmd.." "..pkg.." >/dev/null 2>&1" - local r = os.execute(c) - return (r == 0), r -end - --- Internal lookup function -function _lookup(act, pkg) - local cmd = ipkg .. " " .. act - if pkg then - cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'" - end - - return _parselist(ffluci.sys.exec(cmd .. " 2>/dev/null")) -end - --- Internal parser function -function _parselist(rawdata) - if type(rawdata) ~= "string" then - error("IPKG: Invalid rawdata given") - end - - rawdata = ffluci.util.split(rawdata) - local data = {} - local c = {} - local l = nil - - for k, line in pairs(rawdata) do - if line:sub(1, 1) ~= " " then - local split = ffluci.util.split(line, ":", 1) - local key = nil - local val = nil - - if split[1] then - key = ffluci.util.trim(split[1]) - end - - if split[2] then - val = ffluci.util.trim(split[2]) - end - - if key and val then - if key == "Package" then - c = {Package = val} - data[val] = c - elseif key == "Status" then - c.Status = {} - for i, j in pairs(ffluci.util.split(val, " ")) do - c.Status[j] = true - end - else - c[key] = val - end - l = key - end - else - -- Multi-line field - c[l] = c[l] .. "\n" .. line:sub(2) - end - end - - return data -end \ No newline at end of file diff --git a/core/src/ffluci/model/uci.lua b/core/src/ffluci/model/uci.lua deleted file mode 100644 index ca5b232eb..000000000 --- a/core/src/ffluci/model/uci.lua +++ /dev/null @@ -1,92 +0,0 @@ ---[[ -FFLuCI - UCI mpdel - -Description: -Generalized UCI model - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.model.uci", package.seeall) - --- Default savedir -savedir = "/tmp/.uci" - --- Test whether to load libuci-Wrapper or /sbin/uci-Wrapper -if pcall(require, "uci") then - Session = require("ffluci.model.uci.libuci").Session -else - Session = require("ffluci.model.uci.wrapper").Session -end - --- The default Session -local default = Session() -local state = Session("/var/state") - --- The state Session -function StateSession() - return state -end - - --- Wrapper for "uci add" -function add(...) - return default:add(...) -end - - --- Wrapper for "uci changes" -function changes(...) - return default:changes(...) -end - --- Wrapper for "uci commit" -function commit(...) - return default:commit(...) -end - - --- Wrapper for "uci del" -function del(...) - return default:del(...) -end - - --- Wrapper for "uci get" -function get(...) - return default:get(...) -end - - --- Wrapper for "uci revert" -function revert(...) - return default:revert(...) -end - - --- Wrapper for "uci show" -function sections(...) - return default:sections(...) -end - - --- Wrapper for "uci set" -function set(...) - return default:set(...) -end \ No newline at end of file diff --git a/core/src/ffluci/model/uci/libuci.lua b/core/src/ffluci/model/uci/libuci.lua deleted file mode 100644 index b160dc10a..000000000 --- a/core/src/ffluci/model/uci/libuci.lua +++ /dev/null @@ -1,193 +0,0 @@ ---[[ -FFLuCI - UCI libuci wrapper - -Description: -Wrapper for the libuci Lua bindings - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.model.uci.libuci", package.seeall) - -require("uci") -require("ffluci.util") -require("ffluci.sys") - --- Session class -Session = ffluci.util.class() - --- Session constructor -function Session.__init__(self, savedir) - self.ucicmd = savedir and "uci -P " .. savedir or "uci" - self.savedir = savedir or ffluci.model.uci.savedir -end - -function Session.add(self, config, section_type) - return self:_uci("add " .. _path(config) .. " " .. _path(section_type)) -end - -function Session.changes(self, config) - return self:_uci("changes " .. _path(config)) -end - -function Session.commit(self, config) - self:t_load(config) - return self:t_commit(config) -end - -function Session.del(self, config, section, option) - return self:_uci2("del " .. _path(config, section, option)) -end - -function Session.get(self, config, section, option) - self:t_load(config) - return self:t_get(config, section, option) -end - -function Session.revert(self, config) - self:t_load(config) - return self:t_revert(config) -end - -function Session.sections(self, config) - self:t_load(config) - return self:t_sections(config) -end - -function Session.set(self, config, section, option, value) - self:t_load(config) - return self:t_set(config, section, option, value) and self:t_save(config) -end - -function Session.synchronize(self) - return uci.set_savedir(self.savedir) -end - - --- UCI-Transactions - -function Session.t_load(self, config) - return self:synchronize() and uci.load(config) -end - -function Session.t_save(self, config) - return uci.save(config) -end - -function Session.t_add(self, config, type) - self:t_save(config) - local r = self:add(config, type) - self:t_load(config) - return r -end - -function Session.t_commit(self, config) - return uci.commit(config) -end - -function Session.t_del(self, config, section, option) - self:t_save(config) - local r = self:del(config, section, option) - self:t_load(config) - return r -end - -function Session.t_get(self, config, section, option) - if option then - return uci.get(config, section, option) - else - return uci.get(config, section) - end -end - -function Session.t_revert(self, config) - return uci.revert(config) -end - -function Session.t_sections(self, config) - local raw = uci.get_all(config) - if not raw then - return nil - end - - local s = {} - local o = {} - - for i, sec in ipairs(raw) do - table.insert(o, sec.name) - - s[sec.name] = sec.options - s[sec.name][".type"] = sec.type - end - - return s, o -end - -function Session.t_set(self, config, section, option, value) - if option then - return uci.set(config.."."..section.."."..option.."="..value) - else - return uci.set(config.."."..section.."="..value) - end -end - --- Internal functions -- - - -function Session._uci(self, cmd) - local res = ffluci.sys.exec(self.ucicmd .. " 2>/dev/null " .. cmd) - - if res:len() == 0 then - return nil - else - return res:sub(1, res:len()-1) - end -end - -function Session._uci2(self, cmd) - local res = ffluci.sys.exec(self.ucicmd .. " 2>&1 " .. cmd) - - if res:len() > 0 then - return false, res - else - return true - end -end - --- Build path (config.section.option=value) and prevent command injection -function _path(...) - local result = "" - - -- Not using ipairs because it is not reliable in case of nil arguments - arg.n = nil - for k,v in pairs(arg) do - if v then - v = tostring(v) - if k == 1 then - result = "'" .. v:gsub("['.]", "") .. "'" - elseif k < 4 then - result = result .. ".'" .. v:gsub("['.]", "") .. "'" - elseif k == 4 then - result = result .. "='" .. v:gsub("'", "") .. "'" - end - end - end - return result -end \ No newline at end of file diff --git a/core/src/ffluci/model/uci/wrapper.lua b/core/src/ffluci/model/uci/wrapper.lua deleted file mode 100644 index 3aa3b5fd1..000000000 --- a/core/src/ffluci/model/uci/wrapper.lua +++ /dev/null @@ -1,171 +0,0 @@ ---[[ -FFLuCI - UCI wrapper library - -Description: -Wrapper for the /sbin/uci application, syntax of implemented functions -is comparable to the syntax of the uci application - -Any return value of false or nil can be interpreted as an error - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.model.uci.wrapper", package.seeall) - -require("ffluci.util") -require("ffluci.sys") - --- Session class -Session = ffluci.util.class() - --- Session constructor -function Session.__init__(self, savedir) - self.ucicmd = savedir and "uci -P " .. savedir or "uci" -end - -function Session.add(self, config, section_type) - return self:_uci("add " .. _path(config) .. " " .. _path(section_type)) -end - -function Session.changes(self, config) - return self:_uci("changes " .. _path(config)) -end - -function Session.commit(self, config) - return self:_uci2("commit " .. _path(config)) -end - -function Session.del(self, config, section, option) - return self:_uci2("del " .. _path(config, section, option)) -end - -function Session.get(self, config, section, option) - return self:_uci("get " .. _path(config, section, option)) -end - -function Session.revert(self, config) - return self:_uci2("revert " .. _path(config)) -end - -function Session.sections(self, config) - if not config then - return nil - end - - local r1, r2 = self:_uci3("show " .. _path(config)) - if type(r1) == "table" then - return r1, r2 - else - return nil, r2 - end -end - -function Session.set(self, config, section, option, value) - return self:_uci2("set " .. _path(config, section, option, value)) -end - -function Session.synchronize(self) end - --- Dummy transaction functions - -function Session.t_load(self) end -function Session.t_save(self) end - -Session.t_add = Session.add -Session.t_commit = Session.commit -Session.t_del = Session.del -Session.t_get = Session.get -Session.t_revert = Session.revert -Session.t_sections = Session.sections -Session.t_set = Session.set - - - - - --- Internal functions -- - - -function Session._uci(self, cmd) - local res = ffluci.sys.exec(self.ucicmd .. " 2>/dev/null " .. cmd) - - if res:len() == 0 then - return nil - else - return res:sub(1, res:len()-1) - end -end - -function Session._uci2(self, cmd) - local res = ffluci.sys.exec(self.ucicmd .. " 2>&1 " .. cmd) - - if res:len() > 0 then - return false, res - else - return true - end -end - -function Session._uci3(self, cmd) - local res = ffluci.sys.execl(self.ucicmd .. " 2>&1 " .. cmd) - if res[1] and res[1]:sub(1, self.ucicmd:len()+1) == self.ucicmd..":" then - return nil, res[1] - end - - local tbl = {} - local ord = {} - - for k,line in pairs(res) do - c, s, t = line:match("^([^.]-)%.([^.]-)=(.-)$") - if c then - tbl[s] = {} - table.insert(ord, s) - tbl[s][".type"] = t - end - - c, s, o, v = line:match("^([^.]-)%.([^.]-)%.([^.]-)=(.-)$") - if c then - tbl[s][o] = v - end - end - - return tbl, ord -end - --- Build path (config.section.option=value) and prevent command injection -function _path(...) - local result = "" - - -- Not using ipairs because it is not reliable in case of nil arguments - arg.n = nil - for k,v in pairs(arg) do - if v then - v = tostring(v) - if k == 1 then - result = "'" .. v:gsub("['.]", "") .. "'" - elseif k < 4 then - result = result .. ".'" .. v:gsub("['.]", "") .. "'" - elseif k == 4 then - result = result .. "='" .. v:gsub("'", "") .. "'" - end - end - end - return result -end \ No newline at end of file diff --git a/core/src/ffluci/sgi/haserl.lua b/core/src/ffluci/sgi/haserl.lua deleted file mode 100644 index 7e9854d7d..000000000 --- a/core/src/ffluci/sgi/haserl.lua +++ /dev/null @@ -1,76 +0,0 @@ ---[[ -FFLuCI - SGI-Module for Haserl - -Description: -Server Gateway Interface for Haserl - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.sgi.haserl", package.seeall) - --- Environment Table -ffluci.http.env = ENV - - --- Returns a table of all COOKIE, GET and POST Parameters -function ffluci.http.formvalues() - return FORM -end - --- Gets form value from key -function ffluci.http.formvalue(key, default) - local c = ffluci.http.formvalues() - - for match in key:gmatch("[%w-_]+") do - c = c[match] - if c == nil then - return default - end - end - - return c -end - --- Gets a table of values with a certain prefix -function ffluci.http.formvaluetable(prefix) - return ffluci.http.formvalue(prefix, {}) -end - --- Sends a custom HTTP-Header -function ffluci.http.header(key, value) - print(key .. ": " .. value) -end - --- Set Content-Type -function ffluci.http.prepare_content(type) - print("Content-Type: "..type.."\n") -end - --- Asks the browser to redirect to "url" -function ffluci.http.redirect(url) - ffluci.http.status(302, "Found") - ffluci.http.header("Location", url) - print() -end - --- Sets HTTP-Status-Header -function ffluci.http.status(code, message) - print("Status: " .. tostring(code) .. " " .. message) -end diff --git a/core/src/ffluci/sgi/webuci.lua b/core/src/ffluci/sgi/webuci.lua deleted file mode 100644 index 5e17ab793..000000000 --- a/core/src/ffluci/sgi/webuci.lua +++ /dev/null @@ -1,83 +0,0 @@ ---[[ -FFLuCI - SGI-Module for Haserl - -Description: -Server Gateway Interface for Haserl - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.sgi.webuci", package.seeall) - --- Environment Table -ffluci.http.env = webuci.env - - -local status_set = false - --- Returns a table of all COOKIE, GET and POST Parameters -function ffluci.http.formvalues() - return webuci.vars -end - --- Gets form value from key -function ffluci.http.formvalue(key, default) - return ffluci.http.formvalues()[key] or default -end - --- Gets a table of values with a certain prefix -function ffluci.http.formvaluetable(prefix) - local vals = {} - prefix = prefix and prefix .. "." or "." - - for k, v in pairs(ffluci.http.formvalues()) do - if k:find(prefix, 1, true) == 1 then - vals[k:sub(#prefix + 1)] = v - end - end - - return vals -end - --- Sends a custom HTTP-Header -function ffluci.http.header(key, value) - print(key .. ": " .. value) -end - --- Set Content-Type -function ffluci.http.prepare_content(type) - if not status_set then - ffluci.http.status(200, "OK") - end - - print("Content-Type: "..type.."\n") -end - --- Asks the browser to redirect to "url" -function ffluci.http.redirect(url) - ffluci.http.status(302, "Found") - ffluci.http.header("Location", url) - print() -end - --- Sets HTTP-Status-Header -function ffluci.http.status(code, message) - print(webuci.env.SERVER_PROTOCOL .. " " .. tostring(code) .. " " .. message) - status_set = true -end diff --git a/core/src/ffluci/sys.lua b/core/src/ffluci/sys.lua deleted file mode 100644 index 376654893..000000000 --- a/core/src/ffluci/sys.lua +++ /dev/null @@ -1,363 +0,0 @@ ---[[ -FFLuCI - System library - -Description: -Utilities for interaction with the Linux system - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.sys", package.seeall) -require("posix") -require("ffluci.bits") -require("ffluci.util") - --- Returns whether a system is bigendian -function bigendian() - local fp = io.open("/bin/sh") - fp:seek("set", 5) - return (fp:read(1):byte() ~= 1) -end - --- Runs "command" and returns its output -function exec(command) - local pp = io.popen(command) - local data = pp:read("*a") - pp:close() - - return data -end - --- Runs "command" and returns its output as a array of lines -function execl(command) - local pp = io.popen(command) - local line = "" - local data = {} - - while true do - line = pp:read() - if (line == nil) then break end - table.insert(data, line) - end - pp:close() - - return data -end - --- Uses "ffluci-flash" to flash a new image file to the system -function flash(image, kpattern) - local cmd = "ffluci-flash " - if kpattern then - cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' " - end - cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1" - - return os.execute(cmd) -end - --- Returns the hostname -function hostname() - return io.lines("/proc/sys/kernel/hostname")() -end - --- Returns the contents of a documented referred by an URL -function httpget(url) - return exec("wget -qO- '"..url:gsub("'", "").."'") -end - --- Returns the FFLuci-Basedir -function libpath() - return ffluci.fs.dirname(require("ffluci.debug").__file__) -end - --- Returns the load average -function loadavg() - local loadavg = io.lines("/proc/loadavg")() - return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$") -end - --- Reboots the system -function reboot() - return os.execute("reboot >/dev/null 2>&1") -end - --- Returns the system type, cpu name, and installed physical memory -function sysinfo() - local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null" - local c2 = "uname -m 2>/dev/null" - local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null" - local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null" - local c5 = "cat /proc/meminfo|grep MemTotal|cut -d: -f2 2>/dev/null" - - local s = ffluci.util.trim(exec(c1)) - local m = "" - local r = "" - - if s == "" then - s = ffluci.util.trim(exec(c2)) - m = ffluci.util.trim(exec(c3)) - else - m = ffluci.util.trim(exec(c4)) - end - - r = ffluci.util.trim(exec(c5)) - - return s, m, r -end - - -group = {} -group.getgroup = posix.getgroup - -net = {} --- Returns the ARP-Table -function net.arptable() - return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+") -end - --- Returns whether an IP-Adress belongs to a certain net -function net.belongs(ip, ipnet, prefix) - return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix)) -end - --- Detect the default route -function net.defaultroute() - local routes = net.routes() - local route = nil - - for i, r in pairs(ffluci.sys.net.routes()) do - if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then - route = r - end - end - - return route -end - --- Returns all available network interfaces -function net.devices() - local devices = {} - for line in io.lines("/proc/net/dev") do - table.insert(devices, line:match(" *(.-):")) - end - return devices -end - --- Returns the MAC-Address belonging to the given IP-Address -function net.ip4mac(ip) - local mac = nil - - for i, l in ipairs(net.arptable()) do - if l["IP address"] == ip then - mac = l["HW address"] - end - end - - return mac -end - --- Returns the prefix to a given netmask -function net.mask4prefix(mask) - local bin = net.ip4bin(mask) - - if not bin then - return nil - end - - return #ffluci.util.split(bin, "1")-1 -end - --- Returns the kernel routing table -function net.routes() - return _parse_delimited_table(io.lines("/proc/net/route")) -end - --- Returns the numeric IP to a given hexstring -function net.hexip4(hex, be) - if #hex ~= 8 then - return nil - end - - be = be or bigendian() - - local hexdec = ffluci.bits.Hex2Dec - - local ip = "" - if be then - ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "." - ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "." - ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "." - ip = ip .. tostring(hexdec(hex:sub(7,8))) - else - ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "." - ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "." - ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "." - ip = ip .. tostring(hexdec(hex:sub(1,2))) - end - - return ip -end - --- Returns the binary IP to a given IP -function net.ip4bin(ip) - local parts = ffluci.util.split(ip, '.') - if #parts ~= 4 then - return nil - end - - local decbin = ffluci.bits.Dec2Bin - - local bin = "" - bin = bin .. decbin(parts[1], 8) - bin = bin .. decbin(parts[2], 8) - bin = bin .. decbin(parts[3], 8) - bin = bin .. decbin(parts[4], 8) - - return bin -end - --- Tests whether a host is pingable -function net.pingtest(host) - return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") -end - - -process = {} -process.info = posix.getpid - --- Sets the gid of a process -function process.setgroup(pid, gid) - return posix.setpid("g", pid, gid) -end - --- Sets the uid of a process -function process.setuser(pid, uid) - return posix.setpid("u", pid, uid) -end - -user = {} --- returns user information to a given uid -user.getuser = posix.getpasswd - --- Changes the user password of given user -function user.setpasswd(user, pwd) - if pwd then - pwd = pwd:gsub("'", "") - end - - if user then - user = user:gsub("'", "") - end - - local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|" - cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1" - return os.execute(cmd) -end - - -wifi = {} - -function wifi.getiwconfig() - local cnt = exec("/usr/sbin/iwconfig 2>/dev/null") - local iwc = {} - - for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do - local k = l:match("^(.-) ") - l = l:gsub("^(.-) +", "", 1) - if k then - iwc[k] = _parse_mixed_record(l) - end - end - - return iwc -end - -function wifi.iwscan() - local cnt = exec("iwlist scan 2>/dev/null") - local iws = {} - - for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do - local k = l:match("^(.-) ") - l = l:gsub("^[^\n]+", "", 1) - l = ffluci.util.trim(l) - if k then - iws[k] = {} - for j, c in pairs(ffluci.util.split(l, "\n Cell")) do - c = c:gsub("^(.-)- ", "", 1) - c = ffluci.util.split(c, "\n", 7) - c = table.concat(c, "\n", 1) - table.insert(iws[k], _parse_mixed_record(c)) - end - end - end - - return iws -end - - --- Internal functions - -function _parse_delimited_table(iter, delimiter) - delimiter = delimiter or "%s+" - - local data = {} - local trim = ffluci.util.trim - local split = ffluci.util.split - - local keys = split(trim(iter()), delimiter, nil, true) - for i, j in pairs(keys) do - keys[i] = trim(keys[i]) - end - - for line in iter do - local row = {} - line = trim(line) - if #line > 0 then - for i, j in pairs(split(line, delimiter, nil, true)) do - if keys[i] then - row[keys[i]] = j - end - end - end - table.insert(data, row) - end - - return data -end - -function _parse_mixed_record(cnt) - local data = {} - - for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n")) do - for j, f in pairs(ffluci.util.split(ffluci.util.trim(l), " ")) do - local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*') - - if k then - if x == "" then - table.insert(data, k) - else - data[k] = v - end - end - end - end - - return data -end \ No newline at end of file diff --git a/core/src/ffluci/template.lua b/core/src/ffluci/template.lua deleted file mode 100644 index 7ac47cbbe..000000000 --- a/core/src/ffluci/template.lua +++ /dev/null @@ -1,227 +0,0 @@ ---[[ -FFLuCI - Template Parser - -Description: -A template parser supporting includes, translations, Lua code blocks -and more. It can be used either as a compiler or as an interpreter. - -FileId: $Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- -module("ffluci.template", package.seeall) - -require("ffluci.config") -require("ffluci.util") -require("ffluci.fs") -require("ffluci.http") - -viewdir = ffluci.sys.libpath() .. "/view/" - - --- Compile modes: --- none: Never compile, only use precompiled data from files --- memory: Always compile, do not save compiled files, ignore precompiled --- file: Compile on demand, save compiled files, update precompiled -compiler_mode = "memory" - - --- This applies to compiler modes "always" and "smart" --- --- Produce compiled lua code rather than lua sourcecode --- WARNING: Increases template size heavily!!! --- This produces the same bytecode as luac but does not have a strip option -compiler_enable_bytecode = false - - --- Define the namespace for template modules -viewns = { - translate = function(...) return require("ffluci.i18n").translate(...) end, - config = function(...) return require("ffluci.model.uci").get(...) or "" end, - controller = ffluci.http.env.SCRIPT_NAME, - media = ffluci.config.main.mediaurlbase, - write = io.write, - include = function(name) Template(name):render(getfenv(2)) end, -} - --- Compiles a given template into an executable Lua module -function compile(template) - -- Search all <% %> expressions (remember: Lua table indexes begin with #1) - local function expr_add(command) - table.insert(expr, command) - return "<%" .. tostring(#expr) .. "%>" - end - - -- As "expr" should be local, we have to assign it to the "expr_add" scope - local expr = {} - ffluci.util.extfenv(expr_add, "expr", expr) - - -- Save all expressiosn to table "expr" - template = template:gsub("<%%(.-)%%>", expr_add) - - local function sanitize(s) - s = ffluci.util.escape(s) - s = ffluci.util.escape(s, "'") - s = ffluci.util.escape(s, "\n") - return s - end - - -- Escape and sanitize all the template (all non-expressions) - template = sanitize(template) - - -- Template module header/footer declaration - local header = "write('" - local footer = "')" - - template = header .. template .. footer - - -- Replacements - local r_include = "')\ninclude('%s')\nwrite('" - local r_i18n = "'..translate('%1','%2')..'" - local r_uci = "'..config('%1','%2','%3')..'" - local r_pexec = "'..(%s or '')..'" - local r_exec = "')\n%s\nwrite('" - - -- Parse the expressions - for k,v in pairs(expr) do - local p = v:sub(1, 1) - local re = nil - if p == "+" then - re = r_include:format(sanitize(string.sub(v, 2))) - elseif p == ":" then - re = sanitize(v):gsub(":(.-) (.+)", r_i18n) - elseif p == "~" then - re = sanitize(v):gsub("~(.-)%.(.-)%.(.+)", r_uci) - elseif p == "=" then - re = r_pexec:format(v:sub(2)) - else - re = r_exec:format(v) - end - template = template:gsub("<%%"..tostring(k).."%%>", re) - end - - if compiler_enable_bytecode then - tf = loadstring(template) - template = string.dump(tf) - end - - return template -end - --- Oldstyle render shortcut -function render(name, scope, ...) - scope = scope or getfenv(2) - local s, t = pcall(Template, name) - if not s then - error(t) - else - t:render(scope, ...) - end -end - - --- Template class -Template = ffluci.util.class() - --- Shared template cache to store templates in to avoid unnecessary reloading -Template.cache = {} - - --- Constructor - Reads and compiles the template on-demand -function Template.__init__(self, name) - if self.cache[name] then - self.template = self.cache[name] - else - self.template = nil - end - - -- Create a new namespace for this template - self.viewns = {} - - -- Copy over from general namespace - for k, v in pairs(viewns) do - self.viewns[k] = v - end - - -- If we have a cached template, skip compiling and loading - if self.template then - return - end - - -- Compile and build - local sourcefile = viewdir .. name .. ".htm" - local compiledfile = viewdir .. name .. ".lua" - local err - - if compiler_mode == "file" then - local tplmt = ffluci.fs.mtime(sourcefile) - local commt = ffluci.fs.mtime(compiledfile) - - -- Build if there is no compiled file or if compiled file is outdated - if ((commt == nil) and not (tplmt == nil)) - or (not (commt == nil) and not (tplmt == nil) and commt < tplmt) then - local source - source, err = ffluci.fs.readfile(sourcefile) - - if source then - local compiled = compile(source) - ffluci.fs.writefile(compiledfile, compiled) - self.template, err = loadstring(compiled) - end - else - self.template, err = loadfile(compiledfile) - end - - elseif compiler_mode == "none" then - self.template, err = loadfile(self.compiledfile) - - elseif compiler_mode == "memory" then - local source - source, err = ffluci.fs.readfile(sourcefile) - if source then - self.template, err = loadstring(compile(source)) - end - - end - - -- If we have no valid template throw error, otherwise cache the template - if not self.template then - error(err) - else - self.cache[name] = self.template - end -end - - --- Renders a template -function Template.render(self, scope) - scope = scope or getfenv(2) - - -- Save old environment - local oldfenv = getfenv(self.template) - - -- Put our predefined objects in the scope of the template - ffluci.util.resfenv(self.template) - ffluci.util.updfenv(self.template, scope) - ffluci.util.updfenv(self.template, self.viewns) - - -- Now finally render the thing - self.template() - - -- Reset environment - setfenv(self.template, oldfenv) -end diff --git a/core/src/ffluci/util.lua b/core/src/ffluci/util.lua deleted file mode 100644 index 9e3c7f25e..000000000 --- a/core/src/ffluci/util.lua +++ /dev/null @@ -1,203 +0,0 @@ ---[[ -FFLuCI - Utility library - -Description: -Several common useful Lua functions - -FileId: -$Id$ - -License: -Copyright 2008 Steven Barth - -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. - -]]-- - -module("ffluci.util", package.seeall) - - --- Lua simplified Python-style OO class support emulation -function class(base) - local class = {} - - local create = function(class, ...) - local inst = {} - setmetatable(inst, {__index = class}) - - if inst.__init__ then - local stat, err = pcall(inst.__init__, inst, ...) - if not stat then - error(err) - end - end - - return inst - end - - local classmeta = {__call = create} - - if base then - classmeta.__index = base - end - - setmetatable(class, classmeta) - return class -end - - --- Clones an object (deep on-demand) -function clone(object, deep) - local copy = {} - - for k, v in pairs(object) do - if deep and type(v) == "table" then - v = clone(v, deep) - end - copy[k] = v - end - - setmetatable(copy, getmetatable(object)) - - return copy -end - - --- Checks whether a table has an object "value" in it -function contains(table, value) - for k,v in pairs(table) do - if value == v then - return true - end - end - return false -end - - --- Dumps a table to stdout (useful for testing and debugging) -function dumptable(t, i) - i = i or 0 - for k,v in pairs(t) do - print(string.rep("\t", i) .. k, v) - if type(v) == "table" then - dumptable(v, i+1) - end - end -end - - --- Escapes all occurences of c in s -function escape(s, c) - c = c or "\\" - return s:gsub(c, "\\" .. c) -end - - --- Populate obj in the scope of f as key -function extfenv(f, key, obj) - local scope = getfenv(f) - scope[key] = obj -end - - --- Checks whether an object is an instanceof class -function instanceof(object, class) - local meta = getmetatable(object) - while meta and meta.__index do - if meta.__index == class then - return true - end - meta = getmetatable(meta.__index) - end - return false -end - - --- Creates valid XML PCDATA from a string -function pcdata(value) - value = value:gsub("&", "&") - value = value:gsub('"', """) - value = value:gsub("'", "'") - value = value:gsub("<", "<") - return value:gsub(">", ">") -end - - --- Resets the scope of f doing a shallow copy of its scope into a new table -function resfenv(f) - setfenv(f, clone(getfenv(f))) -end - - --- Splits a string into an array -function split(str, pat, max, regex) - pat = pat or "\n" - max = max or #str - - local t = {} - local c = 1 - - if #str == 0 then - return {""} - end - - if #pat == 0 then - return nil - end - - if max == 0 then - return str - end - - repeat - local s, e = str:find(pat, c, not regex) - table.insert(t, str:sub(c, s and s - 1)) - max = max - 1 - c = e and e + 1 or #str + 1 - until not s or max < 0 - - return t -end - --- Removes whitespace from beginning and end of a string -function trim(str) - local s = str:gsub("^%s*(.-)%s*$", "%1") - return s -end - --- Updates given table with new values -function update(t, updates) - for k, v in pairs(updates) do - t[k] = v - end -end - - --- Updates the scope of f with "extscope" -function updfenv(f, extscope) - update(getfenv(f), extscope) -end - - --- Validates a variable -function validate(value, cast_number, cast_int) - if cast_number or cast_int then - value = tonumber(value) - end - - if cast_int and value and not(value % 1 == 0) then - value = nil - end - - return value -end \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/dvalue.htm b/core/src/ffluci/view/cbi/dvalue.htm deleted file mode 100644 index f54667def..000000000 --- a/core/src/ffluci/view/cbi/dvalue.htm +++ /dev/null @@ -1,12 +0,0 @@ -<%+cbi/valueheader%> -<% if self.value then - if type(self.value) == "function" then %> - <%=self:value(section)%> -<% else %> - <%=self.value%> -<% end -else %> - <%=self:cfgvalue(section)%> -<% end %> -  -<%+cbi/valuefooter%> diff --git a/core/src/ffluci/view/cbi/footer.htm b/core/src/ffluci/view/cbi/footer.htm deleted file mode 100644 index 2acf710cd..000000000 --- a/core/src/ffluci/view/cbi/footer.htm +++ /dev/null @@ -1,7 +0,0 @@ -
- - - -
- -<%+footer%> \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/fvalue.htm b/core/src/ffluci/view/cbi/fvalue.htm deleted file mode 100644 index b609f1d4f..000000000 --- a/core/src/ffluci/view/cbi/fvalue.htm +++ /dev/null @@ -1,3 +0,0 @@ -<%+cbi/valueheader%> - " name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == self.enabled then %> checked="checked"<% end %> value="1" /> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/header.htm b/core/src/ffluci/view/cbi/header.htm deleted file mode 100644 index 3b615d729..000000000 --- a/core/src/ffluci/view/cbi/header.htm +++ /dev/null @@ -1,7 +0,0 @@ -<%+header%> -
-
- - - -
diff --git a/core/src/ffluci/view/cbi/lvalue.htm b/core/src/ffluci/view/cbi/lvalue.htm deleted file mode 100644 index f1ae5a093..000000000 --- a/core/src/ffluci/view/cbi/lvalue.htm +++ /dev/null @@ -1,16 +0,0 @@ -<%+cbi/valueheader%> -<% if self.widget == "select" then %> - -<% elseif self.widget == "radio" then - local c = 0; - for i, key in pairs(self.keylist) do - c = c + 1%> - <%=self.vallist[i]%>"<% if self:cfgvalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" /> -<% if c == self.size then c = 0 %>
-<% end end %> -<% end %> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/map.htm b/core/src/ffluci/view/cbi/map.htm deleted file mode 100644 index 835393c1c..000000000 --- a/core/src/ffluci/view/cbi/map.htm +++ /dev/null @@ -1,6 +0,0 @@ -
-

<%=self.title%>

-
<%=self.description%>
-<% self:render_children() %> -
-
diff --git a/core/src/ffluci/view/cbi/mvalue.htm b/core/src/ffluci/view/cbi/mvalue.htm deleted file mode 100644 index 97a1c4295..000000000 --- a/core/src/ffluci/view/cbi/mvalue.htm +++ /dev/null @@ -1,19 +0,0 @@ -<% -local v = self:valuelist(section) -%> -<%+cbi/valueheader%> -<% if self.widget == "select" then %> - -<% elseif self.widget == "checkbox" then - local c = 0; - for i, key in pairs(self.keylist) do - c = c + 1%> - <%=self.vallist[i]%>[]"<% if ffluci.util.contains(v, key) then %> checked="checked"<% end %> value="<%=key%>" /> -<% if c == self.size then c = 0 %>
-<% end end %> -<% end %> -<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/nsection.htm b/core/src/ffluci/view/cbi/nsection.htm deleted file mode 100644 index bbc87f185..000000000 --- a/core/src/ffluci/view/cbi/nsection.htm +++ /dev/null @@ -1,17 +0,0 @@ -<% if self:cfgvalue(self.section) then -section = self.section %> -
-

<%=self.title%>

-
<%=self.description%>
- <% if self.addremove then %>
- -
<% end %> -<%+cbi/ucisection%> -
-<% elseif self.addremove then %> -
-

<%=self.title%>

-
<%=self.description%>
- -
-<% end %> diff --git a/core/src/ffluci/view/cbi/tsection.htm b/core/src/ffluci/view/cbi/tsection.htm deleted file mode 100644 index 666c14b2e..000000000 --- a/core/src/ffluci/view/cbi/tsection.htm +++ /dev/null @@ -1,22 +0,0 @@ -
-

<%=self.title%>

-
<%=self.description%>
-<% for i, k in ipairs(self:cfgsections()) do%> - <% if self.addremove then %>
- -
<% end %> - <% if not self.anonymous then %>

<%=k%>

<% end %> -<% section = k %> -<%+cbi/ucisection%> -<% end %> -<% if self.addremove then %> -
- <% if self.anonymous then %> - - <% else %> - - - <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> -
-<% end %> -
diff --git a/core/src/ffluci/view/cbi/ucisection.htm b/core/src/ffluci/view/cbi/ucisection.htm deleted file mode 100644 index a1fb39cf4..000000000 --- a/core/src/ffluci/view/cbi/ucisection.htm +++ /dev/null @@ -1,23 +0,0 @@ -
-<% self:render_children(section) %> - <% if #self.optionals[section] > 0 or self.dynamic then %> -
- <% if self.dynamic then %> - - <% else %> - - - <% end %> - -
- <% end %> -
-
\ No newline at end of file diff --git a/core/src/ffluci/view/cbi/value.htm b/core/src/ffluci/view/cbi/value.htm deleted file mode 100644 index 31bf38f77..000000000 --- a/core/src/ffluci/view/cbi/value.htm +++ /dev/null @@ -1,3 +0,0 @@ -<%+cbi/valueheader%> - size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" id="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=self:cfgvalue(section)%>" /> -<%+cbi/valuefooter%> diff --git a/core/src/ffluci/view/cbi/valuefooter.htm b/core/src/ffluci/view/cbi/valuefooter.htm deleted file mode 100644 index 6151a3a66..000000000 --- a/core/src/ffluci/view/cbi/valuefooter.htm +++ /dev/null @@ -1,8 +0,0 @@ -
<%=self.description%> 
- - <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> - - <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/core/src/ffluci/view/cbi/valueheader.htm b/core/src/ffluci/view/cbi/valueheader.htm deleted file mode 100644 index 062efa2dd..000000000 --- a/core/src/ffluci/view/cbi/valueheader.htm +++ /dev/null @@ -1,3 +0,0 @@ -
"> -
<%=self.title%>
-
\ No newline at end of file diff --git a/core/src/ffluci/view/error404.htm b/core/src/ffluci/view/error404.htm deleted file mode 100644 index 51ea176d6..000000000 --- a/core/src/ffluci/view/error404.htm +++ /dev/null @@ -1,5 +0,0 @@ -<%+header%> -

404 Not Found

-

Sorry, the object you requested was not found.

-Unable to dispatch: <%=ffluci.http.env.PATH_INFO%> -<%+footer%> \ No newline at end of file diff --git a/core/src/ffluci/view/error500.htm b/core/src/ffluci/view/error500.htm deleted file mode 100644 index 8af22e8f2..000000000 --- a/core/src/ffluci/view/error500.htm +++ /dev/null @@ -1,5 +0,0 @@ -<%+header%> -

500 Internal Server Error

-

Sorry, the server encountered an unexpected error.

-<%=message%> -<%+footer%> \ No newline at end of file diff --git a/core/src/ffluci/view/footer.htm b/core/src/ffluci/view/footer.htm deleted file mode 100644 index 67856771b..000000000 --- a/core/src/ffluci/view/footer.htm +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
- - - - \ No newline at end of file diff --git a/core/src/ffluci/view/header.htm b/core/src/ffluci/view/header.htm deleted file mode 100644 index bc65e3e89..000000000 --- a/core/src/ffluci/view/header.htm +++ /dev/null @@ -1,79 +0,0 @@ -<% -require("ffluci.sys") -local load1, load5, load15 = ffluci.sys.loadavg() -local req = require("ffluci.dispatcher").request -local menu = require("ffluci.menu").get()[req.category] -menu = menu or {} -require("ffluci.i18n").loadc("default") -require("ffluci.http").prepare_content("text/html") -%> - - - - - - - - FFLuCI - - - - - - -
- - <% end %> -
- -
\ No newline at end of file diff --git a/core/src/fs.lua b/core/src/fs.lua new file mode 100644 index 000000000..0ca191be9 --- /dev/null +++ b/core/src/fs.lua @@ -0,0 +1,94 @@ +--[[ +FFLuCI - Filesystem tools + +Description: +A module offering often needed filesystem manipulation functions + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.fs", package.seeall) + +require("posix") + +-- Glob +function glob(pattern) + return posix.glob(pattern) +end + +-- Checks whether a file exists +function isfile(filename) + local fp = io.open(filename, "r") + if fp then fp:close() end + return fp ~= nil +end + +-- Returns the content of file +function readfile(filename) + local fp, err = io.open(filename) + + if fp == nil then + return nil, err + end + + local data = fp:read("*a") + fp:close() + return data +end + +-- Writes given data to a file +function writefile(filename, data) + local fp, err = io.open(filename, "w") + + if fp == nil then + return nil, err + end + + fp:write(data) + fp:close() + + return true +end + +-- Returns the file modification date/time of "path" +function mtime(path) + return posix.stat(path, "mtime") +end + +-- basename wrapper +basename = posix.basename + +-- dirname wrapper +dirname = posix.dirname + +-- dir wrapper +function dir(path) + local dir = {} + for node in posix.files(path) do + table.insert(dir, 1, node) + end + return dir +end + +-- Alias for posix.mkdir +mkdir = posix.mkdir + +-- Alias for posix.rmdir +rmdir = posix.rmdir \ No newline at end of file diff --git a/core/src/http.lua b/core/src/http.lua new file mode 100644 index 000000000..f4ba57094 --- /dev/null +++ b/core/src/http.lua @@ -0,0 +1,36 @@ +--[[ +FFLuCI - HTTP-Interaction + +Description: +HTTP-Header manipulator and form variable preprocessor + +FileId: +$Id$ + +ToDo: +- Cookie handling + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.http", package.seeall) + +if ENV and ENV.HASERLVER then + require("ffluci.sgi.haserl") +elseif webuci then + require("ffluci.sgi.webuci") +end \ No newline at end of file diff --git a/core/src/i18n.lua b/core/src/i18n.lua new file mode 100644 index 000000000..88381dde7 --- /dev/null +++ b/core/src/i18n.lua @@ -0,0 +1,58 @@ +--[[ +FFLuCI - Internationalisation + +Description: +A very minimalistic but yet effective internationalisation module + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.i18n", package.seeall) +require("ffluci.sys") + +table = {} +i18ndir = ffluci.sys.libpath() .. "/i18n/" + +-- Clears the translation table +function clear() + table = {} +end + +-- Loads a translation and copies its data into the global translation table +function load(file) + local f = loadfile(i18ndir .. file) + if f then + setfenv(f, table) + f() + return true + else + return false + end +end + +-- Same as load but autocompletes the filename with .LANG from config.lang +function loadc(file) + return load(file .. "." .. require("ffluci.config").main.lang) +end + +-- Returns the i18n-value defined by "key" or if there is no such: "default" +function translate(key, default) + return table[key] or default +end \ No newline at end of file diff --git a/core/src/i18n/cbi.en b/core/src/i18n/cbi.en new file mode 100644 index 000000000..6069cec45 --- /dev/null +++ b/core/src/i18n/cbi.en @@ -0,0 +1,4 @@ +cbi_add = "Add entry" +cbi_del = "Remove entry" +cbi_invalid = "Error: Invalid input value" +cbi_addopt = "-- Field --" \ No newline at end of file diff --git a/core/src/i18n/default.en b/core/src/i18n/default.en new file mode 100644 index 000000000..726095ae7 --- /dev/null +++ b/core/src/i18n/default.en @@ -0,0 +1,22 @@ +add = "Add" +save = "Save" +reset = "Reset" + +load = "Load" +webif = "Webinterface" +public = "Public" +admin = "Administration" +apply = "Apply" +changes = "Changes" +revert = "Revert" + +index = "Overview" +system = "System" +services = "Services" +network = "Network" +wifi = "Wifi" +status = "Status" +statistic = "Statistic" + +config = "Configuration" +path = "Path" \ No newline at end of file diff --git a/core/src/init.lua b/core/src/init.lua new file mode 100644 index 000000000..8d4cea2e9 --- /dev/null +++ b/core/src/init.lua @@ -0,0 +1,29 @@ +--[[ +FFLuCI - Freifunk Lua Configuration Interface + +Description: +This is the init file + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci", package.seeall) + +__version__ = "0.4" +__appname__ = "FFLuCI" diff --git a/core/src/menu.lua b/core/src/menu.lua new file mode 100644 index 000000000..5724b2cb9 --- /dev/null +++ b/core/src/menu.lua @@ -0,0 +1,137 @@ +--[[ +FFLuCI - Menu Builder + +Description: +Collects menu building information from controllers + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci.menu", package.seeall) + +require("ffluci.fs") +require("ffluci.util") +require("ffluci.sys") + +-- Default modelpath +modelpath = ffluci.sys.libpath() .. "/model/menu/" + +-- Menu definition extra scope +scope = { + translate = function(...) return require("ffluci.i18n").translate(...) end, + loadtrans = function(...) return require("ffluci.i18n").loadc(...) end, + isfile = ffluci.fs.isfile +} + +-- Local menu database +local menu = nil + +-- The current pointer +local menuc = {} + +-- Adds a menu category to the current menu and selects it +function add(cat, controller, title, order) + order = order or 100 + if not menu[cat] then + menu[cat] = {} + end + + local entry = {} + entry[".descr"] = title + entry[".order"] = order + entry[".contr"] = controller + + menuc = entry + + local i = 0 + for k,v in ipairs(menu[cat]) do + if v[".order"] > entry[".order"] then + break + end + i = k + end + table.insert(menu[cat], i+1, entry) + + return true +end + +-- Adds an action to the current menu +function act(action, title) + table.insert(menuc, {action = action, descr = title}) + return true +end + +-- Selects a menu category +function sel(cat, controller) + if not menu[cat] then + return nil + end + menuc = menu[cat] + + local stat = nil + for k,v in ipairs(menuc) do + if v[".contr"] == controller then + menuc = v + stat = true + end + end + + return stat +end + + +-- Collect all menu information provided in the model dir +function collect() + local generators = {} + + for k, menu in pairs(ffluci.fs.dir(modelpath)) do + if menu:sub(1, 1) ~= "." then + local f = loadfile(modelpath.."/"..menu) + if f then + table.insert(generators, f) + end + end + end + + return generators +end + +-- Parse the collected information +function parse(generators) + menu = {} + for i, f in pairs(generators) do + local env = ffluci.util.clone(scope) + + env.add = add + env.sel = sel + env.act = act + + setfenv(f, env) + f() + end + return menu +end + +-- Returns the menu information +function get() + if not menu then + menu = parse(collect()) + end + return menu +end \ No newline at end of file diff --git a/core/src/model/ipkg.lua b/core/src/model/ipkg.lua new file mode 100644 index 000000000..3b149fb16 --- /dev/null +++ b/core/src/model/ipkg.lua @@ -0,0 +1,140 @@ +--[[ +FFLuCI - IPKG wrapper library + +Description: +Wrapper for the ipkg Package manager + +Any return value of false or nil can be interpreted as an error + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci.model.ipkg", package.seeall) +require("ffluci.sys") +require("ffluci.util") + +ipkg = "ipkg" + +-- Returns repository information +function info(pkg) + return _lookup("info", pkg) +end + +-- Returns a table with status information +function status(pkg) + return _lookup("status", pkg) +end + +-- Installs packages +function install(...) + return _action("install", ...) +end + +-- Returns whether a package is installed +function installed(pkg, ...) + local p = status(...)[pkg] + return (p and p.Status and p.Status.installed) +end + +-- Removes packages +function remove(...) + return _action("remove", ...) +end + +-- Updates package lists +function update() + return _action("update") +end + +-- Upgrades installed packages +function upgrade() + return _action("upgrade") +end + + +-- Internal action function +function _action(cmd, ...) + local pkg = "" + arg.n = nil + for k, v in pairs(arg) do + pkg = pkg .. " '" .. v:gsub("'", "") .. "'" + end + + local c = ipkg.." "..cmd.." "..pkg.." >/dev/null 2>&1" + local r = os.execute(c) + return (r == 0), r +end + +-- Internal lookup function +function _lookup(act, pkg) + local cmd = ipkg .. " " .. act + if pkg then + cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'" + end + + return _parselist(ffluci.sys.exec(cmd .. " 2>/dev/null")) +end + +-- Internal parser function +function _parselist(rawdata) + if type(rawdata) ~= "string" then + error("IPKG: Invalid rawdata given") + end + + rawdata = ffluci.util.split(rawdata) + local data = {} + local c = {} + local l = nil + + for k, line in pairs(rawdata) do + if line:sub(1, 1) ~= " " then + local split = ffluci.util.split(line, ":", 1) + local key = nil + local val = nil + + if split[1] then + key = ffluci.util.trim(split[1]) + end + + if split[2] then + val = ffluci.util.trim(split[2]) + end + + if key and val then + if key == "Package" then + c = {Package = val} + data[val] = c + elseif key == "Status" then + c.Status = {} + for i, j in pairs(ffluci.util.split(val, " ")) do + c.Status[j] = true + end + else + c[key] = val + end + l = key + end + else + -- Multi-line field + c[l] = c[l] .. "\n" .. line:sub(2) + end + end + + return data +end \ No newline at end of file diff --git a/core/src/model/uci.lua b/core/src/model/uci.lua new file mode 100644 index 000000000..ca5b232eb --- /dev/null +++ b/core/src/model/uci.lua @@ -0,0 +1,92 @@ +--[[ +FFLuCI - UCI mpdel + +Description: +Generalized UCI model + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci.model.uci", package.seeall) + +-- Default savedir +savedir = "/tmp/.uci" + +-- Test whether to load libuci-Wrapper or /sbin/uci-Wrapper +if pcall(require, "uci") then + Session = require("ffluci.model.uci.libuci").Session +else + Session = require("ffluci.model.uci.wrapper").Session +end + +-- The default Session +local default = Session() +local state = Session("/var/state") + +-- The state Session +function StateSession() + return state +end + + +-- Wrapper for "uci add" +function add(...) + return default:add(...) +end + + +-- Wrapper for "uci changes" +function changes(...) + return default:changes(...) +end + +-- Wrapper for "uci commit" +function commit(...) + return default:commit(...) +end + + +-- Wrapper for "uci del" +function del(...) + return default:del(...) +end + + +-- Wrapper for "uci get" +function get(...) + return default:get(...) +end + + +-- Wrapper for "uci revert" +function revert(...) + return default:revert(...) +end + + +-- Wrapper for "uci show" +function sections(...) + return default:sections(...) +end + + +-- Wrapper for "uci set" +function set(...) + return default:set(...) +end \ No newline at end of file diff --git a/core/src/model/uci/libuci.lua b/core/src/model/uci/libuci.lua new file mode 100644 index 000000000..b160dc10a --- /dev/null +++ b/core/src/model/uci/libuci.lua @@ -0,0 +1,193 @@ +--[[ +FFLuCI - UCI libuci wrapper + +Description: +Wrapper for the libuci Lua bindings + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.model.uci.libuci", package.seeall) + +require("uci") +require("ffluci.util") +require("ffluci.sys") + +-- Session class +Session = ffluci.util.class() + +-- Session constructor +function Session.__init__(self, savedir) + self.ucicmd = savedir and "uci -P " .. savedir or "uci" + self.savedir = savedir or ffluci.model.uci.savedir +end + +function Session.add(self, config, section_type) + return self:_uci("add " .. _path(config) .. " " .. _path(section_type)) +end + +function Session.changes(self, config) + return self:_uci("changes " .. _path(config)) +end + +function Session.commit(self, config) + self:t_load(config) + return self:t_commit(config) +end + +function Session.del(self, config, section, option) + return self:_uci2("del " .. _path(config, section, option)) +end + +function Session.get(self, config, section, option) + self:t_load(config) + return self:t_get(config, section, option) +end + +function Session.revert(self, config) + self:t_load(config) + return self:t_revert(config) +end + +function Session.sections(self, config) + self:t_load(config) + return self:t_sections(config) +end + +function Session.set(self, config, section, option, value) + self:t_load(config) + return self:t_set(config, section, option, value) and self:t_save(config) +end + +function Session.synchronize(self) + return uci.set_savedir(self.savedir) +end + + +-- UCI-Transactions + +function Session.t_load(self, config) + return self:synchronize() and uci.load(config) +end + +function Session.t_save(self, config) + return uci.save(config) +end + +function Session.t_add(self, config, type) + self:t_save(config) + local r = self:add(config, type) + self:t_load(config) + return r +end + +function Session.t_commit(self, config) + return uci.commit(config) +end + +function Session.t_del(self, config, section, option) + self:t_save(config) + local r = self:del(config, section, option) + self:t_load(config) + return r +end + +function Session.t_get(self, config, section, option) + if option then + return uci.get(config, section, option) + else + return uci.get(config, section) + end +end + +function Session.t_revert(self, config) + return uci.revert(config) +end + +function Session.t_sections(self, config) + local raw = uci.get_all(config) + if not raw then + return nil + end + + local s = {} + local o = {} + + for i, sec in ipairs(raw) do + table.insert(o, sec.name) + + s[sec.name] = sec.options + s[sec.name][".type"] = sec.type + end + + return s, o +end + +function Session.t_set(self, config, section, option, value) + if option then + return uci.set(config.."."..section.."."..option.."="..value) + else + return uci.set(config.."."..section.."="..value) + end +end + +-- Internal functions -- + + +function Session._uci(self, cmd) + local res = ffluci.sys.exec(self.ucicmd .. " 2>/dev/null " .. cmd) + + if res:len() == 0 then + return nil + else + return res:sub(1, res:len()-1) + end +end + +function Session._uci2(self, cmd) + local res = ffluci.sys.exec(self.ucicmd .. " 2>&1 " .. cmd) + + if res:len() > 0 then + return false, res + else + return true + end +end + +-- Build path (config.section.option=value) and prevent command injection +function _path(...) + local result = "" + + -- Not using ipairs because it is not reliable in case of nil arguments + arg.n = nil + for k,v in pairs(arg) do + if v then + v = tostring(v) + if k == 1 then + result = "'" .. v:gsub("['.]", "") .. "'" + elseif k < 4 then + result = result .. ".'" .. v:gsub("['.]", "") .. "'" + elseif k == 4 then + result = result .. "='" .. v:gsub("'", "") .. "'" + end + end + end + return result +end \ No newline at end of file diff --git a/core/src/model/uci/wrapper.lua b/core/src/model/uci/wrapper.lua new file mode 100644 index 000000000..3aa3b5fd1 --- /dev/null +++ b/core/src/model/uci/wrapper.lua @@ -0,0 +1,171 @@ +--[[ +FFLuCI - UCI wrapper library + +Description: +Wrapper for the /sbin/uci application, syntax of implemented functions +is comparable to the syntax of the uci application + +Any return value of false or nil can be interpreted as an error + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.model.uci.wrapper", package.seeall) + +require("ffluci.util") +require("ffluci.sys") + +-- Session class +Session = ffluci.util.class() + +-- Session constructor +function Session.__init__(self, savedir) + self.ucicmd = savedir and "uci -P " .. savedir or "uci" +end + +function Session.add(self, config, section_type) + return self:_uci("add " .. _path(config) .. " " .. _path(section_type)) +end + +function Session.changes(self, config) + return self:_uci("changes " .. _path(config)) +end + +function Session.commit(self, config) + return self:_uci2("commit " .. _path(config)) +end + +function Session.del(self, config, section, option) + return self:_uci2("del " .. _path(config, section, option)) +end + +function Session.get(self, config, section, option) + return self:_uci("get " .. _path(config, section, option)) +end + +function Session.revert(self, config) + return self:_uci2("revert " .. _path(config)) +end + +function Session.sections(self, config) + if not config then + return nil + end + + local r1, r2 = self:_uci3("show " .. _path(config)) + if type(r1) == "table" then + return r1, r2 + else + return nil, r2 + end +end + +function Session.set(self, config, section, option, value) + return self:_uci2("set " .. _path(config, section, option, value)) +end + +function Session.synchronize(self) end + +-- Dummy transaction functions + +function Session.t_load(self) end +function Session.t_save(self) end + +Session.t_add = Session.add +Session.t_commit = Session.commit +Session.t_del = Session.del +Session.t_get = Session.get +Session.t_revert = Session.revert +Session.t_sections = Session.sections +Session.t_set = Session.set + + + + + +-- Internal functions -- + + +function Session._uci(self, cmd) + local res = ffluci.sys.exec(self.ucicmd .. " 2>/dev/null " .. cmd) + + if res:len() == 0 then + return nil + else + return res:sub(1, res:len()-1) + end +end + +function Session._uci2(self, cmd) + local res = ffluci.sys.exec(self.ucicmd .. " 2>&1 " .. cmd) + + if res:len() > 0 then + return false, res + else + return true + end +end + +function Session._uci3(self, cmd) + local res = ffluci.sys.execl(self.ucicmd .. " 2>&1 " .. cmd) + if res[1] and res[1]:sub(1, self.ucicmd:len()+1) == self.ucicmd..":" then + return nil, res[1] + end + + local tbl = {} + local ord = {} + + for k,line in pairs(res) do + c, s, t = line:match("^([^.]-)%.([^.]-)=(.-)$") + if c then + tbl[s] = {} + table.insert(ord, s) + tbl[s][".type"] = t + end + + c, s, o, v = line:match("^([^.]-)%.([^.]-)%.([^.]-)=(.-)$") + if c then + tbl[s][o] = v + end + end + + return tbl, ord +end + +-- Build path (config.section.option=value) and prevent command injection +function _path(...) + local result = "" + + -- Not using ipairs because it is not reliable in case of nil arguments + arg.n = nil + for k,v in pairs(arg) do + if v then + v = tostring(v) + if k == 1 then + result = "'" .. v:gsub("['.]", "") .. "'" + elseif k < 4 then + result = result .. ".'" .. v:gsub("['.]", "") .. "'" + elseif k == 4 then + result = result .. "='" .. v:gsub("'", "") .. "'" + end + end + end + return result +end \ No newline at end of file diff --git a/core/src/sys.lua b/core/src/sys.lua new file mode 100644 index 000000000..376654893 --- /dev/null +++ b/core/src/sys.lua @@ -0,0 +1,363 @@ +--[[ +FFLuCI - System library + +Description: +Utilities for interaction with the Linux system + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.sys", package.seeall) +require("posix") +require("ffluci.bits") +require("ffluci.util") + +-- Returns whether a system is bigendian +function bigendian() + local fp = io.open("/bin/sh") + fp:seek("set", 5) + return (fp:read(1):byte() ~= 1) +end + +-- Runs "command" and returns its output +function exec(command) + local pp = io.popen(command) + local data = pp:read("*a") + pp:close() + + return data +end + +-- Runs "command" and returns its output as a array of lines +function execl(command) + local pp = io.popen(command) + local line = "" + local data = {} + + while true do + line = pp:read() + if (line == nil) then break end + table.insert(data, line) + end + pp:close() + + return data +end + +-- Uses "ffluci-flash" to flash a new image file to the system +function flash(image, kpattern) + local cmd = "ffluci-flash " + if kpattern then + cmd = cmd .. "-k '" .. kpattern:gsub("'", "") .. "' " + end + cmd = cmd .. "'" .. image:gsub("'", "") .. "' >/dev/null 2>&1" + + return os.execute(cmd) +end + +-- Returns the hostname +function hostname() + return io.lines("/proc/sys/kernel/hostname")() +end + +-- Returns the contents of a documented referred by an URL +function httpget(url) + return exec("wget -qO- '"..url:gsub("'", "").."'") +end + +-- Returns the FFLuci-Basedir +function libpath() + return ffluci.fs.dirname(require("ffluci.debug").__file__) +end + +-- Returns the load average +function loadavg() + local loadavg = io.lines("/proc/loadavg")() + return loadavg:match("^(.-) (.-) (.-) (.-) (.-)$") +end + +-- Reboots the system +function reboot() + return os.execute("reboot >/dev/null 2>&1") +end + +-- Returns the system type, cpu name, and installed physical memory +function sysinfo() + local c1 = "cat /proc/cpuinfo|grep system\\ typ|cut -d: -f2 2>/dev/null" + local c2 = "uname -m 2>/dev/null" + local c3 = "cat /proc/cpuinfo|grep model\\ name|cut -d: -f2 2>/dev/null" + local c4 = "cat /proc/cpuinfo|grep cpu\\ model|cut -d: -f2 2>/dev/null" + local c5 = "cat /proc/meminfo|grep MemTotal|cut -d: -f2 2>/dev/null" + + local s = ffluci.util.trim(exec(c1)) + local m = "" + local r = "" + + if s == "" then + s = ffluci.util.trim(exec(c2)) + m = ffluci.util.trim(exec(c3)) + else + m = ffluci.util.trim(exec(c4)) + end + + r = ffluci.util.trim(exec(c5)) + + return s, m, r +end + + +group = {} +group.getgroup = posix.getgroup + +net = {} +-- Returns the ARP-Table +function net.arptable() + return _parse_delimited_table(io.lines("/proc/net/arp"), "%s%s+") +end + +-- Returns whether an IP-Adress belongs to a certain net +function net.belongs(ip, ipnet, prefix) + return (net.ip4bin(ip):sub(1, prefix) == net.ip4bin(ipnet):sub(1, prefix)) +end + +-- Detect the default route +function net.defaultroute() + local routes = net.routes() + local route = nil + + for i, r in pairs(ffluci.sys.net.routes()) do + if r.Destination == "00000000" and (not route or route.Metric > r.Metric) then + route = r + end + end + + return route +end + +-- Returns all available network interfaces +function net.devices() + local devices = {} + for line in io.lines("/proc/net/dev") do + table.insert(devices, line:match(" *(.-):")) + end + return devices +end + +-- Returns the MAC-Address belonging to the given IP-Address +function net.ip4mac(ip) + local mac = nil + + for i, l in ipairs(net.arptable()) do + if l["IP address"] == ip then + mac = l["HW address"] + end + end + + return mac +end + +-- Returns the prefix to a given netmask +function net.mask4prefix(mask) + local bin = net.ip4bin(mask) + + if not bin then + return nil + end + + return #ffluci.util.split(bin, "1")-1 +end + +-- Returns the kernel routing table +function net.routes() + return _parse_delimited_table(io.lines("/proc/net/route")) +end + +-- Returns the numeric IP to a given hexstring +function net.hexip4(hex, be) + if #hex ~= 8 then + return nil + end + + be = be or bigendian() + + local hexdec = ffluci.bits.Hex2Dec + + local ip = "" + if be then + ip = ip .. tostring(hexdec(hex:sub(1,2))) .. "." + ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "." + ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "." + ip = ip .. tostring(hexdec(hex:sub(7,8))) + else + ip = ip .. tostring(hexdec(hex:sub(7,8))) .. "." + ip = ip .. tostring(hexdec(hex:sub(5,6))) .. "." + ip = ip .. tostring(hexdec(hex:sub(3,4))) .. "." + ip = ip .. tostring(hexdec(hex:sub(1,2))) + end + + return ip +end + +-- Returns the binary IP to a given IP +function net.ip4bin(ip) + local parts = ffluci.util.split(ip, '.') + if #parts ~= 4 then + return nil + end + + local decbin = ffluci.bits.Dec2Bin + + local bin = "" + bin = bin .. decbin(parts[1], 8) + bin = bin .. decbin(parts[2], 8) + bin = bin .. decbin(parts[3], 8) + bin = bin .. decbin(parts[4], 8) + + return bin +end + +-- Tests whether a host is pingable +function net.pingtest(host) + return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") +end + + +process = {} +process.info = posix.getpid + +-- Sets the gid of a process +function process.setgroup(pid, gid) + return posix.setpid("g", pid, gid) +end + +-- Sets the uid of a process +function process.setuser(pid, uid) + return posix.setpid("u", pid, uid) +end + +user = {} +-- returns user information to a given uid +user.getuser = posix.getpasswd + +-- Changes the user password of given user +function user.setpasswd(user, pwd) + if pwd then + pwd = pwd:gsub("'", "") + end + + if user then + user = user:gsub("'", "") + end + + local cmd = "(echo '"..pwd.."';sleep 1;echo '"..pwd.."')|" + cmd = cmd .. "passwd '"..user.."' >/dev/null 2>&1" + return os.execute(cmd) +end + + +wifi = {} + +function wifi.getiwconfig() + local cnt = exec("/usr/sbin/iwconfig 2>/dev/null") + local iwc = {} + + for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do + local k = l:match("^(.-) ") + l = l:gsub("^(.-) +", "", 1) + if k then + iwc[k] = _parse_mixed_record(l) + end + end + + return iwc +end + +function wifi.iwscan() + local cnt = exec("iwlist scan 2>/dev/null") + local iws = {} + + for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n\n")) do + local k = l:match("^(.-) ") + l = l:gsub("^[^\n]+", "", 1) + l = ffluci.util.trim(l) + if k then + iws[k] = {} + for j, c in pairs(ffluci.util.split(l, "\n Cell")) do + c = c:gsub("^(.-)- ", "", 1) + c = ffluci.util.split(c, "\n", 7) + c = table.concat(c, "\n", 1) + table.insert(iws[k], _parse_mixed_record(c)) + end + end + end + + return iws +end + + +-- Internal functions + +function _parse_delimited_table(iter, delimiter) + delimiter = delimiter or "%s+" + + local data = {} + local trim = ffluci.util.trim + local split = ffluci.util.split + + local keys = split(trim(iter()), delimiter, nil, true) + for i, j in pairs(keys) do + keys[i] = trim(keys[i]) + end + + for line in iter do + local row = {} + line = trim(line) + if #line > 0 then + for i, j in pairs(split(line, delimiter, nil, true)) do + if keys[i] then + row[keys[i]] = j + end + end + end + table.insert(data, row) + end + + return data +end + +function _parse_mixed_record(cnt) + local data = {} + + for i, l in pairs(ffluci.util.split(ffluci.util.trim(cnt), "\n")) do + for j, f in pairs(ffluci.util.split(ffluci.util.trim(l), " ")) do + local k, x, v = f:match('([^%s][^:=]+) *([:=]*) *"*([^\n"]*)"*') + + if k then + if x == "" then + table.insert(data, k) + else + data[k] = v + end + end + end + end + + return data +end \ No newline at end of file diff --git a/core/src/template.lua b/core/src/template.lua new file mode 100644 index 000000000..7ac47cbbe --- /dev/null +++ b/core/src/template.lua @@ -0,0 +1,227 @@ +--[[ +FFLuCI - Template Parser + +Description: +A template parser supporting includes, translations, Lua code blocks +and more. It can be used either as a compiler or as an interpreter. + +FileId: $Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- +module("ffluci.template", package.seeall) + +require("ffluci.config") +require("ffluci.util") +require("ffluci.fs") +require("ffluci.http") + +viewdir = ffluci.sys.libpath() .. "/view/" + + +-- Compile modes: +-- none: Never compile, only use precompiled data from files +-- memory: Always compile, do not save compiled files, ignore precompiled +-- file: Compile on demand, save compiled files, update precompiled +compiler_mode = "memory" + + +-- This applies to compiler modes "always" and "smart" +-- +-- Produce compiled lua code rather than lua sourcecode +-- WARNING: Increases template size heavily!!! +-- This produces the same bytecode as luac but does not have a strip option +compiler_enable_bytecode = false + + +-- Define the namespace for template modules +viewns = { + translate = function(...) return require("ffluci.i18n").translate(...) end, + config = function(...) return require("ffluci.model.uci").get(...) or "" end, + controller = ffluci.http.env.SCRIPT_NAME, + media = ffluci.config.main.mediaurlbase, + write = io.write, + include = function(name) Template(name):render(getfenv(2)) end, +} + +-- Compiles a given template into an executable Lua module +function compile(template) + -- Search all <% %> expressions (remember: Lua table indexes begin with #1) + local function expr_add(command) + table.insert(expr, command) + return "<%" .. tostring(#expr) .. "%>" + end + + -- As "expr" should be local, we have to assign it to the "expr_add" scope + local expr = {} + ffluci.util.extfenv(expr_add, "expr", expr) + + -- Save all expressiosn to table "expr" + template = template:gsub("<%%(.-)%%>", expr_add) + + local function sanitize(s) + s = ffluci.util.escape(s) + s = ffluci.util.escape(s, "'") + s = ffluci.util.escape(s, "\n") + return s + end + + -- Escape and sanitize all the template (all non-expressions) + template = sanitize(template) + + -- Template module header/footer declaration + local header = "write('" + local footer = "')" + + template = header .. template .. footer + + -- Replacements + local r_include = "')\ninclude('%s')\nwrite('" + local r_i18n = "'..translate('%1','%2')..'" + local r_uci = "'..config('%1','%2','%3')..'" + local r_pexec = "'..(%s or '')..'" + local r_exec = "')\n%s\nwrite('" + + -- Parse the expressions + for k,v in pairs(expr) do + local p = v:sub(1, 1) + local re = nil + if p == "+" then + re = r_include:format(sanitize(string.sub(v, 2))) + elseif p == ":" then + re = sanitize(v):gsub(":(.-) (.+)", r_i18n) + elseif p == "~" then + re = sanitize(v):gsub("~(.-)%.(.-)%.(.+)", r_uci) + elseif p == "=" then + re = r_pexec:format(v:sub(2)) + else + re = r_exec:format(v) + end + template = template:gsub("<%%"..tostring(k).."%%>", re) + end + + if compiler_enable_bytecode then + tf = loadstring(template) + template = string.dump(tf) + end + + return template +end + +-- Oldstyle render shortcut +function render(name, scope, ...) + scope = scope or getfenv(2) + local s, t = pcall(Template, name) + if not s then + error(t) + else + t:render(scope, ...) + end +end + + +-- Template class +Template = ffluci.util.class() + +-- Shared template cache to store templates in to avoid unnecessary reloading +Template.cache = {} + + +-- Constructor - Reads and compiles the template on-demand +function Template.__init__(self, name) + if self.cache[name] then + self.template = self.cache[name] + else + self.template = nil + end + + -- Create a new namespace for this template + self.viewns = {} + + -- Copy over from general namespace + for k, v in pairs(viewns) do + self.viewns[k] = v + end + + -- If we have a cached template, skip compiling and loading + if self.template then + return + end + + -- Compile and build + local sourcefile = viewdir .. name .. ".htm" + local compiledfile = viewdir .. name .. ".lua" + local err + + if compiler_mode == "file" then + local tplmt = ffluci.fs.mtime(sourcefile) + local commt = ffluci.fs.mtime(compiledfile) + + -- Build if there is no compiled file or if compiled file is outdated + if ((commt == nil) and not (tplmt == nil)) + or (not (commt == nil) and not (tplmt == nil) and commt < tplmt) then + local source + source, err = ffluci.fs.readfile(sourcefile) + + if source then + local compiled = compile(source) + ffluci.fs.writefile(compiledfile, compiled) + self.template, err = loadstring(compiled) + end + else + self.template, err = loadfile(compiledfile) + end + + elseif compiler_mode == "none" then + self.template, err = loadfile(self.compiledfile) + + elseif compiler_mode == "memory" then + local source + source, err = ffluci.fs.readfile(sourcefile) + if source then + self.template, err = loadstring(compile(source)) + end + + end + + -- If we have no valid template throw error, otherwise cache the template + if not self.template then + error(err) + else + self.cache[name] = self.template + end +end + + +-- Renders a template +function Template.render(self, scope) + scope = scope or getfenv(2) + + -- Save old environment + local oldfenv = getfenv(self.template) + + -- Put our predefined objects in the scope of the template + ffluci.util.resfenv(self.template) + ffluci.util.updfenv(self.template, scope) + ffluci.util.updfenv(self.template, self.viewns) + + -- Now finally render the thing + self.template() + + -- Reset environment + setfenv(self.template, oldfenv) +end diff --git a/core/src/util.lua b/core/src/util.lua new file mode 100644 index 000000000..9e3c7f25e --- /dev/null +++ b/core/src/util.lua @@ -0,0 +1,203 @@ +--[[ +FFLuCI - Utility library + +Description: +Several common useful Lua functions + +FileId: +$Id$ + +License: +Copyright 2008 Steven Barth + +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. + +]]-- + +module("ffluci.util", package.seeall) + + +-- Lua simplified Python-style OO class support emulation +function class(base) + local class = {} + + local create = function(class, ...) + local inst = {} + setmetatable(inst, {__index = class}) + + if inst.__init__ then + local stat, err = pcall(inst.__init__, inst, ...) + if not stat then + error(err) + end + end + + return inst + end + + local classmeta = {__call = create} + + if base then + classmeta.__index = base + end + + setmetatable(class, classmeta) + return class +end + + +-- Clones an object (deep on-demand) +function clone(object, deep) + local copy = {} + + for k, v in pairs(object) do + if deep and type(v) == "table" then + v = clone(v, deep) + end + copy[k] = v + end + + setmetatable(copy, getmetatable(object)) + + return copy +end + + +-- Checks whether a table has an object "value" in it +function contains(table, value) + for k,v in pairs(table) do + if value == v then + return true + end + end + return false +end + + +-- Dumps a table to stdout (useful for testing and debugging) +function dumptable(t, i) + i = i or 0 + for k,v in pairs(t) do + print(string.rep("\t", i) .. k, v) + if type(v) == "table" then + dumptable(v, i+1) + end + end +end + + +-- Escapes all occurences of c in s +function escape(s, c) + c = c or "\\" + return s:gsub(c, "\\" .. c) +end + + +-- Populate obj in the scope of f as key +function extfenv(f, key, obj) + local scope = getfenv(f) + scope[key] = obj +end + + +-- Checks whether an object is an instanceof class +function instanceof(object, class) + local meta = getmetatable(object) + while meta and meta.__index do + if meta.__index == class then + return true + end + meta = getmetatable(meta.__index) + end + return false +end + + +-- Creates valid XML PCDATA from a string +function pcdata(value) + value = value:gsub("&", "&") + value = value:gsub('"', """) + value = value:gsub("'", "'") + value = value:gsub("<", "<") + return value:gsub(">", ">") +end + + +-- Resets the scope of f doing a shallow copy of its scope into a new table +function resfenv(f) + setfenv(f, clone(getfenv(f))) +end + + +-- Splits a string into an array +function split(str, pat, max, regex) + pat = pat or "\n" + max = max or #str + + local t = {} + local c = 1 + + if #str == 0 then + return {""} + end + + if #pat == 0 then + return nil + end + + if max == 0 then + return str + end + + repeat + local s, e = str:find(pat, c, not regex) + table.insert(t, str:sub(c, s and s - 1)) + max = max - 1 + c = e and e + 1 or #str + 1 + until not s or max < 0 + + return t +end + +-- Removes whitespace from beginning and end of a string +function trim(str) + local s = str:gsub("^%s*(.-)%s*$", "%1") + return s +end + +-- Updates given table with new values +function update(t, updates) + for k, v in pairs(updates) do + t[k] = v + end +end + + +-- Updates the scope of f with "extscope" +function updfenv(f, extscope) + update(getfenv(f), extscope) +end + + +-- Validates a variable +function validate(value, cast_number, cast_int) + if cast_number or cast_int then + value = tonumber(value) + end + + if cast_int and value and not(value % 1 == 0) then + value = nil + end + + return value +end \ No newline at end of file diff --git a/core/src/view/cbi/dvalue.htm b/core/src/view/cbi/dvalue.htm new file mode 100644 index 000000000..f54667def --- /dev/null +++ b/core/src/view/cbi/dvalue.htm @@ -0,0 +1,12 @@ +<%+cbi/valueheader%> +<% if self.value then + if type(self.value) == "function" then %> + <%=self:value(section)%> +<% else %> + <%=self.value%> +<% end +else %> + <%=self:cfgvalue(section)%> +<% end %> +  +<%+cbi/valuefooter%> diff --git a/core/src/view/cbi/footer.htm b/core/src/view/cbi/footer.htm new file mode 100644 index 000000000..2acf710cd --- /dev/null +++ b/core/src/view/cbi/footer.htm @@ -0,0 +1,7 @@ +
+ + + +
+ +<%+footer%> \ No newline at end of file diff --git a/core/src/view/cbi/fvalue.htm b/core/src/view/cbi/fvalue.htm new file mode 100644 index 000000000..b609f1d4f --- /dev/null +++ b/core/src/view/cbi/fvalue.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> + " name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == self.enabled then %> checked="checked"<% end %> value="1" /> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/header.htm b/core/src/view/cbi/header.htm new file mode 100644 index 000000000..3b615d729 --- /dev/null +++ b/core/src/view/cbi/header.htm @@ -0,0 +1,7 @@ +<%+header%> +
+
+ + + +
diff --git a/core/src/view/cbi/lvalue.htm b/core/src/view/cbi/lvalue.htm new file mode 100644 index 000000000..f1ae5a093 --- /dev/null +++ b/core/src/view/cbi/lvalue.htm @@ -0,0 +1,16 @@ +<%+cbi/valueheader%> +<% if self.widget == "select" then %> + +<% elseif self.widget == "radio" then + local c = 0; + for i, key in pairs(self.keylist) do + c = c + 1%> + <%=self.vallist[i]%>"<% if self:cfgvalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" /> +<% if c == self.size then c = 0 %>
+<% end end %> +<% end %> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/map.htm b/core/src/view/cbi/map.htm new file mode 100644 index 000000000..835393c1c --- /dev/null +++ b/core/src/view/cbi/map.htm @@ -0,0 +1,6 @@ +
+

<%=self.title%>

+
<%=self.description%>
+<% self:render_children() %> +
+
diff --git a/core/src/view/cbi/mvalue.htm b/core/src/view/cbi/mvalue.htm new file mode 100644 index 000000000..97a1c4295 --- /dev/null +++ b/core/src/view/cbi/mvalue.htm @@ -0,0 +1,19 @@ +<% +local v = self:valuelist(section) +%> +<%+cbi/valueheader%> +<% if self.widget == "select" then %> + +<% elseif self.widget == "checkbox" then + local c = 0; + for i, key in pairs(self.keylist) do + c = c + 1%> + <%=self.vallist[i]%>[]"<% if ffluci.util.contains(v, key) then %> checked="checked"<% end %> value="<%=key%>" /> +<% if c == self.size then c = 0 %>
+<% end end %> +<% end %> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/core/src/view/cbi/nsection.htm b/core/src/view/cbi/nsection.htm new file mode 100644 index 000000000..bbc87f185 --- /dev/null +++ b/core/src/view/cbi/nsection.htm @@ -0,0 +1,17 @@ +<% if self:cfgvalue(self.section) then +section = self.section %> +
+

<%=self.title%>

+
<%=self.description%>
+ <% if self.addremove then %>
+ +
<% end %> +<%+cbi/ucisection%> +
+<% elseif self.addremove then %> +
+

<%=self.title%>

+
<%=self.description%>
+ +
+<% end %> diff --git a/core/src/view/cbi/tsection.htm b/core/src/view/cbi/tsection.htm new file mode 100644 index 000000000..666c14b2e --- /dev/null +++ b/core/src/view/cbi/tsection.htm @@ -0,0 +1,22 @@ +
+

<%=self.title%>

+
<%=self.description%>
+<% for i, k in ipairs(self:cfgsections()) do%> + <% if self.addremove then %>
+ +
<% end %> + <% if not self.anonymous then %>

<%=k%>

<% end %> +<% section = k %> +<%+cbi/ucisection%> +<% end %> +<% if self.addremove then %> +
+ <% if self.anonymous then %> + + <% else %> + + + <% end %><% if self.err_invalid then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+<% end %> +
diff --git a/core/src/view/cbi/ucisection.htm b/core/src/view/cbi/ucisection.htm new file mode 100644 index 000000000..a1fb39cf4 --- /dev/null +++ b/core/src/view/cbi/ucisection.htm @@ -0,0 +1,23 @@ +
+<% self:render_children(section) %> + <% if #self.optionals[section] > 0 or self.dynamic then %> +
+ <% if self.dynamic then %> + + <% else %> + + + <% end %> + +
+ <% end %> +
+
\ No newline at end of file diff --git a/core/src/view/cbi/value.htm b/core/src/view/cbi/value.htm new file mode 100644 index 000000000..31bf38f77 --- /dev/null +++ b/core/src/view/cbi/value.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> + size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" id="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=self:cfgvalue(section)%>" /> +<%+cbi/valuefooter%> diff --git a/core/src/view/cbi/valuefooter.htm b/core/src/view/cbi/valuefooter.htm new file mode 100644 index 000000000..6151a3a66 --- /dev/null +++ b/core/src/view/cbi/valuefooter.htm @@ -0,0 +1,8 @@ +
<%=self.description%> 
+
+ <% if self.tag_invalid[section] then %>
<%:cbi_invalid Fehler: Ungültige Eingabe%>
<% end %> +
+ <% if #self.deps > 0 then %><% end %> \ No newline at end of file diff --git a/core/src/view/cbi/valueheader.htm b/core/src/view/cbi/valueheader.htm new file mode 100644 index 000000000..062efa2dd --- /dev/null +++ b/core/src/view/cbi/valueheader.htm @@ -0,0 +1,3 @@ +
"> +
<%=self.title%>
+
\ No newline at end of file diff --git a/core/src/view/error404.htm b/core/src/view/error404.htm new file mode 100644 index 000000000..51ea176d6 --- /dev/null +++ b/core/src/view/error404.htm @@ -0,0 +1,5 @@ +<%+header%> +

404 Not Found

+

Sorry, the object you requested was not found.

+Unable to dispatch: <%=ffluci.http.env.PATH_INFO%> +<%+footer%> \ No newline at end of file diff --git a/core/src/view/error500.htm b/core/src/view/error500.htm new file mode 100644 index 000000000..8af22e8f2 --- /dev/null +++ b/core/src/view/error500.htm @@ -0,0 +1,5 @@ +<%+header%> +

500 Internal Server Error

+

Sorry, the server encountered an unexpected error.

+<%=message%> +<%+footer%> \ No newline at end of file diff --git a/core/src/view/footer.htm b/core/src/view/footer.htm new file mode 100644 index 000000000..67856771b --- /dev/null +++ b/core/src/view/footer.htm @@ -0,0 +1,7 @@ +
+
+
+ + + + \ No newline at end of file diff --git a/core/src/view/header.htm b/core/src/view/header.htm new file mode 100644 index 000000000..bc65e3e89 --- /dev/null +++ b/core/src/view/header.htm @@ -0,0 +1,79 @@ +<% +require("ffluci.sys") +local load1, load5, load15 = ffluci.sys.loadavg() +local req = require("ffluci.dispatcher").request +local menu = require("ffluci.menu").get()[req.category] +menu = menu or {} +require("ffluci.i18n").loadc("default") +require("ffluci.http").prepare_content("text/html") +%> + + + + + + + + FFLuCI + + + + + + +
+ + <% end %> +
+ +
\ No newline at end of file -- cgit v1.2.3