build: introduce luci-base
authorJo-Philipp Wich <jow@openwrt.org>
Wed, 11 Jun 2014 13:29:05 +0000 (13:29 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Wed, 11 Jun 2014 13:29:05 +0000 (13:29 +0000)
Merges libs/core, libs/ipkg, libs/web, libs/sys, libs/sgi-cgi, libs/sgi-uhttpd,
modules/admin-core, themes/base and protcols/core into modules/base and renames
luci-lib-core to luci-base.

304 files changed:
Makefile
contrib/package/luci-addons/Makefile
contrib/package/luci/Makefile
libs/core/Makefile [deleted file]
libs/core/luasrc/ccache.lua [deleted file]
libs/core/luasrc/debug.lua [deleted file]
libs/core/luasrc/fs.lua [deleted file]
libs/core/luasrc/init.lua [deleted file]
libs/core/luasrc/ip.lua [deleted file]
libs/core/luasrc/ltn12.lua [deleted file]
libs/core/luasrc/model/firewall.lua [deleted file]
libs/core/luasrc/model/network.lua [deleted file]
libs/core/luasrc/model/uci.lua [deleted file]
libs/core/luasrc/store.lua [deleted file]
libs/core/luasrc/util.lua [deleted file]
libs/core/luasrc/version.lua [deleted file]
libs/core/root/etc/config/ucitrack [deleted file]
libs/core/root/sbin/luci-reload [deleted file]
libs/ipkg/Makefile [deleted file]
libs/ipkg/luasrc/model/ipkg.lua [deleted file]
libs/sgi-cgi/Makefile [deleted file]
libs/sgi-cgi/htdocs/cgi-bin/luci [deleted file]
libs/sgi-cgi/luasrc/sgi/cgi.lua [deleted file]
libs/sgi-uhttpd/Makefile [deleted file]
libs/sgi-uhttpd/luasrc/sgi/uhttpd.lua [deleted file]
libs/sys/Makefile [deleted file]
libs/sys/luasrc/sys.lua [deleted file]
libs/sys/luasrc/sys/iptparser.lua [deleted file]
libs/sys/luasrc/sys/zoneinfo.lua [deleted file]
libs/sys/luasrc/sys/zoneinfo/tzdata.lua [deleted file]
libs/sys/luasrc/sys/zoneinfo/tzoffset.lua [deleted file]
libs/web/Makefile [deleted file]
libs/web/htdocs/luci-static/resources/cbi.js [deleted file]
libs/web/htdocs/luci-static/resources/cbi/add.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/apply.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/arrow.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/down.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/download.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/edit.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/fieldadd.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/file.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/find.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/folder.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/help.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/key.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/link.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/reload.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/remove.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/reset.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/save.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/up.gif [deleted file]
libs/web/htdocs/luci-static/resources/cbi/user.gif [deleted file]
libs/web/luasrc/cacheloader.lua [deleted file]
libs/web/luasrc/cbi.lua [deleted file]
libs/web/luasrc/cbi/datatypes.lua [deleted file]
libs/web/luasrc/config.lua [deleted file]
libs/web/luasrc/dispatcher.lua [deleted file]
libs/web/luasrc/http.lua [deleted file]
libs/web/luasrc/http/protocol.lua [deleted file]
libs/web/luasrc/http/protocol/conditionals.lua [deleted file]
libs/web/luasrc/http/protocol/date.lua [deleted file]
libs/web/luasrc/http/protocol/mime.lua [deleted file]
libs/web/luasrc/i18n.lua [deleted file]
libs/web/luasrc/sauth.lua [deleted file]
libs/web/luasrc/template.lua [deleted file]
libs/web/luasrc/view/cbi/apply_xhr.htm [deleted file]
libs/web/luasrc/view/cbi/browser.htm [deleted file]
libs/web/luasrc/view/cbi/button.htm [deleted file]
libs/web/luasrc/view/cbi/cell_valuefooter.htm [deleted file]
libs/web/luasrc/view/cbi/cell_valueheader.htm [deleted file]
libs/web/luasrc/view/cbi/compound.htm [deleted file]
libs/web/luasrc/view/cbi/delegator.htm [deleted file]
libs/web/luasrc/view/cbi/dvalue.htm [deleted file]
libs/web/luasrc/view/cbi/dynlist.htm [deleted file]
libs/web/luasrc/view/cbi/filebrowser.htm [deleted file]
libs/web/luasrc/view/cbi/firewall_zoneforwards.htm [deleted file]
libs/web/luasrc/view/cbi/firewall_zonelist.htm [deleted file]
libs/web/luasrc/view/cbi/footer.htm [deleted file]
libs/web/luasrc/view/cbi/full_valuefooter.htm [deleted file]
libs/web/luasrc/view/cbi/full_valueheader.htm [deleted file]
libs/web/luasrc/view/cbi/fvalue.htm [deleted file]
libs/web/luasrc/view/cbi/header.htm [deleted file]
libs/web/luasrc/view/cbi/lvalue.htm [deleted file]
libs/web/luasrc/view/cbi/map.htm [deleted file]
libs/web/luasrc/view/cbi/mvalue.htm [deleted file]
libs/web/luasrc/view/cbi/network_ifacelist.htm [deleted file]
libs/web/luasrc/view/cbi/network_netinfo.htm [deleted file]
libs/web/luasrc/view/cbi/network_netlist.htm [deleted file]
libs/web/luasrc/view/cbi/nsection.htm [deleted file]
libs/web/luasrc/view/cbi/nullsection.htm [deleted file]
libs/web/luasrc/view/cbi/simpleform.htm [deleted file]
libs/web/luasrc/view/cbi/tabcontainer.htm [deleted file]
libs/web/luasrc/view/cbi/tabmenu.htm [deleted file]
libs/web/luasrc/view/cbi/tblsection.htm [deleted file]
libs/web/luasrc/view/cbi/tsection.htm [deleted file]
libs/web/luasrc/view/cbi/tvalue.htm [deleted file]
libs/web/luasrc/view/cbi/ucisection.htm [deleted file]
libs/web/luasrc/view/cbi/upload.htm [deleted file]
libs/web/luasrc/view/cbi/value.htm [deleted file]
libs/web/luasrc/view/cbi/valuefooter.htm [deleted file]
libs/web/luasrc/view/cbi/valueheader.htm [deleted file]
libs/web/root/etc/config/luci [deleted file]
libs/web/root/lib/uci/upload/.gitignore [deleted file]
libs/web/src/po2lmo.c [deleted file]
libs/web/src/template_lmo.c [deleted file]
libs/web/src/template_lmo.h [deleted file]
libs/web/src/template_lualib.c [deleted file]
libs/web/src/template_lualib.h [deleted file]
libs/web/src/template_parser.c [deleted file]
libs/web/src/template_parser.h [deleted file]
libs/web/src/template_utils.c [deleted file]
libs/web/src/template_utils.h [deleted file]
libs/web/standalone.mk [deleted file]
modules/admin-core/Makefile [deleted file]
modules/admin-core/ipkg/postinst [deleted file]
modules/admin-core/luasrc/controller/admin/servicectl.lua [deleted file]
modules/admin-core/luasrc/tools/status.lua [deleted file]
modules/admin-core/luasrc/tools/webadmin.lua [deleted file]
modules/admin-core/luasrc/view/error404.htm [deleted file]
modules/admin-core/luasrc/view/error500.htm [deleted file]
modules/admin-core/luasrc/view/footer.htm [deleted file]
modules/admin-core/luasrc/view/header.htm [deleted file]
modules/admin-core/luasrc/view/indexer.htm [deleted file]
modules/admin-core/luasrc/view/sysauth.htm [deleted file]
modules/admin-core/root/etc/init.d/luci_dhcp_migrate [deleted file]
modules/admin-core/root/etc/init.d/luci_fixtime [deleted file]
modules/admin-core/root/www/index.html [deleted file]
modules/base/Makefile [new file with mode: 0644]
modules/base/htdocs/cgi-bin/luci [new file with mode: 0755]
modules/base/htdocs/luci-static/resources/cbi.js [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/add.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/apply.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/arrow.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/down.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/download.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/edit.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/fieldadd.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/file.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/find.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/folder.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/help.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/key.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/link.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/reload.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/remove.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/reset.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/save.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/up.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/cbi/user.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/bridge.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/bridge_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/encryption.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/encryption_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/ethernet.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/ethernet_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/loading.gif [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/port_down.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/port_up.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-0-25.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-0.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-25-50.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-50-75.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-75-100.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/signal-none.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/switch.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/switch_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/tunnel.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/tunnel_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/vlan.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/vlan_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/wifi.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/wifi_big.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/icons/wifi_disabled.png [new file with mode: 0644]
modules/base/htdocs/luci-static/resources/xhr.js [new file with mode: 0644]
modules/base/luasrc/ccache.lua [new file with mode: 0644]
modules/base/luasrc/controller/admin/servicectl.lua [new file with mode: 0644]
modules/base/luasrc/debug.lua [new file with mode: 0644]
modules/base/luasrc/fs.lua [new file with mode: 0644]
modules/base/luasrc/init.lua [new file with mode: 0644]
modules/base/luasrc/ip.lua [new file with mode: 0644]
modules/base/luasrc/ltn12.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/cacheloader.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/cbi.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/cbi/datatypes.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/config.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/dispatcher.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/http.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/http/protocol.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/http/protocol/conditionals.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/http/protocol/date.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/http/protocol/mime.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/i18n.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/sauth.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/template.lua [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/apply_xhr.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/browser.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/button.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/cell_valuefooter.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/cell_valueheader.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/compound.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/delegator.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/dvalue.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/dynlist.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/filebrowser.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/firewall_zoneforwards.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/firewall_zonelist.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/footer.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/full_valuefooter.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/full_valueheader.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/fvalue.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/header.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/lvalue.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/map.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/mvalue.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/network_ifacelist.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/network_netinfo.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/network_netlist.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/nsection.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/nullsection.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/simpleform.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/tabcontainer.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/tabmenu.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/tblsection.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/tsection.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/tvalue.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/ucisection.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/upload.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/value.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/valuefooter.htm [new file with mode: 0644]
modules/base/luasrc/luasrc/view/cbi/valueheader.htm [new file with mode: 0644]
modules/base/luasrc/model/cbi/admin_network/proto_dhcp.lua [new file with mode: 0644]
modules/base/luasrc/model/cbi/admin_network/proto_none.lua [new file with mode: 0644]
modules/base/luasrc/model/cbi/admin_network/proto_static.lua [new file with mode: 0644]
modules/base/luasrc/model/firewall.lua [new file with mode: 0644]
modules/base/luasrc/model/ipkg.lua [new file with mode: 0644]
modules/base/luasrc/model/network.lua [new file with mode: 0644]
modules/base/luasrc/model/uci.lua [new file with mode: 0644]
modules/base/luasrc/sgi/cgi.lua [new file with mode: 0644]
modules/base/luasrc/sgi/uhttpd.lua [new file with mode: 0644]
modules/base/luasrc/store.lua [new file with mode: 0644]
modules/base/luasrc/sys.lua [new file with mode: 0644]
modules/base/luasrc/sys/iptparser.lua [new file with mode: 0644]
modules/base/luasrc/sys/zoneinfo.lua [new file with mode: 0644]
modules/base/luasrc/sys/zoneinfo/tzdata.lua [new file with mode: 0644]
modules/base/luasrc/sys/zoneinfo/tzoffset.lua [new file with mode: 0644]
modules/base/luasrc/tools/proto.lua [new file with mode: 0644]
modules/base/luasrc/tools/status.lua [new file with mode: 0644]
modules/base/luasrc/tools/webadmin.lua [new file with mode: 0644]
modules/base/luasrc/util.lua [new file with mode: 0644]
modules/base/luasrc/version.lua [new file with mode: 0644]
modules/base/luasrc/view/error404.htm [new file with mode: 0644]
modules/base/luasrc/view/error500.htm [new file with mode: 0644]
modules/base/luasrc/view/footer.htm [new file with mode: 0644]
modules/base/luasrc/view/header.htm [new file with mode: 0644]
modules/base/luasrc/view/indexer.htm [new file with mode: 0644]
modules/base/luasrc/view/sysauth.htm [new file with mode: 0644]
modules/base/root/etc/config/ucitrack [new file with mode: 0644]
modules/base/root/root/etc/config/luci [new file with mode: 0644]
modules/base/root/root/lib/uci/upload/.gitignore [new file with mode: 0644]
modules/base/root/sbin/luci-reload [new file with mode: 0755]
modules/base/root/www/index.html [new file with mode: 0644]
modules/base/src/po2lmo.c [new file with mode: 0644]
modules/base/src/template_lmo.c [new file with mode: 0644]
modules/base/src/template_lmo.h [new file with mode: 0644]
modules/base/src/template_lualib.c [new file with mode: 0644]
modules/base/src/template_lualib.h [new file with mode: 0644]
modules/base/src/template_parser.c [new file with mode: 0644]
modules/base/src/template_parser.h [new file with mode: 0644]
modules/base/src/template_utils.c [new file with mode: 0644]
modules/base/src/template_utils.h [new file with mode: 0644]
modules/base/standalone.mk [new file with mode: 0644]
protocols/core/Makefile [deleted file]
protocols/core/luasrc/model/cbi/admin_network/proto_dhcp.lua [deleted file]
protocols/core/luasrc/model/cbi/admin_network/proto_none.lua [deleted file]
protocols/core/luasrc/model/cbi/admin_network/proto_static.lua [deleted file]
protocols/core/luasrc/tools/proto.lua [deleted file]
themes/base/Makefile [deleted file]
themes/base/htdocs/luci-static/resources/icons/bridge.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/bridge_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/encryption.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/encryption_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/ethernet.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/ethernet_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/loading.gif [deleted file]
themes/base/htdocs/luci-static/resources/icons/port_down.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/port_up.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-0-25.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-0.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-25-50.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-50-75.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-75-100.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/signal-none.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/switch.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/switch_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/tunnel.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/tunnel_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/vlan.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/vlan_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/wifi.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/wifi_big.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/icons/wifi_disabled.png [deleted file]
themes/base/htdocs/luci-static/resources/xhr.js [deleted file]

index b99e8f7..70a7e92 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ all: build
 build: gccbuild luabuild
 
 gccbuild:
 build: gccbuild luabuild
 
 gccbuild:
-       make -C libs/web CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
+       make -C modules/base CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
        for i in $(MODULES); do \
                make -C$$i SDK="$(shell test -f .running-sdk && echo 1)" compile || { \
                        echo "*** Compilation of $$i failed!"; \
        for i in $(MODULES); do \
                make -C$$i SDK="$(shell test -f .running-sdk && echo 1)" compile || { \
                        echo "*** Compilation of $$i failed!"; \
@@ -33,7 +33,7 @@ i18nbuild:
 clean:
        rm -f .running-sdk
        rm -rf docs
 clean:
        rm -f .running-sdk
        rm -rf docs
-       make -C libs/web host-clean
+       make -C modules/base host-clean
        for i in $(MODULES); do make -C$$i clean; done
 
 
        for i in $(MODULES); do make -C$$i clean; done
 
 
index c223054..6a51a6a 100644 (file)
@@ -49,7 +49,7 @@ define Package/luci-mod-freifunk-community
   TITLE:=Freifunk Community Meta-Package
   DEPENDS+=$(call add_deps,mod-freifunk-community, \
     iptables-mod-nat-extra iptables-mod-ipopt \
   TITLE:=Freifunk Community Meta-Package
   DEPENDS+=$(call add_deps,mod-freifunk-community, \
     iptables-mod-nat-extra iptables-mod-ipopt \
-       luci-lib-web luci-app-splash luci-i18n-german \
+       luci-app-splash luci-i18n-german \
        olsrd olsrd-mod-dyn-gw-plain \
        olsrd-mod-jsoninfo olsrd-mod-nameservice \
        olsrd-mod-watchdog kmod-tun \
        olsrd olsrd-mod-dyn-gw-plain \
        olsrd-mod-jsoninfo olsrd-mod-nameservice \
        olsrd-mod-watchdog kmod-tun \
@@ -224,8 +224,7 @@ define theme
     SUBMENU:=4. Themes
     TITLE:=$(if $(2),$(2),LuCI $(1) theme)
        MAINTAINER:=$(if $(3),$(3),LuCI Development Team <luci@lists.subsignal.org>)
     SUBMENU:=4. Themes
     TITLE:=$(if $(2),$(2),LuCI $(1) theme)
        MAINTAINER:=$(if $(3),$(3),LuCI Development Team <luci@lists.subsignal.org>)
-       DEPENDS:=$(if $(filter-out base,$(1)),+luci-theme-base) $(4)
-       $(if $(5),DEFAULT:=PACKAGE_luci-lib-core)
+       DEPENDS:=+luci-base $(4)
   endef
 
   define Package/luci-theme-$(1)/install
   endef
 
   define Package/luci-theme-$(1)/install
index 8bb3531..b875f0d 100644 (file)
@@ -32,7 +32,7 @@ endef
 
 
 ### Core package ###
 
 
 ### Core package ###
-define Package/luci-lib-core
+define Package/luci-base
   SECTION:=luci
   CATEGORY:=LuCI
   TITLE:=LuCI - Lua Configuration Interface
   SECTION:=luci
   CATEGORY:=LuCI
   TITLE:=LuCI - Lua Configuration Interface
@@ -43,8 +43,8 @@ define Package/luci-lib-core
   TITLE:=LuCI core libraries
 endef
 
   TITLE:=LuCI core libraries
 endef
 
-define Package/luci-lib-core/install
-       $(call Package/luci/install/template,$(1),libs/core)
+define Package/luci-base/install
+       $(call Package/luci/install/template,$(1),modules/base)
        $(PKG_BUILD_DIR)/build/mkversion.sh $(1)/usr/lib/lua/luci/version.lua \
                "OpenWrt Firmware" \
                "$(OPENWRTVERSION)" \
        $(PKG_BUILD_DIR)/build/mkversion.sh $(1)/usr/lib/lua/luci/version.lua \
                "OpenWrt Firmware" \
                "$(OPENWRTVERSION)" \
@@ -52,43 +52,47 @@ define Package/luci-lib-core/install
                "$(PKG_VERSION)"
 endef
 
                "$(PKG_VERSION)"
 endef
 
-define Package/luci-lib-core/config
+define Package/luci-base/config
        choice
                prompt "Build Target"
        choice
                prompt "Build Target"
-               default PACKAGE_luci-lib-core_source
+               default PACKAGE_luci-base_source
 
 
-       config PACKAGE_luci-lib-core_compile
+       config PACKAGE_luci-base_compile
                bool "Precompiled"
 
                bool "Precompiled"
 
-       config PACKAGE_luci-lib-core_stripped
+       config PACKAGE_luci-base_stripped
                bool "Stripped"
 
                bool "Stripped"
 
-       config PACKAGE_luci-lib-core_srcdiet
+       config PACKAGE_luci-base_srcdiet
                bool "Compressed Source"
 
                bool "Compressed Source"
 
-       config PACKAGE_luci-lib-core_source
+       config PACKAGE_luci-base_source
                bool "Full Source"
 
        endchoice
 endef
 
                bool "Full Source"
 
        endchoice
 endef
 
-ifneq ($(CONFIG_PACKAGE_luci-lib-core_compile),)
+define Package/luci-base/conffiles
+/etc/config/luci
+endef
+
+ifneq ($(CONFIG_PACKAGE_luci-base_compile),)
   LUA_TARGET:=compile
 endif
 
   LUA_TARGET:=compile
 endif
 
-ifneq ($(CONFIG_PACKAGE_luci-lib-core_stripped),)
+ifneq ($(CONFIG_PACKAGE_luci-base_stripped),)
   LUA_TARGET:=strip
 endif
 
   LUA_TARGET:=strip
 endif
 
-ifneq ($(CONFIG_PACKAGE_luci-lib-core_srcdiet),)
+ifneq ($(CONFIG_PACKAGE_luci-base_srcdiet),)
   LUA_TARGET:=diet
 endif
 
   LUA_TARGET:=diet
 endif
 
-ifneq ($(CONFIG_PACKAGE_luci-lib-core),)
-  LUCI_SELECTED_MODULES+=libs/core
+ifneq ($(CONFIG_PACKAGE_luci-base),)
+  LUCI_SELECTED_MODULES+=modules/base
 endif
 
 endif
 
-LUCI_BUILD_PACKAGES += luci-lib-core
+LUCI_BUILD_PACKAGES += luci-base
 
 
 ### Libraries ###
 
 
 ### Libraries ###
@@ -101,7 +105,7 @@ define library
     MAINTAINER:=LuCI Development Team <luci@lists.subsignal.org>
     SUBMENU:=8. Libraries
     TITLE:=$(if $(2),$(2),LuCI $(1) library)
     MAINTAINER:=LuCI Development Team <luci@lists.subsignal.org>
     SUBMENU:=8. Libraries
     TITLE:=$(if $(2),$(2),LuCI $(1) library)
-    $(if $(3),DEPENDS:=+luci-lib-core $(3))
+    $(if $(3),DEPENDS:=+luci-base $(3))
   endef
 
   define Package/luci-lib-$(1)/install
   endef
 
   define Package/luci-lib-$(1)/install
@@ -116,10 +120,6 @@ define library
   LUCI_BUILD_PACKAGES += luci-lib-$(1)
 endef
 
   LUCI_BUILD_PACKAGES += luci-lib-$(1)
 endef
 
-define Package/luci-lib-web/conffiles
-/etc/config/luci
-endef
-
 define Package/luci-lib-nixio/config
        choice
                prompt "TLS Provider"
 define Package/luci-lib-nixio/config
        choice
                prompt "TLS Provider"
@@ -158,14 +158,11 @@ ifneq ($(CONFIG_PACKAGE_luci-lib-nixio_cyassl),)
 endif
 
 
 endif
 
 
-$(eval $(call library,httpclient,HTTP(S) client library,+luci-lib-web +luci-lib-nixio))
-$(eval $(call library,ipkg,LuCI IPKG/OPKG call abstraction library))
+$(eval $(call library,httpclient,HTTP(S) client library,+luci-base +luci-lib-nixio))
 $(eval $(call library,json,LuCI JSON library))
 $(eval $(call library,nixio,NIXIO POSIX library,+PACKAGE_luci-lib-nixio_openssl:libopenssl +PACKAGE_luci-lib-nixio_cyassl:libcyassl))
 $(eval $(call library,px5g,RSA/X.509 Key Generator (required for LuCId SSL support),+luci-lib-nixio))
 $(eval $(call library,json,LuCI JSON library))
 $(eval $(call library,nixio,NIXIO POSIX library,+PACKAGE_luci-lib-nixio_openssl:libopenssl +PACKAGE_luci-lib-nixio_cyassl:libcyassl))
 $(eval $(call library,px5g,RSA/X.509 Key Generator (required for LuCId SSL support),+luci-lib-nixio))
-$(eval $(call library,sys,LuCI Linux/POSIX system library))
-$(eval $(call library,web,MVC Webframework,+luci-lib-sys +luci-lib-nixio +luci-lib-core +luci-sgi-cgi))
-$(eval $(call library,luaneightbl,neightbl - Lua lib for IPv6 neighbors,+luci-lib-core))
+$(eval $(call library,luaneightbl,neightbl - Lua lib for IPv6 neighbors,+luci-base))
 
 
 ### Protocols ###
 
 
 ### Protocols ###
@@ -192,7 +189,6 @@ define protocol
   LUCI_BUILD_PACKAGES += luci-proto-$(1)
 endef
 
   LUCI_BUILD_PACKAGES += luci-proto-$(1)
 endef
 
-$(eval $(call protocol,core,Support for static/dhcp/none))
 $(eval $(call protocol,ppp,Support for PPP/PPPoE/PPPoA/PPtP))
 $(eval $(call protocol,ipv6,Support for DHCPv6/6in4/6to4/6rd/DS-Lite))
 $(eval $(call protocol,3g,Support for 3G,+PACKAGE_luci-proto-3g:comgt))
 $(eval $(call protocol,ppp,Support for PPP/PPPoE/PPPoA/PPtP))
 $(eval $(call protocol,ipv6,Support for DHCPv6/6in4/6to4/6rd/DS-Lite))
 $(eval $(call protocol,3g,Support for 3G,+PACKAGE_luci-proto-3g:comgt))
@@ -225,14 +221,9 @@ define module
 endef
 
 
 endef
 
 
-define Package/luci-mod-admin-core/extra-install
-       touch $(1)/etc/init.d/luci_fixtime || true
-endef
-
-$(eval $(call module,admin-core,Web UI Core module,+luci-lib-web +luci-proto-core +luci-i18n-english))
-$(eval $(call module,admin-mini,LuCI Essentials - stripped down and user-friendly,+luci-mod-admin-core @BROKEN))
-$(eval $(call module,admin-full,LuCI Administration - full-featured for full control,+luci-mod-admin-core +luci-lib-ipkg))
-$(eval $(call module,failsafe,LuCI Fail-Safe - Fail-Safe sysupgrade module,+luci-mod-admin-core))
+$(eval $(call module,admin-mini,LuCI Essentials - stripped down and user-friendly,+luci-base @BROKEN))
+$(eval $(call module,admin-full,LuCI Administration - full-featured for full control,+luci-base))
+$(eval $(call module,failsafe,LuCI Fail-Safe - Fail-Safe sysupgrade module,+luci-base))
 $(eval $(call module,rpc,LuCI RPC - JSON-RPC API,+luci-lib-json))
 
 
 $(eval $(call module,rpc,LuCI RPC - JSON-RPC API,+luci-lib-json))
 
 
@@ -270,34 +261,6 @@ $(eval $(call application,qos,Quality of Service configuration module,\
 $(eval $(call application,commands,LuCI Shell Command Module))
 
 
 $(eval $(call application,commands,LuCI Shell Command Module))
 
 
-### Server Gateway Interfaces ###
-define sgi
-  define Package/luci-sgi-$(1)
-    SECTION:=luci
-    CATEGORY:=LuCI
-    TITLE:=LuCI - Lua Configuration Interface
-    URL:=http://luci.subsignal.org/
-    MAINTAINER:=LuCI Development Team <luci@lists.subsignal.org>
-    SUBMENU:=7. Server Interfaces
-    TITLE:=$(if $(2),$(2),LuCI $(1) server gateway interface)
-       DEPENDS:=$(3)
-  endef
-
-  define Package/luci-sgi-$(1)/install
-       $(call Package/luci/install/template,$$(1),libs/sgi-$(1))
-  endef
-
-  ifneq ($(CONFIG_PACKAGE_luci-sgi-$(1)),)
-    LUCI_SELECTED_MODULES+=libs/sgi-$(1)
-  endif
-
-  LUCI_BUILD_PACKAGES += luci-sgi-$(1)
-endef
-
-$(eval $(call sgi,cgi,CGI Gateway behind existing Webserver))
-$(eval $(call sgi,uhttpd,Binding for the uHTTPd server,+uhttpd +uhttpd-mod-lua))
-
-
 ### Themes ###
 define theme
   define Package/luci-theme-$(1)
 ### Themes ###
 define theme
   define Package/luci-theme-$(1)
@@ -308,8 +271,8 @@ define theme
     SUBMENU:=4. Themes
     TITLE:=$(if $(2),$(2),LuCI $(1) theme)
        MAINTAINER:=$(if $(3),$(3),LuCI Development Team <luci@lists.subsignal.org>)
     SUBMENU:=4. Themes
     TITLE:=$(if $(2),$(2),LuCI $(1) theme)
        MAINTAINER:=$(if $(3),$(3),LuCI Development Team <luci@lists.subsignal.org>)
-       DEPENDS:=$(if $(filter-out base,$(1)),+luci-theme-base) $(4)
-       $(if $(5),DEFAULT:=PACKAGE_luci-lib-core)
+       DEPENDS:=+luci-base $(4)
+       $(if $(5),DEFAULT:=PACKAGE_luci-base)
   endef
 
   define Package/luci-theme-$(1)/install
   endef
 
   define Package/luci-theme-$(1)/install
@@ -323,7 +286,6 @@ define theme
   LUCI_BUILD_PACKAGES += luci-theme-$(1)
 endef
 
   LUCI_BUILD_PACKAGES += luci-theme-$(1)
 endef
 
-$(eval $(call theme,base,Common base for all themes))
 $(eval $(call theme,openwrt,OpenWrt.org))
 $(eval $(call theme,bootstrap,Bootstrap Theme (default),,,1))
 
 $(eval $(call theme,openwrt,OpenWrt.org))
 $(eval $(call theme,bootstrap,Bootstrap Theme (default),,,1))
 
@@ -397,7 +359,7 @@ $(eval $(call collection,,\
        Standard OpenWrt set including full admin with ppp support and the \
        default OpenWrt theme,\
        +uhttpd +uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap \
        Standard OpenWrt set including full admin with ppp support and the \
        default OpenWrt theme,\
        +uhttpd +uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap \
-       +luci-app-firewall +luci-proto-core +luci-proto-ppp +libiwinfo-lua))
+       +luci-app-firewall +luci-proto-ppp +libiwinfo-lua))
 
 $(eval $(call collection,ssl,\
        Standard OpenWrt set with HTTPS support,\
 
 $(eval $(call collection,ssl,\
        Standard OpenWrt set with HTTPS support,\
diff --git a/libs/core/Makefile b/libs/core/Makefile
deleted file mode 100644 (file)
index f7fac77..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
diff --git a/libs/core/luasrc/ccache.lua b/libs/core/luasrc/ccache.lua
deleted file mode 100644 (file)
index 56ccbc3..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-]]--
-
-local io = require "io"
-local fs = require "nixio.fs"
-local util = require "luci.util"
-local nixio = require "nixio"
-local debug = require "debug"
-local string = require "string"
-local package = require "package"
-
-local type, loadfile = type, loadfile
-
-
-module "luci.ccache"
-
-function cache_ondemand(...)
-       if debug.getinfo(1, 'S').source ~= "=?" then
-               cache_enable(...)
-       end
-end
-
-function cache_enable(cachepath, mode)
-       cachepath = cachepath or "/tmp/luci-modulecache"
-       mode = mode or "r--r--r--"
-
-       local loader = package.loaders[2]
-       local uid    = nixio.getuid()
-
-       if not fs.stat(cachepath) then
-               fs.mkdir(cachepath)
-       end
-
-       local function _encode_filename(name)
-               local encoded = ""
-               for i=1, #name do
-                       encoded = encoded .. ("%2X" % string.byte(name, i))
-               end
-               return encoded
-       end
-
-       local function _load_sane(file)
-               local stat = fs.stat(file)
-               if stat and stat.uid == uid and stat.modestr == mode then
-                       return loadfile(file)
-               end
-       end
-
-       local function _write_sane(file, func)
-               if nixio.getuid() == uid then
-                       local fp = io.open(file, "w")
-                       if fp then
-                               fp:write(util.get_bytecode(func))
-                               fp:close()
-                               fs.chmod(file, mode)
-                       end
-               end
-       end
-
-       package.loaders[2] = function(mod)
-               local encoded = cachepath .. "/" .. _encode_filename(mod)
-               local modcons = _load_sane(encoded)
-               
-               if modcons then
-                       return modcons
-               end
-
-               -- No cachefile
-               modcons = loader(mod)
-               if type(modcons) == "function" then
-                       _write_sane(encoded, modcons)
-               end
-               return modcons
-       end
-end
diff --git a/libs/core/luasrc/debug.lua b/libs/core/luasrc/debug.lua
deleted file mode 100644 (file)
index 8ff1bb6..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-local debug = require "debug"
-local io = require "io"
-local collectgarbage, floor = collectgarbage, math.floor
-
-module "luci.debug"
-__file__ = debug.getinfo(1, 'S').source:sub(2)
-
--- Enables the memory tracer with given flags and returns a function to disable the tracer again
-function trap_memtrace(flags, dest)
-       flags = flags or "clr"
-       local tracefile = io.open(dest or "/tmp/memtrace", "w")
-       local peak = 0
-
-       local function trap(what, line)
-               local info = debug.getinfo(2, "Sn")
-               local size = floor(collectgarbage("count"))
-               if size > peak then
-                       peak = size
-               end
-               if tracefile then
-                       tracefile:write(
-                               "[", what, "] ", info.source, ":", (line or "?"), "\t",
-                               (info.namewhat or ""), "\t",
-                               (info.name or ""), "\t",
-                               size, " (", peak, ")\n"
-                       )
-               end
-       end
-
-       debug.sethook(trap, flags)
-
-       return function()
-               debug.sethook()
-               tracefile:close()
-       end
-end
-
diff --git a/libs/core/luasrc/fs.lua b/libs/core/luasrc/fs.lua
deleted file mode 100644 (file)
index a81ff67..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
---[[
-LuCI - Filesystem tools
-
-Description:
-A module offering often needed filesystem manipulation functions
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local io    = require "io"
-local os    = require "os"
-local ltn12 = require "luci.ltn12"
-local fs       = require "nixio.fs"
-local nutil = require "nixio.util"
-
-local type  = type
-
---- LuCI filesystem library.
-module "luci.fs"
-
---- Test for file access permission on given path.
--- @class              function
--- @name               access
--- @param str  String value containing the path
--- @return             Number containing the return code, 0 on sucess or nil on error
--- @return             String containing the error description (if any)
--- @return             Number containing the os specific errno (if any)
-access = fs.access
-
---- Evaluate given shell glob pattern and return a table containing all matching
--- file and directory entries.
--- @class                      function
--- @name                       glob
--- @param filename     String containing the path of the file to read
--- @return                     Table containing file and directory entries or nil if no matches
--- @return                     String containing the error description (if no matches)
--- @return                     Number containing the os specific errno (if no matches)
-function glob(...)
-       local iter, code, msg = fs.glob(...)
-       if iter then
-               return nutil.consume(iter)
-       else
-               return nil, code, msg
-       end
-end
-
---- Checks wheather the given path exists and points to a regular file.
--- @param filename     String containing the path of the file to test
--- @return                     Boolean indicating wheather given path points to regular file
-function isfile(filename)
-       return fs.stat(filename, "type") == "reg"
-end
-
---- Checks wheather the given path exists and points to a directory.
--- @param dirname      String containing the path of the directory to test
--- @return                     Boolean indicating wheather given path points to directory
-function isdirectory(dirname)
-       return fs.stat(dirname, "type") == "dir"
-end
-
---- Read the whole content of the given file into memory.
--- @param filename     String containing the path of the file to read
--- @return                     String containing the file contents or nil on error
--- @return                     String containing the error message on error
-readfile = fs.readfile
-
---- Write the contents of given string to given file.
--- @param filename     String containing the path of the file to read
--- @param data         String containing the data to write
--- @return                     Boolean containing true on success or nil on error
--- @return                     String containing the error message on error
-writefile = fs.writefile
-
---- Copies a file.
--- @param source       Source file
--- @param dest         Destination
--- @return                     Boolean containing true on success or nil on error
-copy = fs.datacopy
-
---- Renames a file.
--- @param source       Source file
--- @param dest         Destination
--- @return                     Boolean containing true on success or nil on error
-rename = fs.move
-
---- Get the last modification time of given file path in Unix epoch format.
--- @param path String containing the path of the file or directory to read
--- @return             Number containing the epoch time or nil on error
--- @return             String containing the error description (if any)
--- @return             Number containing the os specific errno (if any)
-function mtime(path)
-       return fs.stat(path, "mtime")
-end
-
---- Set the last modification time  of given file path in Unix epoch format.
--- @param path String containing the path of the file or directory to read
--- @param mtime        Last modification timestamp
--- @param atime Last accessed timestamp
--- @return             0 in case of success nil on error
--- @return             String containing the error description (if any)
--- @return             Number containing the os specific errno (if any)
-function utime(path, mtime, atime)
-       return fs.utimes(path, atime, mtime)
-end
-
---- Return the last element - usually the filename - from the given path with
--- the directory component stripped.
--- @class              function
--- @name               basename
--- @param path String containing the path to strip
--- @return             String containing the base name of given path
--- @see                        dirname
-basename = fs.basename
-
---- Return the directory component of the given path with the last element
--- stripped of.
--- @class              function
--- @name               dirname
--- @param path String containing the path to strip
--- @return             String containing the directory component of given path
--- @see                        basename
-dirname = fs.dirname
-
---- Return a table containing all entries of the specified directory.
--- @class              function
--- @name               dir
--- @param path String containing the path of the directory to scan
--- @return             Table containing file and directory entries or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-function dir(...)
-       local iter, code, msg = fs.dir(...)
-       if iter then
-               local t = nutil.consume(iter)
-               t[#t+1] = "."
-               t[#t+1] = ".."
-               return t
-       else
-               return nil, code, msg
-       end
-end
-
---- Create a new directory, recursively on demand.
--- @param path         String with the name or path of the directory to create
--- @param recursive    Create multiple directory levels (optional, default is true)
--- @return                     Number with the return code, 0 on sucess or nil on error
--- @return                     String containing the error description on error
--- @return                     Number containing the os specific errno on error
-function mkdir(path, recursive)
-       return recursive and fs.mkdirr(path) or fs.mkdir(path)
-end
-
---- Remove the given empty directory.
--- @class              function
--- @name               rmdir
--- @param path String containing the path of the directory to remove
--- @return             Number with the return code, 0 on sucess or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-rmdir = fs.rmdir
-
-local stat_tr = {
-       reg = "regular",
-       dir = "directory",
-       lnk = "link",
-       chr = "character device",
-       blk = "block device",
-       fifo = "fifo",
-       sock = "socket"
-}
---- Get information about given file or directory.
--- @class              function
--- @name               stat
--- @param path String containing the path of the directory to query
--- @return             Table containing file or directory properties or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-function stat(path, key)
-       local data, code, msg = fs.stat(path)
-       if data then
-               data.mode = data.modestr
-               data.type = stat_tr[data.type] or "?"
-       end
-       return key and data and data[key] or data, code, msg
-end
-
---- Set permissions on given file or directory.
--- @class              function
--- @name               chmod
--- @param path String containing the path of the directory
--- @param perm String containing the permissions to set ([ugoa][+-][rwx])
--- @return             Number with the return code, 0 on sucess or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-chmod = fs.chmod
-
---- Create a hard- or symlink from given file (or directory) to specified target
--- file (or directory) path.
--- @class                      function
--- @name                       link
--- @param path1                String containing the source path to link
--- @param path2                String containing the destination path for the link
--- @param symlink      Boolean indicating wheather to create a symlink (optional)
--- @return                     Number with the return code, 0 on sucess or nil on error
--- @return                     String containing the error description on error
--- @return                     Number containing the os specific errno on error
-function link(src, dest, sym)
-       return sym and fs.symlink(src, dest) or fs.link(src, dest)
-end
-
---- Remove the given file.
--- @class              function
--- @name               unlink
--- @param path String containing the path of the file to remove
--- @return             Number with the return code, 0 on sucess or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-unlink = fs.unlink
-
---- Retrieve target of given symlink.
--- @class              function
--- @name               readlink
--- @param path String containing the path of the symlink to read
--- @return             String containing the link target or nil on error
--- @return             String containing the error description on error
--- @return             Number containing the os specific errno on error
-readlink = fs.readlink
diff --git a/libs/core/luasrc/init.lua b/libs/core/luasrc/init.lua
deleted file mode 100644 (file)
index 4d66e86..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Description:
-Main class
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local require = require
-
--- Make sure that bitlib is loaded
-if not _G.bit then
-       _G.bit = require "bit"
-end
-
-module "luci"
-
-local v = require "luci.version"
-
-__version__ = v.luciversion or "trunk"
-__appname__ = v.luciname    or "LuCI"
diff --git a/libs/core/luasrc/ip.lua b/libs/core/luasrc/ip.lua
deleted file mode 100644 (file)
index 60ca090..0000000
+++ /dev/null
@@ -1,673 +0,0 @@
---[[
-
-LuCI ip calculation libarary
-(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-(c) 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-
-]]--
-
---- LuCI IP calculation library.
-module( "luci.ip", package.seeall )
-
-require "nixio"
-local bit  = nixio.bit
-local util = require "luci.util"
-
---- Boolean; true if system is little endian
-LITTLE_ENDIAN = not util.bigendian()
-
---- Boolean; true if system is big endian
-BIG_ENDIAN    = not LITTLE_ENDIAN
-
---- Specifier for IPv4 address family
-FAMILY_INET4  = 0x04
-
---- Specifier for IPv6 address family
-FAMILY_INET6  = 0x06
-
-
-local function __bless(x)
-       return setmetatable( x, {
-               __index = luci.ip.cidr,
-               __add   = luci.ip.cidr.add,
-               __sub   = luci.ip.cidr.sub,
-               __lt    = luci.ip.cidr.lower,
-               __eq    = luci.ip.cidr.equal,
-               __le    =
-                       function(...)
-                               return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...)
-                       end
-       } )
-end
-
-local function __array16( x, family )
-       local list
-
-       if type(x) == "number" then
-               list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
-
-       elseif type(x) == "string" then
-               if x:find(":") then x = IPv6(x) else x = IPv4(x) end
-               if x then
-                       assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
-                       list = { unpack(x[2]) }
-               end
-
-       elseif type(x) == "table" and type(x[2]) == "table" then
-               assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
-               list = { unpack(x[2]) }
-
-       elseif type(x) == "table" then
-               list = { unpack(x) }
-       end
-
-       assert( list, "Invalid operand" )
-
-       return list
-end
-
-local function __mask16(bits)
-       return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
-end
-
-local function __not16(bits)
-       return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
-end
-
-local function __maxlen(family)
-       return ( family == FAMILY_INET4 ) and 32 or 128
-end
-
-local function __sublen(family)
-       return ( family == FAMILY_INET4 ) and 30 or 127
-end
-
-
---- Convert given short value to network byte order on little endian hosts
--- @param x    Unsigned integer value between 0x0000 and 0xFFFF
--- @return     Byte-swapped value
--- @see                htonl
--- @see                ntohs
-function htons(x)
-       if LITTLE_ENDIAN then
-               return bit.bor(
-                       bit.rshift( x, 8 ),
-                       bit.band( bit.lshift( x, 8 ), 0xFF00 )
-               )
-       else
-               return x
-       end
-end
-
---- Convert given long value to network byte order on little endian hosts
--- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
--- @return     Byte-swapped value
--- @see                htons
--- @see                ntohl
-function htonl(x)
-       if LITTLE_ENDIAN then
-               return bit.bor(
-                       bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ),
-                       htons( bit.rshift( x, 16 ) )
-               )
-       else
-               return x
-       end
-end
-
---- Convert given short value to host byte order on little endian hosts
--- @class      function
--- @name       ntohs
--- @param x    Unsigned integer value between 0x0000 and 0xFFFF
--- @return     Byte-swapped value
--- @see                htonl
--- @see                ntohs
-ntohs = htons
-
---- Convert given short value to host byte order on little endian hosts
--- @class      function
--- @name       ntohl
--- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
--- @return     Byte-swapped value
--- @see                htons
--- @see                ntohl
-ntohl = htonl
-
-
---- Parse given IPv4 address in dotted quad or CIDR notation. If an optional
--- netmask is given as second argument and the IP address is encoded in CIDR
--- notation then the netmask parameter takes precedence. If neither a CIDR
--- encoded prefix nor a netmask parameter is given, then a prefix length of
--- 32 bit is assumed.
--- @param address      IPv4 address in dotted quad or CIDR notation
--- @param netmask      IPv4 netmask in dotted quad notation (optional)
--- @return                     luci.ip.cidr instance or nil if given address was invalid
--- @see                                IPv6
--- @see                                Hex
-function IPv4(address, netmask)
-       address = address or "0.0.0.0/0"
-
-       local obj = __bless({ FAMILY_INET4 })
-
-       local data = {}
-       local prefix = address:match("/(.+)")
-       address = address:gsub("/.+","")
-       address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
-
-       if netmask then
-               prefix = obj:prefix(netmask)
-       elseif prefix then
-               prefix = tonumber(prefix)
-               if not prefix or prefix < 0 or prefix > 32 then return nil end
-       else
-               prefix = 32
-       end
-
-       local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
-
-       b1 = tonumber(b1)
-       b2 = tonumber(b2)
-       b3 = tonumber(b3)
-       b4 = tonumber(b4)
-
-       if b1 and b1 <= 255 and
-          b2 and b2 <= 255 and
-          b3 and b3 <= 255 and
-          b4 and b4 <= 255 and
-          prefix
-       then
-               table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 })
-               table.insert(obj, prefix)
-               return obj
-       end
-end
-
---- Parse given IPv6 address in full, compressed, mixed or CIDR notation.
--- If an optional netmask is given as second argument and the IP address is
--- encoded in CIDR notation then the netmask parameter takes precedence.
--- If neither a CIDR encoded prefix nor a netmask parameter is given, then a
--- prefix length of 128 bit is assumed.
--- @param address      IPv6 address in full/compressed/mixed or CIDR notation
--- @param netmask      IPv6 netmask in full/compressed/mixed notation (optional)
--- @return                     luci.ip.cidr instance or nil if given address was invalid
--- @see                                IPv4
--- @see                                Hex
-function IPv6(address, netmask)
-       address = address or "::/0"
-
-       local obj = __bless({ FAMILY_INET6 })
-
-       local data = {}
-       local prefix = address:match("/(.+)")
-       address = address:gsub("/.+","")
-       address = address:gsub("^%[(.*)%]$", "%1")
-
-       if netmask then
-               prefix = obj:prefix(netmask)
-       elseif prefix then
-               prefix = tonumber(prefix)
-               if not prefix or prefix < 0 or prefix > 128 then return nil end
-       else
-               prefix = 128
-       end
-
-       local borderl = address:sub(1, 1) == ":" and 2 or 1
-       local borderh, zeroh, chunk, block, i
-
-       if #address > 45 then return nil end
-
-       repeat
-               borderh = address:find(":", borderl, true)
-               if not borderh then break end
-
-               block = tonumber(address:sub(borderl, borderh - 1), 16)
-               if block and block <= 0xFFFF then
-                       data[#data+1] = block
-               else
-                       if zeroh or borderh - borderl > 1 then return nil end
-                       zeroh = #data + 1
-               end
-
-               borderl = borderh + 1
-       until #data == 7
-
-       chunk = address:sub(borderl)
-       if #chunk > 0 and #chunk <= 4 then
-               block = tonumber(chunk, 16)
-               if not block or block > 0xFFFF then return nil end
-
-               data[#data+1] = block
-       elseif #chunk > 4 then
-               if #data == 7 or #chunk > 15 then return nil end
-               borderl = 1
-               for i=1, 4 do
-                       borderh = chunk:find(".", borderl, true)
-                       if not borderh and i < 4 then return nil end
-                       borderh = borderh and borderh - 1
-
-                       block = tonumber(chunk:sub(borderl, borderh))
-                       if not block or block > 255 then return nil end
-
-                       if i == 1 or i == 3 then
-                               data[#data+1] = block * 256
-                       else
-                               data[#data] = data[#data] + block
-                       end
-
-                       borderl = borderh and borderh + 2
-               end
-       end
-
-       if zeroh then
-               if #data == 8 then return nil end
-               while #data < 8 do
-                       table.insert(data, zeroh, 0)
-               end
-       end
-
-       if #data == 8 and prefix then
-               table.insert(obj, data)
-               table.insert(obj, prefix)
-               return obj
-       end
-end
-
---- Transform given hex-encoded value to luci.ip.cidr instance of specified
--- address family.
--- @param hex          String containing hex encoded value
--- @param prefix       Prefix length of CIDR instance (optional, default is 32/128)
--- @param family       Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6
--- @param swap         Bool indicating whether to swap byteorder on low endian host
--- @return                     luci.ip.cidr instance or nil if given value was invalid
--- @see                                IPv4
--- @see                                IPv6
-function Hex( hex, prefix, family, swap )
-       family = ( family ~= nil ) and family or FAMILY_INET4
-       swap   = ( swap   == nil ) and true   or swap
-       prefix = prefix or __maxlen(family)
-
-       local len  = __maxlen(family)
-       local tmp  = ""
-       local data = { }
-       local i
-
-       for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
-
-       if swap and LITTLE_ENDIAN then
-               for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end
-       else
-               tmp = tmp .. hex
-       end
-
-       hex = tmp
-
-       for i = 1, ( len / 4 ), 4 do
-               local n = tonumber( hex:sub( i, i+3 ), 16 )
-               if n then
-                       data[#data+1] = n
-               else
-                       return nil
-               end
-       end
-
-       return __bless({ family, data, prefix })
-end
-
-
---- LuCI IP Library / CIDR instances
--- @class      module
--- @cstyle     instance
--- @name       luci.ip.cidr
-cidr = util.class()
-
---- Test whether the instance is a IPv4 address.
--- @return     Boolean indicating a IPv4 address type
--- @see                cidr.is6
-function cidr.is4( self )
-       return self[1] == FAMILY_INET4
-end
-
---- Test whether this instance is an IPv4 RFC1918 private address
--- @return     Boolean indicating whether this instance is an RFC1918 address
-function cidr.is4rfc1918( self )
-       if self[1] == FAMILY_INET4 then
-               return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
-                      ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
-                       (self[2][1] == 0xC0A8)
-       end
-       return false
-end
-
---- Test whether this instance is an IPv4 link-local address (Zeroconf)
--- @return     Boolean indicating whether this instance is IPv4 link-local
-function cidr.is4linklocal( self )
-       if self[1] == FAMILY_INET4 then
-               return (self[2][1] == 0xA9FE)
-       end
-       return false
-end
-
---- Test whether the instance is a IPv6 address.
--- @return     Boolean indicating a IPv6 address type
--- @see                cidr.is4
-function cidr.is6( self )
-       return self[1] == FAMILY_INET6
-end
-
---- Test whether this instance is an IPv6 link-local address
--- @return     Boolean indicating whether this instance is IPv6 link-local
-function cidr.is6linklocal( self )
-       if self[1] == FAMILY_INET6 then
-               return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
-       end
-       return false
-end
-
---- Return a corresponding string representation of the instance.
--- If the prefix length is lower then the maximum possible prefix length for the
--- corresponding address type then the address is returned in CIDR notation,
--- otherwise the prefix will be left out.
-function cidr.string( self )
-       local str
-       if self:is4() then
-               str = string.format(
-                       "%d.%d.%d.%d",
-                       bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF),
-                       bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF)
-               )
-               if self[3] < 32 then
-                       str = str .. "/" .. self[3]
-               end
-       elseif self:is6() then
-               str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) )
-               if self[3] < 128 then
-                       str = str .. "/" .. self[3]
-               end
-       end
-       return str
-end
-
---- Test whether the value of the instance is lower then the given address.
--- This function will throw an exception if the given address has a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is lower
--- @see                        cidr.higher
--- @see                        cidr.equal
-function cidr.lower( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return self[2][i] < addr[2][i]
-               end
-       end
-       return false
-end
-
---- Test whether the value of the instance is higher then the given address.
--- This function will throw an exception if the given address has a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is higher
--- @see                        cidr.lower
--- @see                        cidr.equal
-function cidr.higher( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return self[2][i] > addr[2][i]
-               end
-       end
-       return false
-end
-
---- Test whether the value of the instance is equal to the given address.
--- This function will throw an exception if the given address is a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is equal
--- @see                        cidr.lower
--- @see                        cidr.higher
-function cidr.equal( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return false
-               end
-       end
-       return true
-end
-
---- Return the prefix length of this CIDR instance.
--- @param mask Override instance prefix with given netmask (optional)
--- @return             Prefix length in bit
-function cidr.prefix( self, mask )
-       local prefix = self[3]
-
-       if mask then
-               prefix = 0
-
-               local stop = false
-               local obj = type(mask) ~= "table"
-                       and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
-
-               if not obj then return nil end
-
-               local _, word
-               for _, word in ipairs(obj[2]) do
-                       if word == 0xFFFF then
-                               prefix = prefix + 16
-                       else
-                               local bitmask = bit.lshift(1, 15)
-                               while bit.band(word, bitmask) == bitmask do
-                                       prefix  = prefix + 1
-                                       bitmask = bit.lshift(1, 15 - (prefix % 16))
-                               end
-
-                               break
-                       end
-               end
-       end
-
-       return prefix
-end
-
---- Return a corresponding CIDR representing the network address of this
--- instance.
--- @param bits Override prefix length of this instance (optional)
--- @return             CIDR instance containing the network address
--- @see                        cidr.host
--- @see                        cidr.broadcast
--- @see                        cidr.mask
-function cidr.network( self, bits )
-       local data = { }
-       bits = bits or self[3]
-
-       local i
-       for i = 1, math.floor( bits / 16 ) do
-               data[#data+1] = self[2][i]
-       end
-
-       if #data < #self[2] then
-               data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
-
-               for i = #data + 1, #self[2] do
-                       data[#data+1] = 0
-               end
-       end
-
-       return __bless({ self[1], data, __maxlen(self[1]) })
-end
-
---- Return a corresponding CIDR representing the host address of this
--- instance. This is intended to extract the host address from larger subnet.
--- @return             CIDR instance containing the network address
--- @see                        cidr.network
--- @see                        cidr.broadcast
--- @see                        cidr.mask
-function cidr.host( self )
-       return __bless({ self[1], self[2], __maxlen(self[1]) })
-end
-
---- Return a corresponding CIDR representing the netmask of this instance.
--- @param bits Override prefix length of this instance (optional)
--- @return             CIDR instance containing the netmask
--- @see                        cidr.network
--- @see                        cidr.host
--- @see                        cidr.broadcast
-function cidr.mask( self, bits )
-       local data = { }
-       bits = bits or self[3]
-
-       for i = 1, math.floor( bits / 16 ) do
-               data[#data+1] = 0xFFFF
-       end
-
-       if #data < #self[2] then
-               data[#data+1] = __mask16(bits)
-
-               for i = #data + 1, #self[2] do
-                       data[#data+1] = 0
-               end
-       end
-
-       return __bless({ self[1], data, __maxlen(self[1]) })
-end
-
---- Return CIDR containing the broadcast address of this instance.
--- @return             CIDR instance containing the netmask, always nil for IPv6
--- @see                        cidr.network
--- @see                        cidr.host
--- @see                        cidr.mask
-function cidr.broadcast( self )
-       -- IPv6 has no broadcast addresses (XXX: assert() instead?)
-       if self[1] == FAMILY_INET4 then
-               local data   = { unpack(self[2]) }
-               local offset = math.floor( self[3] / 16 ) + 1
-
-               if offset <= #data then
-                       data[offset] = bit.bor( data[offset], __not16(self[3]) )
-                       for i = offset + 1, #data do data[i] = 0xFFFF end
-
-                       return __bless({ self[1], data, __maxlen(self[1]) })
-               end
-       end
-end
-
---- Test whether this instance fully contains the given CIDR instance.
--- @param addr CIDR instance to test against
--- @return             Boolean indicating whether this instance contains the given CIDR
-function cidr.contains( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-
-       if self:prefix() <= addr:prefix() then
-               return self:network() == addr:network(self:prefix())
-       end
-
-       return false
-end
-
---- Add specified amount of hosts to this instance.
--- @param amount       Number of hosts to add to this instance
--- @param inplace      Boolen indicating whether to alter values inplace (optional)
--- @return                     CIDR representing the new address or nil on overflow error
--- @see                                cidr.sub
-function cidr.add( self, amount, inplace )
-       local pos
-       local data   = { unpack(self[2]) }
-       local shorts = __array16( amount, self[1] )
-
-       for pos = #data, 1, -1 do
-               local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
-               if ( data[pos] + add ) > 0xFFFF then
-                       data[pos] = ( data[pos] + add ) % 0xFFFF
-                       if pos > 1 then
-                               data[pos-1] = data[pos-1] + ( add - data[pos] )
-                       else
-                               return nil
-                       end
-               else
-                       data[pos] = data[pos] + add
-               end
-       end
-
-       if inplace then
-               self[2] = data
-               return self
-       else
-               return __bless({ self[1], data, self[3] })
-       end
-end
-
---- Substract specified amount of hosts from this instance.
--- @param amount       Number of hosts to substract from this instance
--- @param inplace      Boolen indicating whether to alter values inplace (optional)
--- @return                     CIDR representing the new address or nil on underflow error
--- @see                                cidr.add
-function cidr.sub( self, amount, inplace )
-       local pos
-       local data   = { unpack(self[2]) }
-       local shorts = __array16( amount, self[1] )
-
-       for pos = #data, 1, -1 do
-               local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
-               if ( data[pos] - sub ) < 0 then
-                       data[pos] = ( sub - data[pos] ) % 0xFFFF
-                       if pos > 1 then
-                               data[pos-1] = data[pos-1] - ( sub + data[pos] )
-                       else
-                               return nil
-                       end
-               else
-                       data[pos] = data[pos] - sub
-               end
-       end
-
-       if inplace then
-               self[2] = data
-               return self
-       else
-               return __bless({ self[1], data, self[3] })
-       end
-end
-
---- Return CIDR containing the lowest available host address within this subnet.
--- @return             CIDR containing the host address, nil if subnet is too small
--- @see                        cidr.maxhost
-function cidr.minhost( self )
-       if self[3] <= __sublen(self[1]) then
-               -- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
-               return self:network():add(1, true)
-       end
-end
-
---- Return CIDR containing the highest available host address within the subnet.
--- @return             CIDR containing the host address, nil if subnet is too small
--- @see                        cidr.minhost
-function cidr.maxhost( self )
-       if self[3] <= __sublen(self[1]) then
-               local i
-               local data   = { unpack(self[2]) }
-               local offset = math.floor( self[3] / 16 ) + 1
-
-               data[offset] = bit.bor( data[offset], __not16(self[3]) )
-               for i = offset + 1, #data do data[i] = 0xFFFF end
-               data = __bless({ self[1], data, __maxlen(self[1]) })
-
-               -- Last address in reserved for Broadcast Address in IPv4
-               if data[1] == FAMILY_INET4 then data:sub(1, true) end
-
-               return data
-       end
-end
diff --git a/libs/core/luasrc/ltn12.lua b/libs/core/luasrc/ltn12.lua
deleted file mode 100644 (file)
index 9371290..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
---[[
-LuaSocket 2.0.2 license
-Copyright ï¿½ 2004-2007 Diego Nehab
-
-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.
-]]--
---[[
-       Changes made by LuCI project:
-               * Renamed to luci.ltn12 to avoid collisions with luasocket
-               * Added inline documentation
-]]--
------------------------------------------------------------------------------
--- LTN12 - Filters, sources, sinks and pumps.
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id$
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module
------------------------------------------------------------------------------
-local string = require("string")
-local table = require("table")
-local base = _G
-
---- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
--- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
-module("luci.ltn12")
-
-filter = {}
-source = {}
-sink = {}
-pump = {}
-
--- 2048 seems to be better in windows...
-BLOCKSIZE = 2048
-_VERSION = "LTN12 1.0.1"
-
------------------------------------------------------------------------------
--- Filter stuff
------------------------------------------------------------------------------
-
---- LTN12 Filter constructors
--- @class module
--- @name luci.ltn12.filter
-
---- Return a high level filter that cycles a low-level filter
--- by passing it each chunk and updating a context between calls. 
--- @param low   Low-level filter
--- @param ctx   Context
--- @param extra Extra argument passed to the low-level filter
--- @return LTN12 filter
-function filter.cycle(low, ctx, extra)
-    base.assert(low)
-    return function(chunk)
-        local ret
-        ret, ctx = low(ctx, chunk, extra)
-        return ret
-    end
-end
-
---- Chain a bunch of filters together.
--- (thanks to Wim Couwenberg)
--- @param ... filters to be chained
--- @return LTN12 filter
-function filter.chain(...)
-    local n = table.getn(arg)
-    local top, index = 1, 1
-    local retry = ""
-    return function(chunk)
-        retry = chunk and retry
-        while true do
-            if index == top then
-                chunk = arg[index](chunk)
-                if chunk == "" or top == n then return chunk
-                elseif chunk then index = index + 1
-                else
-                    top = top+1
-                    index = top
-                end
-            else
-                chunk = arg[index](chunk or "")
-                if chunk == "" then
-                    index = index - 1
-                    chunk = retry
-                elseif chunk then
-                    if index == n then return chunk
-                    else index = index + 1 end
-                else base.error("filter returned inappropriate nil") end
-            end
-        end
-    end
-end
-
------------------------------------------------------------------------------
--- Source stuff
------------------------------------------------------------------------------
-
---- LTN12 Source constructors
--- @class module
--- @name luci.ltn12.source
-
--- create an empty source
-local function empty()
-    return nil
-end
-
---- Create an empty source.
--- @return LTN12 source
-function source.empty()
-    return empty
-end
-
---- Return a source that just outputs an error.
--- @param err Error object
--- @return LTN12 source
-function source.error(err)
-    return function()
-        return nil, err
-    end
-end
-
---- Create a file source.
--- @param handle File handle ready for reading
--- @param io_err IO error object
--- @return LTN12 source
-function source.file(handle, io_err)
-    if handle then
-        return function()
-            local chunk = handle:read(BLOCKSIZE)
-            if not chunk then handle:close() end
-            return chunk
-        end
-    else return source.error(io_err or "unable to open file") end
-end
-
---- Turn a fancy source into a simple source.
--- @param src fancy source
--- @return LTN12 source
-function source.simplify(src)
-    base.assert(src)
-    return function()
-        local chunk, err_or_new = src()
-        src = err_or_new or src
-        if not chunk then return nil, err_or_new
-        else return chunk end
-    end
-end
-
---- Create a string source.
--- @param s Data
--- @return LTN12 source
-function source.string(s)
-    if s then
-        local i = 1
-        return function()
-            local chunk = string.sub(s, i, i+BLOCKSIZE-1)
-            i = i + BLOCKSIZE
-            if chunk ~= "" then return chunk
-            else return nil end
-        end
-    else return source.empty() end
-end
-
---- Creates rewindable source.
--- @param src LTN12 source to be made rewindable
--- @return LTN12 source
-function source.rewind(src)
-    base.assert(src)
-    local t = {}
-    return function(chunk)
-        if not chunk then
-            chunk = table.remove(t)
-            if not chunk then return src()
-            else return chunk end
-        else
-            t[#t+1] = chunk
-        end
-    end
-end
-
---- Chain a source and a filter together.
--- @param src LTN12 source
--- @param f LTN12 filter
--- @return LTN12 source
-function source.chain(src, f)
-    base.assert(src and f)
-    local last_in, last_out = "", ""
-    local state = "feeding"
-    local err
-    return function()
-        if not last_out then
-            base.error('source is empty!', 2)
-        end
-        while true do
-            if state == "feeding" then
-                last_in, err = src()
-                if err then return nil, err end
-                last_out = f(last_in)
-                if not last_out then
-                    if last_in then
-                        base.error('filter returned inappropriate nil')
-                    else
-                        return nil
-                    end
-                elseif last_out ~= "" then
-                    state = "eating"
-                    if last_in then last_in = "" end
-                    return last_out
-                end
-            else
-                last_out = f(last_in)
-                if last_out == "" then
-                    if last_in == "" then
-                        state = "feeding"
-                    else
-                        base.error('filter returned ""')
-                    end
-                elseif not last_out then
-                    if last_in then
-                        base.error('filter returned inappropriate nil')
-                    else
-                        return nil
-                    end
-                else
-                    return last_out
-                end
-            end
-        end
-    end
-end
-
---- Create a source that produces contents of several sources.
--- Sources will be used one after the other, as if they were concatenated
--- (thanks to Wim Couwenberg)
--- @param ... LTN12 sources
--- @return LTN12 source
-function source.cat(...)
-    local src = table.remove(arg, 1)
-    return function()
-        while src do
-            local chunk, err = src()
-            if chunk then return chunk end
-            if err then return nil, err end
-            src = table.remove(arg, 1)
-        end
-    end
-end
-
------------------------------------------------------------------------------
--- Sink stuff
------------------------------------------------------------------------------
-
---- LTN12 sink constructors
--- @class module
--- @name luci.ltn12.sink
-
---- Create a sink that stores into a table.
--- @param t output table to store into
--- @return LTN12 sink
-function sink.table(t)
-    t = t or {}
-    local f = function(chunk, err)
-        if chunk then t[#t+1] = chunk end
-        return 1
-    end
-    return f, t
-end
-
---- Turn a fancy sink into a simple sink.
--- @param snk fancy sink
--- @return LTN12 sink
-function sink.simplify(snk)
-    base.assert(snk)
-    return function(chunk, err)
-        local ret, err_or_new = snk(chunk, err)
-        if not ret then return nil, err_or_new end
-        snk = err_or_new or snk
-        return 1
-    end
-end
-
---- Create a file sink.
--- @param handle file handle to write to
--- @param io_err IO error
--- @return LTN12 sink
-function sink.file(handle, io_err)
-    if handle then
-        return function(chunk, err)
-            if not chunk then
-                handle:close()
-                return 1
-            else return handle:write(chunk) end
-        end
-    else return sink.error(io_err or "unable to open file") end
-end
-
--- creates a sink that discards data
-local function null()
-    return 1
-end
-
---- Create a sink that discards data.
--- @return LTN12 sink
-function sink.null()
-    return null
-end
-
---- Create a sink that just returns an error.
--- @param err Error object
--- @return LTN12 sink
-function sink.error(err)
-    return function()
-        return nil, err
-    end
-end
-
---- Chain a sink with a filter.
--- @param f LTN12 filter
--- @param snk LTN12 sink
--- @return LTN12 sink
-function sink.chain(f, snk)
-    base.assert(f and snk)
-    return function(chunk, err)
-        if chunk ~= "" then
-            local filtered = f(chunk)
-            local done = chunk and ""
-            while true do
-                local ret, snkerr = snk(filtered, err)
-                if not ret then return nil, snkerr end
-                if filtered == done then return 1 end
-                filtered = f(done)
-            end
-        else return 1 end
-    end
-end
-
------------------------------------------------------------------------------
--- Pump stuff
------------------------------------------------------------------------------
-
---- LTN12 pump functions
--- @class module
--- @name luci.ltn12.pump
-
---- Pump one chunk from the source to the sink.
--- @param src LTN12 source
--- @param snk LTN12 sink
--- @return Chunk of data or nil if an error occured
--- @return Error object
-function pump.step(src, snk)
-    local chunk, src_err = src()
-    local ret, snk_err = snk(chunk, src_err)
-    if chunk and ret then return 1
-    else return nil, src_err or snk_err end
-end
-
---- Pump all data from a source to a sink, using a step function.
--- @param src LTN12 source
--- @param snk LTN12 sink
--- @param step step function (optional)
--- @return 1 if the operation succeeded otherwise nil
--- @return Error object
-function pump.all(src, snk, step)
-    base.assert(src and snk)
-    step = step or pump.step
-    while true do
-        local ret, err = step(src, snk)
-        if not ret then
-            if err then return nil, err
-            else return 1 end
-        end
-    end
-end
-
diff --git a/libs/core/luasrc/model/firewall.lua b/libs/core/luasrc/model/firewall.lua
deleted file mode 100644 (file)
index a9f6fdb..0000000
+++ /dev/null
@@ -1,582 +0,0 @@
---[[
-LuCI - Firewall model
-
-Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local type, pairs, ipairs, table, luci, math
-       = type, pairs, ipairs, table, luci, math
-
-local tpl = require "luci.template.parser"
-local utl = require "luci.util"
-local uci = require "luci.model.uci"
-
-module "luci.model.firewall"
-
-
-local uci_r, uci_s
-
-function _valid_id(x)
-       return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$"))
-end
-
-function _get(c, s, o)
-       return uci_r:get(c, s, o)
-end
-
-function _set(c, s, o, v)
-       if v ~= nil then
-               if type(v) == "boolean" then v = v and "1" or "0" end
-               return uci_r:set(c, s, o, v)
-       else
-               return uci_r:delete(c, s, o)
-       end
-end
-
-
-function init(cursor)
-       uci_r = cursor or uci_r or uci.cursor()
-       uci_s = uci_r:substate()
-
-       return _M
-end
-
-function save(self, ...)
-       uci_r:save(...)
-       uci_r:load(...)
-end
-
-function commit(self, ...)
-       uci_r:commit(...)
-       uci_r:load(...)
-end
-
-function get_defaults()
-       return defaults()
-end
-
-function new_zone(self)
-       local name = "newzone"
-       local count = 1
-
-       while self:get_zone(name) do
-               count = count + 1
-               name = "newzone%d" % count
-       end
-
-       return self:add_zone(name)
-end
-
-function add_zone(self, n)
-       if _valid_id(n) and not self:get_zone(n) then
-               local d = defaults()
-               local z = uci_r:section("firewall", "zone", nil, {
-                       name    = n,
-                       network = " ",
-                       input   = d:input()   or "DROP",
-                       forward = d:forward() or "DROP",
-                       output  = d:output()  or "DROP"
-               })
-
-               return z and zone(z)
-       end
-end
-
-function get_zone(self, n)
-       if uci_r:get("firewall", n) == "zone" then
-               return zone(n)
-       else
-               local z
-               uci_r:foreach("firewall", "zone",
-                       function(s)
-                               if n and s.name == n then
-                                       z = s['.name']
-                                       return false
-                               end
-                       end)
-               return z and zone(z)
-       end
-end
-
-function get_zones(self)
-       local zones = { }
-       local znl = { }
-
-       uci_r:foreach("firewall", "zone",
-               function(s)
-                       if s.name then
-                               znl[s.name] = zone(s['.name'])
-                       end
-               end)
-
-       local z
-       for z in utl.kspairs(znl) do
-               zones[#zones+1] = znl[z]
-       end
-
-       return zones
-end
-
-function get_zone_by_network(self, net)
-       local z
-
-       uci_r:foreach("firewall", "zone",
-               function(s)
-                       if s.name and net then
-                               local n
-                               for n in utl.imatch(s.network or s.name) do
-                                       if n == net then
-                                               z = s['.name']
-                                               return false
-                                       end
-                               end
-                       end
-               end)
-
-       return z and zone(z)
-end
-
-function del_zone(self, n)
-       local r = false
-
-       if uci_r:get("firewall", n) == "zone" then
-               local z = uci_r:get("firewall", n, "name")
-               r = uci_r:delete("firewall", n)
-               n = z
-       else
-               uci_r:foreach("firewall", "zone",
-                       function(s)
-                               if n and s.name == n then
-                                       r = uci_r:delete("firewall", s['.name'])
-                                       return false
-                               end
-                       end)
-       end
-
-       if r then
-               uci_r:foreach("firewall", "rule",
-                       function(s)
-                               if s.src == n or s.dest == n then
-                                       uci_r:delete("firewall", s['.name'])
-                               end
-                       end)
-
-               uci_r:foreach("firewall", "redirect",
-                       function(s)
-                               if s.src == n or s.dest == n then
-                                       uci_r:delete("firewall", s['.name'])
-                               end
-                       end)
-
-               uci_r:foreach("firewall", "forwarding",
-                       function(s)
-                               if s.src == n or s.dest == n then
-                                       uci_r:delete("firewall", s['.name'])
-                               end
-                       end)
-       end
-
-       return r
-end
-
-function rename_zone(self, old, new)
-       local r = false
-
-       if _valid_id(new) and not self:get_zone(new) then
-               uci_r:foreach("firewall", "zone",
-                       function(s)
-                               if old and s.name == old then
-                                       if not s.network then
-                                               uci_r:set("firewall", s['.name'], "network", old)
-                                       end
-                                       uci_r:set("firewall", s['.name'], "name", new)
-                                       r = true
-                                       return false
-                               end
-                       end)
-
-               if r then
-                       uci_r:foreach("firewall", "rule",
-                               function(s)
-                                       if s.src == old then
-                                               uci_r:set("firewall", s['.name'], "src", new)
-                                       end
-                                       if s.dest == old then
-                                               uci_r:set("firewall", s['.name'], "dest", new)
-                                       end
-                               end)
-
-                       uci_r:foreach("firewall", "redirect",
-                               function(s)
-                                       if s.src == old then
-                                               uci_r:set("firewall", s['.name'], "src", new)
-                                       end
-                                       if s.dest == old then
-                                               uci_r:set("firewall", s['.name'], "dest", new)
-                                       end
-                               end)
-
-                       uci_r:foreach("firewall", "forwarding",
-                               function(s)
-                                       if s.src == old then
-                                               uci_r:set("firewall", s['.name'], "src", new)
-                                       end
-                                       if s.dest == old then
-                                               uci_r:set("firewall", s['.name'], "dest", new)
-                                       end
-                               end)
-               end
-       end
-
-       return r
-end
-
-function del_network(self, net)
-       local z
-       if net then
-               for _, z in ipairs(self:get_zones()) do
-                       z:del_network(net)
-               end
-       end
-end
-
-
-defaults = utl.class()
-function defaults.__init__(self)
-       uci_r:foreach("firewall", "defaults",
-               function(s)
-                       self.sid  = s['.name']
-                       return false
-               end)
-
-       self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { })
-end
-
-function defaults.get(self, opt)
-       return _get("firewall", self.sid, opt)
-end
-
-function defaults.set(self, opt, val)
-       return _set("firewall", self.sid, opt, val)
-end
-
-function defaults.syn_flood(self)
-       return (self:get("syn_flood") == "1")
-end
-
-function defaults.drop_invalid(self)
-       return (self:get("drop_invalid") == "1")
-end
-
-function defaults.input(self)
-       return self:get("input") or "DROP"
-end
-
-function defaults.forward(self)
-       return self:get("forward") or "DROP"
-end
-
-function defaults.output(self)
-       return self:get("output") or "DROP"
-end
-
-
-zone = utl.class()
-function zone.__init__(self, z)
-       if uci_r:get("firewall", z) == "zone" then
-               self.sid  = z
-               self.data = uci_r:get_all("firewall", z)
-       else
-               uci_r:foreach("firewall", "zone",
-                       function(s)
-                               if s.name == z then
-                                       self.sid  = s['.name']
-                                       self.data = s
-                                       return false
-                               end
-                       end)
-       end
-end
-
-function zone.get(self, opt)
-       return _get("firewall", self.sid, opt)
-end
-
-function zone.set(self, opt, val)
-       return _set("firewall", self.sid, opt, val)
-end
-
-function zone.masq(self)
-       return (self:get("masq") == "1")
-end
-
-function zone.name(self)
-       return self:get("name")
-end
-
-function zone.network(self)
-       return self:get("network")
-end
-
-function zone.input(self)
-       return self:get("input") or defaults():input() or "DROP"
-end
-
-function zone.forward(self)
-       return self:get("forward") or defaults():forward() or "DROP"
-end
-
-function zone.output(self)
-       return self:get("output") or defaults():output() or "DROP"
-end
-
-function zone.add_network(self, net)
-       if uci_r:get("network", net) == "interface" then
-               local nets = { }
-
-               local n
-               for n in utl.imatch(self:get("network") or self:get("name")) do
-                       if n ~= net then
-                               nets[#nets+1] = n
-                       end
-               end
-
-               nets[#nets+1] = net
-
-               _M:del_network(net)
-               self:set("network", table.concat(nets, " "))
-       end
-end
-
-function zone.del_network(self, net)
-       local nets = { }
-
-       local n
-       for n in utl.imatch(self:get("network") or self:get("name")) do
-               if n ~= net then
-                       nets[#nets+1] = n
-               end
-       end
-
-       if #nets > 0 then
-               self:set("network", table.concat(nets, " "))
-       else
-               self:set("network", " ")
-       end
-end
-
-function zone.get_networks(self)
-       local nets = { }
-
-       local n
-       for n in utl.imatch(self:get("network") or self:get("name")) do
-               nets[#nets+1] = n
-       end
-
-       return nets
-end
-
-function zone.clear_networks(self)
-       self:set("network", " ")
-end
-
-function zone.get_forwardings_by(self, what)
-       local name = self:name()
-       local forwards = { }
-
-       uci_r:foreach("firewall", "forwarding",
-               function(s)
-                       if s.src and s.dest and s[what] == name then
-                               forwards[#forwards+1] = forwarding(s['.name'])
-                       end
-               end)
-
-       return forwards
-end
-
-function zone.add_forwarding_to(self, dest)
-       local exist, forward
-
-       for _, forward in ipairs(self:get_forwardings_by('src')) do
-               if forward:dest() == dest then
-                       exist = true
-                       break
-               end
-       end
-
-       if not exist and dest ~= self:name() and _valid_id(dest) then
-               local s = uci_r:section("firewall", "forwarding", nil, {
-                       src     = self:name(),
-                       dest    = dest
-               })
-
-               return s and forwarding(s)
-       end
-end
-
-function zone.add_forwarding_from(self, src)
-       local exist, forward
-
-       for _, forward in ipairs(self:get_forwardings_by('dest')) do
-               if forward:src() == src then
-                       exist = true
-                       break
-               end
-       end
-
-       if not exist and src ~= self:name() and _valid_id(src) then
-               local s = uci_r:section("firewall", "forwarding", nil, {
-                       src     = src,
-                       dest    = self:name()
-               })
-
-               return s and forwarding(s)
-       end
-end
-
-function zone.del_forwardings_by(self, what)
-       local name = self:name()
-
-       uci_r:delete_all("firewall", "forwarding",
-               function(s)
-                       return (s.src and s.dest and s[what] == name)
-               end)
-end
-
-function zone.add_redirect(self, options)
-       options = options or { }
-       options.src = self:name()
-
-       local s = uci_r:section("firewall", "redirect", nil, options)
-       return s and redirect(s)
-end
-
-function zone.add_rule(self, options)
-       options = options or { }
-       options.src = self:name()
-
-       local s = uci_r:section("firewall", "rule", nil, options)
-       return s and rule(s)
-end
-
-function zone.get_color(self)
-       if self and self:name() == "lan" then
-               return "#90f090"
-       elseif self and self:name() == "wan" then
-               return "#f09090"
-       elseif self then
-               math.randomseed(tpl.hash(self:name()))
-
-               local r   = math.random(128)
-               local g   = math.random(128)
-               local min = 0
-               local max = 128
-
-               if ( r + g ) < 128 then
-                       min = 128 - r - g
-               else
-                       max = 255 - r - g
-               end
-
-               local b = min + math.floor( math.random() * ( max - min ) )
-
-               return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
-       else
-               return "#eeeeee"
-       end
-end
-
-
-forwarding = utl.class()
-function forwarding.__init__(self, f)
-       self.sid = f
-end
-
-function forwarding.src(self)
-       return uci_r:get("firewall", self.sid, "src")
-end
-
-function forwarding.dest(self)
-       return uci_r:get("firewall", self.sid, "dest")
-end
-
-function forwarding.src_zone(self)
-       return zone(self:src())
-end
-
-function forwarding.dest_zone(self)
-       return zone(self:dest())
-end
-
-
-rule = utl.class()
-function rule.__init__(self, f)
-       self.sid = f
-end
-
-function rule.get(self, opt)
-       return _get("firewall", self.sid, opt)
-end
-
-function rule.set(self, opt, val)
-       return _set("firewall", self.sid, opt, val)
-end
-
-function rule.src(self)
-       return uci_r:get("firewall", self.sid, "src")
-end
-
-function rule.dest(self)
-       return uci_r:get("firewall", self.sid, "dest")
-end
-
-function rule.src_zone(self)
-       return zone(self:src())
-end
-
-function rule.dest_zone(self)
-       return zone(self:dest())
-end
-
-
-redirect = utl.class()
-function redirect.__init__(self, f)
-       self.sid = f
-end
-
-function redirect.get(self, opt)
-       return _get("firewall", self.sid, opt)
-end
-
-function redirect.set(self, opt, val)
-       return _set("firewall", self.sid, opt, val)
-end
-
-function redirect.src(self)
-       return uci_r:get("firewall", self.sid, "src")
-end
-
-function redirect.dest(self)
-       return uci_r:get("firewall", self.sid, "dest")
-end
-
-function redirect.src_zone(self)
-       return zone(self:src())
-end
-
-function redirect.dest_zone(self)
-       return zone(self:dest())
-end
diff --git a/libs/core/luasrc/model/network.lua b/libs/core/luasrc/model/network.lua
deleted file mode 100644 (file)
index a409621..0000000
+++ /dev/null
@@ -1,1584 +0,0 @@
---[[
-LuCI - Network model
-
-Copyright 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local type, next, pairs, ipairs, loadfile, table
-       = type, next, pairs, ipairs, loadfile, table
-
-local tonumber, tostring, math = tonumber, tostring, math
-
-local require = require
-
-local bus = require "ubus"
-local nxo = require "nixio"
-local nfs = require "nixio.fs"
-local ipc = require "luci.ip"
-local sys = require "luci.sys"
-local utl = require "luci.util"
-local dsp = require "luci.dispatcher"
-local uci = require "luci.model.uci"
-local lng = require "luci.i18n"
-
-module "luci.model.network"
-
-
-IFACE_PATTERNS_VIRTUAL  = { }
-IFACE_PATTERNS_IGNORE   = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^lo$" }
-IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
-
-
-protocol = utl.class()
-
-local _protocols = { }
-
-local _interfaces, _bridge, _switch, _tunnel
-local _ubus, _ubusnetcache, _ubusdevcache, _ubuswificache
-local _uci_real, _uci_state
-
-function _filter(c, s, o, r)
-       local val = _uci_real:get(c, s, o)
-       if val then
-               local l = { }
-               if type(val) == "string" then
-                       for val in val:gmatch("%S+") do
-                               if val ~= r then
-                                       l[#l+1] = val
-                               end
-                       end
-                       if #l > 0 then
-                               _uci_real:set(c, s, o, table.concat(l, " "))
-                       else
-                               _uci_real:delete(c, s, o)
-                       end
-               elseif type(val) == "table" then
-                       for _, val in ipairs(val) do
-                               if val ~= r then
-                                       l[#l+1] = val
-                               end
-                       end
-                       if #l > 0 then
-                               _uci_real:set(c, s, o, l)
-                       else
-                               _uci_real:delete(c, s, o)
-                       end
-               end
-       end
-end
-
-function _append(c, s, o, a)
-       local val = _uci_real:get(c, s, o) or ""
-       if type(val) == "string" then
-               local l = { }
-               for val in val:gmatch("%S+") do
-                       if val ~= a then
-                               l[#l+1] = val
-                       end
-               end
-               l[#l+1] = a
-               _uci_real:set(c, s, o, table.concat(l, " "))
-       elseif type(val) == "table" then
-               local l = { }
-               for _, val in ipairs(val) do
-                       if val ~= a then
-                               l[#l+1] = val
-                       end
-               end
-               l[#l+1] = a
-               _uci_real:set(c, s, o, l)
-       end
-end
-
-function _stror(s1, s2)
-       if not s1 or #s1 == 0 then
-               return s2 and #s2 > 0 and s2
-       else
-               return s1
-       end
-end
-
-function _get(c, s, o)
-       return _uci_real:get(c, s, o)
-end
-
-function _set(c, s, o, v)
-       if v ~= nil then
-               if type(v) == "boolean" then v = v and "1" or "0" end
-               return _uci_real:set(c, s, o, v)
-       else
-               return _uci_real:delete(c, s, o)
-       end
-end
-
-function _wifi_iface(x)
-       local _, p
-       for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
-               if x:match(p) then
-                       return true
-               end
-       end
-       return false
-end
-
-function _wifi_state(key, val, field)
-       if not next(_ubuswificache) then
-               _ubuswificache = _ubus:call("network.wireless", "status", {}) or {}
-       end
-
-       local radio, radiostate
-       for radio, radiostate in pairs(_ubuswificache) do
-               local ifc, ifcstate
-               for ifc, ifcstate in pairs(radiostate.interfaces) do
-                       if ifcstate[key] == val then
-                               return ifcstate[field]
-                       end
-               end
-       end
-end
-
-function _wifi_lookup(ifn)
-       -- got a radio#.network# pseudo iface, locate the corresponding section
-       local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
-       if radio and ifnidx then
-               local sid = nil
-               local num = 0
-
-               ifnidx = tonumber(ifnidx)
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               if s.device == radio then
-                                       num = num + 1
-                                       if num == ifnidx then
-                                               sid = s['.name']
-                                               return false
-                                       end
-                               end
-                       end)
-
-               return sid
-
-       -- looks like wifi, try to locate the section via state vars
-       elseif _wifi_iface(ifn) then
-               local sid = _wifi_state("ifname", ifn, "section")
-               if not sid then
-                       _uci_state:foreach("wireless", "wifi-iface",
-                               function(s)
-                                       if s.ifname == ifn then
-                                               sid = s['.name']
-                                               return false
-                                       end
-                               end)
-               end
-
-               return sid
-       end
-end
-
-function _iface_virtual(x)
-       local _, p
-       for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
-               if x:match(p) then
-                       return true
-               end
-       end
-       return false
-end
-
-function _iface_ignore(x)
-       local _, p
-       for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
-               if x:match(p) then
-                       return true
-               end
-       end
-       return _iface_virtual(x)
-end
-
-
-function init(cursor)
-       _uci_real  = cursor or _uci_real or uci.cursor()
-       _uci_state = _uci_real:substate()
-
-       _interfaces = { }
-       _bridge     = { }
-       _switch     = { }
-       _tunnel     = { }
-
-       _ubus          = bus.connect()
-       _ubusnetcache  = { }
-       _ubusdevcache  = { }
-       _ubuswificache = { }
-
-       -- read interface information
-       local n, i
-       for n, i in ipairs(nxo.getifaddrs()) do
-               local name = i.name:match("[^:]+")
-               local prnt = name:match("^([^%.]+)%.")
-
-               if _iface_virtual(name) then
-                       _tunnel[name] = true
-               end
-
-               if _tunnel[name] or not _iface_ignore(name) then
-                       _interfaces[name] = _interfaces[name] or {
-                               idx      = i.ifindex or n,
-                               name     = name,
-                               rawname  = i.name,
-                               flags    = { },
-                               ipaddrs  = { },
-                               ip6addrs = { }
-                       }
-
-                       if prnt then
-                               _switch[name] = true
-                               _switch[prnt] = true
-                       end
-
-                       if i.family == "packet" then
-                               _interfaces[name].flags   = i.flags
-                               _interfaces[name].stats   = i.data
-                               _interfaces[name].macaddr = i.addr
-                       elseif i.family == "inet" then
-                               _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
-                       elseif i.family == "inet6" then
-                               _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
-                       end
-               end
-       end
-
-       -- read bridge informaton
-       local b, l
-       for l in utl.execi("brctl show") do
-               if not l:match("STP") then
-                       local r = utl.split(l, "%s+", nil, true)
-                       if #r == 4 then
-                               b = {
-                                       name    = r[1],
-                                       id      = r[2],
-                                       stp     = r[3] == "yes",
-                                       ifnames = { _interfaces[r[4]] }
-                               }
-                               if b.ifnames[1] then
-                                       b.ifnames[1].bridge = b
-                               end
-                               _bridge[r[1]] = b
-                       elseif b then
-                               b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
-                               b.ifnames[#b.ifnames].bridge = b
-                       end
-               end
-       end
-
-       return _M
-end
-
-function save(self, ...)
-       _uci_real:save(...)
-       _uci_real:load(...)
-end
-
-function commit(self, ...)
-       _uci_real:commit(...)
-       _uci_real:load(...)
-end
-
-function ifnameof(self, x)
-       if utl.instanceof(x, interface) then
-               return x:name()
-       elseif utl.instanceof(x, protocol) then
-               return x:ifname()
-       elseif type(x) == "string" then
-               return x:match("^[^:]+")
-       end
-end
-
-function get_protocol(self, protoname, netname)
-       local v = _protocols[protoname]
-       if v then
-               return v(netname or "__dummy__")
-       end
-end
-
-function get_protocols(self)
-       local p = { }
-       local _, v
-       for _, v in ipairs(_protocols) do
-               p[#p+1] = v("__dummy__")
-       end
-       return p
-end
-
-function register_protocol(self, protoname)
-       local proto = utl.class(protocol)
-
-       function proto.__init__(self, name)
-               self.sid = name
-       end
-
-       function proto.proto(self)
-               return protoname
-       end
-
-       _protocols[#_protocols+1] = proto
-       _protocols[protoname]     = proto
-
-       return proto
-end
-
-function register_pattern_virtual(self, pat)
-       IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
-end
-
-
-function has_ipv6(self)
-       return nfs.access("/proc/net/ipv6_route")
-end
-
-function add_network(self, n, options)
-       local oldnet = self:get_network(n)
-       if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
-               if _uci_real:section("network", "interface", n, options) then
-                       return network(n)
-               end
-       elseif oldnet and oldnet:is_empty() then
-               if options then
-                       local k, v
-                       for k, v in pairs(options) do
-                               oldnet:set(k, v)
-                       end
-               end
-               return oldnet
-       end
-end
-
-function get_network(self, n)
-       if n and _uci_real:get("network", n) == "interface" then
-               return network(n)
-       end
-end
-
-function get_networks(self)
-       local nets = { }
-       local nls = { }
-
-       _uci_real:foreach("network", "interface",
-               function(s)
-                       nls[s['.name']] = network(s['.name'])
-               end)
-
-       local n
-       for n in utl.kspairs(nls) do
-               nets[#nets+1] = nls[n]
-       end
-
-       return nets
-end
-
-function del_network(self, n)
-       local r = _uci_real:delete("network", n)
-       if r then
-               _uci_real:delete_all("network", "alias",
-                       function(s) return (s.interface == n) end)
-
-               _uci_real:delete_all("network", "route",
-                       function(s) return (s.interface == n) end)
-
-               _uci_real:delete_all("network", "route6",
-                       function(s) return (s.interface == n) end)
-
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               local net
-                               local rest = { }
-                               for net in utl.imatch(s.network) do
-                                       if net ~= n then
-                                               rest[#rest+1] = net
-                                       end
-                               end
-                               if #rest > 0 then
-                                       _uci_real:set("wireless", s['.name'], "network",
-                                                     table.concat(rest, " "))
-                               else
-                                       _uci_real:delete("wireless", s['.name'], "network")
-                               end
-                       end)
-       end
-       return r
-end
-
-function rename_network(self, old, new)
-       local r
-       if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
-               r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
-
-               if r then
-                       _uci_real:foreach("network", "alias",
-                               function(s)
-                                       if s.interface == old then
-                                               _uci_real:set("network", s['.name'], "interface", new)
-                                       end
-                               end)
-
-                       _uci_real:foreach("network", "route",
-                               function(s)
-                                       if s.interface == old then
-                                               _uci_real:set("network", s['.name'], "interface", new)
-                                       end
-                               end)
-
-                       _uci_real:foreach("network", "route6",
-                               function(s)
-                                       if s.interface == old then
-                                               _uci_real:set("network", s['.name'], "interface", new)
-                                       end
-                               end)
-
-                       _uci_real:foreach("wireless", "wifi-iface",
-                               function(s)
-                                       local net
-                                       local list = { }
-                                       for net in utl.imatch(s.network) do
-                                               if net == old then
-                                                       list[#list+1] = new
-                                               else
-                                                       list[#list+1] = net
-                                               end
-                                       end
-                                       if #list > 0 then
-                                               _uci_real:set("wireless", s['.name'], "network",
-                                                             table.concat(list, " "))
-                                       end
-                               end)
-
-                       _uci_real:delete("network", old)
-               end
-       end
-       return r or false
-end
-
-function get_interface(self, i)
-       if _interfaces[i] or _wifi_iface(i) then
-               return interface(i)
-       else
-               local ifc
-               local num = { }
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               if s.device then
-                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
-                                       if s['.name'] == i then
-                                               ifc = interface(
-                                                       "%s.network%d" %{s.device, num[s.device] })
-                                               return false
-                                       end
-                               end
-                       end)
-               return ifc
-       end
-end
-
-function get_interfaces(self)
-       local iface
-       local ifaces = { }
-       local seen = { }
-       local nfs = { }
-       local baseof = { }
-
-       -- find normal interfaces
-       _uci_real:foreach("network", "interface",
-               function(s)
-                       for iface in utl.imatch(s.ifname) do
-                               if not _iface_ignore(iface) and not _wifi_iface(iface) then
-                                       seen[iface] = true
-                                       nfs[iface] = interface(iface)
-                               end
-                       end
-               end)
-
-       for iface in utl.kspairs(_interfaces) do
-               if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
-                       nfs[iface] = interface(iface)
-               end
-       end
-
-       -- find vlan interfaces
-       _uci_real:foreach("network", "switch_vlan",
-               function(s)
-                       if not s.device then
-                               return
-                       end
-
-                       local base = baseof[s.device]
-                       if not base then
-                               if not s.device:match("^eth%d") then
-                                       local l
-                                       for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
-                                               if not base then
-                                                       base = l:match("^%w+: (%w+)")
-                                               end
-                                       end
-                                       if not base or not base:match("^eth%d") then
-                                               base = "eth0"
-                                       end
-                               else
-                                       base = s.device
-                               end
-                               baseof[s.device] = base
-                       end
-
-                       local vid = tonumber(s.vid or s.vlan)
-                       if vid ~= nil and vid >= 0 and vid <= 4095 then
-                               local iface = "%s.%d" %{ base, vid }
-                               if not seen[iface] then
-                                       seen[iface] = true
-                                       nfs[iface] = interface(iface)
-                               end
-                       end
-               end)
-
-       for iface in utl.kspairs(nfs) do
-               ifaces[#ifaces+1] = nfs[iface]
-       end
-
-       -- find wifi interfaces
-       local num = { }
-       local wfs = { }
-       _uci_real:foreach("wireless", "wifi-iface",
-               function(s)
-                       if s.device then
-                               num[s.device] = num[s.device] and num[s.device] + 1 or 1
-                               local i = "%s.network%d" %{ s.device, num[s.device] }
-                               wfs[i] = interface(i)
-                       end
-               end)
-
-       for iface in utl.kspairs(wfs) do
-               ifaces[#ifaces+1] = wfs[iface]
-       end
-
-       return ifaces
-end
-
-function ignore_interface(self, x)
-       return _iface_ignore(x)
-end
-
-function get_wifidev(self, dev)
-       if _uci_real:get("wireless", dev) == "wifi-device" then
-               return wifidev(dev)
-       end
-end
-
-function get_wifidevs(self)
-       local devs = { }
-       local wfd  = { }
-
-       _uci_real:foreach("wireless", "wifi-device",
-               function(s) wfd[#wfd+1] = s['.name'] end)
-
-       local dev
-       for _, dev in utl.vspairs(wfd) do
-               devs[#devs+1] = wifidev(dev)
-       end
-
-       return devs
-end
-
-function get_wifinet(self, net)
-       local wnet = _wifi_lookup(net)
-       if wnet then
-               return wifinet(wnet)
-       end
-end
-
-function add_wifinet(self, net, options)
-       if type(options) == "table" and options.device and
-               _uci_real:get("wireless", options.device) == "wifi-device"
-       then
-               local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
-               return wifinet(wnet)
-       end
-end
-
-function del_wifinet(self, net)
-       local wnet = _wifi_lookup(net)
-       if wnet then
-               _uci_real:delete("wireless", wnet)
-               return true
-       end
-       return false
-end
-
-function get_status_by_route(self, addr, mask)
-       local _, object
-       for _, object in ipairs(_ubus:objects()) do
-               local net = object:match("^network%.interface%.(.+)")
-               if net then
-                       local s = _ubus:call(object, "status", {})
-                       if s and s.route then
-                               local rt
-                               for _, rt in ipairs(s.route) do
-                                       if not rt.table and rt.target == addr and rt.mask == mask then
-                                               return net, s
-                                       end
-                               end
-                       end
-               end
-       end
-end
-
-function get_status_by_address(self, addr)
-       local _, object
-       for _, object in ipairs(_ubus:objects()) do
-               local net = object:match("^network%.interface%.(.+)")
-               if net then
-                       local s = _ubus:call(object, "status", {})
-                       if s and s['ipv4-address'] then
-                               local a
-                               for _, a in ipairs(s['ipv4-address']) do
-                                       if a.address == addr then
-                                               return net, s
-                                       end
-                               end
-                       end
-                       if s and s['ipv6-address'] then
-                               local a
-                               for _, a in ipairs(s['ipv6-address']) do
-                                       if a.address == addr then
-                                               return net, s
-                                       end
-                               end
-                       end
-               end
-       end
-end
-
-function get_wannet(self)
-       local net = self:get_status_by_route("0.0.0.0", 0)
-       return net and network(net)
-end
-
-function get_wandev(self)
-       local _, stat = self:get_status_by_route("0.0.0.0", 0)
-       return stat and interface(stat.l3_device or stat.device)
-end
-
-function get_wan6net(self)
-       local net = self:get_status_by_route("::", 0)
-       return net and network(net)
-end
-
-function get_wan6dev(self)
-       local _, stat = self:get_status_by_route("::", 0)
-       return stat and interface(stat.l3_device or stat.device)
-end
-
-
-function network(name, proto)
-       if name then
-               local p = proto or _uci_real:get("network", name, "proto")
-               local c = p and _protocols[p] or protocol
-               return c(name)
-       end
-end
-
-function protocol.__init__(self, name)
-       self.sid = name
-end
-
-function protocol._get(self, opt)
-       local v = _uci_real:get("network", self.sid, opt)
-       if type(v) == "table" then
-               return table.concat(v, " ")
-       end
-       return v or ""
-end
-
-function protocol._ubus(self, field)
-       if not _ubusnetcache[self.sid] then
-               _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
-                                                    "status", { })
-       end
-       if _ubusnetcache[self.sid] and field then
-               return _ubusnetcache[self.sid][field]
-       end
-       return _ubusnetcache[self.sid]
-end
-
-function protocol.get(self, opt)
-       return _get("network", self.sid, opt)
-end
-
-function protocol.set(self, opt, val)
-       return _set("network", self.sid, opt, val)
-end
-
-function protocol.ifname(self)
-       local ifname
-       if self:is_floating() then
-               ifname = self:_ubus("l3_device")
-       else
-               ifname = self:_ubus("device")
-       end
-       if not ifname then
-               local num = { }
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               if s.device then
-                                       num[s.device] = num[s.device]
-                                               and num[s.device] + 1 or 1
-
-                                       local net
-                                       for net in utl.imatch(s.network) do
-                                               if net == self.sid then
-                                                       ifname = "%s.network%d" %{ s.device, num[s.device] }
-                                                       return false
-                                               end
-                                       end
-                               end
-                       end)
-       end
-       return ifname
-end
-
-function protocol.proto(self)
-       return "none"
-end
-
-function protocol.get_i18n(self)
-       local p = self:proto()
-       if p == "none" then
-               return lng.translate("Unmanaged")
-       elseif p == "static" then
-               return lng.translate("Static address")
-       elseif p == "dhcp" then
-               return lng.translate("DHCP client")
-       else
-               return lng.translate("Unknown")
-       end
-end
-
-function protocol.type(self)
-       return self:_get("type")
-end
-
-function protocol.name(self)
-       return self.sid
-end
-
-function protocol.uptime(self)
-       return self:_ubus("uptime") or 0
-end
-
-function protocol.expires(self)
-       local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
-       local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
-       if a and l then
-               l = l - (nxo.sysinfo().uptime - a)
-               return l > 0 and l or 0
-       end
-       return -1
-end
-
-function protocol.metric(self)
-       return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
-end
-
-function protocol.ipaddr(self)
-       local addrs = self:_ubus("ipv4-address")
-       return addrs and #addrs > 0 and addrs[1].address
-end
-
-function protocol.netmask(self)
-       local addrs = self:_ubus("ipv4-address")
-       return addrs and #addrs > 0 and
-               ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
-end
-
-function protocol.gwaddr(self)
-       local _, route
-       for _, route in ipairs(self:_ubus("route") or { }) do
-               if route.target == "0.0.0.0" and route.mask == 0 then
-                       return route.nexthop
-               end
-       end
-end
-
-function protocol.dnsaddrs(self)
-       local dns = { }
-       local _, addr
-       for _, addr in ipairs(self:_ubus("dns-server") or { }) do
-               if not addr:match(":") then
-                       dns[#dns+1] = addr
-               end
-       end
-       return dns
-end
-
-function protocol.ip6addr(self)
-       local addrs = self:_ubus("ipv6-address")
-       if addrs and #addrs > 0 then
-               return "%s/%d" %{ addrs[1].address, addrs[1].mask }
-       else
-               addrs = self:_ubus("ipv6-prefix-assignment")
-               if addrs and #addrs > 0 then
-                       return "%s/%d" %{ addrs[1].address, addrs[1].mask }
-               end
-       end
-end
-
-function protocol.gw6addr(self)
-       local _, route
-       for _, route in ipairs(self:_ubus("route") or { }) do
-               if route.target == "::" and route.mask == 0 then
-                       return ipc.IPv6(route.nexthop):string()
-               end
-       end
-end
-
-function protocol.dns6addrs(self)
-       local dns = { }
-       local _, addr
-       for _, addr in ipairs(self:_ubus("dns-server") or { }) do
-               if addr:match(":") then
-                       dns[#dns+1] = addr
-               end
-       end
-       return dns
-end
-
-function protocol.is_bridge(self)
-       return (not self:is_virtual() and self:type() == "bridge")
-end
-
-function protocol.opkg_package(self)
-       return nil
-end
-
-function protocol.is_installed(self)
-       return true
-end
-
-function protocol.is_virtual(self)
-       return false
-end
-
-function protocol.is_floating(self)
-       return false
-end
-
-function protocol.is_empty(self)
-       if self:is_floating() then
-               return false
-       else
-               local rv = true
-
-               if (self:_get("ifname") or ""):match("%S+") then
-                       rv = false
-               end
-
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               local n
-                               for n in utl.imatch(s.network) do
-                                       if n == self.sid then
-                                               rv = false
-                                               return false
-                                       end
-                               end
-                       end)
-
-               return rv
-       end
-end
-
-function protocol.add_interface(self, ifname)
-       ifname = _M:ifnameof(ifname)
-       if ifname and not self:is_floating() then
-               -- if its a wifi interface, change its network option
-               local wif = _wifi_lookup(ifname)
-               if wif then
-                       _append("wireless", wif, "network", self.sid)
-
-               -- add iface to our iface list
-               else
-                       _append("network", self.sid, "ifname", ifname)
-               end
-       end
-end
-
-function protocol.del_interface(self, ifname)
-       ifname = _M:ifnameof(ifname)
-       if ifname and not self:is_floating() then
-               -- if its a wireless interface, clear its network option
-               local wif = _wifi_lookup(ifname)
-               if wif then _filter("wireless", wif, "network", self.sid) end
-
-               -- remove the interface
-               _filter("network", self.sid, "ifname", ifname)
-       end
-end
-
-function protocol.get_interface(self)
-       if self:is_virtual() then
-               _tunnel[self:proto() .. "-" .. self.sid] = true
-               return interface(self:proto() .. "-" .. self.sid, self)
-       elseif self:is_bridge() then
-               _bridge["br-" .. self.sid] = true
-               return interface("br-" .. self.sid, self)
-       else
-               local ifn = nil
-               local num = { }
-               for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
-                       ifn = ifn:match("^[^:/]+")
-                       return ifn and interface(ifn, self)
-               end
-               ifn = nil
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               if s.device then
-                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
-
-                                       local net
-                                       for net in utl.imatch(s.network) do
-                                               if net == self.sid then
-                                                       ifn = "%s.network%d" %{ s.device, num[s.device] }
-                                                       return false
-                                               end
-                                       end
-                               end
-                       end)
-               return ifn and interface(ifn, self)
-       end
-end
-
-function protocol.get_interfaces(self)
-       if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
-               local ifaces = { }
-
-               local ifn
-               local nfs = { }
-               for ifn in utl.imatch(self:get("ifname")) do
-                       ifn = ifn:match("^[^:/]+")
-                       nfs[ifn] = interface(ifn, self)
-               end
-
-               for ifn in utl.kspairs(nfs) do
-                       ifaces[#ifaces+1] = nfs[ifn]
-               end
-
-               local num = { }
-               local wfs = { }
-               _uci_real:foreach("wireless", "wifi-iface",
-                       function(s)
-                               if s.device then
-                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
-
-                                       local net
-                                       for net in utl.imatch(s.network) do
-                                               if net == self.sid then
-                                                       ifn = "%s.network%d" %{ s.device, num[s.device] }
-                                                       wfs[ifn] = interface(ifn, self)
-                                               end
-                                       end
-                               end
-                       end)
-
-               for ifn in utl.kspairs(wfs) do
-                       ifaces[#ifaces+1] = wfs[ifn]
-               end
-
-               return ifaces
-       end
-end
-
-function protocol.contains_interface(self, ifname)
-       ifname = _M:ifnameof(ifname)
-       if not ifname then
-               return false
-       elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
-               return true
-       elseif self:is_bridge() and "br-" .. self.sid == ifname then
-               return true
-       else
-               local ifn
-               for ifn in utl.imatch(self:get("ifname")) do
-                       ifn = ifn:match("[^:]+")
-                       if ifn == ifname then
-                               return true
-                       end
-               end
-
-               local wif = _wifi_lookup(ifname)
-               if wif then
-                       local n
-                       for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
-                               if n == self.sid then
-                                       return true
-                               end
-                       end
-               end
-       end
-
-       return false
-end
-
-function protocol.adminlink(self)
-       return dsp.build_url("admin", "network", "network", self.sid)
-end
-
-
-interface = utl.class()
-
-function interface.__init__(self, ifname, network)
-       local wif = _wifi_lookup(ifname)
-       if wif then
-               self.wif    = wifinet(wif)
-               self.ifname = _wifi_state("section", wif, "ifname")
-       end
-
-       self.ifname  = self.ifname or ifname
-       self.dev     = _interfaces[self.ifname]
-       self.network = network
-end
-
-function interface._ubus(self, field)
-       if not _ubusdevcache[self.ifname] then
-               _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
-                                                       { name = self.ifname })
-       end
-       if _ubusdevcache[self.ifname] and field then
-               return _ubusdevcache[self.ifname][field]
-       end
-       return _ubusdevcache[self.ifname]
-end
-
-function interface.name(self)
-       return self.wif and self.wif:ifname() or self.ifname
-end
-
-function interface.mac(self)
-       return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
-end
-
-function interface.ipaddrs(self)
-       return self.dev and self.dev.ipaddrs or { }
-end
-
-function interface.ip6addrs(self)
-       return self.dev and self.dev.ip6addrs or { }
-end
-
-function interface.type(self)
-       if self.wif or _wifi_iface(self.ifname) then
-               return "wifi"
-       elseif _bridge[self.ifname] then
-               return "bridge"
-       elseif _tunnel[self.ifname] then
-               return "tunnel"
-       elseif self.ifname:match("%.") then
-               return "vlan"
-       elseif _switch[self.ifname] then
-               return "switch"
-       else
-               return "ethernet"
-       end
-end
-
-function interface.shortname(self)
-       if self.wif then
-               return "%s %q" %{
-                       self.wif:active_mode(),
-                       self.wif:active_ssid() or self.wif:active_bssid()
-               }
-       else
-               return self.ifname
-       end
-end
-
-function interface.get_i18n(self)
-       if self.wif then
-               return "%s: %s %q" %{
-                       lng.translate("Wireless Network"),
-                       self.wif:active_mode(),
-                       self.wif:active_ssid() or self.wif:active_bssid()
-               }
-       else
-               return "%s: %q" %{ self:get_type_i18n(), self:name() }
-       end
-end
-
-function interface.get_type_i18n(self)
-       local x = self:type()
-       if x == "wifi" then
-               return lng.translate("Wireless Adapter")
-       elseif x == "bridge" then
-               return lng.translate("Bridge")
-       elseif x == "switch" then
-               return lng.translate("Ethernet Switch")
-       elseif x == "vlan" then
-               return lng.translate("VLAN Interface")
-       elseif x == "tunnel" then
-               return lng.translate("Tunnel Interface")
-       else
-               return lng.translate("Ethernet Adapter")
-       end
-end
-
-function interface.adminlink(self)
-       if self.wif then
-               return self.wif:adminlink()
-       end
-end
-
-function interface.ports(self)
-       local members = self:_ubus("bridge-members")
-       if members then
-               local _, iface
-               local ifaces = { }
-               for _, iface in ipairs(members) do
-                       ifaces[#ifaces+1] = interface(iface)
-               end
-       end
-end
-
-function interface.bridge_id(self)
-       if self.br then
-               return self.br.id
-       else
-               return nil
-       end
-end
-
-function interface.bridge_stp(self)
-       if self.br then
-               return self.br.stp
-       else
-               return false
-       end
-end
-
-function interface.is_up(self)
-       return self:_ubus("up") or false
-end
-
-function interface.is_bridge(self)
-       return (self:type() == "bridge")
-end
-
-function interface.is_bridgeport(self)
-       return self.dev and self.dev.bridge and true or false
-end
-
-function interface.tx_bytes(self)
-       local stat = self:_ubus("statistics")
-       return stat and stat.tx_bytes or 0
-end
-
-function interface.rx_bytes(self)
-       local stat = self:_ubus("statistics")
-       return stat and stat.rx_bytes or 0
-end
-
-function interface.tx_packets(self)
-       local stat = self:_ubus("statistics")
-       return stat and stat.tx_packets or 0
-end
-
-function interface.rx_packets(self)
-       local stat = self:_ubus("statistics")
-       return stat and stat.rx_packets or 0
-end
-
-function interface.get_network(self)
-       return self:get_networks()[1]
-end
-
-function interface.get_networks(self)
-       if not self.networks then
-               local nets = { }
-               local _, net
-               for _, net in ipairs(_M:get_networks()) do
-                       if net:contains_interface(self.ifname) or
-                          net:ifname() == self.ifname
-                       then
-                               nets[#nets+1] = net
-                       end
-               end
-               table.sort(nets, function(a, b) return a.sid < b.sid end)
-               self.networks = nets
-               return nets
-       else
-               return self.networks
-       end
-end
-
-function interface.get_wifinet(self)
-       return self.wif
-end
-
-
-wifidev = utl.class()
-
-function wifidev.__init__(self, dev)
-       self.sid    = dev
-       self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
-end
-
-function wifidev.get(self, opt)
-       return _get("wireless", self.sid, opt)
-end
-
-function wifidev.set(self, opt, val)
-       return _set("wireless", self.sid, opt, val)
-end
-
-function wifidev.name(self)
-       return self.sid
-end
-
-function wifidev.hwmodes(self)
-       local l = self.iwinfo.hwmodelist
-       if l and next(l) then
-               return l
-       else
-               return { b = true, g = true }
-       end
-end
-
-function wifidev.get_i18n(self)
-       local t = "Generic"
-       if self.iwinfo.type == "wl" then
-               t = "Broadcom"
-       elseif self.iwinfo.type == "madwifi" then
-               t = "Atheros"
-       end
-
-       local m = ""
-       local l = self:hwmodes()
-       if l.a then m = m .. "a" end
-       if l.b then m = m .. "b" end
-       if l.g then m = m .. "g" end
-       if l.n then m = m .. "n" end
-
-       return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
-end
-
-function wifidev.is_up(self)
-       if _ubuswificache[self.sid] then
-               return (_ubuswificache[self.sid].up == true)
-       end
-
-       local up = false
-       _uci_state:foreach("wireless", "wifi-iface",
-               function(s)
-                       if s.device == self.sid then
-                               if s.up == "1" then
-                                       up = true
-                                       return false
-                               end
-                       end
-               end)
-
-       return up
-end
-
-function wifidev.get_wifinet(self, net)
-       if _uci_real:get("wireless", net) == "wifi-iface" then
-               return wifinet(net)
-       else
-               local wnet = _wifi_lookup(net)
-               if wnet then
-                       return wifinet(wnet)
-               end
-       end
-end
-
-function wifidev.get_wifinets(self)
-       local nets = { }
-
-       _uci_real:foreach("wireless", "wifi-iface",
-               function(s)
-                       if s.device == self.sid then
-                               nets[#nets+1] = wifinet(s['.name'])
-                       end
-               end)
-
-       return nets
-end
-
-function wifidev.add_wifinet(self, options)
-       options = options or { }
-       options.device = self.sid
-
-       local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
-       if wnet then
-               return wifinet(wnet, options)
-       end
-end
-
-function wifidev.del_wifinet(self, net)
-       if utl.instanceof(net, wifinet) then
-               net = net.sid
-       elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
-               net = _wifi_lookup(net)
-       end
-
-       if net and _uci_real:get("wireless", net, "device") == self.sid then
-               _uci_real:delete("wireless", net)
-               return true
-       end
-
-       return false
-end
-
-
-wifinet = utl.class()
-
-function wifinet.__init__(self, net, data)
-       self.sid = net
-
-       local num = { }
-       local netid
-       _uci_real:foreach("wireless", "wifi-iface",
-               function(s)
-                       if s.device then
-                               num[s.device] = num[s.device] and num[s.device] + 1 or 1
-                               if s['.name'] == self.sid then
-                                       netid = "%s.network%d" %{ s.device, num[s.device] }
-                                       return false
-                               end
-                       end
-               end)
-
-       local dev = _wifi_state("section", self.sid, "ifname") or netid
-
-       self.netid  = netid
-       self.wdev   = dev
-       self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
-       self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
-               _uci_real:get_all("wireless", self.sid) or { }
-end
-
-function wifinet.get(self, opt)
-       return _get("wireless", self.sid, opt)
-end
-
-function wifinet.set(self, opt, val)
-       return _set("wireless", self.sid, opt, val)
-end
-
-function wifinet.mode(self)
-       return _uci_state:get("wireless", self.sid, "mode") or "ap"
-end
-
-function wifinet.ssid(self)
-       return _uci_state:get("wireless", self.sid, "ssid")
-end
-
-function wifinet.bssid(self)
-       return _uci_state:get("wireless", self.sid, "bssid")
-end
-
-function wifinet.network(self)
-       return _uci_state:get("wifinet", self.sid, "network")
-end
-
-function wifinet.id(self)
-       return self.netid
-end
-
-function wifinet.name(self)
-       return self.sid
-end
-
-function wifinet.ifname(self)
-       local ifname = self.iwinfo.ifname
-       if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
-               ifname = self.wdev
-       end
-       return ifname
-end
-
-function wifinet.get_device(self)
-       if self.iwdata.device then
-               return wifidev(self.iwdata.device)
-       end
-end
-
-function wifinet.is_up(self)
-       local ifc = self:get_interface()
-       return (ifc and ifc:is_up() or false)
-end
-
-function wifinet.active_mode(self)
-       local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
-
-       if     m == "ap"      then m = "Master"
-       elseif m == "sta"     then m = "Client"
-       elseif m == "adhoc"   then m = "Ad-Hoc"
-       elseif m == "mesh"    then m = "Mesh"
-       elseif m == "monitor" then m = "Monitor"
-       end
-
-       return m
-end
-
-function wifinet.active_mode_i18n(self)
-       return lng.translate(self:active_mode())
-end
-
-function wifinet.active_ssid(self)
-       return _stror(self.iwinfo.ssid, self.iwdata.ssid)
-end
-
-function wifinet.active_bssid(self)
-       return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
-end
-
-function wifinet.active_encryption(self)
-       local enc = self.iwinfo and self.iwinfo.encryption
-       return enc and enc.description or "-"
-end
-
-function wifinet.assoclist(self)
-       return self.iwinfo.assoclist or { }
-end
-
-function wifinet.frequency(self)
-       local freq = self.iwinfo.frequency
-       if freq and freq > 0 then
-               return "%.03f" % (freq / 1000)
-       end
-end
-
-function wifinet.bitrate(self)
-       local rate = self.iwinfo.bitrate
-       if rate and rate > 0 then
-               return (rate / 1000)
-       end
-end
-
-function wifinet.channel(self)
-       return self.iwinfo.channel or
-               tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
-end
-
-function wifinet.signal(self)
-       return self.iwinfo.signal or 0
-end
-
-function wifinet.noise(self)
-       return self.iwinfo.noise or 0
-end
-
-function wifinet.country(self)
-       return self.iwinfo.country or "00"
-end
-
-function wifinet.txpower(self)
-       local pwr = (self.iwinfo.txpower or 0)
-       return pwr + self:txpower_offset()
-end
-
-function wifinet.txpower_offset(self)
-       return self.iwinfo.txpower_offset or 0
-end
-
-function wifinet.signal_level(self, s, n)
-       if self:active_bssid() ~= "00:00:00:00:00:00" then
-               local signal = s or self:signal()
-               local noise  = n or self:noise()
-
-               if signal < 0 and noise < 0 then
-                       local snr = -1 * (noise - signal)
-                       return math.floor(snr / 5)
-               else
-                       return 0
-               end
-       else
-               return -1
-       end
-end
-
-function wifinet.signal_percent(self)
-       local qc = self.iwinfo.quality or 0
-       local qm = self.iwinfo.quality_max or 0
-
-       if qc > 0 and qm > 0 then
-               return math.floor((100 / qm) * qc)
-       else
-               return 0
-       end
-end
-
-function wifinet.shortname(self)
-       return "%s %q" %{
-               lng.translate(self:active_mode()),
-               self:active_ssid() or self:active_bssid()
-       }
-end
-
-function wifinet.get_i18n(self)
-       return "%s: %s %q (%s)" %{
-               lng.translate("Wireless Network"),
-               lng.translate(self:active_mode()),
-               self:active_ssid() or self:active_bssid(),
-               self:ifname()
-       }
-end
-
-function wifinet.adminlink(self)
-       return dsp.build_url("admin", "network", "wireless", self.netid)
-end
-
-function wifinet.get_network(self)
-       return self:get_networks()[1]
-end
-
-function wifinet.get_networks(self)
-       local nets = { }
-       local net
-       for net in utl.imatch(tostring(self.iwdata.network)) do
-               if _uci_real:get("network", net) == "interface" then
-                       nets[#nets+1] = network(net)
-               end
-       end
-       table.sort(nets, function(a, b) return a.sid < b.sid end)
-       return nets
-end
-
-function wifinet.get_interface(self)
-       return interface(self:ifname())
-end
-
-
--- setup base protocols
-_M:register_protocol("static")
-_M:register_protocol("dhcp")
-_M:register_protocol("none")
-
--- load protocol extensions
-local exts = nfs.dir(utl.libpath() .. "/model/network")
-if exts then
-       local ext
-       for ext in exts do
-               if ext:match("%.lua$") then
-                       require("luci.model.network." .. ext:gsub("%.lua$", ""))
-               end
-       end
-end
diff --git a/libs/core/luasrc/model/uci.lua b/libs/core/luasrc/model/uci.lua
deleted file mode 100644 (file)
index a394563..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
---[[
-LuCI - UCI model
-
-Description:
-Generalized UCI model
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-local os    = require "os"
-local uci   = require "uci"
-local util  = require "luci.util"
-local table = require "table"
-
-
-local setmetatable, rawget, rawset = setmetatable, rawget, rawset
-local require, getmetatable = require, getmetatable
-local error, pairs, ipairs = error, pairs, ipairs
-local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
-
---- LuCI UCI model library.
--- The typical workflow for UCI is:  Get a cursor instance from the
--- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
--- save the changes to the staging area via Cursor.save and finally
--- Cursor.commit the data to the actual config files.
--- LuCI then needs to Cursor.apply the changes so deamons etc. are
--- reloaded.
--- @cstyle     instance
-module "luci.model.uci"
-
---- Create a new UCI-Cursor.
--- @class function
--- @name cursor
--- @return     UCI-Cursor
-cursor = uci.cursor
-
-APIVERSION = uci.APIVERSION
-
---- Create a new Cursor initialized to the state directory.
--- @return UCI cursor
-function cursor_state()
-       return cursor(nil, "/var/state")
-end
-
-
-inst = cursor()
-inst_state = cursor_state()
-
-local Cursor = getmetatable(inst)
-
---- Applies UCI configuration changes
--- @param configlist           List of UCI configurations
--- @param command                      Don't apply only return the command
-function Cursor.apply(self, configlist, command)
-       configlist = self:_affected(configlist)
-       if command then
-               return { "/sbin/luci-reload", unpack(configlist) }
-       else
-               return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
-                       % table.concat(configlist, " "))
-       end
-end
-
-
---- Delete all sections of a given type that match certain criteria.
--- @param config               UCI config
--- @param type                 UCI section type
--- @param comparator   Function that will be called for each section and
--- returns a boolean whether to delete the current section (optional)
-function Cursor.delete_all(self, config, stype, comparator)
-       local del = {}
-
-       if type(comparator) == "table" then
-               local tbl = comparator
-               comparator = function(section)
-                       for k, v in pairs(tbl) do
-                               if section[k] ~= v then
-                                       return false
-                               end
-                       end
-                       return true
-               end
-       end
-
-       local function helper (section)
-
-               if not comparator or comparator(section) then
-                       del[#del+1] = section[".name"]
-               end
-       end
-
-       self:foreach(config, stype, helper)
-
-       for i, j in ipairs(del) do
-               self:delete(config, j)
-       end
-end
-
---- Create a new section and initialize it with data.
--- @param config       UCI config
--- @param type         UCI section type
--- @param name         UCI section name (optional)
--- @param values       Table of key - value pairs to initialize the section with
--- @return                     Name of created section
-function Cursor.section(self, config, type, name, values)
-       local stat = true
-       if name then
-               stat = self:set(config, name, type)
-       else
-               name = self:add(config, type)
-               stat = name and true
-       end
-
-       if stat and values then
-               stat = self:tset(config, name, values)
-       end
-
-       return stat and name
-end
-
---- Updated the data of a section using data from a table.
--- @param config       UCI config
--- @param section      UCI section name (optional)
--- @param values       Table of key - value pairs to update the section with
-function Cursor.tset(self, config, section, values)
-       local stat = true
-       for k, v in pairs(values) do
-               if k:sub(1, 1) ~= "." then
-                       stat = stat and self:set(config, section, k, v)
-               end
-       end
-       return stat
-end
-
---- Get a boolean option and return it's value as true or false.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @return                     Boolean
-function Cursor.get_bool(self, ...)
-       local val = self:get(...)
-       return ( val == "1" or val == "true" or val == "yes" or val == "on" )
-end
-
---- Get an option or list and return values as table.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @return                     UCI value
-function Cursor.get_list(self, config, section, option)
-       if config and section and option then
-               local val = self:get(config, section, option)
-               return ( type(val) == "table" and val or { val } )
-       end
-       return nil
-end
-
---- Get the given option from the first section with the given type.
--- @param config       UCI config
--- @param type         UCI section type
--- @param option       UCI option (optional)
--- @param default      Default value (optional)
--- @return                     UCI value
-function Cursor.get_first(self, conf, stype, opt, def)
-       local rv = def
-
-       self:foreach(conf, stype,
-               function(s)
-                       local val = not opt and s['.name'] or s[opt]
-
-                       if type(def) == "number" then
-                               val = tonumber(val)
-                       elseif type(def) == "boolean" then
-                               val = (val == "1" or val == "true" or
-                                      val == "yes" or val == "on")
-                       end
-
-                       if val ~= nil then
-                               rv = val
-                               return false
-                       end
-               end)
-
-       return rv
-end
-
---- Set given values as list.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @param value                UCI value
--- @return                     Boolean whether operation succeeded
-function Cursor.set_list(self, config, section, option, value)
-       if config and section and option then
-               return self:set(
-                       config, section, option,
-                       ( type(value) == "table" and value or { value } )
-               )
-       end
-       return false
-end
-
--- Return a list of initscripts affected by configuration changes.
-function Cursor._affected(self, configlist)
-       configlist = type(configlist) == "table" and configlist or {configlist}
-
-       local c = cursor()
-       c:load("ucitrack")
-
-       -- Resolve dependencies
-       local reloadlist = {}
-
-       local function _resolve_deps(name)
-               local reload = {name}
-               local deps = {}
-
-               c:foreach("ucitrack", name,
-                       function(section)
-                               if section.affects then
-                                       for i, aff in ipairs(section.affects) do
-                                               deps[#deps+1] = aff
-                                       end
-                               end
-                       end)
-
-               for i, dep in ipairs(deps) do
-                       for j, add in ipairs(_resolve_deps(dep)) do
-                               reload[#reload+1] = add
-                       end
-               end
-
-               return reload
-       end
-
-       -- Collect initscripts
-       for j, config in ipairs(configlist) do
-               for i, e in ipairs(_resolve_deps(config)) do
-                       if not util.contains(reloadlist, e) then
-                               reloadlist[#reloadlist+1] = e
-                       end
-               end
-       end
-
-       return reloadlist
-end
-
---- Create a sub-state of this cursor. The sub-state is tied to the parent
--- curser, means it the parent unloads or loads configs, the sub state will
--- do so as well.
--- @return                     UCI state cursor tied to the parent cursor
-function Cursor.substate(self)
-       Cursor._substates = Cursor._substates or { }
-       Cursor._substates[self] = Cursor._substates[self] or cursor_state()
-       return Cursor._substates[self]
-end
-
-local _load = Cursor.load
-function Cursor.load(self, ...)
-       if Cursor._substates and Cursor._substates[self] then
-               _load(Cursor._substates[self], ...)
-       end
-       return _load(self, ...)
-end
-
-local _unload = Cursor.unload
-function Cursor.unload(self, ...)
-       if Cursor._substates and Cursor._substates[self] then
-               _unload(Cursor._substates[self], ...)
-       end
-       return _unload(self, ...)
-end
-
-
---- Add an anonymous section.
--- @class function
--- @name Cursor.add
--- @param config       UCI config
--- @param type         UCI section type
--- @return                     Name of created section
-
---- Get a table of saved but uncommitted changes.
--- @class function
--- @name Cursor.changes
--- @param config       UCI config
--- @return                     Table of changes
--- @see Cursor.save
-
---- Commit saved changes.
--- @class function
--- @name Cursor.commit
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.revert
--- @see Cursor.save
-
---- Deletes a section or an option.
--- @class function
--- @name Cursor.delete
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option (optional)
--- @return                     Boolean whether operation succeeded
-
---- Call a function for every section of a certain type.
--- @class function
--- @name Cursor.foreach
--- @param config       UCI config
--- @param type         UCI section type
--- @param callback     Function to be called
--- @return                     Boolean whether operation succeeded
-
---- Get a section type or an option
--- @class function
--- @name Cursor.get
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option (optional)
--- @return                     UCI value
-
---- Get all sections of a config or all values of a section.
--- @class function
--- @name Cursor.get_all
--- @param config       UCI config
--- @param section      UCI section name (optional)
--- @return                     Table of UCI sections or table of UCI values
-
---- Manually load a config.
--- @class function
--- @name Cursor.load
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.save
--- @see Cursor.unload
-
---- Revert saved but uncommitted changes.
--- @class function
--- @name Cursor.revert
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.commit
--- @see Cursor.save
-
---- Saves changes made to a config to make them committable.
--- @class function
--- @name Cursor.save
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.load
--- @see Cursor.unload
-
---- Set a value or create a named section.
--- @class function
--- @name Cursor.set
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option or UCI section type
--- @param value                UCI value or nil if you want to create a section
--- @return                     Boolean whether operation succeeded
-
---- Get the configuration directory.
--- @class function
--- @name Cursor.get_confdir
--- @return                     Configuration directory
-
---- Get the directory for uncomitted changes.
--- @class function
--- @name Cursor.get_savedir
--- @return                     Save directory
-
---- Set the configuration directory.
--- @class function
--- @name Cursor.set_confdir
--- @param directory    UCI configuration directory
--- @return                     Boolean whether operation succeeded
-
---- Set the directory for uncommited changes.
--- @class function
--- @name Cursor.set_savedir
--- @param directory    UCI changes directory
--- @return                     Boolean whether operation succeeded
-
---- Discard changes made to a config.
--- @class function
--- @name Cursor.unload
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.load
--- @see Cursor.save
diff --git a/libs/core/luasrc/store.lua b/libs/core/luasrc/store.lua
deleted file mode 100644 (file)
index c33ef07..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
---[[
-
-LuCI - Lua Development Framework
-(c) 2009 Steven Barth <steven@midlink.org>
-(c) 2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-local util = require "luci.util"
-module("luci.store", util.threadlocal)
\ No newline at end of file
diff --git a/libs/core/luasrc/util.lua b/libs/core/luasrc/util.lua
deleted file mode 100644 (file)
index da761e2..0000000
+++ /dev/null
@@ -1,791 +0,0 @@
---[[
-LuCI - Utility library
-
-Description:
-Several common useful Lua functions
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local io = require "io"
-local math = require "math"
-local table = require "table"
-local debug = require "debug"
-local ldebug = require "luci.debug"
-local string = require "string"
-local coroutine = require "coroutine"
-local tparser = require "luci.template.parser"
-
-local getmetatable, setmetatable = getmetatable, setmetatable
-local rawget, rawset, unpack = rawget, rawset, unpack
-local tostring, type, assert = tostring, type, assert
-local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
-local require, pcall, xpcall = require, pcall, xpcall
-local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
-
---- LuCI utility functions.
-module "luci.util"
-
---
--- Pythonic string formatting extension
---
-getmetatable("").__mod = function(a, b)
-       if not b then
-               return a
-       elseif type(b) == "table" then
-               for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
-               return a:format(unpack(b))
-       else
-               if type(b) == "userdata" then b = tostring(b) end
-               return a:format(b)
-       end
-end
-
-
---
--- Class helper routines
---
-
--- Instantiates a class
-local function _instantiate(class, ...)
-       local inst = setmetatable({}, {__index = class})
-
-       if inst.__init__ then
-               inst:__init__(...)
-       end
-
-       return inst
-end
-
---- Create a Class object (Python-style object model).
--- The class object can be instantiated by calling itself.
--- Any class functions or shared parameters can be attached to this object.
--- Attaching a table to the class object makes this table shared between
--- all instances of this class. For object parameters use the __init__ function.
--- Classes can inherit member functions and values from a base class.
--- Class can be instantiated by calling them. All parameters will be passed
--- to the __init__ function of this class - if such a function exists.
--- The __init__ function must be used to set any object parameters that are not shared
--- with other objects of this class. Any return values will be ignored.
--- @param base The base class to inherit from (optional)
--- @return             A class object
--- @see                        instanceof
--- @see                        clone
-function class(base)
-       return setmetatable({}, {
-               __call  = _instantiate,
-               __index = base
-       })
-end
-
---- Test whether the given object is an instance of the given class.
--- @param object       Object instance
--- @param class                Class object to test against
--- @return                     Boolean indicating whether the object is an instance
--- @see                                class
--- @see                                clone
-function instanceof(object, class)
-       local meta = getmetatable(object)
-       while meta and meta.__index do
-               if meta.__index == class then
-                       return true
-               end
-               meta = getmetatable(meta.__index)
-       end
-       return false
-end
-
-
---
--- Scope manipulation routines
---
-
-local tl_meta = {
-       __mode = "k",
-
-       __index = function(self, key)
-               local t = rawget(self, coxpt[coroutine.running()]
-                or coroutine.running() or 0)
-               return t and t[key]
-       end,
-
-       __newindex = function(self, key, value)
-               local c = coxpt[coroutine.running()] or coroutine.running() or 0
-               if not rawget(self, c) then
-                       rawset(self, c, { [key] = value })
-               else
-                       rawget(self, c)[key] = value
-               end
-       end
-}
-
---- Create a new or get an already existing thread local store associated with
--- the current active coroutine. A thread local store is private a table object
--- whose values can't be accessed from outside of the running coroutine.
--- @return     Table value representing the corresponding thread local store
-function threadlocal(tbl)
-       return setmetatable(tbl or {}, tl_meta)
-end
-
-
---
--- Debugging routines
---
-
---- Write given object to stderr.
--- @param obj  Value to write to stderr
--- @return             Boolean indicating whether the write operation was successful
-function perror(obj)
-       return io.stderr:write(tostring(obj) .. "\n")
-end
-
---- Recursively dumps a table to stdout, useful for testing and debugging.
--- @param t    Table value to dump
--- @param maxdepth     Maximum depth
--- @return     Always nil
-function dumptable(t, maxdepth, i, seen)
-       i = i or 0
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       for k,v in pairs(t) do
-               perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
-               if type(v) == "table" and (not maxdepth or i < maxdepth) then
-                       if not seen[v] then
-                               seen[v] = true
-                               dumptable(v, maxdepth, i+1, seen)
-                       else
-                               perror(string.rep("\t", i) .. "*** RECURSION ***")
-                       end
-               end
-       end
-end
-
-
---
--- String and data manipulation routines
---
-
---- Create valid XML PCDATA from given string.
--- @param value        String value containing the data to escape
--- @return             String value containing the escaped data
-function pcdata(value)
-       return value and tparser.pcdata(tostring(value))
-end
-
---- Strip HTML tags from given string.
--- @param value        String containing the HTML text
--- @return     String with HTML tags stripped of
-function striptags(value)
-       return value and tparser.striptags(tostring(value))
-end
-
---- Splits given string on a defined separator sequence and return a table
--- containing the resulting substrings. The optional max parameter specifies
--- the number of bytes to process, regardless of the actual length of the given
--- string. The optional last parameter, regex, specifies whether the separator
--- sequence is interpreted as regular expression.
--- @param str          String value containing the data to split up
--- @param pat          String with separator pattern (optional, defaults to "\n")
--- @param max          Maximum times to split (optional)
--- @param regex        Boolean indicating whether to interpret the separator
---                                     pattern as regular expression (optional, default is false)
--- @return                     Table containing the resulting substrings
-function split(str, pat, max, regex)
-       pat = pat or "\n"
-       max = max or #str
-
-       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)
-               max = max - 1
-               if s and max < 0 then
-                       t[#t+1] = str:sub(c)
-               else
-                       t[#t+1] = str:sub(c, s and s - 1)
-               end
-               c = e and e + 1 or #str + 1
-       until not s or max < 0
-
-       return t
-end
-
---- Remove leading and trailing whitespace from given string value.
--- @param str  String value containing whitespace padded data
--- @return             String value with leading and trailing space removed
-function trim(str)
-       return (str:gsub("^%s*(.-)%s*$", "%1"))
-end
-
---- Count the occurences of given substring in given string.
--- @param str          String to search in
--- @param pattern      String containing pattern to find
--- @return                     Number of found occurences
-function cmatch(str, pat)
-       local count = 0
-       for _ in str:gmatch(pat) do count = count + 1 end
-       return count
-end
-
---- Return a matching iterator for the given value. The iterator will return
--- one token per invocation, the tokens are separated by whitespace. If the
--- input value is a table, it is transformed into a string first. A nil value
--- will result in a valid interator which aborts with the first invocation.
--- @param val          The value to scan (table, string or nil)
--- @return                     Iterator which returns one token per call
-function imatch(v)
-       if type(v) == "table" then
-               local k = nil
-               return function()
-                       k = next(v, k)
-                       return v[k]
-               end
-
-       elseif type(v) == "number" or type(v) == "boolean" then
-               local x = true
-               return function()
-                       if x then
-                               x = false
-                               return tostring(v)
-                       end
-               end
-
-       elseif type(v) == "userdata" or type(v) == "string" then
-               return tostring(v):gmatch("%S+")
-       end
-
-       return function() end
-end
-
---- Parse certain units from the given string and return the canonical integer
--- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
--- Recognized units are:
---     o "y"   - one year   (60*60*24*366)
---  o "m"      - one month  (60*60*24*31)
---  o "w"      - one week   (60*60*24*7)
---  o "d"      - one day    (60*60*24)
---  o "h"      - one hour       (60*60)
---  o "min"    - one minute (60)
---  o "kb"  - one kilobyte (1024)
---  o "mb"     - one megabyte (1024*1024)
---  o "gb"     - one gigabyte (1024*1024*1024)
---  o "kib" - one si kilobyte (1000)
---  o "mib"    - one si megabyte (1000*1000)
---  o "gib"    - one si gigabyte (1000*1000*1000)
--- @param ustr String containing a numerical value with trailing unit
--- @return             Number containing the canonical value
-function parse_units(ustr)
-
-       local val = 0
-
-       -- unit map
-       local map = {
-               -- date stuff
-               y   = 60 * 60 * 24 * 366,
-               m   = 60 * 60 * 24 * 31,
-               w   = 60 * 60 * 24 * 7,
-               d   = 60 * 60 * 24,
-               h   = 60 * 60,
-               min = 60,
-
-               -- storage sizes
-               kb  = 1024,
-               mb  = 1024 * 1024,
-               gb  = 1024 * 1024 * 1024,
-
-               -- storage sizes (si)
-               kib = 1000,
-               mib = 1000 * 1000,
-               gib = 1000 * 1000 * 1000
-       }
-
-       -- parse input string
-       for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
-
-               local num = spec:gsub("[^0-9%.]+$","")
-               local spn = spec:gsub("^[0-9%.]+", "")
-
-               if map[spn] or map[spn:sub(1,1)] then
-                       val = val + num * ( map[spn] or map[spn:sub(1,1)] )
-               else
-                       val = val + num
-               end
-       end
-
-
-       return val
-end
-
--- also register functions above in the central string class for convenience
-string.pcdata      = pcdata
-string.striptags   = striptags
-string.split       = split
-string.trim        = trim
-string.cmatch      = cmatch
-string.parse_units = parse_units
-
-
---- Appends numerically indexed tables or single objects to a given table.
--- @param src  Target table
--- @param ...  Objects to insert
--- @return             Target table
-function append(src, ...)
-       for i, a in ipairs({...}) do
-               if type(a) == "table" then
-                       for j, v in ipairs(a) do
-                               src[#src+1] = v
-                       end
-               else
-                       src[#src+1] = a
-               end
-       end
-       return src
-end
-
---- Combines two or more numerically indexed tables and single objects into one table.
--- @param tbl1 Table value to combine
--- @param tbl2 Table value to combine
--- @param ...  More tables to combine
--- @return             Table value containing all values of given tables
-function combine(...)
-       return append({}, ...)
-end
-
---- Checks whether the given table contains the given value.
--- @param table        Table value
--- @param value        Value to search within the given table
--- @return             Boolean indicating whether the given value occurs within table
-function contains(table, value)
-       for k, v in pairs(table) do
-               if value == v then
-                       return k
-               end
-       end
-       return false
-end
-
---- Update values in given table with the values from the second given table.
--- Both table are - in fact - merged together.
--- @param t                    Table which should be updated
--- @param updates      Table containing the values to update
--- @return                     Always nil
-function update(t, updates)
-       for k, v in pairs(updates) do
-               t[k] = v
-       end
-end
-
---- Retrieve all keys of given associative table.
--- @param t    Table to extract keys from
--- @return     Sorted table containing the keys
-function keys(t)
-       local keys = { }
-       if t then
-               for k, _ in kspairs(t) do
-                       keys[#keys+1] = k
-               end
-       end
-       return keys
-end
-
---- Clones the given object and return it's copy.
--- @param object       Table value to clone
--- @param deep         Boolean indicating whether to do recursive cloning
--- @return                     Cloned table value
-function clone(object, deep)
-       local copy = {}
-
-       for k, v in pairs(object) do
-               if deep and type(v) == "table" then
-                       v = clone(v, deep)
-               end
-               copy[k] = v
-       end
-
-       return setmetatable(copy, getmetatable(object))
-end
-
-
---- Create a dynamic table which automatically creates subtables.
--- @return     Dynamic Table
-function dtable()
-        return setmetatable({}, { __index =
-                function(tbl, key)
-                        return rawget(tbl, key)
-                         or rawget(rawset(tbl, key, dtable()), key)
-                end
-        })
-end
-
-
--- Serialize the contents of a table value.
-function _serialize_table(t, seen)
-       assert(not seen[t], "Recursion detected.")
-       seen[t] = true
-
-       local data  = ""
-       local idata = ""
-       local ilen  = 0
-
-       for k, v in pairs(t) do
-               if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
-                       k = serialize_data(k, seen)
-                       v = serialize_data(v, seen)
-                       data = data .. ( #data > 0 and ", " or "" ) ..
-                               '[' .. k .. '] = ' .. v
-               elseif k > ilen then
-                       ilen = k
-               end
-       end
-
-       for i = 1, ilen do
-               local v = serialize_data(t[i], seen)
-               idata = idata .. ( #idata > 0 and ", " or "" ) .. v
-       end
-
-       return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
-end
-
---- Recursively serialize given data to lua code, suitable for restoring
--- with loadstring().
--- @param val  Value containing the data to serialize
--- @return             String value containing the serialized code
--- @see                        restore_data
--- @see                        get_bytecode
-function serialize_data(val, seen)
-       seen = seen or setmetatable({}, {__mode="k"})
-
-       if val == nil then
-               return "nil"
-       elseif type(val) == "number" then
-               return val
-       elseif type(val) == "string" then
-               return "%q" % val
-       elseif type(val) == "boolean" then
-               return val and "true" or "false"
-       elseif type(val) == "function" then
-               return "loadstring(%q)" % get_bytecode(val)
-       elseif type(val) == "table" then
-               return "{ " .. _serialize_table(val, seen) .. " }"
-       else
-               return '"[unhandled data type:' .. type(val) .. ']"'
-       end
-end
-
---- Restore data previously serialized with serialize_data().
--- @param str  String containing the data to restore
--- @return             Value containing the restored data structure
--- @see                        serialize_data
--- @see                        get_bytecode
-function restore_data(str)
-       return loadstring("return " .. str)()
-end
-
-
---
--- Byte code manipulation routines
---
-
---- Return the current runtime bytecode of the given data. The byte code
--- will be stripped before it is returned.
--- @param val  Value to return as bytecode
--- @return             String value containing the bytecode of the given data
-function get_bytecode(val)
-       local code
-
-       if type(val) == "function" then
-               code = string.dump(val)
-       else
-               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
-       end
-
-       return code -- and strip_bytecode(code)
-end
-
---- Strips unnescessary lua bytecode from given string. Information like line
--- numbers and debugging numbers will be discarded. Original version by
--- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
--- @param code String value containing the original lua byte code
--- @return             String value containing the stripped lua byte code
-function strip_bytecode(code)
-       local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
-       local subint
-       if endian == 1 then
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = l, 1, -1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       else
-               subint = function(code, i, l)
-                       local val = 0
-                       for n = 1, l, 1 do
-                               val = val * 256 + code:byte(i + n - 1)
-                       end
-                       return val, i + l
-               end
-       end
-
-       local function strip_function(code)
-               local count, offset = subint(code, 1, size)
-               local stripped = { string.rep("\0", size) }
-               local dirty = offset + count
-               offset = offset + count + int * 2 + 4
-               offset = offset + int + subint(code, offset, int) * ins
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       local t
-                       t, offset = subint(code, offset, 1)
-                       if t == 1 then
-                               offset = offset + 1
-                       elseif t == 4 then
-                               offset = offset + size + subint(code, offset, size)
-                       elseif t == 3 then
-                               offset = offset + num
-                       elseif t == 254 or t == 9 then
-                               offset = offset + lnum
-                       end
-               end
-               count, offset = subint(code, offset, int)
-               stripped[#stripped+1] = code:sub(dirty, offset - 1)
-               for n = 1, count do
-                       local proto, off = strip_function(code:sub(offset, -1))
-                       stripped[#stripped+1] = proto
-                       offset = offset + off - 1
-               end
-               offset = offset + subint(code, offset, int) * int + int
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size + int * 2
-               end
-               count, offset = subint(code, offset, int)
-               for n = 1, count do
-                       offset = offset + subint(code, offset, size) + size
-               end
-               stripped[#stripped+1] = string.rep("\0", int * 3)
-               return table.concat(stripped), offset
-       end
-
-       return code:sub(1,12) .. strip_function(code:sub(13,-1))
-end
-
-
---
--- Sorting iterator functions
---
-
-function _sortiter( t, f )
-       local keys = { }
-
-       local k, v
-       for k, v in pairs(t) do
-               keys[#keys+1] = k
-       end
-
-       local _pos = 0
-
-       table.sort( keys, f )
-
-       return function()
-               _pos = _pos + 1
-               if _pos <= #keys then
-                       return keys[_pos], t[keys[_pos]], _pos
-               end
-       end
-end
-
---- Return a key, value iterator which returns the values sorted according to
--- the provided callback function.
--- @param t    The table to iterate
--- @param f A callback function to decide the order of elements
--- @return     Function value containing the corresponding iterator
-function spairs(t,f)
-       return _sortiter( t, f )
-end
-
---- Return a key, value iterator for the given table.
--- The table pairs are sorted by key.
--- @param t    The table to iterate
--- @return     Function value containing the corresponding iterator
-function kspairs(t)
-       return _sortiter( t )
-end
-
---- Return a key, value iterator for the given table.
--- The table pairs are sorted by value.
--- @param t    The table to iterate
--- @return     Function value containing the corresponding iterator
-function vspairs(t)
-       return _sortiter( t, function (a,b) return t[a] < t[b] end )
-end
-
-
---
--- System utility functions
---
-
---- Test whether the current system is operating in big endian mode.
--- @return     Boolean value indicating whether system is big endian
-function bigendian()
-       return string.byte(string.dump(function() end), 7) == 0
-end
-
---- Execute given commandline and gather stdout.
--- @param command      String containing command to execute
--- @return                     String containing the command's stdout
-function exec(command)
-       local pp   = io.popen(command)
-       local data = pp:read("*a")
-       pp:close()
-
-       return data
-end
-
---- Return a line-buffered iterator over the output of given command.
--- @param command      String containing the command to execute
--- @return                     Iterator
-function execi(command)
-       local pp = io.popen(command)
-
-       return pp and function()
-               local line = pp:read()
-
-               if not line then
-                       pp:close()
-               end
-
-               return line
-       end
-end
-
--- Deprecated
-function execl(command)
-       local pp   = io.popen(command)
-       local line = ""
-       local data = {}
-
-       while true do
-               line = pp:read()
-               if (line == nil) then break end
-               data[#data+1] = line
-       end
-       pp:close()
-
-       return data
-end
-
---- Returns the absolute path to LuCI base directory.
--- @return             String containing the directory path
-function libpath()
-       return require "nixio.fs".dirname(ldebug.__file__)
-end
-
-
---
--- Coroutine safe xpcall and pcall versions modified for Luci
--- original version:
--- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
---
--- Copyright Â© 2005 Kepler Project.
--- 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.
-
-local performResume, handleReturnValue
-local oldpcall, oldxpcall = pcall, xpcall
-coxpt = {}
-setmetatable(coxpt, {__mode = "kv"})
-
--- Identity function for copcall
-local function copcall_id(trace, ...)
-  return ...
-end
-
---- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
--- @param f            Lua function to be called protected
--- @param err  Custom error handler
--- @param ...  Parameters passed to the function
--- @return             A boolean whether the function call succeeded and the return
---                             values of either the function or the error handler
-function coxpcall(f, err, ...)
-       local res, co = oldpcall(coroutine.create, f)
-       if not res then
-               local params = {...}
-               local newf = function() return f(unpack(params)) end
-               co = coroutine.create(newf)
-       end
-       local c = coroutine.running()
-       coxpt[co] = coxpt[c] or c or 0
-
-       return performResume(err, co, ...)
-end
-
---- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
--- @param f            Lua function to be called protected
--- @param ...  Parameters passed to the function
--- @return             A boolean whether the function call succeeded and the returns
---                             values of the function or the error object
-function copcall(f, ...)
-       return coxpcall(f, copcall_id, ...)
-end
-
--- Handle return value of protected call
-function handleReturnValue(err, co, status, ...)
-       if not status then
-               return false, err(debug.traceback(co, (...)), ...)
-       end
-
-       if coroutine.status(co) ~= 'suspended' then
-               return true, ...
-       end
-
-       return performResume(err, co, coroutine.yield(...))
-end
-
--- Resume execution of protected function call
-function performResume(err, co, ...)
-       return handleReturnValue(err, co, coroutine.resume(co, ...))
-end
diff --git a/libs/core/luasrc/version.lua b/libs/core/luasrc/version.lua
deleted file mode 100644 (file)
index 9e5cb71..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-Version definition - do not edit this file
-]]--
-
-module "luci.version"
-
-distname    = "Host System"
-distversion = "SDK"
-
-luciname    = "LuCI"
-luciversion = "SVN"
diff --git a/libs/core/root/etc/config/ucitrack b/libs/core/root/etc/config/ucitrack
deleted file mode 100644 (file)
index 04467f4..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-config network
-       option init network
-       list affects dhcp
-       list affects radvd
-
-config wireless
-       list affects network
-
-config firewall
-       option init firewall
-       list affects luci-splash
-       list affects qos
-       list affects miniupnpd
-
-config olsr
-       option init olsrd
-
-config dhcp
-       option init dnsmasq
-
-config dropbear
-       option init dropbear
-
-config httpd
-       option init httpd
-
-config fstab
-       option init fstab
-
-config qos
-       option init qos
-
-config system
-       option init led
-       list affects luci_statistics
-
-config luci_splash
-       option init luci_splash
-
-config upnpd
-       option init miniupnpd
-
-config ntpclient
-       option init ntpclient
-
-config samba
-       option init samba
-
-config tinyproxy
-       option init tinyproxy
-
-config 6relayd
-       option init 6relayd
diff --git a/libs/core/root/sbin/luci-reload b/libs/core/root/sbin/luci-reload
deleted file mode 100755 (executable)
index cc41da2..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/sh
-. /lib/functions.sh
-
-apply_config() {
-       config_get init "$1" init
-       config_get exec "$1" exec
-       config_get test "$1" test
-
-       echo "$2" > "/var/run/luci-reload-status"
-
-       [ -n "$init" ] && reload_init "$2" "$init" "$test"
-       [ -n "$exec" ] && reload_exec "$2" "$exec" "$test"
-}
-
-reload_exec() {
-       local service="$1"
-       local ok="$3"
-       set -- $2
-       local cmd="$1"; shift
-       
-       [ -x "$cmd" ] && {
-               echo "Reloading $service... "
-               ( $cmd "$@" ) 2>/dev/null 1>&2
-               [ -n "$ok" -a "$?" != "$ok" ] && echo '!!! Failed to reload' $service '!!!'
-       }
-}
-
-reload_init() {
-       [ -x /etc/init.d/$2 ] && /etc/init.d/$2 enabled && {
-               echo "Reloading $1... "
-               /etc/init.d/$2 reload >/dev/null 2>&1
-               [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!'
-       }
-}
-
-lock "/var/run/luci-reload"
-
-config_load ucitrack
-
-for i in $*; do
-       config_foreach apply_config $i $i
-done
-
-rm -f "/var/run/luci-reload-status"
-lock -u "/var/run/luci-reload"
diff --git a/libs/ipkg/Makefile b/libs/ipkg/Makefile
deleted file mode 100644 (file)
index f7fac77..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
diff --git a/libs/ipkg/luasrc/model/ipkg.lua b/libs/ipkg/luasrc/model/ipkg.lua
deleted file mode 100644 (file)
index c927e71..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-(c) 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
-(c) 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-local os   = require "os"
-local io   = require "io"
-local fs   = require "nixio.fs"
-local util = require "luci.util"
-
-local type  = type
-local pairs = pairs
-local error = error
-local table = table
-
-local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
-local icfg = "/etc/opkg.conf"
-
---- LuCI OPKG call abstraction library
-module "luci.model.ipkg"
-
-
--- Internal action function
-local function _action(cmd, ...)
-       local pkg = ""
-       for k, v in pairs({...}) do
-               pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
-       end
-
-       local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg }
-       local r = os.execute(c)
-       local e = fs.readfile("/tmp/opkg.stderr")
-       local o = fs.readfile("/tmp/opkg.stdout")
-
-       fs.unlink("/tmp/opkg.stderr")
-       fs.unlink("/tmp/opkg.stdout")
-
-       return r, o or "", e or ""
-end
-
--- Internal parser function
-local function _parselist(rawdata)
-       if type(rawdata) ~= "function" then
-               error("OPKG: Invalid rawdata given")
-       end
-
-       local data = {}
-       local c = {}
-       local l = nil
-
-       for line in rawdata do
-               if line:sub(1, 1) ~= " " then
-                       local key, val = line:match("(.-): ?(.*)%s*")
-
-                       if key and val then
-                               if key == "Package" then
-                                       c = {Package = val}
-                                       data[val] = c
-                               elseif key == "Status" then
-                                       c.Status = {}
-                                       for j in val:gmatch("([^ ]+)") do
-                                               c.Status[j] = true
-                                       end
-                               else
-                                       c[key] = val
-                               end
-                               l = key
-                       end
-               else
-                       -- Multi-line field
-                       c[l] = c[l] .. "\n" .. line
-               end
-       end
-
-       return data
-end
-
--- Internal lookup function
-local function _lookup(act, pkg)
-       local cmd = ipkg .. " " .. act
-       if pkg then
-               cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'"
-       end
-
-       -- OPKG sometimes kills the whole machine because it sucks
-       -- Therefore we have to use a sucky approach too and use
-       -- tmpfiles instead of directly reading the output
-       local tmpfile = os.tmpname()
-       os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile))
-
-       local data = _parselist(io.lines(tmpfile))
-       os.remove(tmpfile)
-       return data
-end
-
-
---- Return information about installed and available packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package information
-function info(pkg)
-       return _lookup("info", pkg)
-end
-
---- Return the package status of one or more packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package status information
-function status(pkg)
-       return _lookup("status", pkg)
-end
-
---- Install one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
-function install(...)
-       return _action("install", ...)
-end
-
---- Determine whether a given package is installed.
--- @param pkg Package
--- @return Boolean
-function installed(pkg)
-       local p = status(pkg)[pkg]
-       return (p and p.Status and p.Status.installed)
-end
-
---- Remove one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
-function remove(...)
-       return _action("remove", ...)
-end
-
---- Update package lists.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
-function update()
-       return _action("update")
-end
-
---- Upgrades all installed packages.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
-function upgrade()
-       return _action("upgrade")
-end
-
--- List helper
-function _list(action, pat, cb)
-       local fd = io.popen(ipkg .. " " .. action ..
-               (pat and (" '%s'" % pat:gsub("'", "")) or ""))
-
-       if fd then
-               local name, version, desc
-               while true do
-                       local line = fd:read("*l")
-                       if not line then break end
-
-                       name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
-
-                       if not name then
-                               name, version = line:match("^(.-) %- (.+)")
-                               desc = ""
-                       end
-
-                       cb(name, version, desc)
-
-                       name    = nil
-                       version = nil
-                       desc    = nil
-               end
-
-               fd:close()
-       end
-end
-
---- List all packages known to opkg.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
-function list_all(pat, cb)
-       _list("list", pat, cb)
-end
-
---- List installed packages.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
-function list_installed(pat, cb)
-       _list("list_installed", pat, cb)
-end
-
---- Find packages that match the given pattern.
--- @param pat  Find packages whose names or descriptions match this pattern, nil results in zero results
--- @param cb   Callback function invoked for each patckage, receives name, version and description as arguments
--- @return     nothing
-function find(pat, cb)
-       _list("find", pat, cb)
-end
-
-
---- Determines the overlay root used by opkg.
--- @return             String containing the directory path of the overlay root.
-function overlay_root()
-       local od = "/"
-       local fd = io.open(icfg, "r")
-
-       if fd then
-               local ln
-
-               repeat
-                       ln = fd:read("*l")
-                       if ln and ln:match("^%s*option%s+overlay_root%s+") then
-                               od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
-
-                               local s = fs.stat(od)
-                               if not s or s.type ~= "dir" then
-                                       od = "/"
-                               end
-
-                               break
-                       end
-               until not ln
-
-               fd:close()
-       end
-
-       return od
-end
diff --git a/libs/sgi-cgi/Makefile b/libs/sgi-cgi/Makefile
deleted file mode 100644 (file)
index 81a96f6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
\ No newline at end of file
diff --git a/libs/sgi-cgi/htdocs/cgi-bin/luci b/libs/sgi-cgi/htdocs/cgi-bin/luci
deleted file mode 100755 (executable)
index 529d1d0..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/lua
-require "luci.cacheloader"
-require "luci.sgi.cgi"
-luci.dispatcher.indexcache = "/tmp/luci-indexcache"
-luci.sgi.cgi.run()
\ No newline at end of file
diff --git a/libs/sgi-cgi/luasrc/sgi/cgi.lua b/libs/sgi-cgi/luasrc/sgi/cgi.lua
deleted file mode 100644 (file)
index 04ae9aa..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
---[[
-LuCI - SGI-Module for CGI
-
-Description:
-Server Gateway Interface for CGI
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at 
-
-       http://www.apache.org/licenses/LICENSE-2.0 
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-exectime = os.clock()
-module("luci.sgi.cgi", package.seeall)
-local ltn12 = require("luci.ltn12")
-require("nixio.util")
-require("luci.http")
-require("luci.sys")
-require("luci.dispatcher")
-
--- Limited source to avoid endless blocking
-local function limitsource(handle, limit)
-       limit = limit or 0
-       local BLOCKSIZE = ltn12.BLOCKSIZE
-
-       return function()
-               if limit < 1 then
-                       handle:close()
-                       return nil
-               else
-                       local read = (limit > BLOCKSIZE) and BLOCKSIZE or limit
-                       limit = limit - read
-
-                       local chunk = handle:read(read)
-                       if not chunk then handle:close() end
-                       return chunk
-               end
-       end
-end
-
-function run()
-       local r = luci.http.Request(
-               luci.sys.getenv(),
-               limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
-               ltn12.sink.file(io.stderr)
-       )
-       
-       local x = coroutine.create(luci.dispatcher.httpdispatch)
-       local hcache = ""
-       local active = true
-       
-       while coroutine.status(x) ~= "dead" do
-               local res, id, data1, data2 = coroutine.resume(x, r)
-
-               if not res then
-                       print("Status: 500 Internal Server Error")
-                       print("Content-Type: text/plain\n")
-                       print(id)
-                       break;
-               end
-
-               if active then
-                       if id == 1 then
-                               io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
-                       elseif id == 2 then
-                               hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
-                       elseif id == 3 then
-                               io.write(hcache)
-                               io.write("\r\n")
-                       elseif id == 4 then
-                               io.write(tostring(data1 or ""))
-                       elseif id == 5 then
-                               io.flush()
-                               io.close()
-                               active = false
-                       elseif id == 6 then
-                               data1:copyz(nixio.stdout, data2)
-                               data1:close()
-                       end
-               end
-       end
-end
diff --git a/libs/sgi-uhttpd/Makefile b/libs/sgi-uhttpd/Makefile
deleted file mode 100644 (file)
index 81a96f6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
\ No newline at end of file
diff --git a/libs/sgi-uhttpd/luasrc/sgi/uhttpd.lua b/libs/sgi-uhttpd/luasrc/sgi/uhttpd.lua
deleted file mode 100644 (file)
index db8780f..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
---[[
-LuCI - Server Gateway Interface for the uHTTPd server
-
-Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at 
-
-       http://www.apache.org/licenses/LICENSE-2.0 
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-require "nixio.util"
-require "luci.http"
-require "luci.sys"
-require "luci.dispatcher"
-require "luci.ltn12"
-
-function handle_request(env)
-       exectime = os.clock()
-       local renv = {
-               CONTENT_LENGTH  = env.CONTENT_LENGTH,
-               CONTENT_TYPE    = env.CONTENT_TYPE,
-               REQUEST_METHOD  = env.REQUEST_METHOD,
-               REQUEST_URI     = env.REQUEST_URI,
-               PATH_INFO       = env.PATH_INFO,
-               SCRIPT_NAME     = env.SCRIPT_NAME:gsub("/+$", ""),
-               SCRIPT_FILENAME = env.SCRIPT_NAME,
-               SERVER_PROTOCOL = env.SERVER_PROTOCOL,
-               QUERY_STRING    = env.QUERY_STRING
-       }
-
-       local k, v
-       for k, v in pairs(env.headers) do
-               k = k:upper():gsub("%-", "_")
-               renv["HTTP_" .. k] = v
-       end
-
-       local len = tonumber(env.CONTENT_LENGTH) or 0
-       local function recv()
-               if len > 0 then
-                       local rlen, rbuf = uhttpd.recv(4096)
-                       if rlen >= 0 then
-                               len = len - rlen
-                               return rbuf
-                       end
-               end
-               return nil
-       end
-
-       local send = uhttpd.send
-
-       local req = luci.http.Request(
-               renv, recv, luci.ltn12.sink.file(io.stderr)
-       )
-       
-
-       local x = coroutine.create(luci.dispatcher.httpdispatch)
-       local hcache = { }
-       local active = true
-
-       while coroutine.status(x) ~= "dead" do
-               local res, id, data1, data2 = coroutine.resume(x, req)
-
-               if not res then
-                       send("Status: 500 Internal Server Error\r\n")
-                       send("Content-Type: text/plain\r\n\r\n")
-                       send(tostring(id))
-                       break
-               end
-
-               if active then
-                       if id == 1 then
-                               send("Status: ")
-                               send(tostring(data1))
-                               send(" ")
-                               send(tostring(data2))
-                               send("\r\n")
-                       elseif id == 2 then
-                               hcache[data1] = data2
-                       elseif id == 3 then
-                               for k, v in pairs(hcache) do
-                                       send(tostring(k))
-                                       send(": ")
-                                       send(tostring(v))
-                                       send("\r\n")
-                               end
-                               send("\r\n")
-                       elseif id == 4 then
-                               send(tostring(data1 or ""))
-                       elseif id == 5 then
-                               active = false
-                       elseif id == 6 then
-                               data1:copyz(nixio.stdout, data2)
-                       end
-               end
-       end
-end
diff --git a/libs/sys/Makefile b/libs/sys/Makefile
deleted file mode 100644 (file)
index f7fac77..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
diff --git a/libs/sys/luasrc/sys.lua b/libs/sys/luasrc/sys.lua
deleted file mode 100644 (file)
index df6280d..0000000
+++ /dev/null
@@ -1,961 +0,0 @@
---[[
-LuCI - System library
-
-Description:
-Utilities for interaction with the Linux system
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-
-local io     = require "io"
-local os     = require "os"
-local table  = require "table"
-local nixio  = require "nixio"
-local fs     = require "nixio.fs"
-local uci    = require "luci.model.uci"
-
-local luci  = {}
-luci.util   = require "luci.util"
-luci.ip     = require "luci.ip"
-
-local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
-       tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
-
-
---- LuCI Linux and POSIX system utilities.
-module "luci.sys"
-
---- Execute a given shell command and return the error code
--- @class              function
--- @name               call
--- @param              ...             Command to call
--- @return             Error code of the command
-function call(...)
-       return os.execute(...) / 256
-end
-
---- Execute a given shell command and capture its standard output
--- @class              function
--- @name               exec
--- @param command      Command to call
--- @return                     String containg the return the output of the command
-exec = luci.util.exec
-
---- Retrieve information about currently mounted file systems.
--- @return     Table containing mount information
-function mounts()
-       local data = {}
-       local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
-       local ps = luci.util.execi("df")
-
-       if not ps then
-               return
-       else
-               ps()
-       end
-
-       for line in ps do
-               local row = {}
-
-               local j = 1
-               for value in line:gmatch("[^%s]+") do
-                       row[k[j]] = value
-                       j = j + 1
-               end
-
-               if row[k[1]] then
-
-                       -- this is a rather ugly workaround to cope with wrapped lines in
-                       -- the df output:
-                       --
-                       --      /dev/scsi/host0/bus0/target0/lun0/part3
-                       --                   114382024  93566472  15005244  86% /mnt/usb
-                       --
-
-                       if not row[k[2]] then
-                               j = 2
-                               line = ps()
-                               for value in line:gmatch("[^%s]+") do
-                                       row[k[j]] = value
-                                       j = j + 1
-                               end
-                       end
-
-                       table.insert(data, row)
-               end
-       end
-
-       return data
-end
-
---- Retrieve environment variables. If no variable is given then a table
--- containing the whole environment is returned otherwise this function returns
--- the corresponding string value for the given name or nil if no such variable
--- exists.
--- @class              function
--- @name               getenv
--- @param var  Name of the environment variable to retrieve (optional)
--- @return             String containg the value of the specified variable
--- @return             Table containing all variables if no variable name is given
-getenv = nixio.getenv
-
---- Get or set the current hostname.
--- @param              String containing a new hostname to set (optional)
--- @return             String containing the system hostname
-function hostname(newname)
-       if type(newname) == "string" and #newname > 0 then
-               fs.writefile( "/proc/sys/kernel/hostname", newname )
-               return newname
-       else
-               return nixio.uname().nodename
-       end
-end
-
---- Returns the contents of a documented referred by an URL.
--- @param url   The URL to retrieve
--- @param stream Return a stream instead of a buffer
--- @param target Directly write to target file name
--- @return             String containing the contents of given the URL
-function httpget(url, stream, target)
-       if not target then
-               local source = stream and io.popen or luci.util.exec
-               return source("wget -qO- '"..url:gsub("'", "").."'")
-       else
-               return os.execute("wget -qO '%s' '%s'" %
-                       {target:gsub("'", ""), url:gsub("'", "")})
-       end
-end
-
---- Returns the system load average values.
--- @return     String containing the average load value 1 minute ago
--- @return     String containing the average load value 5 minutes ago
--- @return     String containing the average load value 15 minutes ago
-function loadavg()
-       local info = nixio.sysinfo()
-       return info.loads[1], info.loads[2], info.loads[3]
-end
-
---- Initiate a system reboot.
--- @return     Return value of os.execute()
-function reboot()
-       return os.execute("reboot >/dev/null 2>&1")
-end
-
---- Returns the system type, cpu name and installed physical memory.
--- @return     String containing the system or platform identifier
--- @return     String containing hardware model information
--- @return     String containing the total memory amount in kB
--- @return     String containing the memory used for caching in kB
--- @return     String containing the memory used for buffering in kB
--- @return     String containing the free memory amount in kB
--- @return     String containing the cpu bogomips (number)
-function sysinfo()
-       local cpuinfo = fs.readfile("/proc/cpuinfo")
-       local meminfo = fs.readfile("/proc/meminfo")
-
-       local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)"))
-       local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)"))
-       local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)"))
-       local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)"))
-       local bogomips = tonumber(cpuinfo:match("[Bb]ogo[Mm][Ii][Pp][Ss].-: ([^\n]+)")) or 0
-       local swaptotal = tonumber(meminfo:match("SwapTotal:%s*(%d+)"))
-       local swapcached = tonumber(meminfo:match("SwapCached:%s*(%d+)"))
-       local swapfree = tonumber(meminfo:match("SwapFree:%s*(%d+)"))
-
-       local system =
-               cpuinfo:match("system type\t+: ([^\n]+)") or
-               cpuinfo:match("Processor\t+: ([^\n]+)") or
-               cpuinfo:match("model name\t+: ([^\n]+)")
-
-       local model =
-               luci.util.pcdata(fs.readfile("/tmp/sysinfo/model")) or
-               cpuinfo:match("machine\t+: ([^\n]+)") or
-               cpuinfo:match("Hardware\t+: ([^\n]+)") or
-               luci.util.pcdata(fs.readfile("/proc/diag/model")) or
-               nixio.uname().machine or
-               system
-
-       return system, model, memtotal, memcached, membuffers, memfree, bogomips, swaptotal, swapcached, swapfree
-end
-
---- Retrieves the output of the "logread" command.
--- @return     String containing the current log buffer
-function syslog()
-       return luci.util.exec("logread")
-end
-
---- Retrieves the output of the "dmesg" command.
--- @return     String containing the current log buffer
-function dmesg()
-       return luci.util.exec("dmesg")
-end
-
---- Generates a random id with specified length.
--- @param bytes        Number of bytes for the unique id
--- @return             String containing hex encoded id
-function uniqueid(bytes)
-       local rand = fs.readfile("/dev/urandom", bytes)
-       return rand and nixio.bin.hexlify(rand)
-end
-
---- Returns the current system uptime stats.
--- @return     String containing total uptime in seconds
-function uptime()
-       return nixio.sysinfo().uptime
-end
-
-
---- LuCI system utilities / network related functions.
--- @class      module
--- @name       luci.sys.net
-net = {}
-
---- Returns the current arp-table entries as two-dimensional table.
--- @return     Table of table containing the current arp entries.
---                     The following fields are defined for arp entry objects:
---                     { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
-function net.arptable(callback)
-       local arp = (not callback) and {} or nil
-       local e, r, v
-       if fs.access("/proc/net/arp") then
-               for e in io.lines("/proc/net/arp") do
-                       local r = { }, v
-                       for v in e:gmatch("%S+") do
-                               r[#r+1] = v
-                       end
-
-                       if r[1] ~= "IP" then
-                               local x = {
-                                       ["IP address"] = r[1],
-                                       ["HW type"]    = r[2],
-                                       ["Flags"]      = r[3],
-                                       ["HW address"] = r[4],
-                                       ["Mask"]       = r[5],
-                                       ["Device"]     = r[6]
-                               }
-
-                               if callback then
-                                       callback(x)
-                               else
-                                       arp = arp or { }
-                                       arp[#arp+1] = x
-                               end
-                       end
-               end
-       end
-       return arp
-end
-
-local function _nethints(what, callback)
-       local _, k, e, mac, ip, name
-       local cur = uci.cursor()
-       local ifn = { }
-       local hosts = { }
-
-       local function _add(i, ...)
-               local k = select(i, ...)
-               if k then
-                       if not hosts[k] then hosts[k] = { } end
-                       hosts[k][1] = select(1, ...) or hosts[k][1]
-                       hosts[k][2] = select(2, ...) or hosts[k][2]
-                       hosts[k][3] = select(3, ...) or hosts[k][3]
-                       hosts[k][4] = select(4, ...) or hosts[k][4]
-               end
-       end
-
-       if fs.access("/proc/net/arp") then
-               for e in io.lines("/proc/net/arp") do
-                       ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+")
-                       if ip and mac then
-                               _add(what, mac:upper(), ip, nil, nil)
-                       end
-               end
-       end
-
-       if fs.access("/etc/ethers") then
-               for e in io.lines("/etc/ethers") do
-                       mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
-                       if mac and ip then
-                               _add(what, mac:upper(), ip, nil, nil)
-                       end
-               end
-       end
-
-       if fs.access("/var/dhcp.leases") then
-               for e in io.lines("/var/dhcp.leases") do
-                       mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
-                       if mac and ip then
-                               _add(what, mac:upper(), ip, nil, name ~= "*" and name)
-                       end
-               end
-       end
-
-       cur:foreach("dhcp", "host",
-               function(s)
-                       for mac in luci.util.imatch(s.mac) do
-                               _add(what, mac:upper(), s.ip, nil, s.name)
-                       end
-               end)
-
-       for _, e in ipairs(nixio.getifaddrs()) do
-               if e.name ~= "lo" then
-                       ifn[e.name] = ifn[e.name] or { }
-                       if e.family == "packet" and e.addr and #e.addr == 17 then
-                               ifn[e.name][1] = e.addr:upper()
-                       elseif e.family == "inet" then
-                               ifn[e.name][2] = e.addr
-                       elseif e.family == "inet6" then
-                               ifn[e.name][3] = e.addr
-                       end
-               end
-       end
-
-       for _, e in pairs(ifn) do
-               if e[what] and (e[2] or e[3]) then
-                       _add(what, e[1], e[2], e[3], e[4])
-               end
-       end
-
-       for _, e in luci.util.kspairs(hosts) do
-               callback(e[1], e[2], e[3], e[4])
-       end
-end
-
---- Returns a two-dimensional table of mac address hints.
--- @return  Table of table containing known hosts from various sources.
---          Each entry contains the values in the following order:
---          [ "mac", "name" ]
-function net.mac_hints(callback)
-       if callback then
-               _nethints(1, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
-                       if name and name ~= mac then
-                               callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
-                       end
-               end)
-       else
-               local rv = { }
-               _nethints(1, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
-                       if name and name ~= mac then
-                               rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
-                       end
-               end)
-               return rv
-       end
-end
-
---- Returns a two-dimensional table of IPv4 address hints.
--- @return  Table of table containing known hosts from various sources.
---          Each entry contains the values in the following order:
---          [ "ip", "name" ]
-function net.ipv4_hints(callback)
-       if callback then
-               _nethints(2, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
-                       if name and name ~= v4 then
-                               callback(v4, name)
-                       end
-               end)
-       else
-               local rv = { }
-               _nethints(2, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
-                       if name and name ~= v4 then
-                               rv[#rv+1] = { v4, name }
-                       end
-               end)
-               return rv
-       end
-end
-
---- Returns a two-dimensional table of IPv6 address hints.
--- @return  Table of table containing known hosts from various sources.
---          Each entry contains the values in the following order:
---          [ "ip", "name" ]
-function net.ipv6_hints(callback)
-       if callback then
-               _nethints(3, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
-                       if name and name ~= v6 then
-                               callback(v6, name)
-                       end
-               end)
-       else
-               local rv = { }
-               _nethints(3, function(mac, v4, v6, name)
-                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
-                       if name and name ~= v6 then
-                               rv[#rv+1] = { v6, name }
-                       end
-               end)
-               return rv
-       end
-end
-
---- Returns conntrack information
--- @return     Table with the currently tracked IP connections
-function net.conntrack(callback)
-       local connt = {}
-       if fs.access("/proc/net/nf_conntrack", "r") then
-               for line in io.lines("/proc/net/nf_conntrack") do
-                       line = line:match "^(.-( [^ =]+=).-)%2"
-                       local entry, flags = _parse_mixed_record(line, " +")
-                       if flags[6] ~= "TIME_WAIT" then
-                               entry.layer3 = flags[1]
-                               entry.layer4 = flags[3]
-                               for i=1, #entry do
-                                       entry[i] = nil
-                               end
-
-                               if callback then
-                                       callback(entry)
-                               else
-                                       connt[#connt+1] = entry
-                               end
-                       end
-               end
-       elseif fs.access("/proc/net/ip_conntrack", "r") then
-               for line in io.lines("/proc/net/ip_conntrack") do
-                       line = line:match "^(.-( [^ =]+=).-)%2"
-                       local entry, flags = _parse_mixed_record(line, " +")
-                       if flags[4] ~= "TIME_WAIT" then
-                               entry.layer3 = "ipv4"
-                               entry.layer4 = flags[1]
-                               for i=1, #entry do
-                                       entry[i] = nil
-                               end
-
-                               if callback then
-                                       callback(entry)
-                               else
-                                       connt[#connt+1] = entry
-                               end
-                       end
-               end
-       else
-               return nil
-       end
-       return connt
-end
-
---- Determine the current IPv4 default route. If multiple default routes exist,
--- return the one with the lowest metric.
--- @return     Table with the properties of the current default route.
---                     The following fields are defined:
---                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
---                       "flags", "device" }
-function net.defaultroute()
-       local route
-
-       net.routes(function(rt)
-               if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
-                       route = rt
-               end
-       end)
-
-       return route
-end
-
---- Determine the current IPv6 default route. If multiple default routes exist,
--- return the one with the lowest metric.
--- @return     Table with the properties of the current default route.
---                     The following fields are defined:
---                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
---                       "flags", "device" }
-function net.defaultroute6()
-       local route
-
-       net.routes6(function(rt)
-               if rt.dest:prefix() == 0 and rt.device ~= "lo" and
-                  (not route or route.metric > rt.metric)
-               then
-                       route = rt
-               end
-       end)
-
-       if not route then
-               local global_unicast = luci.ip.IPv6("2000::/3")
-               net.routes6(function(rt)
-                       if rt.dest:equal(global_unicast) and
-                          (not route or route.metric > rt.metric)
-                       then
-                               route = rt
-                       end
-               end)
-       end
-
-       return route
-end
-
---- Determine the names of available network interfaces.
--- @return     Table containing all current interface names
-function net.devices()
-       local devs = {}
-       for k, v in ipairs(nixio.getifaddrs()) do
-               if v.family == "packet" then
-                       devs[#devs+1] = v.name
-               end
-       end
-       return devs
-end
-
-
---- Return information about available network interfaces.
--- @return     Table containing all current interface names and their information
-function net.deviceinfo()
-       local devs = {}
-       for k, v in ipairs(nixio.getifaddrs()) do
-               if v.family == "packet" then
-                       local d = v.data
-                       d[1] = d.rx_bytes
-                       d[2] = d.rx_packets
-                       d[3] = d.rx_errors
-                       d[4] = d.rx_dropped
-                       d[5] = 0
-                       d[6] = 0
-                       d[7] = 0
-                       d[8] = d.multicast
-                       d[9] = d.tx_bytes
-                       d[10] = d.tx_packets
-                       d[11] = d.tx_errors
-                       d[12] = d.tx_dropped
-                       d[13] = 0
-                       d[14] = d.collisions
-                       d[15] = 0
-                       d[16] = 0
-                       devs[v.name] = d
-               end
-       end
-       return devs
-end
-
-
--- Determine the MAC address belonging to the given IP address.
--- @param ip   IPv4 address
--- @return             String containing the MAC address or nil if it cannot be found
-function net.ip4mac(ip)
-       local mac = nil
-       net.arptable(function(e)
-               if e["IP address"] == ip then
-                       mac = e["HW address"]
-               end
-       end)
-       return mac
-end
-
---- Returns the current kernel routing table entries.
--- @return     Table of tables with properties of the corresponding routes.
---                     The following fields are defined for route entry tables:
---                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
---                       "flags", "device" }
-function net.routes(callback)
-       local routes = { }
-
-       for line in io.lines("/proc/net/route") do
-
-               local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
-                         dst_mask, mtu, win, irtt = line:match(
-                       "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
-                       "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
-               )
-
-               if dev then
-                       gateway  = luci.ip.Hex( gateway,  32, luci.ip.FAMILY_INET4 )
-                       dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
-                       dst_ip   = luci.ip.Hex(
-                               dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
-                       )
-
-                       local rt = {
-                               dest     = dst_ip,
-                               gateway  = gateway,
-                               metric   = tonumber(metric),
-                               refcount = tonumber(refcnt),
-                               usecount = tonumber(usecnt),
-                               mtu      = tonumber(mtu),
-                               window   = tonumber(window),
-                               irtt     = tonumber(irtt),
-                               flags    = tonumber(flags, 16),
-                               device   = dev
-                       }
-
-                       if callback then
-                               callback(rt)
-                       else
-                               routes[#routes+1] = rt
-                       end
-               end
-       end
-
-       return routes
-end
-
---- Returns the current ipv6 kernel routing table entries.
--- @return     Table of tables with properties of the corresponding routes.
---                     The following fields are defined for route entry tables:
---                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
---                       "flags", "device" }
-function net.routes6(callback)
-       if fs.access("/proc/net/ipv6_route", "r") then
-               local routes = { }
-
-               for line in io.lines("/proc/net/ipv6_route") do
-
-                       local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
-                                 metric, refcnt, usecnt, flags, dev = line:match(
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) ([a-f0-9]+) " ..
-                               "([a-f0-9]+) +([^%s]+)"
-                       )
-
-                       if dst_ip and dst_prefix and
-                          src_ip and src_prefix and
-                          nexthop and metric and
-                          refcnt and usecnt and
-                          flags and dev
-                       then
-                               src_ip = luci.ip.Hex(
-                                       src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
-                               )
-
-                               dst_ip = luci.ip.Hex(
-                                       dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
-                               )
-
-                               nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
-
-                               local rt = {
-                                       source   = src_ip,
-                                       dest     = dst_ip,
-                                       nexthop  = nexthop,
-                                       metric   = tonumber(metric, 16),
-                                       refcount = tonumber(refcnt, 16),
-                                       usecount = tonumber(usecnt, 16),
-                                       flags    = tonumber(flags, 16),
-                                       device   = dev,
-
-                                       -- lua number is too small for storing the metric
-                                       -- add a metric_raw field with the original content
-                                       metric_raw = metric
-                               }
-
-                               if callback then
-                                       callback(rt)
-                               else
-                                       routes[#routes+1] = rt
-                               end
-                       end
-               end
-
-               return routes
-       end
-end
-
---- Tests whether the given host responds to ping probes.
--- @param host String containing a hostname or IPv4 address
--- @return             Number containing 0 on success and >= 1 on error
-function net.pingtest(host)
-       return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
-end
-
-
---- LuCI system utilities / process related functions.
--- @class      module
--- @name       luci.sys.process
-process = {}
-
---- Get the current process id.
--- @class function
--- @name  process.info
--- @return     Number containing the current pid
-function process.info(key)
-       local s = {uid = nixio.getuid(), gid = nixio.getgid()}
-       return not key and s or s[key]
-end
-
---- Retrieve information about currently running processes.
--- @return     Table containing process information
-function process.list()
-       local data = {}
-       local k
-       local ps = luci.util.execi("/bin/busybox top -bn1")
-
-       if not ps then
-               return
-       end
-
-       for line in ps do
-               local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
-                       "^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
-               )
-
-               local idx = tonumber(pid)
-               if idx then
-                       data[idx] = {
-                               ['PID']     = pid,
-                               ['PPID']    = ppid,
-                               ['USER']    = user,
-                               ['STAT']    = stat,
-                               ['VSZ']     = vsz,
-                               ['%MEM']    = mem,
-                               ['%CPU']    = cpu,
-                               ['COMMAND'] = cmd
-                       }
-               end
-       end
-
-       return data
-end
-
---- Set the gid of a process identified by given pid.
--- @param gid  Number containing the Unix group id
--- @return             Boolean indicating successful operation
--- @return             String containing the error message if failed
--- @return             Number containing the error code if failed
-function process.setgroup(gid)
-       return nixio.setgid(gid)
-end
-
---- Set the uid of a process identified by given pid.
--- @param uid  Number containing the Unix user id
--- @return             Boolean indicating successful operation
--- @return             String containing the error message if failed
--- @return             Number containing the error code if failed
-function process.setuser(uid)
-       return nixio.setuid(uid)
-end
-
---- Send a signal to a process identified by given pid.
--- @class function
--- @name  process.signal
--- @param pid  Number containing the process id
--- @param sig  Signal to send (default: 15 [SIGTERM])
--- @return             Boolean indicating successful operation
--- @return             Number containing the error code if failed
-process.signal = nixio.kill
-
-
---- LuCI system utilities / user related functions.
--- @class      module
--- @name       luci.sys.user
-user = {}
-
---- Retrieve user informations for given uid.
--- @class              function
--- @name               getuser
--- @param uid  Number containing the Unix user id
--- @return             Table containing the following fields:
---                             { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
-user.getuser = nixio.getpw
-
---- Retrieve the current user password hash.
--- @param username     String containing the username to retrieve the password for
--- @return                     String containing the hash or nil if no password is set.
--- @return                     Password database entry
-function user.getpasswd(username)
-       local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
-       local pwh = pwe and (pwe.pwdp or pwe.passwd)
-       if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then
-               return nil, pwe
-       else
-               return pwh, pwe
-       end
-end
-
---- Test whether given string matches the password of a given system user.
--- @param username     String containing the Unix user name
--- @param pass         String containing the password to compare
--- @return                     Boolean indicating wheather the passwords are equal
-function user.checkpasswd(username, pass)
-       local pwh, pwe = user.getpasswd(username)
-       if pwe then
-               return (pwh == nil or nixio.crypt(pass, pwh) == pwh)
-       end
-       return false
-end
-
---- Change the password of given user.
--- @param username     String containing the Unix user name
--- @param password     String containing the password to compare
--- @return                     Number containing 0 on success and >= 1 on error
-function user.setpasswd(username, password)
-       if password then
-               password = password:gsub("'", [['"'"']])
-       end
-
-       if username then
-               username = username:gsub("'", [['"'"']])
-       end
-
-       return os.execute(
-               "(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " ..
-               "passwd '" .. username .. "' >/dev/null 2>&1"
-       )
-end
-
-
---- LuCI system utilities / wifi related functions.
--- @class      module
--- @name       luci.sys.wifi
-wifi = {}
-
---- Get wireless information for given interface.
--- @param ifname        String containing the interface name
--- @return              A wrapped iwinfo object instance
-function wifi.getiwinfo(ifname)
-       local stat, iwinfo = pcall(require, "iwinfo")
-
-       if ifname then
-               local c = 0
-               local u = uci.cursor_state()
-               local d, n = ifname:match("^(%w+)%.network(%d+)")
-               if d and n then
-                       ifname = d
-                       n = tonumber(n)
-                       u:foreach("wireless", "wifi-iface",
-                               function(s)
-                                       if s.device == d then
-                                               c = c + 1
-                                               if c == n then
-                                                       ifname = s.ifname or s.device
-                                                       return false
-                                               end
-                                       end
-                               end)
-               elseif u:get("wireless", ifname) == "wifi-device" then
-                       u:foreach("wireless", "wifi-iface",
-                               function(s)
-                                       if s.device == ifname and s.ifname then
-                                               ifname = s.ifname
-                                               return false
-                                       end
-                               end)
-               end
-
-               local t = stat and iwinfo.type(ifname)
-               local x = t and iwinfo[t] or { }
-               return setmetatable({}, {
-                       __index = function(t, k)
-                               if k == "ifname" then
-                                       return ifname
-                               elseif x[k] then
-                                       return x[k](ifname)
-                               end
-                       end
-               })
-       end
-end
-
-
---- LuCI system utilities / init related functions.
--- @class      module
--- @name       luci.sys.init
-init = {}
-init.dir = "/etc/init.d/"
-
---- Get the names of all installed init scripts
--- @return     Table containing the names of all inistalled init scripts
-function init.names()
-       local names = { }
-       for name in fs.glob(init.dir.."*") do
-               names[#names+1] = fs.basename(name)
-       end
-       return names
-end
-
---- Get the index of he given init script
--- @param name Name of the init script
--- @return             Numeric index value
-function init.index(name)
-       if fs.access(init.dir..name) then
-               return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
-                       %{ init.dir, name })
-       end
-end
-
-local function init_action(action, name)
-       if fs.access(init.dir..name) then
-               return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action })
-       end
-end
-
---- Test whether the given init script is enabled
--- @param name Name of the init script
--- @return             Boolean indicating whether init is enabled
-function init.enabled(name)
-       return (init_action("enabled", name) == 0)
-end
-
---- Enable the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
-function init.enable(name)
-       return (init_action("enable", name) == 1)
-end
-
---- Disable the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
-function init.disable(name)
-       return (init_action("disable", name) == 0)
-end
-
---- Start the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
-function init.start(name)
-       return (init_action("start", name) == 0)
-end
-
---- Stop the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
-function init.stop(name)
-       return (init_action("stop", name) == 0)
-end
-
-
--- Internal functions
-
-function _parse_mixed_record(cnt, delimiter)
-       delimiter = delimiter or "  "
-       local data = {}
-       local flags = {}
-
-       for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
-               for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
-                       local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
-
-                       if k then
-                               if x == "" then
-                                       table.insert(flags, k)
-                               else
-                                       data[k] = v
-                               end
-                       end
-               end
-       end
-
-       return data, flags
-end
diff --git a/libs/sys/luasrc/sys/iptparser.lua b/libs/sys/luasrc/sys/iptparser.lua
deleted file mode 100644 (file)
index d823633..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
---[[
-
-Iptables parser and query library
-(c) 2008-2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-(c) 2008-2009 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-
-]]--
-
-local luci  = {}
-luci.util   = require "luci.util"
-luci.sys    = require "luci.sys"
-luci.ip     = require "luci.ip"
-
-local tonumber, ipairs, table = tonumber, ipairs, table
-
---- LuCI iptables parser and query library
--- @cstyle     instance
-module("luci.sys.iptparser")
-
---- Create a new iptables parser object.
--- @class      function
--- @name       IptParser
--- @param      family  Number specifying the address family. 4 for IPv4, 6 for IPv6
--- @return     IptParser instance
-IptParser = luci.util.class()
-
-function IptParser.__init__( self, family )
-       self._family = (tonumber(family) == 6) and 6 or 4
-       self._rules  = { }
-       self._chains = { }
-
-       if self._family == 4 then
-               self._nulladdr = "0.0.0.0/0"
-               self._tables   = { "filter", "nat", "mangle", "raw" }
-               self._command  = "iptables -t %s --line-numbers -nxvL"
-       else
-               self._nulladdr = "::/0"
-               self._tables   = { "filter", "mangle", "raw" }
-               self._command  = "ip6tables -t %s --line-numbers -nxvL"
-       end
-
-       self:_parse_rules()
-end
-
---- Find all firewall rules that match the given criteria. Expects a table with
--- search criteria as only argument. If args is nil or an empty table then all
--- rules will be returned.
---
--- The following keys in the args table are recognized:
--- <ul>
---  <li> table          - Match rules that are located within the given table
---  <li> chain          - Match rules that are located within the given chain
---  <li> target                 - Match rules with the given target
---  <li> protocol       - Match rules that match the given protocol, rules with
---                                             protocol "all" are always matched
---  <li> source                 - Match rules with the given source, rules with source
---                                             "0.0.0.0/0" (::/0) are always matched
---  <li> destination - Match rules with the given destination, rules with
---                                             destination "0.0.0.0/0" (::/0) are always matched
---  <li> inputif        - Match rules with the given input interface, rules
---                                             with input      interface "*" (=all) are always matched
---  <li> outputif       - Match rules with the given output interface, rules
---                                             with output     interface "*" (=all) are always matched
---  <li> flags          - Match rules that match the given flags, current
---                                             supported values are "-f" (--fragment)
---                                             and "!f" (! --fragment)
---  <li> options        - Match rules containing all given options
--- </ul>
--- The return value is a list of tables representing the matched rules.
--- Each rule table contains the following fields:
--- <ul>
---  <li> index          - The index number of the rule
---  <li> table          - The table where the rule is located, can be one
---                                             of "filter", "nat" or "mangle"
---  <li> chain          - The chain where the rule is located, e.g. "INPUT"
---                                             or "postrouting_wan"
---  <li> target                 - The rule target, e.g. "REJECT" or "DROP"
---  <li> protocol              The matching protocols, e.g. "all" or "tcp"
---  <li> flags          - Special rule options ("--", "-f" or "!f")
---  <li> inputif        - Input interface of the rule, e.g. "eth0.0"
---                                             or "*" for all interfaces
---  <li> outputif       - Output interface of the rule,e.g. "eth0.0"
---                                             or "*" for all interfaces
---  <li> source                 - The source ip range, e.g. "0.0.0.0/0" (::/0)
---  <li> destination - The destination ip range, e.g. "0.0.0.0/0" (::/0)
---  <li> options        - A list of specific options of the rule,
---                                             e.g. { "reject-with", "tcp-reset" }
---  <li> packets        - The number of packets matched by the rule
---  <li> bytes          - The number of total bytes matched by the rule
--- </ul>
--- Example:
--- <pre>
--- ip = luci.sys.iptparser.IptParser()
--- result = ip.find( {
---     target="REJECT",
---     protocol="tcp",
---     options={ "reject-with", "tcp-reset" }
--- } )
--- </pre>
--- This will match all rules with target "-j REJECT",
--- protocol "-p tcp" (or "-p all")
--- and the option "--reject-with tcp-reset".
--- @params args                Table containing the search arguments (optional)
--- @return                     Table of matching rule tables
-function IptParser.find( self, args )
-
-       local args = args or { }
-       local rv   = { }
-
-       args.source      = args.source      and self:_parse_addr(args.source)
-       args.destination = args.destination and self:_parse_addr(args.destination)
-
-       for i, rule in ipairs(self._rules) do
-               local match = true
-
-               -- match table
-               if not ( not args.table or args.table:lower() == rule.table ) then
-                       match = false
-               end
-
-               -- match chain
-               if not ( match == true and (
-                       not args.chain or args.chain == rule.chain
-               ) ) then
-                       match = false
-               end
-
-               -- match target
-               if not ( match == true and (
-                       not args.target or args.target == rule.target
-               ) ) then
-                       match = false
-               end
-
-               -- match protocol
-               if not ( match == true and (
-                       not args.protocol or rule.protocol == "all" or
-                       args.protocol:lower() == rule.protocol
-               ) ) then
-                       match = false
-               end
-
-               -- match source
-               if not ( match == true and (
-                       not args.source or rule.source == self._nulladdr or
-                       self:_parse_addr(rule.source):contains(args.source)
-               ) ) then
-                       match = false
-               end
-
-               -- match destination
-               if not ( match == true and (
-                       not args.destination or rule.destination == self._nulladdr or
-                       self:_parse_addr(rule.destination):contains(args.destination)
-               ) ) then
-                       match = false
-               end
-
-               -- match input interface
-               if not ( match == true and (
-                       not args.inputif or rule.inputif == "*" or
-                       args.inputif == rule.inputif
-               ) ) then
-                       match = false
-               end
-
-               -- match output interface
-               if not ( match == true and (
-                       not args.outputif or rule.outputif == "*" or
-                       args.outputif == rule.outputif
-               ) ) then
-                       match = false
-               end
-
-               -- match flags (the "opt" column)
-               if not ( match == true and (
-                       not args.flags or rule.flags == args.flags
-               ) ) then
-                       match = false
-               end
-
-               -- match specific options
-               if not ( match == true and (
-                       not args.options or
-                       self:_match_options( rule.options, args.options )
-               ) ) then
-                       match = false
-               end
-
-               -- insert match
-               if match == true then
-                       rv[#rv+1] = rule
-               end
-       end
-
-       return rv
-end
-
-
---- Rebuild the internal lookup table, for example when rules have changed
--- through external commands.
--- @return     nothing
-function IptParser.resync( self )
-       self._rules = { }
-       self._chain = nil
-       self:_parse_rules()
-end
-
-
---- Find the names of all tables.
--- @return             Table of table names.
-function IptParser.tables( self )
-       return self._tables
-end
-
-
---- Find the names of all chains within the given table name.
--- @param table        String containing the table name
--- @return             Table of chain names in the order they occur.
-function IptParser.chains( self, table )
-       local lookup = { }
-       local chains = { }
-       for _, r in ipairs(self:find({table=table})) do
-               if not lookup[r.chain] then
-                       lookup[r.chain]   = true
-                       chains[#chains+1] = r.chain
-               end
-       end
-       return chains
-end
-
-
---- Return the given firewall chain within the given table name.
--- @param table        String containing the table name
--- @param chain        String containing the chain name
--- @return             Table containing the fields "policy", "packets", "bytes"
---                             and "rules". The "rules" field is a table of rule tables.
-function IptParser.chain( self, table, chain )
-       return self._chains[table:lower()] and self._chains[table:lower()][chain]
-end
-
-
---- Test whether the given target points to a custom chain.
--- @param target       String containing the target action
--- @return                     Boolean indicating whether target is a custom chain.
-function IptParser.is_custom_target( self, target )
-       for _, r in ipairs(self._rules) do
-               if r.chain == target then
-                       return true
-               end
-       end
-       return false
-end
-
-
--- [internal] Parse address according to family.
-function IptParser._parse_addr( self, addr )
-       if self._family == 4 then
-               return luci.ip.IPv4(addr)
-       else
-               return luci.ip.IPv6(addr)
-       end
-end
-
--- [internal] Parse iptables output from all tables.
-function IptParser._parse_rules( self )
-
-       for i, tbl in ipairs(self._tables) do
-
-               self._chains[tbl] = { }
-
-               for i, rule in ipairs(luci.util.execl(self._command % tbl)) do
-
-                       if rule:find( "^Chain " ) == 1 then
-
-                               local crefs
-                               local cname, cpol, cpkt, cbytes = rule:match(
-                                       "^Chain ([^%s]*) %(policy (%w+) " ..
-                                       "(%d+) packets, (%d+) bytes%)"
-                               )
-
-                               if not cname then
-                                       cname, crefs = rule:match(
-                                               "^Chain ([^%s]*) %((%d+) references%)"
-                                       )
-                               end
-
-                               self._chain = cname
-                               self._chains[tbl][cname] = {
-                                       policy     = cpol,
-                                       packets    = tonumber(cpkt or 0),
-                                       bytes      = tonumber(cbytes or 0),
-                                       references = tonumber(crefs or 0),
-                                       rules      = { }
-                               }
-
-                       else
-                               if rule:find("%d") == 1 then
-
-                                       local rule_parts   = luci.util.split( rule, "%s+", nil, true )
-                                       local rule_details = { }
-
-                                       -- cope with rules that have no target assigned
-                                       if rule:match("^%d+%s+%d+%s+%d+%s%s") then
-                                               table.insert(rule_parts, 4, nil)
-                                       end
-
-                                       -- ip6tables opt column is usually zero-width
-                                       if self._family == 6 then
-                                               table.insert(rule_parts, 6, "--")
-                                       end
-
-                                       rule_details["table"]       = tbl
-                                       rule_details["chain"]       = self._chain
-                                       rule_details["index"]       = tonumber(rule_parts[1])
-                                       rule_details["packets"]     = tonumber(rule_parts[2])
-                                       rule_details["bytes"]       = tonumber(rule_parts[3])
-                                       rule_details["target"]      = rule_parts[4]
-                                       rule_details["protocol"]    = rule_parts[5]
-                                       rule_details["flags"]       = rule_parts[6]
-                                       rule_details["inputif"]     = rule_parts[7]
-                                       rule_details["outputif"]    = rule_parts[8]
-                                       rule_details["source"]      = rule_parts[9]
-                                       rule_details["destination"] = rule_parts[10]
-                                       rule_details["options"]     = { }
-
-                                       for i = 11, #rule_parts  do
-                                               if #rule_parts[i] > 0 then
-                                                       rule_details["options"][i-10] = rule_parts[i]
-                                               end
-                                       end
-
-                                       self._rules[#self._rules+1] = rule_details
-
-                                       self._chains[tbl][self._chain].rules[
-                                               #self._chains[tbl][self._chain].rules + 1
-                                       ] = rule_details
-                               end
-                       end
-               end
-       end
-
-       self._chain = nil
-end
-
-
--- [internal] Return true if optlist1 contains all elements of optlist 2.
---            Return false in all other cases.
-function IptParser._match_options( self, o1, o2 )
-
-       -- construct a hashtable of first options list to speed up lookups
-       local oh = { }
-       for i, opt in ipairs( o1 ) do oh[opt] = true end
-
-       -- iterate over second options list
-       -- each string in o2 must be also present in o1
-       -- if o2 contains a string which is not found in o1 then return false
-       for i, opt in ipairs( o2 ) do
-               if not oh[opt] then
-                       return false
-               end
-       end
-
-       return true
-end
diff --git a/libs/sys/luasrc/sys/zoneinfo.lua b/libs/sys/luasrc/sys/zoneinfo.lua
deleted file mode 100644 (file)
index f5a12bf..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
---[[
-LuCI - Autogenerated Zoneinfo Module
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-local setmetatable, require, rawget, rawset = setmetatable, require, rawget, rawset
-
-module "luci.sys.zoneinfo"
-
-setmetatable(_M, {
-       __index = function(t, k)
-               if k == "TZ" and not rawget(t, k) then
-                       local m = require "luci.sys.zoneinfo.tzdata"
-                       rawset(t, k, rawget(m, k))
-               elseif k == "OFFSET" and not rawget(t, k) then
-                       local m = require "luci.sys.zoneinfo.tzoffset"
-                       rawset(t, k, rawget(m, k))
-               end
-
-               return rawget(t, k)
-       end
-})
diff --git a/libs/sys/luasrc/sys/zoneinfo/tzdata.lua b/libs/sys/luasrc/sys/zoneinfo/tzdata.lua
deleted file mode 100644 (file)
index 1a99f6a..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
---[[
-LuCI - Autogenerated Zoneinfo Module
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-module "luci.sys.zoneinfo.tzdata"
-
-TZ = {
-       { 'Africa/Abidjan', 'GMT0' },
-       { 'Africa/Accra', 'GMT0' },
-       { 'Africa/Addis Ababa', 'EAT-3' },
-       { 'Africa/Algiers', 'CET-1' },
-       { 'Africa/Asmara', 'EAT-3' },
-       { 'Africa/Bamako', 'GMT0' },
-       { 'Africa/Bangui', 'WAT-1' },
-       { 'Africa/Banjul', 'GMT0' },
-       { 'Africa/Bissau', 'GMT0' },
-       { 'Africa/Blantyre', 'CAT-2' },
-       { 'Africa/Brazzaville', 'WAT-1' },
-       { 'Africa/Bujumbura', 'CAT-2' },
-       { 'Africa/Casablanca', 'WET0' },
-       { 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Africa/Conakry', 'GMT0' },
-       { 'Africa/Dakar', 'GMT0' },
-       { 'Africa/Dar es Salaam', 'EAT-3' },
-       { 'Africa/Djibouti', 'EAT-3' },
-       { 'Africa/Douala', 'WAT-1' },
-       { 'Africa/El Aaiun', 'WET0' },
-       { 'Africa/Freetown', 'GMT0' },
-       { 'Africa/Gaborone', 'CAT-2' },
-       { 'Africa/Harare', 'CAT-2' },
-       { 'Africa/Johannesburg', 'SAST-2' },
-       { 'Africa/Juba', 'EAT-3' },
-       { 'Africa/Kampala', 'EAT-3' },
-       { 'Africa/Khartoum', 'EAT-3' },
-       { 'Africa/Kigali', 'CAT-2' },
-       { 'Africa/Kinshasa', 'WAT-1' },
-       { 'Africa/Lagos', 'WAT-1' },
-       { 'Africa/Libreville', 'WAT-1' },
-       { 'Africa/Lome', 'GMT0' },
-       { 'Africa/Luanda', 'WAT-1' },
-       { 'Africa/Lubumbashi', 'CAT-2' },
-       { 'Africa/Lusaka', 'CAT-2' },
-       { 'Africa/Malabo', 'WAT-1' },
-       { 'Africa/Maputo', 'CAT-2' },
-       { 'Africa/Maseru', 'SAST-2' },
-       { 'Africa/Mbabane', 'SAST-2' },
-       { 'Africa/Mogadishu', 'EAT-3' },
-       { 'Africa/Monrovia', 'GMT0' },
-       { 'Africa/Nairobi', 'EAT-3' },
-       { 'Africa/Ndjamena', 'WAT-1' },
-       { 'Africa/Niamey', 'WAT-1' },
-       { 'Africa/Nouakchott', 'GMT0' },
-       { 'Africa/Ouagadougou', 'GMT0' },
-       { 'Africa/Porto-Novo', 'WAT-1' },
-       { 'Africa/Sao Tome', 'GMT0' },
-       { 'Africa/Tripoli', 'EET-2' },
-       { 'Africa/Tunis', 'CET-1' },
-       { 'Africa/Windhoek', 'WAT-1WAST,M9.1.0,M4.1.0' },
-       { 'America/Adak', 'HAST10HADT,M3.2.0,M11.1.0' },
-       { 'America/Anchorage', 'AKST9AKDT,M3.2.0,M11.1.0' },
-       { 'America/Anguilla', 'AST4' },
-       { 'America/Antigua', 'AST4' },
-       { 'America/Araguaina', 'BRT3' },
-       { 'America/Argentina/Buenos Aires', 'ART3' },
-       { 'America/Argentina/Catamarca', 'ART3' },
-       { 'America/Argentina/Cordoba', 'ART3' },
-       { 'America/Argentina/Jujuy', 'ART3' },
-       { 'America/Argentina/La Rioja', 'ART3' },
-       { 'America/Argentina/Mendoza', 'ART3' },
-       { 'America/Argentina/Rio Gallegos', 'ART3' },
-       { 'America/Argentina/Salta', 'ART3' },
-       { 'America/Argentina/San Juan', 'ART3' },
-       { 'America/Argentina/Tucuman', 'ART3' },
-       { 'America/Argentina/Ushuaia', 'ART3' },
-       { 'America/Aruba', 'AST4' },
-       { 'America/Asuncion', 'PYT4PYST,M10.1.0/0,M4.2.0/0' },
-       { 'America/Atikokan', 'EST5' },
-       { 'America/Bahia', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
-       { 'America/Bahia Banderas', 'CST6CDT,M4.1.0,M10.5.0' },
-       { 'America/Barbados', 'AST4' },
-       { 'America/Belem', 'BRT3' },
-       { 'America/Belize', 'CST6' },
-       { 'America/Blanc-Sablon', 'AST4' },
-       { 'America/Boa Vista', 'AMT4' },
-       { 'America/Bogota', 'COT5' },
-       { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
-       { 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' },
-       { 'America/Caracas', 'VET4:30' },
-       { 'America/Cayenne', 'GFT3' },
-       { 'America/Cayman', 'EST5' },
-       { 'America/Chicago', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Chihuahua', 'MST7MDT,M4.1.0,M10.5.0' },
-       { 'America/Costa Rica', 'CST6' },
-       { 'America/Cuiaba', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
-       { 'America/Curacao', 'AST4' },
-       { 'America/Danmarkshavn', 'GMT0' },
-       { 'America/Dawson', 'PST8PDT,M3.2.0,M11.1.0' },
-       { 'America/Dawson Creek', 'MST7' },
-       { 'America/Denver', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Detroit', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Dominica', 'AST4' },
-       { 'America/Edmonton', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Eirunepe', 'AMT4' },
-       { 'America/El Salvador', 'CST6' },
-       { 'America/Fortaleza', 'BRT3' },
-       { 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'America/Goose Bay', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'America/Grand Turk', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Grenada', 'AST4' },
-       { 'America/Guadeloupe', 'AST4' },
-       { 'America/Guatemala', 'CST6' },
-       { 'America/Guayaquil', 'ECT5' },
-       { 'America/Guyana', 'GYT4' },
-       { 'America/Halifax', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'America/Havana', 'CST5CDT,M3.2.0/0,M10.5.0/1' },
-       { 'America/Hermosillo', 'MST7' },
-       { 'America/Indiana/Indianapolis', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Knox', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Marengo', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Petersburg', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Tell City', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Vevay', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Vincennes', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Indiana/Winamac', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Inuvik', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Iqaluit', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Jamaica', 'EST5' },
-       { 'America/Juneau', 'AKST9AKDT,M3.2.0,M11.1.0' },
-       { 'America/Kentucky/Louisville', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Kentucky/Monticello', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Kralendijk', 'AST4' },
-       { 'America/La Paz', 'BOT4' },
-       { 'America/Lima', 'PET5' },
-       { 'America/Los Angeles', 'PST8PDT,M3.2.0,M11.1.0' },
-       { 'America/Lower Princes', 'AST4' },
-       { 'America/Maceio', 'BRT3' },
-       { 'America/Managua', 'CST6' },
-       { 'America/Manaus', 'AMT4' },
-       { 'America/Marigot', 'AST4' },
-       { 'America/Martinique', 'AST4' },
-       { 'America/Matamoros', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Mazatlan', 'MST7MDT,M4.1.0,M10.5.0' },
-       { 'America/Menominee', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Merida', 'CST6CDT,M4.1.0,M10.5.0' },
-       { 'America/Metlakatla', 'MeST8' },
-       { 'America/Mexico City', 'CST6CDT,M4.1.0,M10.5.0' },
-       { 'America/Miquelon', 'PMST3PMDT,M3.2.0,M11.1.0' },
-       { 'America/Moncton', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'America/Monterrey', 'CST6CDT,M4.1.0,M10.5.0' },
-       { 'America/Montevideo', 'UYT3UYST,M10.1.0,M3.2.0' },
-       { 'America/Montreal', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Montserrat', 'AST4' },
-       { 'America/Nassau', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/New York', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Nipigon', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Nome', 'AKST9AKDT,M3.2.0,M11.1.0' },
-       { 'America/Noronha', 'FNT2' },
-       { 'America/North Dakota/Beulah', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/North Dakota/Center', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/North Dakota/New Salem', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Ojinaga', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Panama', 'EST5' },
-       { 'America/Pangnirtung', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Paramaribo', 'SRT3' },
-       { 'America/Phoenix', 'MST7' },
-       { 'America/Port of Spain', 'AST4' },
-       { 'America/Port-au-Prince', 'EST5' },
-       { 'America/Porto Velho', 'AMT4' },
-       { 'America/Puerto Rico', 'AST4' },
-       { 'America/Rainy River', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Rankin Inlet', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Recife', 'BRT3' },
-       { 'America/Regina', 'CST6' },
-       { 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Rio Branco', 'AMT4' },
-       { 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' },
-       { 'America/Santarem', 'BRT3' },
-       { 'America/Santo Domingo', 'AST4' },
-       { 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
-       { 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' },
-       { 'America/Shiprock', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'America/Sitka', 'AKST9AKDT,M3.2.0,M11.1.0' },
-       { 'America/St Barthelemy', 'AST4' },
-       { 'America/St Johns', 'NST3:30NDT,M3.2.0,M11.1.0' },
-       { 'America/St Kitts', 'AST4' },
-       { 'America/St Lucia', 'AST4' },
-       { 'America/St Thomas', 'AST4' },
-       { 'America/St Vincent', 'AST4' },
-       { 'America/Swift Current', 'CST6' },
-       { 'America/Tegucigalpa', 'CST6' },
-       { 'America/Thule', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'America/Thunder Bay', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Tijuana', 'PST8PDT,M3.2.0,M11.1.0' },
-       { 'America/Toronto', 'EST5EDT,M3.2.0,M11.1.0' },
-       { 'America/Tortola', 'AST4' },
-       { 'America/Vancouver', 'PST8PDT,M3.2.0,M11.1.0' },
-       { 'America/Whitehorse', 'PST8PDT,M3.2.0,M11.1.0' },
-       { 'America/Winnipeg', 'CST6CDT,M3.2.0,M11.1.0' },
-       { 'America/Yakutat', 'AKST9AKDT,M3.2.0,M11.1.0' },
-       { 'America/Yellowknife', 'MST7MDT,M3.2.0,M11.1.0' },
-       { 'Antarctica/Casey', 'WST-8' },
-       { 'Antarctica/Davis', 'DAVT-7' },
-       { 'Antarctica/DumontDUrville', 'DDUT-10' },
-       { 'Antarctica/Macquarie', 'MIST-11' },
-       { 'Antarctica/Mawson', 'MAWT-5' },
-       { 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
-       { 'Antarctica/Rothera', 'ROTT3' },
-       { 'Antarctica/South Pole', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
-       { 'Antarctica/Syowa', 'SYOT-3' },
-       { 'Antarctica/Vostok', 'VOST-6' },
-       { 'Arctic/Longyearbyen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Asia/Aden', 'AST-3' },
-       { 'Asia/Almaty', 'ALMT-6' },
-       { 'Asia/Anadyr', 'ANAT-12' },
-       { 'Asia/Aqtau', 'AQTT-5' },
-       { 'Asia/Aqtobe', 'AQTT-5' },
-       { 'Asia/Ashgabat', 'TMT-5' },
-       { 'Asia/Baghdad', 'AST-3' },
-       { 'Asia/Bahrain', 'AST-3' },
-       { 'Asia/Baku', 'AZT-4AZST,M3.5.0/4,M10.5.0/5' },
-       { 'Asia/Bangkok', 'ICT-7' },
-       { 'Asia/Beirut', 'EET-2EEST,M3.5.0/0,M10.5.0/0' },
-       { 'Asia/Bishkek', 'KGT-6' },
-       { 'Asia/Brunei', 'BNT-8' },
-       { 'Asia/Choibalsan', 'CHOT-8' },
-       { 'Asia/Chongqing', 'CST-8' },
-       { 'Asia/Colombo', 'IST-5:30' },
-       { 'Asia/Damascus', 'EET-2EEST,M4.1.5/0,M10.5.5/0' },
-       { 'Asia/Dhaka', 'BDT-6' },
-       { 'Asia/Dili', 'TLT-9' },
-       { 'Asia/Dubai', 'GST-4' },
-       { 'Asia/Dushanbe', 'TJT-5' },
-       { 'Asia/Gaza', 'EET-2' },
-       { 'Asia/Harbin', 'CST-8' },
-       { 'Asia/Hebron', 'EET-2' },
-       { 'Asia/Ho Chi Minh', 'ICT-7' },
-       { 'Asia/Hong Kong', 'HKT-8' },
-       { 'Asia/Hovd', 'HOVT-7' },
-       { 'Asia/Irkutsk', 'IRKT-9' },
-       { 'Asia/Jakarta', 'WIT-7' },
-       { 'Asia/Jayapura', 'EIT-9' },
-       { 'Asia/Kabul', 'AFT-4:30' },
-       { 'Asia/Kamchatka', 'PETT-12' },
-       { 'Asia/Karachi', 'PKT-5' },
-       { 'Asia/Kashgar', 'CST-8' },
-       { 'Asia/Kathmandu', 'NPT-5:45' },
-       { 'Asia/Kolkata', 'IST-5:30' },
-       { 'Asia/Krasnoyarsk', 'KRAT-8' },
-       { 'Asia/Kuala Lumpur', 'MYT-8' },
-       { 'Asia/Kuching', 'MYT-8' },
-       { 'Asia/Kuwait', 'AST-3' },
-       { 'Asia/Macau', 'CST-8' },
-       { 'Asia/Magadan', 'MAGT-12' },
-       { 'Asia/Makassar', 'CIT-8' },
-       { 'Asia/Manila', 'PHT-8' },
-       { 'Asia/Muscat', 'GST-4' },
-       { 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Asia/Novokuznetsk', 'NOVT-7' },
-       { 'Asia/Novosibirsk', 'NOVT-7' },
-       { 'Asia/Omsk', 'OMST-7' },
-       { 'Asia/Oral', 'ORAT-5' },
-       { 'Asia/Phnom Penh', 'ICT-7' },
-       { 'Asia/Pontianak', 'WIT-7' },
-       { 'Asia/Pyongyang', 'KST-9' },
-       { 'Asia/Qatar', 'AST-3' },
-       { 'Asia/Qyzylorda', 'QYZT-6' },
-       { 'Asia/Rangoon', 'MMT-6:30' },
-       { 'Asia/Riyadh', 'AST-3' },
-       { 'Asia/Sakhalin', 'SAKT-11' },
-       { 'Asia/Samarkand', 'UZT-5' },
-       { 'Asia/Seoul', 'KST-9' },
-       { 'Asia/Shanghai', 'CST-8' },
-       { 'Asia/Singapore', 'SGT-8' },
-       { 'Asia/Taipei', 'CST-8' },
-       { 'Asia/Tashkent', 'UZT-5' },
-       { 'Asia/Tbilisi', 'GET-4' },
-       { 'Asia/Thimphu', 'BTT-6' },
-       { 'Asia/Tokyo', 'JST-9' },
-       { 'Asia/Ulaanbaatar', 'ULAT-8' },
-       { 'Asia/Urumqi', 'CST-8' },
-       { 'Asia/Vientiane', 'ICT-7' },
-       { 'Asia/Vladivostok', 'VLAT-11' },
-       { 'Asia/Yakutsk', 'YAKT-10' },
-       { 'Asia/Yekaterinburg', 'YEKT-6' },
-       { 'Asia/Yerevan', 'AMT-4AMST,M3.5.0,M10.5.0/3' },
-       { 'Atlantic/Azores', 'AZOT1AZOST,M3.5.0/0,M10.5.0/1' },
-       { 'Atlantic/Bermuda', 'AST4ADT,M3.2.0,M11.1.0' },
-       { 'Atlantic/Canary', 'WET0WEST,M3.5.0/1,M10.5.0' },
-       { 'Atlantic/Cape Verde', 'CVT1' },
-       { 'Atlantic/Faroe', 'WET0WEST,M3.5.0/1,M10.5.0' },
-       { 'Atlantic/Madeira', 'WET0WEST,M3.5.0/1,M10.5.0' },
-       { 'Atlantic/Reykjavik', 'GMT0' },
-       { 'Atlantic/South Georgia', 'GST2' },
-       { 'Atlantic/St Helena', 'GMT0' },
-       { 'Atlantic/Stanley', 'FKT4FKST,M9.1.0,M4.3.0' },
-       { 'Australia/Adelaide', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
-       { 'Australia/Brisbane', 'EST-10' },
-       { 'Australia/Broken Hill', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
-       { 'Australia/Currie', 'EST-10EST,M10.1.0,M4.1.0/3' },
-       { 'Australia/Darwin', 'CST-9:30' },
-       { 'Australia/Eucla', 'CWST-8:45' },
-       { 'Australia/Hobart', 'EST-10EST,M10.1.0,M4.1.0/3' },
-       { 'Australia/Lindeman', 'EST-10' },
-       { 'Australia/Lord Howe', 'LHST-10:30LHST-11,M10.1.0,M4.1.0' },
-       { 'Australia/Melbourne', 'EST-10EST,M10.1.0,M4.1.0/3' },
-       { 'Australia/Perth', 'WST-8' },
-       { 'Australia/Sydney', 'EST-10EST,M10.1.0,M4.1.0/3' },
-       { 'Europe/Amsterdam', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Andorra', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Athens', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Belgrade', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Berlin', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Bratislava', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Brussels', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Bucharest', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Budapest', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Chisinau', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Isle of Man', 'GMT0BST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Istanbul', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Jersey', 'GMT0BST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Kaliningrad', 'FET-3' },
-       { 'Europe/Kiev', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Lisbon', 'WET0WEST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Ljubljana', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/London', 'GMT0BST,M3.5.0/1,M10.5.0' },
-       { 'Europe/Luxembourg', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Madrid', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Malta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Mariehamn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Minsk', 'FET-3' },
-       { 'Europe/Monaco', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Moscow', 'MSK-4' },
-       { 'Europe/Oslo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Paris', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Podgorica', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Prague', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Riga', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Rome', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Samara', 'SAMT-4' },
-       { 'Europe/San Marino', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Sarajevo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Simferopol', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Skopje', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Sofia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Stockholm', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Tallinn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Tirane', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Uzhgorod', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Vaduz', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Volgograd', 'VOLT-4' },
-       { 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
-       { 'Europe/Zurich', 'CET-1CEST,M3.5.0,M10.5.0/3' },
-       { 'Indian/Antananarivo', 'EAT-3' },
-       { 'Indian/Chagos', 'IOT-6' },
-       { 'Indian/Christmas', 'CXT-7' },
-       { 'Indian/Cocos', 'CCT-6:30' },
-       { 'Indian/Comoro', 'EAT-3' },
-       { 'Indian/Kerguelen', 'TFT-5' },
-       { 'Indian/Mahe', 'SCT-4' },
-       { 'Indian/Maldives', 'MVT-5' },
-       { 'Indian/Mauritius', 'MUT-4' },
-       { 'Indian/Mayotte', 'EAT-3' },
-       { 'Indian/Reunion', 'RET-4' },
-       { 'Pacific/Apia', 'WST-13' },
-       { 'Pacific/Auckland', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
-       { 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' },
-       { 'Pacific/Chuuk', 'CHUT-10' },
-       { 'Pacific/Efate', 'VUT-11' },
-       { 'Pacific/Enderbury', 'PHOT-13' },
-       { 'Pacific/Fakaofo', 'TKT10' },
-       { 'Pacific/Fiji', 'FJT-12' },
-       { 'Pacific/Funafuti', 'TVT-12' },
-       { 'Pacific/Galapagos', 'GALT6' },
-       { 'Pacific/Gambier', 'GAMT9' },
-       { 'Pacific/Guadalcanal', 'SBT-11' },
-       { 'Pacific/Guam', 'ChST-10' },
-       { 'Pacific/Honolulu', 'HST10' },
-       { 'Pacific/Johnston', 'HST10' },
-       { 'Pacific/Kiritimati', 'LINT-14' },
-       { 'Pacific/Kosrae', 'KOST-11' },
-       { 'Pacific/Kwajalein', 'MHT-12' },
-       { 'Pacific/Majuro', 'MHT-12' },
-       { 'Pacific/Marquesas', 'MART9:30' },
-       { 'Pacific/Midway', 'SST11' },
-       { 'Pacific/Nauru', 'NRT-12' },
-       { 'Pacific/Niue', 'NUT11' },
-       { 'Pacific/Norfolk', 'NFT-11:30' },
-       { 'Pacific/Noumea', 'NCT-11' },
-       { 'Pacific/Pago Pago', 'SST11' },
-       { 'Pacific/Palau', 'PWT-9' },
-       { 'Pacific/Pitcairn', 'PST8' },
-       { 'Pacific/Pohnpei', 'PONT-11' },
-       { 'Pacific/Port Moresby', 'PGT-10' },
-       { 'Pacific/Rarotonga', 'CKT10' },
-       { 'Pacific/Saipan', 'ChST-10' },
-       { 'Pacific/Tahiti', 'TAHT10' },
-       { 'Pacific/Tarawa', 'GILT-12' },
-       { 'Pacific/Tongatapu', 'TOT-13' },
-       { 'Pacific/Wake', 'WAKT-12' },
-       { 'Pacific/Wallis', 'WFT-12' },
-}
diff --git a/libs/sys/luasrc/sys/zoneinfo/tzoffset.lua b/libs/sys/luasrc/sys/zoneinfo/tzoffset.lua
deleted file mode 100644 (file)
index bbe75d5..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
---[[
-LuCI - Autogenerated Zoneinfo Module
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-module "luci.sys.zoneinfo.tzoffset"
-
-OFFSET = {
-       gmt   =      0, -- GMT
-       eat   =  10800, -- EAT
-       cet   =   3600, -- CET
-       wat   =   3600, -- WAT
-       cat   =   7200, -- CAT
-       wet   =      0, -- WET
-       sast  =   7200, -- SAST
-       eet   =   7200, -- EET
-       hast  = -36000, -- HAST
-       hadt  = -32400, -- HADT
-       akst  = -32400, -- AKST
-       akdt  = -28800, -- AKDT
-       ast   = -14400, -- AST
-       brt   = -10800, -- BRT
-       art   = -10800, -- ART
-       pyt   = -14400, -- PYT
-       pyst  = -10800, -- PYST
-       est   = -18000, -- EST
-       cst   = -21600, -- CST
-       cdt   = -18000, -- CDT
-       amt   = -14400, -- AMT
-       cot   = -18000, -- COT
-       mst   = -25200, -- MST
-       mdt   = -21600, -- MDT
-       vet   = -16200, -- VET
-       gft   = -10800, -- GFT
-       pst   = -28800, -- PST
-       pdt   = -25200, -- PDT
-       ect   = -18000, -- ECT
-       gyt   = -14400, -- GYT
-       bot   = -14400, -- BOT
-       pet   = -18000, -- PET
-       pmst  = -10800, -- PMST
-       pmdt  =  -7200, -- PMDT
-       uyt   = -10800, -- UYT
-       uyst  =  -7200, -- UYST
-       fnt   =  -7200, -- FNT
-       srt   = -10800, -- SRT
-       egt   =  -3600, -- EGT
-       egst  =      0, -- EGST
-       nst   = -12600, -- NST
-       ndt   =  -9000, -- NDT
-       wst   =  28800, -- WST
-       davt  =  25200, -- DAVT
-       ddut  =  36000, -- DDUT
-       mist  =  39600, -- MIST
-       mawt  =  18000, -- MAWT
-       nzst  =  43200, -- NZST
-       nzdt  =  46800, -- NZDT
-       rott  = -10800, -- ROTT
-       syot  =  10800, -- SYOT
-       vost  =  21600, -- VOST
-       almt  =  21600, -- ALMT
-       anat  =  43200, -- ANAT
-       aqtt  =  18000, -- AQTT
-       tmt   =  18000, -- TMT
-       azt   =  14400, -- AZT
-       azst  =  18000, -- AZST
-       ict   =  25200, -- ICT
-       kgt   =  21600, -- KGT
-       bnt   =  28800, -- BNT
-       chot  =  28800, -- CHOT
-       ist   =  19800, -- IST
-       bdt   =  21600, -- BDT
-       tlt   =  32400, -- TLT
-       gst   =  14400, -- GST
-       tjt   =  18000, -- TJT
-       hkt   =  28800, -- HKT
-       hovt  =  25200, -- HOVT
-       irkt  =  32400, -- IRKT
-       wit   =  25200, -- WIT
-       eit   =  32400, -- EIT
-       aft   =  16200, -- AFT
-       pett  =  43200, -- PETT
-       pkt   =  18000, -- PKT
-       npt   =  20700, -- NPT
-       krat  =  28800, -- KRAT
-       myt   =  28800, -- MYT
-       magt  =  43200, -- MAGT
-       cit   =  28800, -- CIT
-       pht   =  28800, -- PHT
-       novt  =  25200, -- NOVT
-       omst  =  25200, -- OMST
-       orat  =  18000, -- ORAT
-       kst   =  32400, -- KST
-       qyzt  =  21600, -- QYZT
-       mmt   =  23400, -- MMT
-       sakt  =  39600, -- SAKT
-       uzt   =  18000, -- UZT
-       sgt   =  28800, -- SGT
-       get   =  14400, -- GET
-       btt   =  21600, -- BTT
-       jst   =  32400, -- JST
-       ulat  =  28800, -- ULAT
-       vlat  =  39600, -- VLAT
-       yakt  =  36000, -- YAKT
-       yekt  =  21600, -- YEKT
-       azot  =  -3600, -- AZOT
-       azost =      0, -- AZOST
-       cvt   =  -3600, -- CVT
-       fkt   = -14400, -- FKT
-       fkst  = -10800, -- FKST
-       cwst  =  31500, -- CWST
-       lhst  =  37800, -- LHST
-       lhst  =  39600, -- LHST
-       fet   =  10800, -- FET
-       msk   =  14400, -- MSK
-       samt  =  14400, -- SAMT
-       volt  =  14400, -- VOLT
-       iot   =  21600, -- IOT
-       cxt   =  25200, -- CXT
-       cct   =  23400, -- CCT
-       tft   =  18000, -- TFT
-       sct   =  14400, -- SCT
-       mvt   =  18000, -- MVT
-       mut   =  14400, -- MUT
-       ret   =  14400, -- RET
-       chast =  45900, -- CHAST
-       chadt =  49500, -- CHADT
-       chut  =  36000, -- CHUT
-       vut   =  39600, -- VUT
-       phot  =  46800, -- PHOT
-       tkt   = -36000, -- TKT
-       fjt   =  43200, -- FJT
-       tvt   =  43200, -- TVT
-       galt  = -21600, -- GALT
-       gamt  = -32400, -- GAMT
-       sbt   =  39600, -- SBT
-       hst   = -36000, -- HST
-       lint  =  50400, -- LINT
-       kost  =  39600, -- KOST
-       mht   =  43200, -- MHT
-       mart  = -34200, -- MART
-       sst   = -39600, -- SST
-       nrt   =  43200, -- NRT
-       nut   = -39600, -- NUT
-       nft   =  41400, -- NFT
-       nct   =  39600, -- NCT
-       pwt   =  32400, -- PWT
-       pont  =  39600, -- PONT
-       pgt   =  36000, -- PGT
-       ckt   = -36000, -- CKT
-       taht  = -36000, -- TAHT
-       gilt  =  43200, -- GILT
-       tot   =  46800, -- TOT
-       wakt  =  43200, -- WAKT
-       wft   =  43200, -- WFT
-}
diff --git a/libs/web/Makefile b/libs/web/Makefile
deleted file mode 100644 (file)
index ad16ec7..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-ifneq (,$(wildcard ../../build/config.mk))
-include ../../build/config.mk
-include ../../build/module.mk
-include ../../build/gccconfig.mk
-else
-include standalone.mk
-endif
-
-TPL_LDFLAGS    =
-TPL_CFLAGS     =
-TPL_SO         = parser.so
-TPL_PO2LMO     = po2lmo
-TPL_PO2LMO_OBJ = src/po2lmo.o
-TPL_LMO_OBJ    = src/template_lmo.o
-TPL_COMMON_OBJ = src/template_parser.o src/template_utils.o
-TPL_LUALIB_OBJ = src/template_lualib.o
-
-%.o: %.c
-       $(COMPILE) $(TPL_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
-
-compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
-       $(LINK) $(SHLIB_FLAGS) $(TPL_LDFLAGS) -o src/$(TPL_SO) \
-               $(TPL_COMMON_OBJ) $(TPL_LMO_OBJ) $(TPL_LUALIB_OBJ)
-       $(LINK) -o src/$(TPL_PO2LMO) \
-               $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
-       mkdir -p dist$(LUCI_LIBRARYDIR)/template
-       cp src/$(TPL_SO) dist$(LUCI_LIBRARYDIR)/template/$(TPL_SO)
-
-install: build
-       cp -pR dist$(LUA_LIBRARYDIR)/* $(LUA_LIBRARYDIR)
-
-clean: build-clean
-
-build-clean:
-       rm -f src/*.o src/$(TPL_SO)
-
-host-compile: build-clean host-clean $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
-       $(LINK) -o src/$(TPL_PO2LMO) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
-
-host-install: host-compile
-       cp src/$(TPL_PO2LMO) ../../build/$(TPL_PO2LMO)
-
-host-clean:
-       rm -f src/$(TPL_PO2LMO)
-       rm -f ../../build/$(TPL_PO2LMO)
diff --git a/libs/web/htdocs/luci-static/resources/cbi.js b/libs/web/htdocs/luci-static/resources/cbi.js
deleted file mode 100644 (file)
index 02814a5..0000000
+++ /dev/null
@@ -1,1339 +0,0 @@
-/*
-       LuCI - Lua Configuration Interface
-
-       Copyright 2008 Steven Barth <steven@midlink.org>
-       Copyright 2008-2012 Jo-Philipp Wich <xm@subsignal.org>
-
-       Licensed under the Apache License, Version 2.0 (the "License");
-       you may not use this file except in compliance with the License.
-       You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-*/
-
-var cbi_d = [];
-var cbi_t = [];
-var cbi_c = [];
-
-var cbi_validators = {
-
-       'integer': function()
-       {
-               return (this.match(/^-?[0-9]+$/) != null);
-       },
-
-       'uinteger': function()
-       {
-               return (cbi_validators.integer.apply(this) && (this >= 0));
-       },
-
-       'float': function()
-       {
-               return !isNaN(parseFloat(this));
-       },
-
-       'ufloat': function()
-       {
-               return (cbi_validators['float'].apply(this) && (this >= 0));
-       },
-
-       'ipaddr': function()
-       {
-               return cbi_validators.ip4addr.apply(this) ||
-                       cbi_validators.ip6addr.apply(this);
-       },
-
-       'ip4addr': function()
-       {
-               if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/))
-               {
-                       return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) &&
-                              (RegExp.$2 >= 0) && (RegExp.$2 <= 255) &&
-                              (RegExp.$3 >= 0) && (RegExp.$3 <= 255) &&
-                              (RegExp.$4 >= 0) && (RegExp.$4 <= 255) &&
-                              ((RegExp.$6.indexOf('.') < 0)
-                                 ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32))
-                                 : (cbi_validators.ip4addr.apply(RegExp.$6)))
-                       ;
-               }
-
-               return false;
-       },
-
-       'ip6addr': function()
-       {
-               if( this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) )
-               {
-                       if( !RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)) )
-                       {
-                               var addr = RegExp.$1;
-
-                               if( addr == '::' )
-                               {
-                                       return true;
-                               }
-
-                               if( addr.indexOf('.') > 0 )
-                               {
-                                       var off = addr.lastIndexOf(':');
-
-                                       if( !(off && cbi_validators.ip4addr.apply(addr.substr(off+1))) )
-                                               return false;
-
-                                       addr = addr.substr(0, off) + ':0:0';
-                               }
-
-                               if( addr.indexOf('::') >= 0 )
-                               {
-                                       var colons = 0;
-                                       var fill = '0';
-
-                                       for( var i = 1; i < (addr.length-1); i++ )
-                                               if( addr.charAt(i) == ':' )
-                                                       colons++;
-
-                                       if( colons > 7 )
-                                               return false;
-
-                                       for( var i = 0; i < (7 - colons); i++ )
-                                               fill += ':0';
-
-                                       if (addr.match(/^(.*?)::(.*?)$/))
-                                               addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill +
-                                                      (RegExp.$2 ? ':' + RegExp.$2 : '');
-                               }
-
-                               return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null);
-                       }
-               }
-
-               return false;
-       },
-
-       'port': function()
-       {
-               return cbi_validators.integer.apply(this) &&
-                       (this >= 0) && (this <= 65535);
-       },
-
-       'portrange': function()
-       {
-               if (this.match(/^(\d+)-(\d+)$/))
-               {
-                       var p1 = RegExp.$1;
-                       var p2 = RegExp.$2;
-
-                       return cbi_validators.port.apply(p1) &&
-                              cbi_validators.port.apply(p2) &&
-                              (parseInt(p1) <= parseInt(p2))
-                       ;
-               }
-               else
-               {
-                       return cbi_validators.port.apply(this);
-               }
-       },
-
-       'macaddr': function()
-       {
-               return (this.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null);
-       },
-
-       'host': function()
-       {
-               return cbi_validators.hostname.apply(this) ||
-                       cbi_validators.ipaddr.apply(this);
-       },
-
-       'hostname': function()
-       {
-               if (this.length <= 253)
-                       return (this.match(/^[a-zA-Z0-9]+$/) != null ||
-                               (this.match(/^[a-zA-Z0-9_][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$/) &&
-                                this.match(/[^0-9.]/)));
-
-               return false;
-       },
-
-       'network': function()
-       {
-               return cbi_validators.uciname.apply(this) ||
-                       cbi_validators.host.apply(this);
-       },
-
-       'wpakey': function()
-       {
-               var v = this;
-
-               if( v.length == 64 )
-                       return (v.match(/^[a-fA-F0-9]{64}$/) != null);
-               else
-                       return (v.length >= 8) && (v.length <= 63);
-       },
-
-       'wepkey': function()
-       {
-               var v = this;
-
-               if ( v.substr(0,2) == 's:' )
-                       v = v.substr(2);
-
-               if( (v.length == 10) || (v.length == 26) )
-                       return (v.match(/^[a-fA-F0-9]{10,26}$/) != null);
-               else
-                       return (v.length == 5) || (v.length == 13);
-       },
-
-       'uciname': function()
-       {
-               return (this.match(/^[a-zA-Z0-9_]+$/) != null);
-       },
-
-       'range': function(min, max)
-       {
-               var val = parseFloat(this);
-               if (!isNaN(min) && !isNaN(max) && !isNaN(val))
-                       return ((val >= min) && (val <= max));
-
-               return false;
-       },
-
-       'min': function(min)
-       {
-               var val = parseFloat(this);
-               if (!isNaN(min) && !isNaN(val))
-                       return (val >= min);
-
-               return false;
-       },
-
-       'max': function(max)
-       {
-               var val = parseFloat(this);
-               if (!isNaN(max) && !isNaN(val))
-                       return (val <= max);
-
-               return false;
-       },
-
-       'rangelength': function(min, max)
-       {
-               var val = '' + this;
-               if (!isNaN(min) && !isNaN(max))
-                       return ((val.length >= min) && (val.length <= max));
-
-               return false;
-       },
-
-       'minlength': function(min)
-       {
-               var val = '' + this;
-               if (!isNaN(min))
-                       return (val.length >= min);
-
-               return false;
-       },
-
-       'maxlength': function(max)
-       {
-               var val = '' + this;
-               if (!isNaN(max))
-                       return (val.length <= max);
-
-               return false;
-       },
-
-       'or': function()
-       {
-               for (var i = 0; i < arguments.length; i += 2)
-               {
-                       if (typeof arguments[i] != 'function')
-                       {
-                               if (arguments[i] == this)
-                                       return true;
-                               i--;
-                       }
-                       else if (arguments[i].apply(this, arguments[i+1]))
-                       {
-                               return true;
-                       }
-               }
-               return false;
-       },
-
-       'and': function()
-       {
-               for (var i = 0; i < arguments.length; i += 2)
-               {
-                       if (typeof arguments[i] != 'function')
-                       {
-                               if (arguments[i] != this)
-                                       return false;
-                               i--;
-                       }
-                       else if (!arguments[i].apply(this, arguments[i+1]))
-                       {
-                               return false;
-                       }
-               }
-               return true;
-       },
-
-       'neg': function()
-       {
-               return cbi_validators.or.apply(
-                       this.replace(/^[ \t]*![ \t]*/, ''), arguments);
-       },
-
-       'list': function(subvalidator, subargs)
-       {
-               if (typeof subvalidator != 'function')
-                       return false;
-
-               var tokens = this.match(/[^ \t]+/g);
-               for (var i = 0; i < tokens.length; i++)
-                       if (!subvalidator.apply(tokens[i], subargs))
-                               return false;
-
-               return true;
-       },
-       'phonedigit': function()
-       {
-               return (this.match(/^[0-9\*#!\.]+$/) != null);
-       }
-};
-
-
-function cbi_d_add(field, dep, next) {
-       var obj = document.getElementById(field);
-       if (obj) {
-               var entry
-               for (var i=0; i<cbi_d.length; i++) {
-                       if (cbi_d[i].id == field) {
-                               entry = cbi_d[i];
-                               break;
-                       }
-               }
-               if (!entry) {
-                       entry = {
-                               "node": obj,
-                               "id": field,
-                               "parent": obj.parentNode.id,
-                               "next": next,
-                               "deps": []
-                       };
-                       cbi_d.unshift(entry);
-               }
-               entry.deps.push(dep)
-       }
-}
-
-function cbi_d_checkvalue(target, ref) {
-       var t = document.getElementById(target);
-       var value;
-
-       if (!t) {
-               var tl = document.getElementsByName(target);
-
-               if( tl.length > 0 && (tl[0].type == 'radio' || tl[0].type == 'checkbox'))
-                       for( var i = 0; i < tl.length; i++ )
-                               if( tl[i].checked ) {
-                                       value = tl[i].value;
-                                       break;
-                               }
-
-               value = value ? value : "";
-       } else if (!t.value) {
-               value = "";
-       } else {
-               value = t.value;
-
-               if (t.type == "checkbox") {
-                       value = t.checked ? value : "";
-               }
-       }
-
-       return (value == ref)
-}
-
-function cbi_d_check(deps) {
-       var reverse;
-       var def = false;
-       for (var i=0; i<deps.length; i++) {
-               var istat = true;
-               reverse = false;
-               for (var j in deps[i]) {
-                       if (j == "!reverse") {
-                               reverse = true;
-                       } else if (j == "!default") {
-                               def = true;
-                               istat = false;
-                       } else {
-                               istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
-                       }
-               }
-               if (istat) {
-                       return !reverse;
-               }
-       }
-       return def;
-}
-
-function cbi_d_update() {
-       var state = false;
-       for (var i=0; i<cbi_d.length; i++) {
-               var entry = cbi_d[i];
-               var next  = document.getElementById(entry.next)
-               var node  = document.getElementById(entry.id)
-               var parent = document.getElementById(entry.parent)
-
-               if (node && node.parentNode && !cbi_d_check(entry.deps)) {
-                       node.parentNode.removeChild(node);
-                       state = true;
-                       if( entry.parent )
-                               cbi_c[entry.parent]--;
-               } else if ((!node || !node.parentNode) && cbi_d_check(entry.deps)) {
-                       if (!next) {
-                               parent.appendChild(entry.node);
-                       } else {
-                               next.parentNode.insertBefore(entry.node, next);
-                       }
-                       state = true;
-                       if( entry.parent )
-                               cbi_c[entry.parent]++;
-               }
-       }
-
-       if (entry && entry.parent) {
-               if (!cbi_t_update())
-                       cbi_tag_last(parent);
-       }
-
-       if (state) {
-               cbi_d_update();
-       }
-}
-
-function cbi_bind(obj, type, callback, mode) {
-       if (!obj.addEventListener) {
-               obj.attachEvent('on' + type,
-                       function(){
-                               var e = window.event;
-
-                               if (!e.target && e.srcElement)
-                                       e.target = e.srcElement;
-
-                               return !!callback(e);
-                       }
-               );
-       } else {
-               obj.addEventListener(type, callback, !!mode);
-       }
-       return obj;
-}
-
-function cbi_combobox(id, values, def, man) {
-       var selid = "cbi.combobox." + id;
-       if (document.getElementById(selid)) {
-               return
-       }
-
-       var obj = document.getElementById(id)
-       var sel = document.createElement("select");
-               sel.id = selid;
-               sel.className = obj.className.replace(/cbi-input-text/, 'cbi-input-select');
-
-       if (obj.nextSibling) {
-               obj.parentNode.insertBefore(sel, obj.nextSibling);
-       } else {
-               obj.parentNode.appendChild(sel);
-       }
-
-       var dt = obj.getAttribute('cbi_datatype');
-       var op = obj.getAttribute('cbi_optional');
-
-       if (dt)
-               cbi_validate_field(sel, op == 'true', dt);
-
-       if (!values[obj.value]) {
-               if (obj.value == "") {
-                       var optdef = document.createElement("option");
-                       optdef.value = "";
-                       optdef.appendChild(document.createTextNode(def));
-                       sel.appendChild(optdef);
-               } else {
-                       var opt = document.createElement("option");
-                       opt.value = obj.value;
-                       opt.selected = "selected";
-                       opt.appendChild(document.createTextNode(obj.value));
-                       sel.appendChild(opt);
-               }
-       }
-
-       for (var i in values) {
-               var opt = document.createElement("option");
-               opt.value = i;
-
-               if (obj.value == i) {
-                       opt.selected = "selected";
-               }
-
-               opt.appendChild(document.createTextNode(values[i]));
-               sel.appendChild(opt);
-       }
-
-       var optman = document.createElement("option");
-       optman.value = "";
-       optman.appendChild(document.createTextNode(man));
-       sel.appendChild(optman);
-
-       obj.style.display = "none";
-
-       cbi_bind(sel, "change", function() {
-               if (sel.selectedIndex == sel.options.length - 1) {
-                       obj.style.display = "inline";
-                       sel.parentNode.removeChild(sel);
-                       obj.focus();
-               } else {
-                       obj.value = sel.options[sel.selectedIndex].value;
-               }
-
-               try {
-                       cbi_d_update();
-               } catch (e) {
-                       //Do nothing
-               }
-       })
-
-       // Retrigger validation in select
-       sel.focus();
-       sel.blur();
-}
-
-function cbi_combobox_init(id, values, def, man) {
-       var obj = document.getElementById(id);
-       cbi_bind(obj, "blur", function() {
-               cbi_combobox(id, values, def, man)
-       });
-       cbi_combobox(id, values, def, man);
-}
-
-function cbi_filebrowser(id, url, defpath) {
-       var field   = document.getElementById(id);
-       var browser = window.open(
-               url + ( field.value || defpath || '' ) + '?field=' + id,
-               "luci_filebrowser", "width=300,height=400,left=100,top=200,scrollbars=yes"
-       );
-
-       browser.focus();
-}
-
-function cbi_browser_init(id, respath, url, defpath)
-{
-       function cbi_browser_btnclick(e) {
-               cbi_filebrowser(id, url, defpath);
-               return false;
-       }
-
-       var field = document.getElementById(id);
-
-       var btn = document.createElement('img');
-       btn.className = 'cbi-image-button';
-       btn.src = respath + '/cbi/folder.gif';
-       field.parentNode.insertBefore(btn, field.nextSibling);
-
-       cbi_bind(btn, 'click', cbi_browser_btnclick);
-}
-
-function cbi_dynlist_init(name, respath, datatype, optional, choices)
-{
-       var input0 = document.getElementsByName(name)[0];
-       var prefix = input0.name;
-       var parent = input0.parentNode;
-       var holder = input0.placeholder;
-
-       var values;
-
-       function cbi_dynlist_redraw(focus, add, del)
-       {
-               values = [ ];
-
-               while (parent.firstChild)
-               {
-                       var n = parent.firstChild;
-                       var i = parseInt(n.index);
-
-                       if (i != del)
-                       {
-                               if (n.nodeName.toLowerCase() == 'input')
-                                       values.push(n.value || '');
-                               else if (n.nodeName.toLowerCase() == 'select')
-                                       values[values.length-1] = n.options[n.selectedIndex].value;
-                       }
-
-                       parent.removeChild(n);
-               }
-
-               if (add >= 0)
-               {
-                       focus = add+1;
-                       values.splice(focus, 0, '');
-               }
-               else if (values.length == 0)
-               {
-                       focus = 0;
-                       values.push('');
-               }
-
-               for (var i = 0; i < values.length; i++)
-               {
-                       var t = document.createElement('input');
-                               t.id = prefix + '.' + (i+1);
-                               t.name = prefix;
-                               t.value = values[i];
-                               t.type = 'text';
-                               t.index = i;
-                               t.className = 'cbi-input-text';
-
-                       if (i == 0 && holder)
-                       {
-                               t.placeholder = holder;
-                       }
-
-                       var b = document.createElement('img');
-                               b.src = respath + ((i+1) < values.length ? '/cbi/remove.gif' : '/cbi/add.gif');
-                               b.className = 'cbi-image-button';
-
-                       parent.appendChild(t);
-                       parent.appendChild(b);
-                       parent.appendChild(document.createElement('br'));
-
-                       if (datatype)
-                       {
-                               cbi_validate_field(t.id, ((i+1) == values.length) || optional, datatype);
-                       }
-
-                       if (choices)
-                       {
-                               cbi_combobox_init(t.id, choices[0], '', choices[1]);
-                               t.nextSibling.index = i;
-
-                               cbi_bind(t.nextSibling, 'keydown',  cbi_dynlist_keydown);
-                               cbi_bind(t.nextSibling, 'keypress', cbi_dynlist_keypress);
-
-                               if (i == focus || -i == focus)
-                                       t.nextSibling.focus();
-                       }
-                       else
-                       {
-                               cbi_bind(t, 'keydown',  cbi_dynlist_keydown);
-                               cbi_bind(t, 'keypress', cbi_dynlist_keypress);
-
-                               if (i == focus)
-                               {
-                                       t.focus();
-                               }
-                               else if (-i == focus)
-                               {
-                                       t.focus();
-
-                                       /* force cursor to end */
-                                       var v = t.value;
-                                       t.value = ' '
-                                       t.value = v;
-                               }
-                       }
-
-                       cbi_bind(b, 'click', cbi_dynlist_btnclick);
-               }
-       }
-
-       function cbi_dynlist_keypress(ev)
-       {
-               ev = ev ? ev : window.event;
-
-               var se = ev.target ? ev.target : ev.srcElement;
-
-               if (se.nodeType == 3)
-                       se = se.parentNode;
-
-               switch (ev.keyCode)
-               {
-                       /* backspace, delete */
-                       case 8:
-                       case 46:
-                               if (se.value.length == 0)
-                               {
-                                       if (ev.preventDefault)
-                                               ev.preventDefault();
-
-                                       return false;
-                               }
-
-                               return true;
-
-                       /* enter, arrow up, arrow down */
-                       case 13:
-                       case 38:
-                       case 40:
-                               if (ev.preventDefault)
-                                       ev.preventDefault();
-
-                               return false;
-               }
-
-               return true;
-       }
-
-       function cbi_dynlist_keydown(ev)
-       {
-               ev = ev ? ev : window.event;
-
-               var se = ev.target ? ev.target : ev.srcElement;
-
-               if (se.nodeType == 3)
-                       se = se.parentNode;
-
-               var prev = se.previousSibling;
-               while (prev && prev.name != name)
-                       prev = prev.previousSibling;
-
-               var next = se.nextSibling;
-               while (next && next.name != name)
-                       next = next.nextSibling;
-
-               /* advance one further in combobox case */
-               if (next && next.nextSibling.name == name)
-                       next = next.nextSibling;
-
-               switch (ev.keyCode)
-               {
-                       /* backspace, delete */
-                       case 8:
-                       case 46:
-                               var del = (se.nodeName.toLowerCase() == 'select')
-                                       ? true : (se.value.length == 0);
-
-                               if (del)
-                               {
-                                       if (ev.preventDefault)
-                                               ev.preventDefault();
-
-                                       var focus = se.index;
-                                       if (ev.keyCode == 8)
-                                               focus = -focus+1;
-
-                                       cbi_dynlist_redraw(focus, -1, se.index);
-
-                                       return false;
-                               }
-
-                               break;
-
-                       /* enter */
-                       case 13:
-                               cbi_dynlist_redraw(-1, se.index, -1);
-                               break;
-
-                       /* arrow up */
-                       case 38:
-                               if (prev)
-                                       prev.focus();
-
-                               break;
-
-                       /* arrow down */
-                       case 40:
-                               if (next)
-                                       next.focus();
-
-                               break;
-               }
-
-               return true;
-       }
-
-       function cbi_dynlist_btnclick(ev)
-       {
-               ev = ev ? ev : window.event;
-
-               var se = ev.target ? ev.target : ev.srcElement;
-
-               if (se.src.indexOf('remove') > -1)
-               {
-                       se.previousSibling.value = '';
-
-                       cbi_dynlist_keydown({
-                               target:  se.previousSibling,
-                               keyCode: 8
-                       });
-               }
-               else
-               {
-                       cbi_dynlist_keydown({
-                               target:  se.previousSibling,
-                               keyCode: 13
-                       });
-               }
-
-               return false;
-       }
-
-       cbi_dynlist_redraw(NaN, -1, -1);
-}
-
-//Hijacks the CBI form to send via XHR (requires Prototype)
-function cbi_hijack_forms(layer, win, fail, load) {
-       var forms = layer.getElementsByTagName('form');
-       for (var i=0; i<forms.length; i++) {
-               $(forms[i]).observe('submit', function(event) {
-                       // Prevent the form from also submitting the regular way
-                       event.stop();
-
-                       // Submit via XHR
-                       event.element().request({
-                               onSuccess: win,
-                               onFailure: fail
-                       });
-
-                       if (load) {
-                               load();
-                       }
-               });
-       }
-}
-
-
-function cbi_t_add(section, tab) {
-       var t = document.getElementById('tab.' + section + '.' + tab);
-       var c = document.getElementById('container.' + section + '.' + tab);
-
-       if( t && c ) {
-               cbi_t[section] = (cbi_t[section] || [ ]);
-               cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
-       }
-}
-
-function cbi_t_switch(section, tab) {
-       if( cbi_t[section] && cbi_t[section][tab] ) {
-               var o = cbi_t[section][tab];
-               var h = document.getElementById('tab.' + section);
-               for( var tid in cbi_t[section] ) {
-                       var o2 = cbi_t[section][tid];
-                       if( o.tab.id != o2.tab.id ) {
-                               o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
-                               o2.container.style.display = 'none';
-                       }
-                       else {
-                               if(h) h.value = tab;
-                               o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
-                               o2.container.style.display = 'block';
-                       }
-               }
-       }
-       return false
-}
-
-function cbi_t_update() {
-       var hl_tabs = [ ];
-       var updated = false;
-
-       for( var sid in cbi_t )
-               for( var tid in cbi_t[sid] )
-               {
-                       if( cbi_c[cbi_t[sid][tid].cid] == 0 ) {
-                               cbi_t[sid][tid].tab.style.display = 'none';
-                       }
-                       else if( cbi_t[sid][tid].tab && cbi_t[sid][tid].tab.style.display == 'none' ) {
-                               cbi_t[sid][tid].tab.style.display = '';
-
-                               var t = cbi_t[sid][tid].tab;
-                               t.className += ' cbi-tab-highlighted';
-                               hl_tabs.push(t);
-                       }
-
-                       cbi_tag_last(cbi_t[sid][tid].container);
-                       updated = true;
-               }
-
-       if( hl_tabs.length > 0 )
-               window.setTimeout(function() {
-                       for( var i = 0; i < hl_tabs.length; i++ )
-                               hl_tabs[i].className = hl_tabs[i].className.replace(/ cbi-tab-highlighted/g, '');
-               }, 750);
-
-       return updated;
-}
-
-
-function cbi_validate_form(form, errmsg)
-{
-       /* if triggered by a section removal or addition, don't validate */
-       if( form.cbi_state == 'add-section' || form.cbi_state == 'del-section' )
-               return true;
-
-       if( form.cbi_validators )
-       {
-               for( var i = 0; i < form.cbi_validators.length; i++ )
-               {
-                       var validator = form.cbi_validators[i];
-                       if( !validator() && errmsg )
-                       {
-                               alert(errmsg);
-                               return false;
-                       }
-               }
-       }
-
-       return true;
-}
-
-function cbi_validate_reset(form)
-{
-       window.setTimeout(
-               function() { cbi_validate_form(form, null) }, 100
-       );
-
-       return true;
-}
-
-function cbi_validate_compile(code)
-{
-       var pos = 0;
-       var esc = false;
-       var depth = 0;
-       var stack = [ ];
-
-       code += ',';
-
-       for (var i = 0; i < code.length; i++)
-       {
-               if (esc)
-               {
-                       esc = false;
-                       continue;
-               }
-
-               switch (code.charCodeAt(i))
-               {
-               case 92:
-                       esc = true;
-                       break;
-
-               case 40:
-               case 44:
-                       if (depth <= 0)
-                       {
-                               if (pos < i)
-                               {
-                                       var label = code.substring(pos, i);
-                                               label = label.replace(/\\(.)/g, '$1');
-                                               label = label.replace(/^[ \t]+/g, '');
-                                               label = label.replace(/[ \t]+$/g, '');
-
-                                       if (label && !isNaN(label))
-                                       {
-                                               stack.push(parseFloat(label));
-                                       }
-                                       else if (label.match(/^(['"]).*\1$/))
-                                       {
-                                               stack.push(label.replace(/^(['"])(.*)\1$/, '$2'));
-                                       }
-                                       else if (typeof cbi_validators[label] == 'function')
-                                       {
-                                               stack.push(cbi_validators[label]);
-                                               stack.push(null);
-                                       }
-                                       else
-                                       {
-                                               throw "Syntax error, unhandled token '"+label+"'";
-                                       }
-                               }
-                               pos = i+1;
-                       }
-                       depth += (code.charCodeAt(i) == 40);
-                       break;
-
-               case 41:
-                       if (--depth <= 0)
-                       {
-                               if (typeof stack[stack.length-2] != 'function')
-                                       throw "Syntax error, argument list follows non-function";
-
-                               stack[stack.length-1] =
-                                       arguments.callee(code.substring(pos, i));
-
-                               pos = i+1;
-                       }
-                       break;
-               }
-       }
-
-       return stack;
-}
-
-function cbi_validate_field(cbid, optional, type)
-{
-       var field = (typeof cbid == "string") ? document.getElementById(cbid) : cbid;
-       var vstack; try { vstack = cbi_validate_compile(type); } catch(e) { };
-
-       if (field && vstack && typeof vstack[0] == "function")
-       {
-               var validator = function()
-               {
-                       // is not detached
-                       if( field.form )
-                       {
-                               field.className = field.className.replace(/ cbi-input-invalid/g, '');
-
-                               // validate value
-                               var value = (field.options && field.options.selectedIndex > -1)
-                                       ? field.options[field.options.selectedIndex].value : field.value;
-
-                               if (!(((value.length == 0) && optional) || vstack[0].apply(value, vstack[1])))
-                               {
-                                       // invalid
-                                       field.className += ' cbi-input-invalid';
-                                       return false;
-                               }
-                       }
-
-                       return true;
-               };
-
-               if( ! field.form.cbi_validators )
-                       field.form.cbi_validators = [ ];
-
-               field.form.cbi_validators.push(validator);
-
-               cbi_bind(field, "blur",  validator);
-               cbi_bind(field, "keyup", validator);
-
-               if (field.nodeName == 'SELECT')
-               {
-                       cbi_bind(field, "change", validator);
-                       cbi_bind(field, "click",  validator);
-               }
-
-               field.setAttribute("cbi_validate", validator);
-               field.setAttribute("cbi_datatype", type);
-               field.setAttribute("cbi_optional", (!!optional).toString());
-
-               validator();
-
-               var fcbox = document.getElementById('cbi.combobox.' + field.id);
-               if (fcbox)
-                       cbi_validate_field(fcbox, optional, type);
-       }
-}
-
-function cbi_row_swap(elem, up, store)
-{
-       var tr = elem.parentNode;
-       while (tr && tr.nodeName.toLowerCase() != 'tr')
-               tr = tr.parentNode;
-
-       if (!tr)
-               return false;
-
-       var table = tr.parentNode;
-       while (table && table.nodeName.toLowerCase() != 'table')
-               table = table.parentNode;
-
-       if (!table)
-               return false;
-
-       var s = up ? 3 : 2;
-       var e = up ? table.rows.length : table.rows.length - 1;
-
-       for (var idx = s; idx < e; idx++)
-       {
-               if (table.rows[idx] == tr)
-               {
-                       if (up)
-                               tr.parentNode.insertBefore(table.rows[idx], table.rows[idx-1]);
-                       else
-                               tr.parentNode.insertBefore(table.rows[idx+1], table.rows[idx]);
-
-                       break;
-               }
-       }
-
-       var ids = [ ];
-       for (idx = 2; idx < table.rows.length; idx++)
-       {
-               table.rows[idx].className = table.rows[idx].className.replace(
-                       /cbi-rowstyle-[12]/, 'cbi-rowstyle-' + (1 + (idx % 2))
-               );
-
-               if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/) )
-                       ids.push(RegExp.$1);
-       }
-
-       var input = document.getElementById(store);
-       if (input)
-               input.value = ids.join(' ');
-
-       return false;
-}
-
-function cbi_tag_last(container)
-{
-       var last;
-
-       for (var i = 0; i < container.childNodes.length; i++)
-       {
-               var c = container.childNodes[i];
-               if (c.nodeType == 1 && c.nodeName.toLowerCase() == 'div')
-               {
-                       c.className = c.className.replace(/ cbi-value-last$/, '');
-                       last = c;
-               }
-       }
-
-       if (last)
-       {
-               last.className += ' cbi-value-last';
-       }
-}
-
-String.prototype.serialize = function()
-{
-       var o = this;
-       switch(typeof(o))
-       {
-               case 'object':
-                       // null
-                       if( o == null )
-                       {
-                               return 'null';
-                       }
-
-                       // array
-                       else if( o.length )
-                       {
-                               var i, s = '';
-
-                               for( var i = 0; i < o.length; i++ )
-                                       s += (s ? ', ' : '') + String.serialize(o[i]);
-
-                               return '[ ' + s + ' ]';
-                       }
-
-                       // object
-                       else
-                       {
-                               var k, s = '';
-
-                               for( k in o )
-                                       s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
-
-                               return '{ ' + s + ' }';
-                       }
-
-                       break;
-
-               case 'string':
-                       // complex string
-                       if( o.match(/[^a-zA-Z0-9_,.: -]/) )
-                               return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
-
-                       // simple string
-                       else
-                               return '"' + o + '"';
-
-                       break;
-
-               default:
-                       return o.toString();
-       }
-}
-
-String.prototype.format = function()
-{
-       if (!RegExp)
-               return;
-
-       var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
-       var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
-
-       function esc(s, r) {
-               for( var i = 0; i < r.length; i += 2 )
-                       s = s.replace(r[i], r[i+1]);
-               return s;
-       }
-
-       var str = this;
-       var out = '';
-       var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
-       var a = b = [], numSubstitutions = 0, numMatches = 0;
-
-       while( a = re.exec(str) )
-       {
-               var m = a[1];
-               var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
-               var pPrecision = a[6], pType = a[7];
-
-               numMatches++;
-
-               if (pType == '%')
-               {
-                       subst = '%';
-               }
-               else
-               {
-                       if (numSubstitutions < arguments.length)
-                       {
-                               var param = arguments[numSubstitutions++];
-
-                               var pad = '';
-                               if (pPad && pPad.substr(0,1) == "'")
-                                       pad = leftpart.substr(1,1);
-                               else if (pPad)
-                                       pad = pPad;
-
-                               var justifyRight = true;
-                               if (pJustify && pJustify === "-")
-                                       justifyRight = false;
-
-                               var minLength = -1;
-                               if (pMinLength)
-                                       minLength = parseInt(pMinLength);
-
-                               var precision = -1;
-                               if (pPrecision && pType == 'f')
-                                       precision = parseInt(pPrecision.substring(1));
-
-                               var subst = param;
-
-                               switch(pType)
-                               {
-                                       case 'b':
-                                               subst = (parseInt(param) || 0).toString(2);
-                                               break;
-
-                                       case 'c':
-                                               subst = String.fromCharCode(parseInt(param) || 0);
-                                               break;
-
-                                       case 'd':
-                                               subst = (parseInt(param) || 0);
-                                               break;
-
-                                       case 'u':
-                                               subst = Math.abs(parseInt(param) || 0);
-                                               break;
-
-                                       case 'f':
-                                               subst = (precision > -1)
-                                                       ? ((parseFloat(param) || 0.0)).toFixed(precision)
-                                                       : (parseFloat(param) || 0.0);
-                                               break;
-
-                                       case 'o':
-                                               subst = (parseInt(param) || 0).toString(8);
-                                               break;
-
-                                       case 's':
-                                               subst = param;
-                                               break;
-
-                                       case 'x':
-                                               subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
-                                               break;
-
-                                       case 'X':
-                                               subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
-                                               break;
-
-                                       case 'h':
-                                               subst = esc(param, html_esc);
-                                               break;
-
-                                       case 'q':
-                                               subst = esc(param, quot_esc);
-                                               break;
-
-                                       case 'j':
-                                               subst = String.serialize(param);
-                                               break;
-
-                                       case 't':
-                                               var td = 0;
-                                               var th = 0;
-                                               var tm = 0;
-                                               var ts = (param || 0);
-
-                                               if (ts > 60) {
-                                                       tm = Math.floor(ts / 60);
-                                                       ts = (ts % 60);
-                                               }
-
-                                               if (tm > 60) {
-                                                       th = Math.floor(tm / 60);
-                                                       tm = (tm % 60);
-                                               }
-
-                                               if (th > 24) {
-                                                       td = Math.floor(th / 24);
-                                                       th = (th % 24);
-                                               }
-
-                                               subst = (td > 0)
-                                                       ? String.format('%dd %dh %dm %ds', td, th, tm, ts)
-                                                       : String.format('%dh %dm %ds', th, tm, ts);
-
-                                               break;
-
-                                       case 'm':
-                                               var mf = pMinLength ? parseInt(pMinLength) : 1000;
-                                               var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
-
-                                               var i = 0;
-                                               var val = parseFloat(param || 0);
-                                               var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
-
-                                               for (i = 0; (i < units.length) && (val > mf); i++)
-                                                       val /= mf;
-
-                                               subst = val.toFixed(pr) + ' ' + units[i];
-                                               break;
-                               }
-                       }
-               }
-
-               out += leftpart + subst;
-               str = str.substr(m.length);
-       }
-
-       return out + str;
-}
-
-String.prototype.nobr = function()
-{
-       return this.replace(/[\s\n]+/g, '&#160;');
-}
-
-String.serialize = function()
-{
-       var a = [ ];
-       for (var i = 1; i < arguments.length; i++)
-               a.push(arguments[i]);
-       return ''.serialize.apply(arguments[0], a);
-}
-
-String.format = function()
-{
-       var a = [ ];
-       for (var i = 1; i < arguments.length; i++)
-               a.push(arguments[i]);
-       return ''.format.apply(arguments[0], a);
-}
-
-String.nobr = function()
-{
-       var a = [ ];
-       for (var i = 1; i < arguments.length; i++)
-               a.push(arguments[i]);
-       return ''.nobr.apply(arguments[0], a);
-}
diff --git a/libs/web/htdocs/luci-static/resources/cbi/add.gif b/libs/web/htdocs/luci-static/resources/cbi/add.gif
deleted file mode 100644 (file)
index 0888abf..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/add.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/apply.gif b/libs/web/htdocs/luci-static/resources/cbi/apply.gif
deleted file mode 100644 (file)
index 82ae7ed..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/apply.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/arrow.gif b/libs/web/htdocs/luci-static/resources/cbi/arrow.gif
deleted file mode 100644 (file)
index 10d797e..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/arrow.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/down.gif b/libs/web/htdocs/luci-static/resources/cbi/down.gif
deleted file mode 100644 (file)
index f0bb6a4..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/down.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/download.gif b/libs/web/htdocs/luci-static/resources/cbi/download.gif
deleted file mode 100644 (file)
index f99a538..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/download.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/edit.gif b/libs/web/htdocs/luci-static/resources/cbi/edit.gif
deleted file mode 100644 (file)
index 7b02b6e..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/edit.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/fieldadd.gif b/libs/web/htdocs/luci-static/resources/cbi/fieldadd.gif
deleted file mode 100644 (file)
index 431ff64..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/fieldadd.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/file.gif b/libs/web/htdocs/luci-static/resources/cbi/file.gif
deleted file mode 100644 (file)
index 3b1217d..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/file.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/find.gif b/libs/web/htdocs/luci-static/resources/cbi/find.gif
deleted file mode 100644 (file)
index 9ae5e34..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/find.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/folder.gif b/libs/web/htdocs/luci-static/resources/cbi/folder.gif
deleted file mode 100644 (file)
index 22b583b..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/folder.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/help.gif b/libs/web/htdocs/luci-static/resources/cbi/help.gif
deleted file mode 100644 (file)
index 9dfa0e1..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/help.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/key.gif b/libs/web/htdocs/luci-static/resources/cbi/key.gif
deleted file mode 100644 (file)
index e3853e5..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/key.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/link.gif b/libs/web/htdocs/luci-static/resources/cbi/link.gif
deleted file mode 100644 (file)
index f0bb78d..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/link.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/reload.gif b/libs/web/htdocs/luci-static/resources/cbi/reload.gif
deleted file mode 100644 (file)
index 8268958..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/reload.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/remove.gif b/libs/web/htdocs/luci-static/resources/cbi/remove.gif
deleted file mode 100644 (file)
index bf43a0a..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/remove.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/reset.gif b/libs/web/htdocs/luci-static/resources/cbi/reset.gif
deleted file mode 100644 (file)
index c941c19..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/reset.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/save.gif b/libs/web/htdocs/luci-static/resources/cbi/save.gif
deleted file mode 100644 (file)
index 35e9499..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/save.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/up.gif b/libs/web/htdocs/luci-static/resources/cbi/up.gif
deleted file mode 100644 (file)
index e823417..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/up.gif and /dev/null differ
diff --git a/libs/web/htdocs/luci-static/resources/cbi/user.gif b/libs/web/htdocs/luci-static/resources/cbi/user.gif
deleted file mode 100644 (file)
index dcb5c2a..0000000
Binary files a/libs/web/htdocs/luci-static/resources/cbi/user.gif and /dev/null differ
diff --git a/libs/web/luasrc/cacheloader.lua b/libs/web/luasrc/cacheloader.lua
deleted file mode 100644 (file)
index 942c4b7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-]]--
-
-local config = require "luci.config"
-local ccache = require "luci.ccache"
-
-module "luci.cacheloader"
-
-if config.ccache and config.ccache.enable == "1" then
-       ccache.cache_ondemand()
-end
\ No newline at end of file
diff --git a/libs/web/luasrc/cbi.lua b/libs/web/luasrc/cbi.lua
deleted file mode 100644 (file)
index ae570b1..0000000
+++ /dev/null
@@ -1,1850 +0,0 @@
---[[
-LuCI - Configuration Bind Interface
-
-Description:
-Offers an interface for binding configuration values to certain
-data types. Supports value and range validation and basic dependencies.
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-module("luci.cbi", package.seeall)
-
-require("luci.template")
-local util = require("luci.util")
-require("luci.http")
-
-
---local event      = require "luci.sys.event"
-local fs         = require("nixio.fs")
-local uci        = require("luci.model.uci")
-local datatypes  = require("luci.cbi.datatypes")
-local class      = util.class
-local instanceof = util.instanceof
-
-FORM_NODATA  =  0
-FORM_PROCEED =  0
-FORM_VALID   =  1
-FORM_DONE       =  1
-FORM_INVALID = -1
-FORM_CHANGED =  2
-FORM_SKIP    =  4
-
-AUTO = true
-
-CREATE_PREFIX = "cbi.cts."
-REMOVE_PREFIX = "cbi.rts."
-RESORT_PREFIX = "cbi.sts."
-FEXIST_PREFIX = "cbi.cbe."
-
--- Loads a CBI map from given file, creating an environment and returns it
-function load(cbimap, ...)
-       local fs   = require "nixio.fs"
-       local i18n = require "luci.i18n"
-       require("luci.config")
-       require("luci.util")
-
-       local upldir = "/lib/uci/upload/"
-       local cbidir = luci.util.libpath() .. "/model/cbi/"
-       local func, err
-
-       if fs.access(cbidir..cbimap..".lua") then
-               func, err = loadfile(cbidir..cbimap..".lua")
-       elseif fs.access(cbimap) then
-               func, err = loadfile(cbimap)
-       else
-               func, err = nil, "Model '" .. cbimap .. "' not found!"
-       end
-
-       assert(func, err)
-
-       local env = {
-               translate=i18n.translate,
-               translatef=i18n.translatef,
-               arg={...}
-       }
-
-       setfenv(func, setmetatable(env, {__index =
-               function(tbl, key)
-                       return rawget(tbl, key) or _M[key] or _G[key]
-               end}))
-
-       local maps       = { func() }
-       local uploads    = { }
-       local has_upload = false
-
-       for i, map in ipairs(maps) do
-               if not instanceof(map, Node) then
-                       error("CBI map returns no valid map object!")
-                       return nil
-               else
-                       map:prepare()
-                       if map.upload_fields then
-                               has_upload = true
-                               for _, field in ipairs(map.upload_fields) do
-                                       uploads[
-                                               field.config .. '.' ..
-                                               (field.section.sectiontype or '1') .. '.' ..
-                                               field.option
-                                       ] = true
-                               end
-                       end
-               end
-       end
-
-       if has_upload then
-               local uci = luci.model.uci.cursor()
-               local prm = luci.http.context.request.message.params
-               local fd, cbid
-
-               luci.http.setfilehandler(
-                       function( field, chunk, eof )
-                               if not field then return end
-                               if field.name and not cbid then
-                                       local c, s, o = field.name:gmatch(
-                                               "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
-                                       )()
-
-                                       if c and s and o then
-                                               local t = uci:get( c, s ) or s
-                                               if uploads[c.."."..t.."."..o] then
-                                                       local path = upldir .. field.name
-                                                       fd = io.open(path, "w")
-                                                       if fd then
-                                                               cbid = field.name
-                                                               prm[cbid] = path
-                                                       end
-                                               end
-                                       end
-                               end
-
-                               if field.name == cbid and fd then
-                                       fd:write(chunk)
-                               end
-
-                               if eof and fd then
-                                       fd:close()
-                                       fd   = nil
-                                       cbid = nil
-                               end
-                       end
-               )
-       end
-
-       return maps
-end
-
---
--- Compile a datatype specification into a parse tree for evaluation later on
---
-local cdt_cache = { }
-
-function compile_datatype(code)
-       local i
-       local pos = 0
-       local esc = false
-       local depth = 0
-       local stack = { }
-
-       for i = 1, #code+1 do
-               local byte = code:byte(i) or 44
-               if esc then
-                       esc = false
-               elseif byte == 92 then
-                       esc = true
-               elseif byte == 40 or byte == 44 then
-                       if depth <= 0 then
-                               if pos < i then
-                                       local label = code:sub(pos, i-1)
-                                               :gsub("\\(.)", "%1")
-                                               :gsub("^%s+", "")
-                                               :gsub("%s+$", "")
-
-                                       if #label > 0 and tonumber(label) then
-                                               stack[#stack+1] = tonumber(label)
-                                       elseif label:match("^'.*'$") or label:match('^".*"$') then
-                                               stack[#stack+1] = label:gsub("[\"'](.*)[\"']", "%1")
-                                       elseif type(datatypes[label]) == "function" then
-                                               stack[#stack+1] = datatypes[label]
-                                               stack[#stack+1] = { }
-                                       else
-                                               error("Datatype error, bad token %q" % label)
-                                       end
-                               end
-                               pos = i + 1
-                       end
-                       depth = depth + (byte == 40 and 1 or 0)
-               elseif byte == 41 then
-                       depth = depth - 1
-                       if depth <= 0 then
-                               if type(stack[#stack-1]) ~= "function" then
-                                       error("Datatype error, argument list follows non-function")
-                               end
-                               stack[#stack] = compile_datatype(code:sub(pos, i-1))
-                               pos = i + 1
-                       end
-               end
-       end
-
-       return stack
-end
-
-function verify_datatype(dt, value)
-       if dt and #dt > 0 then
-               if not cdt_cache[dt] then
-                       local c = compile_datatype(dt)
-                       if c and type(c[1]) == "function" then
-                               cdt_cache[dt] = c
-                       else
-                               error("Datatype error, not a function expression")
-                       end
-               end
-               if cdt_cache[dt] then
-                       return cdt_cache[dt][1](value, unpack(cdt_cache[dt][2]))
-               end
-       end
-       return true
-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
-
--- hook helper
-function Node._run_hook(self, hook)
-       if type(self[hook]) == "function" then
-               return self[hook](self)
-       end
-end
-
-function Node._run_hooks(self, ...)
-       local f
-       local r = false
-       for _, f in ipairs(arg) do
-               if type(self[f]) == "function" then
-                       self[f](self)
-                       r = true
-               end
-       end
-       return r
-end
-
--- Prepare nodes
-function Node.prepare(self, ...)
-       for k, child in ipairs(self.children) do
-               child:prepare(...)
-       end
-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, scope)
-       scope = scope or {}
-       scope.self = self
-
-       luci.template.render(self.template, scope)
-end
-
--- Render the children
-function Node.render_children(self, ...)
-       local k, node
-       for k, node in ipairs(self.children) do
-               node.last_child = (k == #self.children)
-               node:render(...)
-       end
-end
-
-
---[[
-A simple template element
-]]--
-Template = class(Node)
-
-function Template.__init__(self, template)
-       Node.__init__(self)
-       self.template = template
-end
-
-function Template.render(self)
-       luci.template.render(self.template, {self=self})
-end
-
-function Template.parse(self, readinput)
-       self.readinput = (readinput ~= false)
-       return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
-end
-
-
---[[
-Map - A map describing a configuration file
-]]--
-Map = class(Node)
-
-function Map.__init__(self, config, ...)
-       Node.__init__(self, ...)
-
-       self.config = config
-       self.parsechain = {self.config}
-       self.template = "cbi/map"
-       self.apply_on_parse = nil
-       self.readinput = true
-       self.proceed = false
-       self.flow = {}
-
-       self.uci = uci.cursor()
-       self.save = true
-
-       self.changed = false
-
-       if not self.uci:load(self.config) then
-               error("Unable to read UCI data: " .. self.config)
-       end
-end
-
-function Map.formvalue(self, key)
-       return self.readinput and luci.http.formvalue(key)
-end
-
-function Map.formvaluetable(self, key)
-       return self.readinput and luci.http.formvaluetable(key) or {}
-end
-
-function Map.get_scheme(self, sectiontype, option)
-       if not option then
-               return self.scheme and self.scheme.sections[sectiontype]
-       else
-               return self.scheme and self.scheme.variables[sectiontype]
-                and self.scheme.variables[sectiontype][option]
-       end
-end
-
-function Map.submitstate(self)
-       return self:formvalue("cbi.submit")
-end
-
--- Chain foreign config
-function Map.chain(self, config)
-       table.insert(self.parsechain, config)
-end
-
-function Map.state_handler(self, state)
-       return state
-end
-
--- Use optimized UCI writing
-function Map.parse(self, readinput, ...)
-       self.readinput = (readinput ~= false)
-       self:_run_hooks("on_parse")
-
-       if self:formvalue("cbi.skip") then
-               self.state = FORM_SKIP
-               return self:state_handler(self.state)
-       end
-
-       Node.parse(self, ...)
-
-       if self.save then
-               self:_run_hooks("on_save", "on_before_save")
-               for i, config in ipairs(self.parsechain) do
-                       self.uci:save(config)
-               end
-               self:_run_hooks("on_after_save")
-               if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
-                       self:_run_hooks("on_before_commit")
-                       for i, config in ipairs(self.parsechain) do
-                               self.uci:commit(config)
-
-                               -- Refresh data because commit changes section names
-                               self.uci:load(config)
-                       end
-                       self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
-                       if self.apply_on_parse then
-                               self.uci:apply(self.parsechain)
-                               self:_run_hooks("on_apply", "on_after_apply")
-                       else
-                               -- This is evaluated by the dispatcher and delegated to the
-                               -- template which in turn fires XHR to perform the actual
-                               -- apply actions.
-                               self.apply_needed = true
-                       end
-
-                       -- Reparse sections
-                       Node.parse(self, true)
-
-               end
-               for i, config in ipairs(self.parsechain) do
-                       self.uci:unload(config)
-               end
-               if type(self.commit_handler) == "function" then
-                       self:commit_handler(self:submitstate())
-               end
-       end
-
-       if self:submitstate() then
-               if not self.save then
-                       self.state = FORM_INVALID
-               elseif self.proceed then
-                       self.state = FORM_PROCEED
-               else
-                       self.state = self.changed and FORM_CHANGED or FORM_VALID
-               end
-       else
-               self.state = FORM_NODATA
-       end
-
-       return self:state_handler(self.state)
-end
-
-function Map.render(self, ...)
-       self:_run_hooks("on_init")
-       Node.render(self, ...)
-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)
-       return self.uci:add(self.config, sectiontype)
-end
-
--- UCI set
-function Map.set(self, section, option, value)
-       if type(value) ~= "table" or #value > 0 then
-               if option then
-                       return self.uci:set(self.config, section, option, value)
-               else
-                       return self.uci:set(self.config, section, value)
-               end
-       else
-               return Map.del(self, section, option)
-       end
-end
-
--- UCI del
-function Map.del(self, section, option)
-       if option then
-               return self.uci:delete(self.config, section, option)
-       else
-               return self.uci:delete(self.config, section)
-       end
-end
-
--- UCI get
-function Map.get(self, section, option)
-       if not section then
-               return self.uci:get_all(self.config)
-       elseif option then
-               return self.uci:get(self.config, section, option)
-       else
-               return self.uci:get_all(self.config, section)
-       end
-end
-
---[[
-Compound - Container
-]]--
-Compound = class(Node)
-
-function Compound.__init__(self, ...)
-       Node.__init__(self)
-       self.template = "cbi/compound"
-       self.children = {...}
-end
-
-function Compound.populate_delegator(self, delegator)
-       for _, v in ipairs(self.children) do
-               v.delegator = delegator
-       end
-end
-
-function Compound.parse(self, ...)
-       local cstate, state = 0
-
-       for k, child in ipairs(self.children) do
-               cstate = child:parse(...)
-               state = (not state or cstate < state) and cstate or state
-       end
-
-       return state
-end
-
-
---[[
-Delegator - Node controller
-]]--
-Delegator = class(Node)
-function Delegator.__init__(self, ...)
-       Node.__init__(self, ...)
-       self.nodes = {}
-       self.defaultpath = {}
-       self.pageaction = false
-       self.readinput = true
-       self.allow_reset = false
-       self.allow_cancel = false
-       self.allow_back = false
-       self.allow_finish = false
-       self.template = "cbi/delegator"
-end
-
-function Delegator.set(self, name, node)
-       assert(not self.nodes[name], "Duplicate entry")
-
-       self.nodes[name] = node
-end
-
-function Delegator.add(self, name, node)
-       node = self:set(name, node)
-       self.defaultpath[#self.defaultpath+1] = name
-end
-
-function Delegator.insert_after(self, name, after)
-       local n = #self.chain + 1
-       for k, v in ipairs(self.chain) do
-               if v == after then
-                       n = k + 1
-                       break
-               end
-       end
-       table.insert(self.chain, n, name)
-end
-
-function Delegator.set_route(self, ...)
-       local n, chain, route = 0, self.chain, {...}
-       for i = 1, #chain do
-               if chain[i] == self.current then
-                       n = i
-                       break
-               end
-       end
-       for i = 1, #route do
-               n = n + 1
-               chain[n] = route[i]
-       end
-       for i = n + 1, #chain do
-               chain[i] = nil
-       end
-end
-
-function Delegator.get(self, name)
-       local node = self.nodes[name]
-
-       if type(node) == "string" then
-               node = load(node, name)
-       end
-
-       if type(node) == "table" and getmetatable(node) == nil then
-               node = Compound(unpack(node))
-       end
-
-       return node
-end
-
-function Delegator.parse(self, ...)
-       if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
-               if self:_run_hooks("on_cancel") then
-                       return FORM_DONE
-               end
-       end
-
-       if not Map.formvalue(self, "cbi.delg.current") then
-               self:_run_hooks("on_init")
-       end
-
-       local newcurrent
-       self.chain = self.chain or self:get_chain()
-       self.current = self.current or self:get_active()
-       self.active = self.active or self:get(self.current)
-       assert(self.active, "Invalid state")
-
-       local stat = FORM_DONE
-       if type(self.active) ~= "function" then
-               self.active:populate_delegator(self)
-               stat = self.active:parse()
-       else
-               self:active()
-       end
-
-       if stat > FORM_PROCEED then
-               if Map.formvalue(self, "cbi.delg.back") then
-                       newcurrent = self:get_prev(self.current)
-               else
-                       newcurrent = self:get_next(self.current)
-               end
-       elseif stat < FORM_PROCEED then
-               return stat
-       end
-
-
-       if not Map.formvalue(self, "cbi.submit") then
-               return FORM_NODATA
-       elseif stat > FORM_PROCEED
-       and (not newcurrent or not self:get(newcurrent)) then
-               return self:_run_hook("on_done") or FORM_DONE
-       else
-               self.current = newcurrent or self.current
-               self.active = self:get(self.current)
-               if type(self.active) ~= "function" then
-                       self.active:populate_delegator(self)
-                       local stat = self.active:parse(false)
-                       if stat == FORM_SKIP then
-                               return self:parse(...)
-                       else
-                               return FORM_PROCEED
-                       end
-               else
-                       return self:parse(...)
-               end
-       end
-end
-
-function Delegator.get_next(self, state)
-       for k, v in ipairs(self.chain) do
-               if v == state then
-                       return self.chain[k+1]
-               end
-       end
-end
-
-function Delegator.get_prev(self, state)
-       for k, v in ipairs(self.chain) do
-               if v == state then
-                       return self.chain[k-1]
-               end
-       end
-end
-
-function Delegator.get_chain(self)
-       local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
-       return type(x) == "table" and x or {x}
-end
-
-function Delegator.get_active(self)
-       return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
-end
-
---[[
-Page - A simple node
-]]--
-
-Page = class(Node)
-Page.__init__ = Node.__init__
-Page.parse    = function() end
-
-
---[[
-SimpleForm - A Simple non-UCI form
-]]--
-SimpleForm = class(Node)
-
-function SimpleForm.__init__(self, config, title, description, data)
-       Node.__init__(self, title, description)
-       self.config = config
-       self.data = data or {}
-       self.template = "cbi/simpleform"
-       self.dorender = true
-       self.pageaction = false
-       self.readinput = true
-end
-
-SimpleForm.formvalue = Map.formvalue
-SimpleForm.formvaluetable = Map.formvaluetable
-
-function SimpleForm.parse(self, readinput, ...)
-       self.readinput = (readinput ~= false)
-
-       if self:formvalue("cbi.skip") then
-               return FORM_SKIP
-       end
-
-       if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
-               return FORM_DONE
-       end
-
-       if self:submitstate() then
-               Node.parse(self, 1, ...)
-       end
-
-       local valid = true
-       for k, j in ipairs(self.children) do
-               for i, v in ipairs(j.children) do
-                       valid = valid
-                        and (not v.tag_missing or not v.tag_missing[1])
-                        and (not v.tag_invalid or not v.tag_invalid[1])
-                        and (not v.error)
-               end
-       end
-
-       local state =
-               not self:submitstate() and FORM_NODATA
-               or valid and FORM_VALID
-               or FORM_INVALID
-
-       self.dorender = not self.handle
-       if self.handle then
-               local nrender, nstate = self:handle(state, self.data)
-               self.dorender = self.dorender or (nrender ~= false)
-               state = nstate or state
-       end
-       return state
-end
-
-function SimpleForm.render(self, ...)
-       if self.dorender then
-               Node.render(self, ...)
-       end
-end
-
-function SimpleForm.submitstate(self)
-       return self:formvalue("cbi.submit")
-end
-
-function SimpleForm.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
-
--- Creates a child field
-function SimpleForm.field(self, class, ...)
-       local section
-       for k, v in ipairs(self.children) do
-               if instanceof(v, SimpleSection) then
-                       section = v
-                       break
-               end
-       end
-       if not section then
-               section = self:section(SimpleSection)
-       end
-
-       if instanceof(class, AbstractValue) then
-               local obj  = class(self, section, ...)
-               obj.track_missing = true
-               section:append(obj)
-               return obj
-       else
-               error("class must be a descendent of AbstractValue")
-       end
-end
-
-function SimpleForm.set(self, section, option, value)
-       self.data[option] = value
-end
-
-
-function SimpleForm.del(self, section, option)
-       self.data[option] = nil
-end
-
-
-function SimpleForm.get(self, section, option)
-       return self.data[option]
-end
-
-
-function SimpleForm.get_scheme()
-       return nil
-end
-
-
-Form = class(SimpleForm)
-
-function Form.__init__(self, ...)
-       SimpleForm.__init__(self, ...)
-       self.embedded = true
-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.defaults = {}
-       self.fields = {}
-       self.tag_error = {}
-       self.tag_invalid = {}
-       self.tag_deperror = {}
-       self.changed = false
-
-       self.optional = true
-       self.addremove = false
-       self.dynamic = false
-end
-
--- Define a tab for the section
-function AbstractSection.tab(self, tab, title, desc)
-       self.tabs      = self.tabs      or { }
-       self.tab_names = self.tab_names or { }
-
-       self.tab_names[#self.tab_names+1] = tab
-       self.tabs[tab] = {
-               title       = title,
-               description = desc,
-               childs      = { }
-       }
-end
-
--- Check whether the section has tabs
-function AbstractSection.has_tabs(self)
-       return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
-end
-
--- Appends a new option
-function AbstractSection.option(self, class, option, ...)
-       if instanceof(class, AbstractValue) then
-               local obj  = class(self.map, self, option, ...)
-               self:append(obj)
-               self.fields[option] = obj
-               return obj
-       elseif class == true then
-               error("No valid class was given and autodetection failed.")
-       else
-               error("class must be a descendant of AbstractValue")
-       end
-end
-
--- Appends a new tabbed option
-function AbstractSection.taboption(self, tab, ...)
-
-       assert(tab and self.tabs and self.tabs[tab],
-               "Cannot assign option to not existing tab %q" % tostring(tab))
-
-       local l = self.tabs[tab].childs
-       local o = AbstractSection.option(self, ...)
-
-       if o then l[#l+1] = o end
-
-       return o
-end
-
--- Render a single tab
-function AbstractSection.render_tab(self, tab, ...)
-
-       assert(tab and self.tabs and self.tabs[tab],
-               "Cannot render not existing tab %q" % tostring(tab))
-
-       local k, node
-       for k, node in ipairs(self.tabs[tab].childs) do
-               node.last_child = (k == #self.tabs[tab].childs)
-               node:render(...)
-       end
-end
-
--- Parse optional options
-function AbstractSection.parse_optionals(self, section)
-       if not self.optional then
-               return
-       end
-
-       self.optionals[section] = {}
-
-       local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
-       for k,v in ipairs(self.children) do
-               if v.optional and not v:cfgvalue(section) and not self:has_tabs() then
-                       if field == v.option then
-                               field = nil
-                               self.map.proceed = true
-                       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  = luci.util.clone(self:cfgvalue(section))
-       local form = self.map: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.map.proceed = true
-                       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
-
--- Push events
-function AbstractSection.push_events(self)
-       --luci.util.append(self.map.events, self.events)
-       self.map.changed = true
-end
-
--- Removes the section
-function AbstractSection.remove(self, section)
-       self.map.proceed = true
-       return self.map:del(section)
-end
-
--- Creates the section
-function AbstractSection.create(self, section)
-       local stat
-
-       if section then
-               stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
-       else
-               section = self.map:add(self.sectiontype)
-               stat = section
-       end
-
-       if stat then
-               for k,v in pairs(self.children) do
-                       if v.default then
-                               self.map:set(section, v.option, v.default)
-                       end
-               end
-
-               for k,v in pairs(self.defaults) do
-                       self.map:set(section, k, v)
-               end
-       end
-
-       self.map.proceed = true
-
-       return stat
-end
-
-
-SimpleSection = class(AbstractSection)
-
-function SimpleSection.__init__(self, form, ...)
-       AbstractSection.__init__(self, form, nil, ...)
-       self.template = "cbi/nullsection"
-end
-
-
-Table = class(AbstractSection)
-
-function Table.__init__(self, form, data, ...)
-       local datasource = {}
-       local tself = self
-       datasource.config = "table"
-       self.data = data or {}
-
-       datasource.formvalue = Map.formvalue
-       datasource.formvaluetable = Map.formvaluetable
-       datasource.readinput = true
-
-       function datasource.get(self, section, option)
-               return tself.data[section] and tself.data[section][option]
-       end
-
-       function datasource.submitstate(self)
-               return Map.formvalue(self, "cbi.submit")
-       end
-
-       function datasource.del(...)
-               return true
-       end
-
-       function datasource.get_scheme()
-               return nil
-       end
-
-       AbstractSection.__init__(self, datasource, "table", ...)
-       self.template = "cbi/tblsection"
-       self.rowcolors = true
-       self.anonymous = true
-end
-
-function Table.parse(self, readinput)
-       self.map.readinput = (readinput ~= false)
-       for i, k in ipairs(self:cfgsections()) do
-               if self.map:submitstate() then
-                       Node.parse(self, k)
-               end
-       end
-end
-
-function Table.cfgsections(self)
-       local sections = {}
-
-       for i, v in luci.util.kspairs(self.data) do
-               table.insert(sections, i)
-       end
-
-       return sections
-end
-
-function Table.update(self, data)
-       self.data = data
-end
-
-
-
---[[
-NamedSection - A fixed configuration section defined by its name
-]]--
-NamedSection = class(AbstractSection)
-
-function NamedSection.__init__(self, map, section, stype, ...)
-       AbstractSection.__init__(self, map, stype, ...)
-
-       -- Defaults
-       self.addremove = false
-       self.template = "cbi/nsection"
-       self.section = section
-end
-
-function NamedSection.parse(self, novld)
-       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 self.map:formvalue("cbi.rns."..path) and self:remove(s) then
-                               self:push_events()
-                               return
-                       end
-               else           -- Create and apply default values
-                       if self.map:formvalue("cbi.cns."..path) then
-                               self:create(s)
-                               return
-                       end
-               end
-       end
-
-       if active then
-               AbstractSection.parse_dynamic(self, s)
-               if self.map:submitstate() then
-                       Node.parse(self, s)
-               end
-               AbstractSection.parse_optionals(self, s)
-
-               if self.changed then
-                       self:push_events()
-               end
-       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, map, type, ...)
-       AbstractSection.__init__(self, map, type, ...)
-
-       self.template = "cbi/tsection"
-       self.deps = {}
-       self.anonymous = false
-end
-
--- Return all matching UCI sections for this TypedSection
-function TypedSection.cfgsections(self)
-       local sections = {}
-       self.map.uci:foreach(self.map.config, self.sectiontype,
-               function (section)
-                       if self:checkscope(section[".name"]) then
-                               table.insert(sections, section[".name"])
-                       end
-               end)
-
-       return sections
-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
-
-function TypedSection.parse(self, novld)
-       if self.addremove then
-               -- Remove
-               local crval = REMOVE_PREFIX .. self.config
-               local name = self.map:formvaluetable(crval)
-               for k,v in pairs(name) do
-                       if k:sub(-2) == ".x" then
-                               k = k:sub(1, #k - 2)
-                       end
-                       if self:cfgvalue(k) and self:checkscope(k) then
-                               self:remove(k)
-                       end
-               end
-       end
-
-       local co
-       for i, k in ipairs(self:cfgsections()) do
-               AbstractSection.parse_dynamic(self, k)
-               if self.map:submitstate() then
-                       Node.parse(self, k, novld)
-               end
-               AbstractSection.parse_optionals(self, k)
-       end
-
-       if self.addremove then
-               -- Create
-               local created
-               local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
-               local origin, name = next(self.map:formvaluetable(crval))
-               if self.anonymous then
-                       if name then
-                               created = self:create(nil, origin)
-                       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 > 0 then
-                                       created = self:create(name, origin) and name
-                                       if not created then
-                                               self.invalid_cts = true
-                                       end
-                               end
-                       end
-               end
-
-               if created then
-                       AbstractSection.parse_optionals(self, created)
-               end
-       end
-
-       if self.sortable then
-               local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
-               local order = self.map:formvalue(stval)
-               if order and #order > 0 then
-                       local sid
-                       local num = 0
-                       for sid in util.imatch(order) do
-                               self.map.uci:reorder(self.config, sid, num)
-                               num = num + 1
-                       end
-                       self.changed = (num > 0)
-               end
-       end
-
-       if created or self.changed then
-               self:push_events()
-       end
-end
-
--- Verifies scope of sections
-function TypedSection.checkscope(self, section)
-       -- Check if we are not excluded
-       if self.filter and not self:filter(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, section, option, ...)
-       Node.__init__(self, ...)
-       self.section = section
-       self.option  = option
-       self.map     = map
-       self.config  = map.config
-       self.tag_invalid = {}
-       self.tag_missing = {}
-       self.tag_reqerror = {}
-       self.tag_error = {}
-       self.deps = {}
-       self.subdeps = {}
-       --self.cast = "string"
-
-       self.track_missing = false
-       self.rmempty   = true
-       self.default   = nil
-       self.size      = nil
-       self.optional  = false
-end
-
-function AbstractValue.prepare(self)
-       self.cast = self.cast or "string"
-end
-
--- Add a dependencie to another section field
-function AbstractValue.depends(self, field, value)
-       local deps
-       if type(field) == "string" then
-               deps = {}
-               deps[field] = value
-       else
-               deps = field
-       end
-
-       table.insert(self.deps, {deps=deps, add=""})
-end
-
--- Generates the unique CBID
-function AbstractValue.cbid(self, section)
-       return "cbid."..self.map.config.."."..section.."."..self.option
-end
-
--- Return whether this object should be created
-function AbstractValue.formcreated(self, section)
-       local key = "cbi.opt."..self.config.."."..section
-       return (self.map:formvalue(key) == self.option)
-end
-
--- Returns the formvalue for this object
-function AbstractValue.formvalue(self, section)
-       return self.map:formvalue(self:cbid(section))
-end
-
-function AbstractValue.additional(self, value)
-       self.optional = value
-end
-
-function AbstractValue.mandatory(self, value)
-       self.rmempty = not value
-end
-
-function AbstractValue.add_error(self, section, type, msg)
-       self.error = self.error or { }
-       self.error[section] = msg or type
-
-       self.section.error = self.section.error or { }
-       self.section.error[section] = self.section.error[section] or { }
-       table.insert(self.section.error[section], msg or type)
-
-       if type == "invalid" then
-               self.tag_invalid[section] = true
-       elseif type == "missing" then
-               self.tag_missing[section] = true
-       end
-
-       self.tag_error[section] = true
-       self.map.save = false
-end
-
-function AbstractValue.parse(self, section, novld)
-       local fvalue = self:formvalue(section)
-       local cvalue = self:cfgvalue(section)
-
-       -- If favlue and cvalue are both tables and have the same content
-       -- make them identical
-       if type(fvalue) == "table" and type(cvalue) == "table" then
-               local equal = #fvalue == #cvalue
-               if equal then
-                       for i=1, #fvalue do
-                               if cvalue[i] ~= fvalue[i] then
-                                       equal = false
-                               end
-                       end
-               end
-               if equal then
-                       fvalue = cvalue
-               end
-       end
-
-       if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
-               local val_err
-               fvalue, val_err = self:validate(fvalue, section)
-               fvalue = self:transform(fvalue)
-
-               if not fvalue and not novld then
-                       self:add_error(section, "invalid", val_err)
-               end
-
-               if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
-                       if self:write(section, fvalue) then
-                               -- Push events
-                               self.section.changed = true
-                               --luci.util.append(self.map.events, self.events)
-                       end
-               end
-       else                                                    -- Unset the UCI or error
-               if self.rmempty or self.optional then
-                       if self:remove(section) then
-                               -- Push events
-                               self.section.changed = true
-                               --luci.util.append(self.map.events, self.events)
-                       end
-               elseif cvalue ~= fvalue and not novld then
-                       -- trigger validator with nil value to get custom user error msg.
-                       local _, val_err = self:validate(nil, section)
-                       self:add_error(section, "missing", val_err)
-               end
-       end
-end
-
--- Render if this value exists or if it is mandatory
-function AbstractValue.render(self, s, scope)
-       if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
-               scope = scope or {}
-               scope.section = s
-               scope.cbid    = self:cbid(s)
-               Node.render(self, scope)
-       end
-end
-
--- Return the UCI value of this object
-function AbstractValue.cfgvalue(self, section)
-       local value
-       if self.tag_error[section] then
-               value = self:formvalue(section)
-       else
-               value = self.map:get(section, self.option)
-       end
-
-       if not value then
-               return nil
-       elseif not self.cast or self.cast == type(value) then
-               return value
-       elseif self.cast == "string" then
-               if type(value) == "table" then
-                       return value[1]
-               end
-       elseif self.cast == "table" then
-               return { value }
-       end
-end
-
--- Validate the form value
-function AbstractValue.validate(self, value)
-       if self.datatype and value then
-               if type(value) == "table" then
-                       local v
-                       for _, v in ipairs(value) do
-                               if v and #v > 0 and not verify_datatype(self.datatype, v) then
-                                       return nil
-                               end
-                       end
-               else
-                       if not verify_datatype(self.datatype, value) then
-                               return nil
-                       end
-               end
-       end
-
-       return value
-end
-
-AbstractValue.transform = AbstractValue.validate
-
-
--- 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
-]]--
-Value = class(AbstractValue)
-
-function Value.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template  = "cbi/value"
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function Value.reset_values(self)
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function Value.value(self, key, val)
-       val = val or key
-       table.insert(self.keylist, tostring(key))
-       table.insert(self.vallist, tostring(val))
-end
-
-
--- DummyValue - This does nothing except being there
-DummyValue = class(AbstractValue)
-
-function DummyValue.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template = "cbi/dvalue"
-       self.value = nil
-end
-
-function DummyValue.cfgvalue(self, section)
-       local value
-       if self.value then
-               if type(self.value) == "function" then
-                       value = self:value(section)
-               else
-                       value = self.value
-               end
-       else
-               value = AbstractValue.cfgvalue(self, section)
-       end
-       return value
-end
-
-function DummyValue.parse(self)
-
-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"
-       self.default  = self.disabled
-end
-
--- A flag can only have two states: set or unset
-function Flag.parse(self, section)
-       local fexists = self.map:formvalue(
-               FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
-
-       if fexists then
-               local fvalue = self:formvalue(section) and self.enabled or self.disabled
-               if fvalue ~= self.default or (not self.optional and not self.rmempty) then
-                       self:write(section, fvalue)
-               else
-                       self:remove(section)
-               end
-       else
-               self:remove(section)
-       end
-end
-
-function Flag.cfgvalue(self, section)
-       return AbstractValue.cfgvalue(self, section) or self.default
-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.reset_values(self)
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function ListValue.value(self, key, val, ...)
-       if luci.util.contains(self.keylist, key) then
-               return
-       end
-
-       val = val or key
-       table.insert(self.keylist, tostring(key))
-       table.insert(self.vallist, tostring(val))
-
-       for i, deps in ipairs({...}) do
-               self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
-       end
-end
-
-function ListValue.validate(self, val)
-       if luci.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.render(self, ...)
-       if self.widget == "select" and not self.size then
-               self.size = #self.vallist
-       end
-
-       AbstractValue.render(self, ...)
-end
-
-function MultiValue.reset_values(self)
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function MultiValue.value(self, key, val)
-       if luci.util.contains(self.keylist, key) then
-               return
-       end
-
-       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 luci.util.split(val, self.delimiter)
-end
-
-function MultiValue.validate(self, val)
-       val = (type(val) == "table") and val or {val}
-
-       local result
-
-       for i, value in ipairs(val) do
-               if luci.util.contains(self.keylist, value) then
-                       result = result and (result .. self.delimiter .. value) or value
-               end
-       end
-
-       return result
-end
-
-
-StaticList = class(MultiValue)
-
-function StaticList.__init__(self, ...)
-       MultiValue.__init__(self, ...)
-       self.cast = "table"
-       self.valuelist = self.cfgvalue
-
-       if not self.override_scheme
-        and self.map:get_scheme(self.section.sectiontype, self.option) then
-               local vs = self.map:get_scheme(self.section.sectiontype, self.option)
-               if self.value and vs.values and not self.override_values then
-                       for k, v in pairs(vs.values) do
-                               self:value(k, v)
-                       end
-               end
-       end
-end
-
-function StaticList.validate(self, value)
-       value = (type(value) == "table") and value or {value}
-
-       local valid = {}
-       for i, v in ipairs(value) do
-               if luci.util.contains(self.keylist, v) then
-                       table.insert(valid, v)
-               end
-       end
-       return valid
-end
-
-
-DynamicList = class(AbstractValue)
-
-function DynamicList.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template  = "cbi/dynlist"
-       self.cast = "table"
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function DynamicList.reset_values(self)
-       self.keylist = {}
-       self.vallist = {}
-end
-
-function DynamicList.value(self, key, val)
-       val = val or key
-       table.insert(self.keylist, tostring(key))
-       table.insert(self.vallist, tostring(val))
-end
-
-function DynamicList.write(self, section, value)
-       local t = { }
-
-       if type(value) == "table" then
-               local x
-               for _, x in ipairs(value) do
-                       if x and #x > 0 then
-                               t[#t+1] = x
-                       end
-               end
-       else
-               t = { value }
-       end
-
-       if self.cast == "string" then
-               value = table.concat(t, " ")
-       else
-               value = t
-       end
-
-       return AbstractValue.write(self, section, value)
-end
-
-function DynamicList.cfgvalue(self, section)
-       local value = AbstractValue.cfgvalue(self, section)
-
-       if type(value) == "string" then
-               local x
-               local t = { }
-               for x in value:gmatch("%S+") do
-                       if #x > 0 then
-                               t[#t+1] = x
-                       end
-               end
-               value = t
-       end
-
-       return value
-end
-
-function DynamicList.formvalue(self, section)
-       local value = AbstractValue.formvalue(self, section)
-
-       if type(value) == "string" then
-               if self.cast == "string" then
-                       local x
-                       local t = { }
-                       for x in value:gmatch("%S+") do
-                               t[#t+1] = x
-                       end
-                       value = t
-               else
-                       value = { value }
-               end
-       end
-
-       return value
-end
-
-
---[[
-TextValue - A multi-line value
-       rows:   Rows
-]]--
-TextValue = class(AbstractValue)
-
-function TextValue.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template  = "cbi/tvalue"
-end
-
---[[
-Button
-]]--
-Button = class(AbstractValue)
-
-function Button.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template  = "cbi/button"
-       self.inputstyle = nil
-       self.rmempty = true
-end
-
-
-FileUpload = class(AbstractValue)
-
-function FileUpload.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template = "cbi/upload"
-       if not self.map.upload_fields then
-               self.map.upload_fields = { self }
-       else
-               self.map.upload_fields[#self.map.upload_fields+1] = self
-       end
-end
-
-function FileUpload.formcreated(self, section)
-       return AbstractValue.formcreated(self, section) or
-               self.map:formvalue("cbi.rlf."..section.."."..self.option) or
-               self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
-end
-
-function FileUpload.cfgvalue(self, section)
-       local val = AbstractValue.cfgvalue(self, section)
-       if val and fs.access(val) then
-               return val
-       end
-       return nil
-end
-
-function FileUpload.formvalue(self, section)
-       local val = AbstractValue.formvalue(self, section)
-       if val then
-               if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
-                  not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
-               then
-                       return val
-               end
-               fs.unlink(val)
-               self.value = nil
-       end
-       return nil
-end
-
-function FileUpload.remove(self, section)
-       local val = AbstractValue.formvalue(self, section)
-       if val and fs.access(val) then fs.unlink(val) end
-       return AbstractValue.remove(self, section)
-end
-
-
-FileBrowser = class(AbstractValue)
-
-function FileBrowser.__init__(self, ...)
-       AbstractValue.__init__(self, ...)
-       self.template = "cbi/browser"
-end
diff --git a/libs/web/luasrc/cbi/datatypes.lua b/libs/web/luasrc/cbi/datatypes.lua
deleted file mode 100644 (file)
index c5f4ec0..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
---[[
-
-LuCI - Configuration Bind Interface - Datatype Tests
-(c) 2010 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-
-]]--
-
-local fs = require "nixio.fs"
-local ip = require "luci.ip"
-local math = require "math"
-local util = require "luci.util"
-local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
-
-
-module "luci.cbi.datatypes"
-
-
-_M['or'] = function(v, ...)
-       local i
-       for i = 1, select('#', ...), 2 do
-               local f = select(i, ...)
-               local a = select(i+1, ...)
-               if type(f) ~= "function" then
-                       if f == v then
-                               return true
-                       end
-                       i = i - 1
-               elseif f(v, unpack(a)) then
-                       return true
-               end
-       end
-       return false
-end
-
-_M['and'] = function(v, ...)
-       local i
-       for i = 1, select('#', ...), 2 do
-               local f = select(i, ...)
-               local a = select(i+1, ...)
-               if type(f) ~= "function" then
-                       if f ~= v then
-                               return false
-                       end
-                       i = i - 1
-               elseif not f(v, unpack(a)) then
-                       return false
-               end
-       end
-       return true
-end
-
-function neg(v, ...)
-       return _M['or'](v:gsub("^%s*!%s*", ""), ...)
-end
-
-function list(v, subvalidator, subargs)
-       if type(subvalidator) ~= "function" then
-               return false
-       end
-       local token
-       for token in v:gmatch("%S+") do
-               if not subvalidator(token, unpack(subargs)) then
-                       return false
-               end
-       end
-       return true
-end
-
-function bool(val)
-       if val == "1" or val == "yes" or val == "on" or val == "true" then
-               return true
-       elseif val == "0" or val == "no" or val == "off" or val == "false" then
-               return true
-       elseif val == "" or val == nil then
-               return true
-       end
-
-       return false
-end
-
-function uinteger(val)
-       local n = tonumber(val)
-       if n ~= nil and math.floor(n) == n and n >= 0 then
-               return true
-       end
-
-       return false
-end
-
-function integer(val)
-       local n = tonumber(val)
-       if n ~= nil and math.floor(n) == n then
-               return true
-       end
-
-       return false
-end
-
-function ufloat(val)
-       local n = tonumber(val)
-       return ( n ~= nil and n >= 0 )
-end
-
-function float(val)
-       return ( tonumber(val) ~= nil )
-end
-
-function ipaddr(val)
-       return ip4addr(val) or ip6addr(val)
-end
-
-function ip4addr(val)
-       if val then
-               return ip.IPv4(val) and true or false
-       end
-
-       return false
-end
-
-function ip4prefix(val)
-       val = tonumber(val)
-       return ( val and val >= 0 and val <= 32 )
-end
-
-function ip6addr(val)
-       if val then
-               return ip.IPv6(val) and true or false
-       end
-
-       return false
-end
-
-function ip6prefix(val)
-       val = tonumber(val)
-       return ( val and val >= 0 and val <= 128 )
-end
-
-function port(val)
-       val = tonumber(val)
-       return ( val and val >= 0 and val <= 65535 )
-end
-
-function portrange(val)
-       local p1, p2 = val:match("^(%d+)%-(%d+)$")
-       if p1 and p2 and port(p1) and port(p2) then
-               return true
-       else
-               return port(val)
-       end
-end
-
-function macaddr(val)
-       if val and val:match(
-               "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
-                "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
-       ) then
-               local parts = util.split( val, ":" )
-
-               for i = 1,6 do
-                       parts[i] = tonumber( parts[i], 16 )
-                       if parts[i] < 0 or parts[i] > 255 then
-                               return false
-                       end
-               end
-
-               return true
-       end
-
-       return false
-end
-
-function hostname(val)
-       if val and (#val < 254) and (
-          val:match("^[a-zA-Z_]+$") or
-          (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
-           val:match("[^0-9%.]"))
-       ) then
-               return true
-       end
-       return false
-end
-
-function host(val)
-       return hostname(val) or ipaddr(val)
-end
-
-function network(val)
-       return uciname(val) or host(val)
-end
-
-function wpakey(val)
-       if #val == 64 then
-               return (val:match("^[a-fA-F0-9]+$") ~= nil)
-       else
-               return (#val >= 8) and (#val <= 63)
-       end
-end
-
-function wepkey(val)
-       if val:sub(1, 2) == "s:" then
-               val = val:sub(3)
-       end
-
-       if (#val == 10) or (#val == 26) then
-               return (val:match("^[a-fA-F0-9]+$") ~= nil)
-       else
-               return (#val == 5) or (#val == 13)
-       end
-end
-
-function string(val)
-       return true             -- Everything qualifies as valid string
-end
-
-function directory( val, seen )
-       local s = fs.stat(val)
-       seen = seen or { }
-
-       if s and not seen[s.ino] then
-               seen[s.ino] = true
-               if s.type == "dir" then
-                       return true
-               elseif s.type == "lnk" then
-                       return directory( fs.readlink(val), seen )
-               end
-       end
-
-       return false
-end
-
-function file( val, seen )
-       local s = fs.stat(val)
-       seen = seen or { }
-
-       if s and not seen[s.ino] then
-               seen[s.ino] = true
-               if s.type == "reg" then
-                       return true
-               elseif s.type == "lnk" then
-                       return file( fs.readlink(val), seen )
-               end
-       end
-
-       return false
-end
-
-function device( val, seen )
-       local s = fs.stat(val)
-       seen = seen or { }
-
-       if s and not seen[s.ino] then
-               seen[s.ino] = true
-               if s.type == "chr" or s.type == "blk" then
-                       return true
-               elseif s.type == "lnk" then
-                       return device( fs.readlink(val), seen )
-               end
-       end
-
-       return false
-end
-
-function uciname(val)
-       return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
-end
-
-function range(val, min, max)
-       val = tonumber(val)
-       min = tonumber(min)
-       max = tonumber(max)
-
-       if val ~= nil and min ~= nil and max ~= nil then
-               return ((val >= min) and (val <= max))
-       end
-
-       return false
-end
-
-function min(val, min)
-       val = tonumber(val)
-       min = tonumber(min)
-
-       if val ~= nil and min ~= nil then
-               return (val >= min)
-       end
-
-       return false
-end
-
-function max(val, max)
-       val = tonumber(val)
-       max = tonumber(max)
-
-       if val ~= nil and max ~= nil then
-               return (val <= max)
-       end
-
-       return false
-end
-
-function rangelength(val, min, max)
-       val = tostring(val)
-       min = tonumber(min)
-       max = tonumber(max)
-
-       if val ~= nil and min ~= nil and max ~= nil then
-               return ((#val >= min) and (#val <= max))
-       end
-
-       return false
-end
-
-function minlength(val, min)
-       val = tostring(val)
-       min = tonumber(min)
-
-       if val ~= nil and min ~= nil then
-               return (#val >= min)
-       end
-
-       return false
-end
-
-function maxlength(val, max)
-       val = tostring(val)
-       max = tonumber(max)
-
-       if val ~= nil and max ~= nil then
-               return (#val <= max)
-       end
-
-       return false
-end
-
-function phonedigit(val)
-       return (val:match("^[0-9\*#!%.]+$") ~= nil)
-end
diff --git a/libs/web/luasrc/config.lua b/libs/web/luasrc/config.lua
deleted file mode 100644 (file)
index 53db82b..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
---[[
-LuCI - Configuration
-
-Description:
-Some LuCI configuration values read from uci file "luci"
-
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at 
-
-       http://www.apache.org/licenses/LICENSE-2.0 
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local util = require "luci.util"
-module("luci.config",
-               function(m)
-                       if pcall(require, "luci.model.uci") then
-                               local config = util.threadlocal()
-                               setmetatable(m, {
-                                       __index = function(tbl, key)
-                                               if not config[key] then
-                                                       config[key] = luci.model.uci.cursor():get_all("luci", key)
-                                               end
-                                               return config[key]
-                                       end
-                               })
-                       end
-               end)
diff --git a/libs/web/luasrc/dispatcher.lua b/libs/web/luasrc/dispatcher.lua
deleted file mode 100644 (file)
index 9e5b78d..0000000
+++ /dev/null
@@ -1,959 +0,0 @@
---[[
-LuCI - Dispatcher
-
-Description:
-The request dispatcher and module dispatcher generators
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
---- LuCI web dispatcher.
-local fs = require "nixio.fs"
-local sys = require "luci.sys"
-local init = require "luci.init"
-local util = require "luci.util"
-local http = require "luci.http"
-local nixio = require "nixio", require "nixio.util"
-
-module("luci.dispatcher", package.seeall)
-context = util.threadlocal()
-uci = require "luci.model.uci"
-i18n = require "luci.i18n"
-_M.fs = fs
-
-authenticator = {}
-
--- Index table
-local index = nil
-
--- Fastindex
-local fi
-
-
---- Build the URL relative to the server webroot from given virtual path.
--- @param ...  Virtual path
--- @return             Relative URL
-function build_url(...)
-       local path = {...}
-       local url = { http.getenv("SCRIPT_NAME") or "" }
-
-       local k, v
-       for k, v in pairs(context.urltoken) do
-               url[#url+1] = "/;"
-               url[#url+1] = http.urlencode(k)
-               url[#url+1] = "="
-               url[#url+1] = http.urlencode(v)
-       end
-
-       local p
-       for _, p in ipairs(path) do
-               if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
-                       url[#url+1] = "/"
-                       url[#url+1] = p
-               end
-       end
-
-       return table.concat(url, "")
-end
-
---- Check whether a dispatch node shall be visible
--- @param node Dispatch node
--- @return             Boolean indicating whether the node should be visible
-function node_visible(node)
-   if node then
-         return not (
-                (not node.title or #node.title == 0) or
-                (not node.target or node.hidden == true) or
-                (type(node.target) == "table" and node.target.type == "firstchild" and
-                 (type(node.nodes) ~= "table" or not next(node.nodes)))
-         )
-   end
-   return false
-end
-
---- Return a sorted table of visible childs within a given node
--- @param node Dispatch node
--- @return             Ordered table of child node names
-function node_childs(node)
-       local rv = { }
-       if node then
-               local k, v
-               for k, v in util.spairs(node.nodes,
-                       function(a, b)
-                               return (node.nodes[a].order or 100)
-                                    < (node.nodes[b].order or 100)
-                       end)
-               do
-                       if node_visible(v) then
-                               rv[#rv+1] = k
-                       end
-               end
-       end
-       return rv
-end
-
-
---- Send a 404 error code and render the "error404" template if available.
--- @param message      Custom error message (optional)
--- @return                     false
-function error404(message)
-       luci.http.status(404, "Not Found")
-       message = message or "Not Found"
-
-       require("luci.template")
-       if not luci.util.copcall(luci.template.render, "error404") then
-               luci.http.prepare_content("text/plain")
-               luci.http.write(message)
-       end
-       return false
-end
-
---- Send a 500 error code and render the "error500" template if available.
--- @param message      Custom error message (optional)#
--- @return                     false
-function error500(message)
-       luci.util.perror(message)
-       if not context.template_header_sent then
-               luci.http.status(500, "Internal Server Error")
-               luci.http.prepare_content("text/plain")
-               luci.http.write(message)
-       else
-               require("luci.template")
-               if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
-                       luci.http.prepare_content("text/plain")
-                       luci.http.write(message)
-               end
-       end
-       return false
-end
-
-function authenticator.htmlauth(validator, accs, default)
-       local user = luci.http.formvalue("username")
-       local pass = luci.http.formvalue("password")
-
-       if user and validator(user, pass) then
-               return user
-       end
-
-       require("luci.i18n")
-       require("luci.template")
-       context.path = {}
-       luci.template.render("sysauth", {duser=default, fuser=user})
-       return false
-
-end
-
---- Dispatch an HTTP request.
--- @param request      LuCI HTTP Request object
-function httpdispatch(request, prefix)
-       luci.http.context.request = request
-
-       local r = {}
-       context.request = r
-       context.urltoken = {}
-
-       local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
-
-       if prefix then
-               for _, node in ipairs(prefix) do
-                       r[#r+1] = node
-               end
-       end
-
-       local tokensok = true
-       for node in pathinfo:gmatch("[^/]+") do
-               local tkey, tval
-               if tokensok then
-                       tkey, tval = node:match(";(%w+)=([a-fA-F0-9]*)")
-               end
-               if tkey then
-                       context.urltoken[tkey] = tval
-               else
-                       tokensok = false
-                       r[#r+1] = node
-               end
-       end
-
-       local stat, err = util.coxpcall(function()
-               dispatch(context.request)
-       end, error500)
-
-       luci.http.close()
-
-       --context._disable_memtrace()
-end
-
---- Dispatches a LuCI virtual path.
--- @param request      Virtual path
-function dispatch(request)
-       --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
-       local ctx = context
-       ctx.path = request
-
-       local conf = require "luci.config"
-       assert(conf.main,
-               "/etc/config/luci seems to be corrupt, unable to find section 'main'")
-
-       local lang = conf.main.lang or "auto"
-       if lang == "auto" then
-               local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
-               for lpat in aclang:gmatch("[%w-]+") do
-                       lpat = lpat and lpat:gsub("-", "_")
-                       if conf.languages[lpat] then
-                               lang = lpat
-                               break
-                       end
-               end
-       end
-       require "luci.i18n".setlanguage(lang)
-
-       local c = ctx.tree
-       local stat
-       if not c then
-               c = createtree()
-       end
-
-       local track = {}
-       local args = {}
-       ctx.args = args
-       ctx.requestargs = ctx.requestargs or args
-       local n
-       local token = ctx.urltoken
-       local preq = {}
-       local freq = {}
-
-       for i, s in ipairs(request) do
-               preq[#preq+1] = s
-               freq[#freq+1] = s
-               c = c.nodes[s]
-               n = i
-               if not c then
-                       break
-               end
-
-               util.update(track, c)
-
-               if c.leaf then
-                       break
-               end
-       end
-
-       if c and c.leaf then
-               for j=n+1, #request do
-                       args[#args+1] = request[j]
-                       freq[#freq+1] = request[j]
-               end
-       end
-
-       ctx.requestpath = ctx.requestpath or freq
-       ctx.path = preq
-
-       if track.i18n then
-               i18n.loadc(track.i18n)
-       end
-
-       -- Init template engine
-       if (c and c.index) or not track.notemplate then
-               local tpl = require("luci.template")
-               local media = track.mediaurlbase or luci.config.main.mediaurlbase
-               if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
-                       media = nil
-                       for name, theme in pairs(luci.config.themes) do
-                               if name:sub(1,1) ~= "." and pcall(tpl.Template,
-                                "themes/%s/header" % fs.basename(theme)) then
-                                       media = theme
-                               end
-                       end
-                       assert(media, "No valid theme found")
-               end
-
-               local function _ifattr(cond, key, val)
-                       if cond then
-                               local env = getfenv(3)
-                               local scope = (type(env.self) == "table") and env.self
-                               return string.format(
-                                       ' %s="%s"', tostring(key),
-                                       luci.util.pcdata(tostring( val
-                                        or (type(env[key]) ~= "function" and env[key])
-                                        or (scope and type(scope[key]) ~= "function" and scope[key])
-                                        or "" ))
-                               )
-                       else
-                               return ''
-                       end
-               end
-
-               tpl.context.viewns = setmetatable({
-                  write       = luci.http.write;
-                  include     = function(name) tpl.Template(name):render(getfenv(2)) end;
-                  translate   = i18n.translate;
-                  translatef  = i18n.translatef;
-                  export      = function(k, v) if tpl.context.viewns[k] == nil then tpl.context.viewns[k] = v end end;
-                  striptags   = util.striptags;
-                  pcdata      = util.pcdata;
-                  media       = media;
-                  theme       = fs.basename(media);
-                  resource    = luci.config.main.resourcebase;
-                  ifattr      = function(...) return _ifattr(...) end;
-                  attr        = function(...) return _ifattr(true, ...) end;
-               }, {__index=function(table, key)
-                       if key == "controller" then
-                               return build_url()
-                       elseif key == "REQUEST_URI" then
-                               return build_url(unpack(ctx.requestpath))
-                       else
-                               return rawget(table, key) or _G[key]
-                       end
-               end})
-       end
-
-       track.dependent = (track.dependent ~= false)
-       assert(not track.dependent or not track.auto,
-               "Access Violation\nThe page at '" .. table.concat(request, "/") .. "/' " ..
-               "has no parent node so the access to this location has been denied.\n" ..
-               "This is a software bug, please report this message at " ..
-               "http://luci.subsignal.org/trac/newticket"
-       )
-
-       if track.sysauth then
-               local sauth = require "luci.sauth"
-
-               local authen = type(track.sysauth_authenticator) == "function"
-                and track.sysauth_authenticator
-                or authenticator[track.sysauth_authenticator]
-
-               local def  = (type(track.sysauth) == "string") and track.sysauth
-               local accs = def and {track.sysauth} or track.sysauth
-               local sess = ctx.authsession
-               local verifytoken = false
-               if not sess then
-                       sess = luci.http.getcookie("sysauth")
-                       sess = sess and sess:match("^[a-f0-9]*$")
-                       verifytoken = true
-               end
-
-               local sdat = sauth.read(sess)
-               local user
-
-               if sdat then
-                       if not verifytoken or ctx.urltoken.stok == sdat.token then
-                               user = sdat.user
-                       end
-               else
-                       local eu = http.getenv("HTTP_AUTH_USER")
-                       local ep = http.getenv("HTTP_AUTH_PASS")
-                       if eu and ep and luci.sys.user.checkpasswd(eu, ep) then
-                               authen = function() return eu end
-                       end
-               end
-
-               if not util.contains(accs, user) then
-                       if authen then
-                               ctx.urltoken.stok = nil
-                               local user, sess = authen(luci.sys.user.checkpasswd, accs, def)
-                               if not user or not util.contains(accs, user) then
-                                       return
-                               else
-                                       local sid = sess or luci.sys.uniqueid(16)
-                                       if not sess then
-                                               local token = luci.sys.uniqueid(16)
-                                               sauth.reap()
-                                               sauth.write(sid, {
-                                                       user=user,
-                                                       token=token,
-                                                       secret=luci.sys.uniqueid(16)
-                                               })
-                                               ctx.urltoken.stok = token
-                                       end
-                                       luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path="..build_url())
-                                       ctx.authsession = sid
-                                       ctx.authuser = user
-                               end
-                       else
-                               luci.http.status(403, "Forbidden")
-                               return
-                       end
-               else
-                       ctx.authsession = sess
-                       ctx.authuser = user
-               end
-       end
-
-       if track.setgroup then
-               luci.sys.process.setgroup(track.setgroup)
-       end
-
-       if track.setuser then
-               luci.sys.process.setuser(track.setuser)
-       end
-
-       local target = nil
-       if c then
-               if type(c.target) == "function" then
-                       target = c.target
-               elseif type(c.target) == "table" then
-                       target = c.target.target
-               end
-       end
-
-       if c and (c.index or type(target) == "function") then
-               ctx.dispatched = c
-               ctx.requested = ctx.requested or ctx.dispatched
-       end
-
-       if c and c.index then
-               local tpl = require "luci.template"
-
-               if util.copcall(tpl.render, "indexer", {}) then
-                       return true
-               end
-       end
-
-       if type(target) == "function" then
-               util.copcall(function()
-                       local oldenv = getfenv(target)
-                       local module = require(c.module)
-                       local env = setmetatable({}, {__index=
-
-                       function(tbl, key)
-                               return rawget(tbl, key) or module[key] or oldenv[key]
-                       end})
-
-                       setfenv(target, env)
-               end)
-
-               local ok, err
-               if type(c.target) == "table" then
-                       ok, err = util.copcall(target, c.target, unpack(args))
-               else
-                       ok, err = util.copcall(target, unpack(args))
-               end
-               assert(ok,
-                      "Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
-                      " dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
-                      "The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
-       else
-               local root = node()
-               if not root or not root.target then
-                       error404("No root node was registered, this usually happens if no module was installed.\n" ..
-                                "Install luci-mod-admin-full and retry. " ..
-                                "If the module is already installed, try removing the /tmp/luci-indexcache file.")
-               else
-                       error404("No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
-                                "If this url belongs to an extension, make sure it is properly installed.\n" ..
-                                "If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
-               end
-       end
-end
-
---- Generate the dispatching index using the best possible strategy.
-function createindex()
-       local path = luci.util.libpath() .. "/controller/"
-       local suff = { ".lua", ".lua.gz" }
-
-       if luci.util.copcall(require, "luci.fastindex") then
-               createindex_fastindex(path, suff)
-       else
-               createindex_plain(path, suff)
-       end
-end
-
---- Generate the dispatching index using the fastindex C-indexer.
--- @param path         Controller base directory
--- @param suffixes     Controller file suffixes
-function createindex_fastindex(path, suffixes)
-       index = {}
-
-       if not fi then
-               fi = luci.fastindex.new("index")
-               for _, suffix in ipairs(suffixes) do
-                       fi.add(path .. "*" .. suffix)
-                       fi.add(path .. "*/*" .. suffix)
-               end
-       end
-       fi.scan()
-
-       for k, v in pairs(fi.indexes) do
-               index[v[2]] = v[1]
-       end
-end
-
---- Generate the dispatching index using the native file-cache based strategy.
--- @param path         Controller base directory
--- @param suffixes     Controller file suffixes
-function createindex_plain(path, suffixes)
-       local controllers = { }
-       for _, suffix in ipairs(suffixes) do
-               nixio.util.consume((fs.glob(path .. "*" .. suffix)), controllers)
-               nixio.util.consume((fs.glob(path .. "*/*" .. suffix)), controllers)
-       end
-
-       if indexcache then
-               local cachedate = fs.stat(indexcache, "mtime")
-               if cachedate then
-                       local realdate = 0
-                       for _, obj in ipairs(controllers) do
-                               local omtime = fs.stat(obj, "mtime")
-                               realdate = (omtime and omtime > realdate) and omtime or realdate
-                       end
-
-                       if cachedate > realdate then
-                               assert(
-                                       sys.process.info("uid") == fs.stat(indexcache, "uid")
-                                       and fs.stat(indexcache, "modestr") == "rw-------",
-                                       "Fatal: Indexcache is not sane!"
-                               )
-
-                               index = loadfile(indexcache)()
-                               return index
-                       end
-               end
-       end
-
-       index = {}
-
-       for i,c in ipairs(controllers) do
-               local modname = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
-               for _, suffix in ipairs(suffixes) do
-                       modname = modname:gsub(suffix.."$", "")
-               end
-
-               local mod = require(modname)
-               assert(mod ~= true,
-                      "Invalid controller file found\n" ..
-                      "The file '" .. c .. "' contains an invalid module line.\n" ..
-                      "Please verify whether the module name is set to '" .. modname ..
-                      "' - It must correspond to the file path!")
-
-               local idx = mod.index
-               assert(type(idx) == "function",
-                      "Invalid controller file found\n" ..
-                      "The file '" .. c .. "' contains no index() function.\n" ..
-                      "Please make sure that the controller contains a valid " ..
-                      "index function and verify the spelling!")
-
-               index[modname] = idx
-       end
-
-       if indexcache then
-               local f = nixio.open(indexcache, "w", 600)
-               f:writeall(util.get_bytecode(index))
-               f:close()
-       end
-end
-
---- Create the dispatching tree from the index.
--- Build the index before if it does not exist yet.
-function createtree()
-       if not index then
-               createindex()
-       end
-
-       local ctx  = context
-       local tree = {nodes={}, inreq=true}
-       local modi = {}
-
-       ctx.treecache = setmetatable({}, {__mode="v"})
-       ctx.tree = tree
-       ctx.modifiers = modi
-
-       -- Load default translation
-       require "luci.i18n".loadc("base")
-
-       local scope = setmetatable({}, {__index = luci.dispatcher})
-
-       for k, v in pairs(index) do
-               scope._NAME = k
-               setfenv(v, scope)
-               v()
-       end
-
-       local function modisort(a,b)
-               return modi[a].order < modi[b].order
-       end
-
-       for _, v in util.spairs(modi, modisort) do
-               scope._NAME = v.module
-               setfenv(v.func, scope)
-               v.func()
-       end
-
-       return tree
-end
-
---- Register a tree modifier.
--- @param      func    Modifier function
--- @param      order   Modifier order value (optional)
-function modifier(func, order)
-       context.modifiers[#context.modifiers+1] = {
-               func = func,
-               order = order or 0,
-               module
-                       = getfenv(2)._NAME
-       }
-end
-
---- Clone a node of the dispatching tree to another position.
--- @param      path    Virtual path destination
--- @param      clone   Virtual path source
--- @param      title   Destination node title (optional)
--- @param      order   Destination node order value (optional)
--- @return                     Dispatching tree node
-function assign(path, clone, title, order)
-       local obj  = node(unpack(path))
-       obj.nodes  = nil
-       obj.module = nil
-
-       obj.title = title
-       obj.order = order
-
-       setmetatable(obj, {__index = _create_node(clone)})
-
-       return obj
-end
-
---- Create a new dispatching node and define common parameters.
--- @param      path    Virtual path
--- @param      target  Target function to call when dispatched.
--- @param      title   Destination node title
--- @param      order   Destination node order value (optional)
--- @return                     Dispatching tree node
-function entry(path, target, title, order)
-       local c = node(unpack(path))
-
-       c.target = target
-       c.title  = title
-       c.order  = order
-       c.module = getfenv(2)._NAME
-
-       return c
-end
-
---- Fetch or create a dispatching node without setting the target module or
--- enabling the node.
--- @param      ...             Virtual path
--- @return                     Dispatching tree node
-function get(...)
-       return _create_node({...})
-end
-
---- Fetch or create a new dispatching node.
--- @param      ...             Virtual path
--- @return                     Dispatching tree node
-function node(...)
-       local c = _create_node({...})
-
-       c.module = getfenv(2)._NAME
-       c.auto = nil
-
-       return c
-end
-
-function _create_node(path)
-       if #path == 0 then
-               return context.tree
-       end
-
-       local name = table.concat(path, ".")
-       local c = context.treecache[name]
-
-       if not c then
-               local last = table.remove(path)
-               local parent = _create_node(path)
-
-               c = {nodes={}, auto=true}
-               -- the node is "in request" if the request path matches
-               -- at least up to the length of the node path
-               if parent.inreq and context.path[#path+1] == last then
-                 c.inreq = true
-               end
-               parent.nodes[last] = c
-               context.treecache[name] = c
-       end
-       return c
-end
-
--- Subdispatchers --
-
-function _firstchild()
-   local path = { unpack(context.path) }
-   local name = table.concat(path, ".")
-   local node = context.treecache[name]
-
-   local lowest
-   if node and node.nodes and next(node.nodes) then
-         local k, v
-         for k, v in pairs(node.nodes) do
-                if not lowest or
-                       (v.order or 100) < (node.nodes[lowest].order or 100)
-                then
-                       lowest = k
-                end
-         end
-   end
-
-   assert(lowest ~= nil,
-                 "The requested node contains no childs, unable to redispatch")
-
-   path[#path+1] = lowest
-   dispatch(path)
-end
-
---- Alias the first (lowest order) page automatically
-function firstchild()
-   return { type = "firstchild", target = _firstchild }
-end
-
---- Create a redirect to another dispatching node.
--- @param      ...             Virtual path destination
-function alias(...)
-       local req = {...}
-       return function(...)
-               for _, r in ipairs({...}) do
-                       req[#req+1] = r
-               end
-
-               dispatch(req)
-       end
-end
-
---- Rewrite the first x path values of the request.
--- @param      n               Number of path values to replace
--- @param      ...             Virtual path to replace removed path values with
-function rewrite(n, ...)
-       local req = {...}
-       return function(...)
-               local dispatched = util.clone(context.dispatched)
-
-               for i=1,n do
-                       table.remove(dispatched, 1)
-               end
-
-               for i, r in ipairs(req) do
-                       table.insert(dispatched, i, r)
-               end
-
-               for _, r in ipairs({...}) do
-                       dispatched[#dispatched+1] = r
-               end
-
-               dispatch(dispatched)
-       end
-end
-
-
-local function _call(self, ...)
-       local func = getfenv()[self.name]
-       assert(func ~= nil,
-              'Cannot resolve function "' .. self.name .. '". Is it misspelled or local?')
-
-       assert(type(func) == "function",
-              'The symbol "' .. self.name .. '" does not refer to a function but data ' ..
-              'of type "' .. type(func) .. '".')
-
-       if #self.argv > 0 then
-               return func(unpack(self.argv), ...)
-       else
-               return func(...)
-       end
-end
-
---- Create a function-call dispatching target.
--- @param      name    Target function of local controller
--- @param      ...             Additional parameters passed to the function
-function call(name, ...)
-       return {type = "call", argv = {...}, name = name, target = _call}
-end
-
-
-local _template = function(self, ...)
-       require "luci.template".render(self.view)
-end
-
---- Create a template render dispatching target.
--- @param      name    Template to be rendered
-function template(name)
-       return {type = "template", view = name, target = _template}
-end
-
-
-local function _cbi(self, ...)
-       local cbi = require "luci.cbi"
-       local tpl = require "luci.template"
-       local http = require "luci.http"
-
-       local config = self.config or {}
-       local maps = cbi.load(self.model, ...)
-
-       local state = nil
-
-       for i, res in ipairs(maps) do
-               res.flow = config
-               local cstate = res:parse()
-               if cstate and (not state or cstate < state) then
-                       state = cstate
-               end
-       end
-
-       local function _resolve_path(path)
-               return type(path) == "table" and build_url(unpack(path)) or path
-       end
-
-       if config.on_valid_to and state and state > 0 and state < 2 then
-               http.redirect(_resolve_path(config.on_valid_to))
-               return
-       end
-
-       if config.on_changed_to and state and state > 1 then
-               http.redirect(_resolve_path(config.on_changed_to))
-               return
-       end
-
-       if config.on_success_to and state and state > 0 then
-               http.redirect(_resolve_path(config.on_success_to))
-               return
-       end
-
-       if config.state_handler then
-               if not config.state_handler(state, maps) then
-                       return
-               end
-       end
-
-       http.header("X-CBI-State", state or 0)
-
-       if not config.noheader then
-               tpl.render("cbi/header", {state = state})
-       end
-
-       local redirect
-       local messages
-       local applymap   = false
-       local pageaction = true
-       local parsechain = { }
-
-       for i, res in ipairs(maps) do
-               if res.apply_needed and res.parsechain then
-                       local c
-                       for _, c in ipairs(res.parsechain) do
-                               parsechain[#parsechain+1] = c
-                       end
-                       applymap = true
-               end
-
-               if res.redirect then
-                       redirect = redirect or res.redirect
-               end
-
-               if res.pageaction == false then
-                       pageaction = false
-               end
-
-               if res.message then
-                       messages = messages or { }
-                       messages[#messages+1] = res.message
-               end
-       end
-
-       for i, res in ipairs(maps) do
-               res:render({
-                       firstmap   = (i == 1),
-                       applymap   = applymap,
-                       redirect   = redirect,
-                       messages   = messages,
-                       pageaction = pageaction,
-                       parsechain = parsechain
-               })
-       end
-
-       if not config.nofooter then
-               tpl.render("cbi/footer", {
-                       flow       = config,
-                       pageaction = pageaction,
-                       redirect   = redirect,
-                       state      = state,
-                       autoapply  = config.autoapply
-               })
-       end
-end
-
---- Create a CBI model dispatching target.
--- @param      model   CBI model to be rendered
-function cbi(model, config)
-       return {type = "cbi", config = config, model = model, target = _cbi}
-end
-
-
-local function _arcombine(self, ...)
-       local argv = {...}
-       local target = #argv > 0 and self.targets[2] or self.targets[1]
-       setfenv(target.target, self.env)
-       target:target(unpack(argv))
-end
-
---- Create a combined dispatching target for non argv and argv requests.
--- @param trg1 Overview Target
--- @param trg2 Detail Target
-function arcombine(trg1, trg2)
-       return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}}
-end
-
-
-local function _form(self, ...)
-       local cbi = require "luci.cbi"
-       local tpl = require "luci.template"
-       local http = require "luci.http"
-
-       local maps = luci.cbi.load(self.model, ...)
-       local state = nil
-
-       for i, res in ipairs(maps) do
-               local cstate = res:parse()
-               if cstate and (not state or cstate < state) then
-                       state = cstate
-               end
-       end
-
-       http.header("X-CBI-State", state or 0)
-       tpl.render("header")
-       for i, res in ipairs(maps) do
-               res:render()
-       end
-       tpl.render("footer")
-end
-
---- Create a CBI form model dispatching target.
--- @param      model   CBI form model tpo be rendered
-function form(model)
-       return {type = "cbi", model = model, target = _form}
-end
-
---- Access the luci.i18n translate() api.
--- @class  function
--- @name   translate
--- @param  text    Text to translate
-translate = i18n.translate
-
---- No-op function used to mark translation entries for menu labels.
--- This function does not actually translate the given argument but
--- is used by build/i18n-scan.pl to find translatable entries.
-function _(text)
-       return text
-end
diff --git a/libs/web/luasrc/http.lua b/libs/web/luasrc/http.lua
deleted file mode 100644 (file)
index c53307a..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
---[[
-LuCI - HTTP-Interaction
-
-Description:
-HTTP-Header manipulator and form variable preprocessor
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local ltn12 = require "luci.ltn12"
-local protocol = require "luci.http.protocol"
-local util  = require "luci.util"
-local string = require "string"
-local coroutine = require "coroutine"
-local table = require "table"
-
-local ipairs, pairs, next, type, tostring, error =
-       ipairs, pairs, next, type, tostring, error
-
---- LuCI Web Framework high-level HTTP functions.
-module "luci.http"
-
-context = util.threadlocal()
-
-Request = util.class()
-function Request.__init__(self, env, sourcein, sinkerr)
-       self.input = sourcein
-       self.error = sinkerr
-
-
-       -- File handler
-       self.filehandler = function() end
-
-       -- HTTP-Message table
-       self.message = {
-               env = env,
-               headers = {},
-               params = protocol.urldecode_params(env.QUERY_STRING or ""),
-       }
-
-       self.parsed_input = false
-end
-
-function Request.formvalue(self, name, noparse)
-       if not noparse and not self.parsed_input then
-               self:_parse_input()
-       end
-
-       if name then
-               return self.message.params[name]
-       else
-               return self.message.params
-       end
-end
-
-function Request.formvaluetable(self, prefix)
-       local vals = {}
-       prefix = prefix and prefix .. "." or "."
-
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       local void = self.message.params[nil]
-       for k, v in pairs(self.message.params) do
-               if k:find(prefix, 1, true) == 1 then
-                       vals[k:sub(#prefix + 1)] = tostring(v)
-               end
-       end
-
-       return vals
-end
-
-function Request.content(self)
-       if not self.parsed_input then
-               self:_parse_input()
-       end
-
-       return self.message.content, self.message.content_length
-end
-
-function Request.getcookie(self, name)
-  local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
-  local p = ";" .. name .. "=(.-);"
-  local i, j, value = c:find(p)
-  return value and urldecode(value)
-end
-
-function Request.getenv(self, name)
-       if name then
-               return self.message.env[name]
-       else
-               return self.message.env
-       end
-end
-
-function Request.setfilehandler(self, callback)
-       self.filehandler = callback
-end
-
-function Request._parse_input(self)
-       protocol.parse_message_body(
-                self.input,
-                self.message,
-                self.filehandler
-       )
-       self.parsed_input = true
-end
-
---- Close the HTTP-Connection.
-function close()
-       if not context.eoh then
-               context.eoh = true
-               coroutine.yield(3)
-       end
-
-       if not context.closed then
-               context.closed = true
-               coroutine.yield(5)
-       end
-end
-
---- Return the request content if the request was of unknown type.
--- @return     HTTP request body
--- @return     HTTP request body length
-function content()
-       return context.request:content()
-end
-
---- Get a certain HTTP input value or a table of all input values.
--- @param name         Name of the GET or POST variable to fetch
--- @param noparse      Don't parse POST data before getting the value
--- @return                     HTTP input value or table of all input value
-function formvalue(name, noparse)
-       return context.request:formvalue(name, noparse)
-end
-
---- Get a table of all HTTP input values with a certain prefix.
--- @param prefix       Prefix
--- @return                     Table of all HTTP input values with given prefix
-function formvaluetable(prefix)
-       return context.request:formvaluetable(prefix)
-end
-
---- Get the value of a certain HTTP-Cookie.
--- @param name         Cookie Name
--- @return                     String containing cookie data
-function getcookie(name)
-       return context.request:getcookie(name)
-end
-
---- Get the value of a certain HTTP environment variable
--- or the environment table itself.
--- @param name         Environment variable
--- @return                     HTTP environment value or environment table
-function getenv(name)
-       return context.request:getenv(name)
-end
-
---- Set a handler function for incoming user file uploads.
--- @param callback     Handler function
-function setfilehandler(callback)
-       return context.request:setfilehandler(callback)
-end
-
---- Send a HTTP-Header.
--- @param key  Header key
--- @param value Header value
-function header(key, value)
-       if not context.headers then
-               context.headers = {}
-       end
-       context.headers[key:lower()] = value
-       coroutine.yield(2, key, value)
-end
-
---- Set the mime type of following content data.
--- @param mime Mimetype of following content
-function prepare_content(mime)
-       if not context.headers or not context.headers["content-type"] then
-               if mime == "application/xhtml+xml" then
-                       if not getenv("HTTP_ACCEPT") or
-                         not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
-                               mime = "text/html; charset=UTF-8"
-                       end
-                       header("Vary", "Accept")
-               end
-               header("Content-Type", mime)
-       end
-end
-
---- Get the RAW HTTP input source
--- @return     HTTP LTN12 source
-function source()
-       return context.request.input
-end
-
---- Set the HTTP status code and status message.
--- @param code         Status code
--- @param message      Status message
-function status(code, message)
-       code = code or 200
-       message = message or "OK"
-       context.status = code
-       coroutine.yield(1, code, message)
-end
-
---- Send a chunk of content data to the client.
--- This function is as a valid LTN12 sink.
--- If the content chunk is nil this function will automatically invoke close.
--- @param content      Content chunk
--- @param src_err      Error object from source (optional)
--- @see close
-function write(content, src_err)
-       if not content then
-               if src_err then
-                       error(src_err)
-               else
-                       close()
-               end
-               return true
-       elseif #content == 0 then
-               return true
-       else
-               if not context.eoh then
-                       if not context.status then
-                               status()
-                       end
-                       if not context.headers or not context.headers["content-type"] then
-                               header("Content-Type", "text/html; charset=utf-8")
-                       end
-                       if not context.headers["cache-control"] then
-                               header("Cache-Control", "no-cache")
-                               header("Expires", "0")
-                       end
-
-
-                       context.eoh = true
-                       coroutine.yield(3)
-               end
-               coroutine.yield(4, content)
-               return true
-       end
-end
-
---- Splice data from a filedescriptor to the client.
--- @param fp   File descriptor
--- @param size Bytes to splice (optional)
-function splice(fd, size)
-       coroutine.yield(6, fd, size)
-end
-
---- Redirects the client to a new URL and closes the connection.
--- @param url  Target URL
-function redirect(url)
-       status(302, "Found")
-       header("Location", url)
-       close()
-end
-
---- Create a querystring out of a table of key - value pairs.
--- @param table                Query string source table
--- @return                     Encoded HTTP query string
-function build_querystring(q)
-       local s = { "?" }
-
-       for k, v in pairs(q) do
-               if #s > 1 then s[#s+1] = "&" end
-
-               s[#s+1] = urldecode(k)
-               s[#s+1] = "="
-               s[#s+1] = urldecode(v)
-       end
-
-       return table.concat(s, "")
-end
-
---- Return the URL-decoded equivalent of a string.
--- @param str          URL-encoded string
--- @param no_plus      Don't decode + to " "
--- @return                     URL-decoded string
--- @see urlencode
-urldecode = protocol.urldecode
-
---- Return the URL-encoded equivalent of a string.
--- @param str          Source string
--- @return                     URL-encoded string
--- @see urldecode
-urlencode = protocol.urlencode
-
---- Send the given data as JSON encoded string.
--- @param data         Data to send
-function write_json(x)
-       if x == nil then
-               write("null")
-       elseif type(x) == "table" then
-               local k, v
-               if type(next(x)) == "number" then
-                       write("[ ")
-                       for k, v in ipairs(x) do
-                               write_json(v)
-                               if next(x, k) then
-                                       write(", ")
-                               end
-                       end
-                       write(" ]")
-               else
-                       write("{ ")
-                       for k, v in pairs(x) do
-                       write("%q: " % k)
-                               write_json(v)
-                               if next(x, k) then
-                                       write(", ")
-                               end
-                       end
-                       write(" }")
-               end
-       elseif type(x) == "number" or type(x) == "boolean" then
-               if (x ~= x) then
-                       -- NaN is the only value that doesn't equal to itself.
-                       write("Number.NaN")
-               else
-                       write(tostring(x))
-               end
-       else
-               write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
-                       return '\\u%04x' % c:byte(1)
-               end))
-       end
-end
diff --git a/libs/web/luasrc/http/protocol.lua b/libs/web/luasrc/http/protocol.lua
deleted file mode 100644 (file)
index 0d41550..0000000
+++ /dev/null
@@ -1,688 +0,0 @@
---[[
-
-HTTP protocol implementation for LuCI
-(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
-]]--
-
---- LuCI http protocol class.
--- This class contains several functions useful for http message- and content
--- decoding and to retrive form data from raw http messages.
-module("luci.http.protocol", package.seeall)
-
-local ltn12 = require("luci.ltn12")
-
-HTTP_MAX_CONTENT      = 1024*8         -- 8 kB maximum content size
-
---- Decode an urlencoded string - optionally without decoding
--- the "+" sign to " " - and return the decoded string.
--- @param str          Input string in x-www-urlencoded format
--- @param no_plus      Don't decode "+" signs to spaces
--- @return                     The decoded string
--- @see                                urlencode
-function urldecode( str, no_plus )
-
-       local function __chrdec( hex )
-               return string.char( tonumber( hex, 16 ) )
-       end
-
-       if type(str) == "string" then
-               if not no_plus then
-                       str = str:gsub( "+", " " )
-               end
-
-               str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
-       end
-
-       return str
-end
-
---- Extract and split urlencoded data pairs, separated bei either "&" or ";"
--- from given url or string. Returns a table with urldecoded values.
--- Simple parameters are stored as string values associated with the parameter
--- name within the table. Parameters with multiple values are stored as array
--- containing the corresponding values.
--- @param url  The url or string which contains x-www-urlencoded form data
--- @param tbl  Use the given table for storing values (optional)
--- @return             Table containing the urldecoded parameters
--- @see                        urlencode_params
-function urldecode_params( url, tbl )
-
-       local params = tbl or { }
-
-       if url:find("?") then
-               url = url:gsub( "^.+%?([^?]+)", "%1" )
-       end
-
-       for pair in url:gmatch( "[^&;]+" ) do
-
-               -- find key and value
-               local key = urldecode( pair:match("^([^=]+)")     )
-               local val = urldecode( pair:match("^[^=]+=(.+)$") )
-
-               -- store
-               if type(key) == "string" and key:len() > 0 then
-                       if type(val) ~= "string" then val = "" end
-
-                       if not params[key] then
-                               params[key] = val
-                       elseif type(params[key]) ~= "table" then
-                               params[key] = { params[key], val }
-                       else
-                               table.insert( params[key], val )
-                       end
-               end
-       end
-
-       return params
-end
-
---- Encode given string to x-www-urlencoded format.
--- @param str  String to encode
--- @return             String containing the encoded data
--- @see                        urldecode
-function urlencode( str )
-
-       local function __chrenc( chr )
-               return string.format(
-                       "%%%02x", string.byte( chr )
-               )
-       end
-
-       if type(str) == "string" then
-               str = str:gsub(
-                       "([^a-zA-Z0-9$_%-%.%+!*'(),])",
-                       __chrenc
-               )
-       end
-
-       return str
-end
-
---- Encode each key-value-pair in given table to x-www-urlencoded format,
--- separated by "&". Tables are encoded as parameters with multiple values by
--- repeating the parameter name with each value.
--- @param tbl  Table with the values
--- @return             String containing encoded values
--- @see                        urldecode_params
-function urlencode_params( tbl )
-       local enc = ""
-
-       for k, v in pairs(tbl) do
-               if type(v) == "table" then
-                       for i, v2 in ipairs(v) do
-                               enc = enc .. ( #enc > 0 and "&" or "" ) ..
-                                       urlencode(k) .. "=" .. urlencode(v2)
-                       end
-               else
-                       enc = enc .. ( #enc > 0 and "&" or "" ) ..
-                               urlencode(k) .. "=" .. urlencode(v)
-               end
-       end
-
-       return enc
-end
-
--- (Internal function)
--- Initialize given parameter and coerce string into table when the parameter
--- already exists.
--- @param tbl  Table where parameter should be created
--- @param key  Parameter name
--- @return             Always nil
-local function __initval( tbl, key )
-       if tbl[key] == nil then
-               tbl[key] = ""
-       elseif type(tbl[key]) == "string" then
-               tbl[key] = { tbl[key], "" }
-       else
-               table.insert( tbl[key], "" )
-       end
-end
-
--- (Internal function)
--- Append given data to given parameter, either by extending the string value
--- or by appending it to the last string in the parameter's value table.
--- @param tbl  Table containing the previously initialized parameter value
--- @param key  Parameter name
--- @param chunk        String containing the data to append
--- @return             Always nil
--- @see                        __initval
-local function __appendval( tbl, key, chunk )
-       if type(tbl[key]) == "table" then
-               tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
-       else
-               tbl[key] = tbl[key] .. chunk
-       end
-end
-
--- (Internal function)
--- Finish the value of given parameter, either by transforming the string value
--- or - in the case of multi value parameters - the last element in the
--- associated values table.
--- @param tbl          Table containing the previously initialized parameter value
--- @param key          Parameter name
--- @param handler      Function which transforms the parameter value
--- @return                     Always nil
--- @see                                __initval
--- @see                                __appendval
-local function __finishval( tbl, key, handler )
-       if handler then
-               if type(tbl[key]) == "table" then
-                       tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
-               else
-                       tbl[key] = handler( tbl[key] )
-               end
-       end
-end
-
-
--- Table of our process states
-local process_states = { }
-
--- Extract "magic", the first line of a http message.
--- Extracts the message type ("get", "post" or "response"), the requested uri
--- or the status code if the line descripes a http response.
-process_states['magic'] = function( msg, chunk, err )
-
-       if chunk ~= nil then
-               -- ignore empty lines before request
-               if #chunk == 0 then
-                       return true, nil
-               end
-
-               -- Is it a request?
-               local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
-
-               -- Yup, it is
-               if method then
-
-                       msg.type           = "request"
-                       msg.request_method = method:lower()
-                       msg.request_uri    = uri
-                       msg.http_version   = tonumber( http_ver )
-                       msg.headers        = { }
-
-                       -- We're done, next state is header parsing
-                       return true, function( chunk )
-                               return process_states['headers']( msg, chunk )
-                       end
-
-               -- Is it a response?
-               else
-
-                       local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
-
-                       -- Is a response
-                       if code then
-
-                               msg.type           = "response"
-                               msg.status_code    = code
-                               msg.status_message = message
-                               msg.http_version   = tonumber( http_ver )
-                               msg.headers        = { }
-
-                               -- We're done, next state is header parsing
-                               return true, function( chunk )
-                                       return process_states['headers']( msg, chunk )
-                               end
-                       end
-               end
-       end
-
-       -- Can't handle it
-       return nil, "Invalid HTTP message magic"
-end
-
-
--- Extract headers from given string.
-process_states['headers'] = function( msg, chunk )
-
-       if chunk ~= nil then
-
-               -- Look for a valid header format
-               local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" )
-
-               if type(hdr) == "string" and hdr:len() > 0 and
-                  type(val) == "string" and val:len() > 0
-               then
-                       msg.headers[hdr] = val
-
-                       -- Valid header line, proceed
-                       return true, nil
-
-               elseif #chunk == 0 then
-                       -- Empty line, we won't accept data anymore
-                       return false, nil
-               else
-                       -- Junk data
-                       return nil, "Invalid HTTP header received"
-               end
-       else
-               return nil, "Unexpected EOF"
-       end
-end
-
-
---- Creates a ltn12 source from the given socket. The source will return it's
--- data line by line with the trailing \r\n stripped of.
--- @param sock Readable network socket
--- @return             Ltn12 source function
-function header_source( sock )
-       return ltn12.source.simplify( function()
-
-               local chunk, err, part = sock:receive("*l")
-
-               -- Line too long
-               if chunk == nil then
-                       if err ~= "timeout" then
-                               return nil, part
-                                       and "Line exceeds maximum allowed length"
-                                       or  "Unexpected EOF"
-                       else
-                               return nil, err
-                       end
-
-               -- Line ok
-               elseif chunk ~= nil then
-
-                       -- Strip trailing CR
-                       chunk = chunk:gsub("\r$","")
-
-                       return chunk, nil
-               end
-       end )
-end
-
---- Decode a mime encoded http message body with multipart/form-data
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table withing the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
--- If an optional file callback function is given then it is feeded with the
--- file contents chunk by chunk and only the extracted file name is stored
--- within the params table. The callback function will be called subsequently
--- with three arguments:
---  o Table containing decoded (name, file) and raw (headers) mime header data
---  o String value containing a chunk of the file data
---  o Boolean which indicates wheather the current chunk is the last one (eof)
--- @param src          Ltn12 source function
--- @param msg          HTTP message object
--- @param filecb       File callback function (optional)
--- @return                     Value indicating successful operation (not nil means "ok")
--- @return                     String containing the error if unsuccessful
--- @see                                parse_message_header
-function mimedecode_message_body( src, msg, filecb )
-
-       if msg and msg.env.CONTENT_TYPE then
-               msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
-       end
-
-       if not msg.mime_boundary then
-               return nil, "Invalid Content-Type found"
-       end
-
-
-       local tlen   = 0
-       local inhdr  = false
-       local field  = nil
-       local store  = nil
-       local lchunk = nil
-
-       local function parse_headers( chunk, field )
-
-               local stat
-               repeat
-                       chunk, stat = chunk:gsub(
-                               "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
-                               function(k,v)
-                                       field.headers[k] = v
-                                       return ""
-                               end
-                       )
-               until stat == 0
-
-               chunk, stat = chunk:gsub("^\r\n","")
-
-               -- End of headers
-               if stat > 0 then
-                       if field.headers["Content-Disposition"] then
-                               if field.headers["Content-Disposition"]:match("^form%-data; ") then
-                                       field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
-                                       field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
-                               end
-                       end
-
-                       if not field.headers["Content-Type"] then
-                               field.headers["Content-Type"] = "text/plain"
-                       end
-
-                       if field.name and field.file and filecb then
-                               __initval( msg.params, field.name )
-                               __appendval( msg.params, field.name, field.file )
-
-                               store = filecb
-                       elseif field.name then
-                               __initval( msg.params, field.name )
-
-                               store = function( hdr, buf, eof )
-                                       __appendval( msg.params, field.name, buf )
-                               end
-                       else
-                               store = nil
-                       end
-
-                       return chunk, true
-               end
-
-               return chunk, false
-       end
-
-       local function snk( chunk )
-
-               tlen = tlen + ( chunk and #chunk or 0 )
-
-               if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               end
-
-               if chunk and not lchunk then
-                       lchunk = "\r\n" .. chunk
-
-               elseif lchunk then
-                       local data = lchunk .. ( chunk or "" )
-                       local spos, epos, found
-
-                       repeat
-                               spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
-
-                               if not spos then
-                                       spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
-                               end
-
-
-                               if spos then
-                                       local predata = data:sub( 1, spos - 1 )
-
-                                       if inhdr then
-                                               predata, eof = parse_headers( predata, field )
-
-                                               if not eof then
-                                                       return nil, "Invalid MIME section header"
-                                               elseif not field.name then
-                                                       return nil, "Invalid Content-Disposition header"
-                                               end
-                                       end
-
-                                       if store then
-                                               store( field, predata, true )
-                                       end
-
-
-                                       field = { headers = { } }
-                                       found = found or true
-
-                                       data, eof = parse_headers( data:sub( epos + 1, #data ), field )
-                                       inhdr = not eof
-                               end
-                       until not spos
-
-                       if found then
-                               -- We found at least some boundary. Save
-                               -- the unparsed remaining data for the
-                               -- next chunk.
-                               lchunk, data = data, nil
-                       else
-                               -- There was a complete chunk without a boundary. Parse it as headers or
-                               -- append it as data, depending on our current state.
-                               if inhdr then
-                                       lchunk, eof = parse_headers( data, field )
-                                       inhdr = not eof
-                               else
-                                       -- We're inside data, so append the data. Note that we only append
-                                       -- lchunk, not all of data, since there is a chance that chunk
-                                       -- contains half a boundary. Assuming that each chunk is at least the
-                                       -- boundary in size, this should prevent problems
-                                       store( field, lchunk, false )
-                                       lchunk, chunk = chunk, nil
-                               end
-                       end
-               end
-
-               return true
-       end
-
-       return ltn12.pump.all( src, snk )
-end
-
---- Decode an urlencoded http message body with application/x-www-urlencoded
--- Content-Type. Stores all extracted data associated with its parameter name
--- in the params table withing the given message object. Multiple parameter
--- values are stored as tables, ordinary ones as strings.
--- @param src  Ltn12 source function
--- @param msg  HTTP message object
--- @return             Value indicating successful operation (not nil means "ok")
--- @return             String containing the error if unsuccessful
--- @see                        parse_message_header
-function urldecode_message_body( src, msg )
-
-       local tlen   = 0
-       local lchunk = nil
-
-       local function snk( chunk )
-
-               tlen = tlen + ( chunk and #chunk or 0 )
-
-               if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
-                       return nil, "Message body size exceeds Content-Length"
-               elseif tlen > HTTP_MAX_CONTENT then
-                       return nil, "Message body size exceeds maximum allowed length"
-               end
-
-               if not lchunk and chunk then
-                       lchunk = chunk
-
-               elseif lchunk then
-                       local data = lchunk .. ( chunk or "&" )
-                       local spos, epos
-
-                       repeat
-                               spos, epos = data:find("^.-[;&]")
-
-                               if spos then
-                                       local pair = data:sub( spos, epos - 1 )
-                                       local key  = pair:match("^(.-)=")
-                                       local val  = pair:match("=([^%s]*)%s*$")
-
-                                       if key and #key > 0 then
-                                               __initval( msg.params, key )
-                                               __appendval( msg.params, key, val )
-                                               __finishval( msg.params, key, urldecode )
-                                       end
-
-                                       data = data:sub( epos + 1, #data )
-                               end
-                       until not spos
-
-                       lchunk = data
-               end
-
-               return true
-       end
-
-       return ltn12.pump.all( src, snk )
-end
-
---- Try to extract an http message header including information like protocol
--- version, message headers and resulting CGI environment variables from the
--- given ltn12 source.
--- @param src  Ltn12 source function
--- @return             HTTP message object
--- @see                        parse_message_body
-function parse_message_header( src )
-
-       local ok   = true
-       local msg  = { }
-
-       local sink = ltn12.sink.simplify(
-               function( chunk )
-                       return process_states['magic']( msg, chunk )
-               end
-       )
-
-       -- Pump input data...
-       while ok do
-
-               -- get data
-               ok, err = ltn12.pump.step( src, sink )
-
-               -- error
-               if not ok and err then
-                       return nil, err
-
-               -- eof
-               elseif not ok then
-
-                       -- Process get parameters
-                       if ( msg.request_method == "get" or msg.request_method == "post" ) and
-                          msg.request_uri:match("?")
-                       then
-                               msg.params = urldecode_params( msg.request_uri )
-                       else
-                               msg.params = { }
-                       end
-
-                       -- Populate common environment variables
-                       msg.env = {
-                               CONTENT_LENGTH    = msg.headers['Content-Length'];
-                               CONTENT_TYPE      = msg.headers['Content-Type'] or msg.headers['Content-type'];
-                               REQUEST_METHOD    = msg.request_method:upper();
-                               REQUEST_URI       = msg.request_uri;
-                               SCRIPT_NAME       = msg.request_uri:gsub("?.+$","");
-                               SCRIPT_FILENAME   = "";         -- XXX implement me
-                               SERVER_PROTOCOL   = "HTTP/" .. string.format("%.1f", msg.http_version);
-                               QUERY_STRING      = msg.request_uri:match("?")
-                                       and msg.request_uri:gsub("^.+?","") or ""
-                       }
-
-                       -- Populate HTTP_* environment variables
-                       for i, hdr in ipairs( {
-                               'Accept',
-                               'Accept-Charset',
-                               'Accept-Encoding',
-                               'Accept-Language',
-                               'Connection',
-                               'Cookie',
-                               'Host',
-                               'Referer',
-                               'User-Agent',
-                       } ) do
-                               local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
-                               local val = msg.headers[hdr]
-
-                               msg.env[var] = val
-                       end
-               end
-       end
-
-       return msg
-end
-
---- Try to extract and decode a http message body from the given ltn12 source.
--- This function will examine the Content-Type within the given message object
--- to select the appropriate content decoder.
--- Currently the application/x-www-urlencoded and application/form-data
--- mime types are supported. If the encountered content encoding can't be
--- handled then the whole message body will be stored unaltered as "content"
--- property within the given message object.
--- @param src          Ltn12 source function
--- @param msg          HTTP message object
--- @param filecb       File data callback (optional, see mimedecode_message_body())
--- @return                     Value indicating successful operation (not nil means "ok")
--- @return                     String containing the error if unsuccessful
--- @see                                parse_message_header
-function parse_message_body( src, msg, filecb )
-       -- Is it multipart/mime ?
-       if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
-          msg.env.CONTENT_TYPE:match("^multipart/form%-data")
-       then
-
-               return mimedecode_message_body( src, msg, filecb )
-
-       -- Is it application/x-www-form-urlencoded ?
-       elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
-              msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
-       then
-               return urldecode_message_body( src, msg, filecb )
-
-
-       -- Unhandled encoding
-       -- If a file callback is given then feed it chunk by chunk, else
-       -- store whole buffer in message.content
-       else
-
-               local sink
-
-               -- If we have a file callback then feed it
-               if type(filecb) == "function" then
-                       sink = filecb
-
-               -- ... else append to .content
-               else
-                       msg.content = ""
-                       msg.content_length = 0
-
-                       sink = function( chunk, err )
-                               if chunk then
-                                       if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
-                                               msg.content        = msg.content        .. chunk
-                                               msg.content_length = msg.content_length + #chunk
-                                               return true
-                                       else
-                                               return nil, "POST data exceeds maximum allowed length"
-                                       end
-                               end
-                               return true
-                       end
-               end
-
-               -- Pump data...
-               while true do
-                       local ok, err = ltn12.pump.step( src, sink )
-
-                       if not ok and err then
-                               return nil, err
-                       elseif not err then
-                               return true
-                       end
-               end
-
-               return true
-       end
-end
-
---- Table containing human readable messages for several http status codes.
--- @class table
-statusmsg = {
-       [200] = "OK",
-       [206] = "Partial Content",
-       [301] = "Moved Permanently",
-       [302] = "Found",
-       [304] = "Not Modified",
-       [400] = "Bad Request",
-       [403] = "Forbidden",
-       [404] = "Not Found",
-       [405] = "Method Not Allowed",
-       [408] = "Request Time-out",
-       [411] = "Length Required",
-       [412] = "Precondition Failed",
-       [416] = "Requested range not satisfiable",
-       [500] = "Internal Server Error",
-       [503] = "Server Unavailable",
-}
diff --git a/libs/web/luasrc/http/protocol/conditionals.lua b/libs/web/luasrc/http/protocol/conditionals.lua
deleted file mode 100644 (file)
index 75e1f7b..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
---[[
-
-HTTP protocol implementation for LuCI - RFC2616 / 14.19, 14.24 - 14.28
-(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
-]]--
-
---- LuCI http protocol implementation - HTTP/1.1 bits.
--- This class provides basic ETag handling and implements most of the
--- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
-module("luci.http.protocol.conditionals", package.seeall)
-
-local date = require("luci.http.protocol.date")
-
-
---- Implement 14.19 / ETag.
--- @param stat A file.stat structure
--- @return             String containing the generated tag suitable for ETag headers
-function mk_etag( stat )
-       if stat ~= nil then
-               return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
-       end
-end
-
---- 14.24 / If-Match
--- Test whether the given message object contains an "If-Match" header and
--- compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
-function if_match( req, stat )
-       local h    = req.headers
-       local etag = mk_etag( stat )
-
-       -- Check for matching resource
-       if type(h['If-Match']) == "string" then
-               for ent in h['If-Match']:gmatch("([^, ]+)") do
-                       if ( ent == '*' or ent == etag ) and stat ~= nil then
-                               return true
-                       end
-               end
-
-               return false, 412
-       end
-
-       return true
-end
-
---- 14.25 / If-Modified-Since
--- Test whether the given message object contains an "If-Modified-Since" header
--- and compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
--- @return             Table containing extra HTTP headers if the precondition failed
-function if_modified_since( req, stat )
-       local h = req.headers
-
-       -- Compare mtimes
-       if type(h['If-Modified-Since']) == "string" then
-               local since = date.to_unix( h['If-Modified-Since'] )
-
-               if stat == nil or since < stat.mtime then
-                       return true
-               end
-
-               return false, 304, {
-                       ["ETag"]          = mk_etag( stat );
-                       ["Date"]          = date.to_http( os.time() );
-                       ["Last-Modified"] = date.to_http( stat.mtime )
-               }
-       end
-
-       return true
-end
-
---- 14.26 / If-None-Match
--- Test whether the given message object contains an "If-None-Match" header and
--- compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
--- @return             Table containing extra HTTP headers if the precondition failed
-function if_none_match( req, stat )
-       local h      = req.headers
-       local etag   = mk_etag( stat )
-       local method = req.env and req.env.REQUEST_METHOD or "GET"
-
-       -- Check for matching resource
-       if type(h['If-None-Match']) == "string" then
-               for ent in h['If-None-Match']:gmatch("([^, ]+)") do
-                       if ( ent == '*' or ent == etag ) and stat ~= nil then
-                               if method == "GET" or method == "HEAD" then
-                                       return false, 304, {
-                                               ["ETag"]          = etag;
-                                               ["Date"]          = date.to_http( os.time() );
-                                               ["Last-Modified"] = date.to_http( stat.mtime )
-                                       }
-                               else
-                                       return false, 412
-                               end
-                       end
-               end
-       end
-
-       return true
-end
-
---- 14.27 / If-Range
--- The If-Range header is currently not implemented due to the lack of general
--- byte range stuff in luci.http.protocol . This function will always return
--- false, 412 to indicate a failed precondition.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
-function if_range( req, stat )
-       -- Sorry, no subranges (yet)
-       return false, 412
-end
-
---- 14.28 / If-Unmodified-Since
--- Test whether the given message object contains an "If-Unmodified-Since"
--- header and compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
-function if_unmodified_since( req, stat )
-       local h = req.headers
-
-       -- Compare mtimes
-       if type(h['If-Unmodified-Since']) == "string" then
-               local since = date.to_unix( h['If-Unmodified-Since'] )
-
-               if stat ~= nil and since <= stat.mtime then
-                       return false, 412
-               end
-       end
-
-       return true
-end
diff --git a/libs/web/luasrc/http/protocol/date.lua b/libs/web/luasrc/http/protocol/date.lua
deleted file mode 100644 (file)
index 83d11e2..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
---[[
-
-HTTP protocol implementation for LuCI - date handling
-(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
-]]--
-
---- LuCI http protocol implementation - date helper class.
--- This class contains functions to parse, compare and format http dates.
-module("luci.http.protocol.date", package.seeall)
-
-require("luci.sys.zoneinfo")
-
-
-MONTHS = {
-       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
-       "Sep", "Oct", "Nov", "Dec"
-}
-
---- Return the time offset in seconds between the UTC and given time zone.
--- @param tz   Symbolic or numeric timezone specifier
--- @return             Time offset to UTC in seconds
-function tz_offset(tz)
-
-       if type(tz) == "string" then
-
-               -- check for a numeric identifier
-               local s, v = tz:match("([%+%-])([0-9]+)")
-               if s == '+' then s = 1 else s = -1 end
-               if v then v = tonumber(v) end
-
-               if s and v then
-                       return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
-
-               -- lookup symbolic tz
-               elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
-                       return luci.sys.zoneinfo.OFFSET[tz:lower()]
-               end
-
-       end
-
-       -- bad luck
-       return 0
-end
-
---- Parse given HTTP date string and convert it to unix epoch time.
--- @param data String containing the date
--- @return             Unix epoch time
-function to_unix(date)
-
-       local wd, day, mon, yr, hr, min, sec, tz = date:match(
-               "([A-Z][a-z][a-z]), ([0-9]+) " ..
-               "([A-Z][a-z][a-z]) ([0-9]+) " ..
-               "([0-9]+):([0-9]+):([0-9]+) " ..
-               "([A-Z0-9%+%-]+)"
-       )
-
-       if day and mon and yr and hr and min and sec then
-               -- find month
-               local month = 1
-               for i = 1, 12 do
-                       if MONTHS[i] == mon then
-                               month = i
-                               break
-                       end
-               end
-
-               -- convert to epoch time
-               return tz_offset(tz) + os.time( {
-                       year  = yr,
-                       month = month,
-                       day   = day,
-                       hour  = hr,
-                       min   = min,
-                       sec   = sec
-               } )
-       end
-
-       return 0
-end
-
---- Convert the given unix epoch time to valid HTTP date string.
--- @param time Unix epoch time
--- @return             String containing the formatted date
-function to_http(time)
-       return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
-end
-
---- Compare two dates which can either be unix epoch times or HTTP date strings.
--- @param d1   The first date or epoch time to compare
--- @param d2   The first date or epoch time to compare
--- @return             -1  -  if d1 is lower then d2
--- @return             0   -  if both dates are equal
--- @return             1   -  if d1 is higher then d2
-function compare(d1, d2)
-
-       if d1:match("[^0-9]") then d1 = to_unix(d1) end
-       if d2:match("[^0-9]") then d2 = to_unix(d2) end
-
-       if d1 == d2 then
-               return 0
-       elseif d1 < d2 then
-               return -1
-       else
-               return 1
-       end
-end
diff --git a/libs/web/luasrc/http/protocol/mime.lua b/libs/web/luasrc/http/protocol/mime.lua
deleted file mode 100644 (file)
index c878160..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
---[[
-
-HTTP protocol implementation for LuCI - mime handling
-(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
-]]--
-
---- LuCI http protocol implementation - mime helper class.
--- This class provides functions to guess mime types from file extensions and
--- vice versa.
-module("luci.http.protocol.mime", package.seeall)
-
-require("luci.util")
-
---- MIME mapping table containg extension - mimetype relations.
--- @class table
-MIME_TYPES = {
-    ["txt"]   = "text/plain";
-    ["js"]    = "text/javascript";
-    ["css"]   = "text/css";
-    ["htm"]   = "text/html";
-    ["html"]  = "text/html";
-    ["patch"] = "text/x-patch";
-    ["c"]     = "text/x-csrc";
-    ["h"]     = "text/x-chdr";
-    ["o"]     = "text/x-object";
-    ["ko"]    = "text/x-object";
-
-    ["bmp"]   = "image/bmp";
-    ["gif"]   = "image/gif";
-    ["png"]   = "image/png";
-    ["jpg"]   = "image/jpeg";
-    ["jpeg"]  = "image/jpeg";
-    ["svg"]   = "image/svg+xml";
-
-    ["zip"]   = "application/zip";
-    ["pdf"]   = "application/pdf";
-    ["xml"]   = "application/xml";
-    ["xsl"]   = "application/xml";
-    ["doc"]   = "application/msword";
-    ["ppt"]   = "application/vnd.ms-powerpoint";
-    ["xls"]   = "application/vnd.ms-excel";
-    ["odt"]   = "application/vnd.oasis.opendocument.text";
-    ["odp"]   = "application/vnd.oasis.opendocument.presentation";
-    ["pl"]    = "application/x-perl";
-    ["sh"]    = "application/x-shellscript";
-    ["php"]   = "application/x-php";
-    ["deb"]   = "application/x-deb";
-    ["iso"]   = "application/x-cd-image";
-    ["tgz"]   = "application/x-compressed-tar";
-
-    ["mp3"]   = "audio/mpeg";
-    ["ogg"]   = "audio/x-vorbis+ogg";
-    ["wav"]   = "audio/x-wav";
-
-    ["mpg"]   = "video/mpeg";
-    ["mpeg"]  = "video/mpeg";
-    ["avi"]   = "video/x-msvideo";
-}
-
---- Extract extension from a filename and return corresponding mime-type or
--- "application/octet-stream" if the extension is unknown.
--- @param filename     The filename for which the mime type is guessed
--- @return                     String containign the determined mime type
-function to_mime(filename)
-       if type(filename) == "string" then
-               local ext = filename:match("[^%.]+$")
-
-               if ext and MIME_TYPES[ext:lower()] then
-                       return MIME_TYPES[ext:lower()]
-               end
-       end
-
-       return "application/octet-stream"
-end
-
---- Return corresponding extension for a given mime type or nil if the
--- given mime-type is unknown.
--- @param mimetype     The mimetype to retrieve the extension from
--- @return                     String with the extension or nil for unknown type
-function to_ext(mimetype)
-       if type(mimetype) == "string" then
-               for ext, type in luci.util.kspairs( MIME_TYPES ) do
-                       if type == mimetype then
-                               return ext
-                       end
-               end
-       end
-
-       return nil
-end
diff --git a/libs/web/luasrc/i18n.lua b/libs/web/luasrc/i18n.lua
deleted file mode 100644 (file)
index 545a8ae..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
---[[
-LuCI - Internationalisation
-
-Description:
-A very minimalistic but yet effective internationalisation module
-
-FileId:
-$Id$
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
---- LuCI translation library.
-module("luci.i18n", package.seeall)
-require("luci.util")
-
-local tparser = require "luci.template.parser"
-
-table   = {}
-i18ndir = luci.util.libpath() .. "/i18n/"
-loaded  = {}
-context = luci.util.threadlocal()
-default = "en"
-
---- Clear the translation table.
-function clear()
-end
-
---- Load a translation and copy its data into the translation table.
--- @param file Language file
--- @param lang Two-letter language code
--- @param force        Force reload even if already loaded (optional)
--- @return             Success status
-function load(file, lang, force)
-end
-
---- Load a translation file using the default translation language.
--- Alternatively load the translation of the fallback language.
--- @param file Language file
--- @param force        Force reload even if already loaded (optional)
-function loadc(file, force)
-end
-
---- Set the context default translation language.
--- @param lang Two-letter language code
-function setlanguage(lang)
-       context.lang   = lang:gsub("_", "-")
-       context.parent = (context.lang:match("^([a-z][a-z])_"))
-       if not tparser.load_catalog(context.lang, i18ndir) then
-               if context.parent then
-                       tparser.load_catalog(context.parent, i18ndir)
-                       return context.parent
-               end
-       end
-       return context.lang
-end
-
---- Return the translated value for a specific translation key.
--- @param key  Default translation text
--- @return             Translated string
-function translate(key)
-       return tparser.translate(key) or key
-end
-
---- Return the translated value for a specific translation key and use it as sprintf pattern.
--- @param key          Default translation text
--- @param ...          Format parameters
--- @return                     Translated and formatted string
-function translatef(key, ...)
-       return tostring(translate(key)):format(...)
-end
-
---- Return the translated value for a specific translation key
--- and ensure that the returned value is a Lua string value.
--- This is the same as calling <code>tostring(translate(...))</code>
--- @param key          Default translation text
--- @return                     Translated string
-function string(key)
-       return tostring(translate(key))
-end
-
---- Return the translated value for a specific translation key and use it as sprintf pattern.
--- Ensure that the returned value is a Lua string value.
--- This is the same as calling <code>tostring(translatef(...))</code>
--- @param key          Default translation text
--- @param ...          Format parameters
--- @return                     Translated and formatted string
-function stringf(key, ...)
-       return tostring(translate(key)):format(...)
-end
diff --git a/libs/web/luasrc/sauth.lua b/libs/web/luasrc/sauth.lua
deleted file mode 100644 (file)
index 32f172d..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
---[[
-
-Session authentication
-(c) 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-
-]]--
-
---- LuCI session library.
-module("luci.sauth", package.seeall)
-require("luci.util")
-require("luci.sys")
-require("luci.config")
-local nixio = require "nixio", require "nixio.util"
-local fs = require "nixio.fs"
-
-
-luci.config.sauth = luci.config.sauth or {}
-sessionpath = luci.config.sauth.sessionpath
-sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
-
---- Prepare session storage by creating the session directory.
-function prepare()
-       fs.mkdir(sessionpath, 700)
-       if not sane() then
-               error("Security Exception: Session path is not sane!")
-       end
-end
-
-local function _read(id)
-       local blob = fs.readfile(sessionpath .. "/" .. id)
-       return blob
-end
-
-local function _write(id, data)
-       local f = nixio.open(sessionpath .. "/" .. id, "w", 600)
-       f:writeall(data)
-       f:close()
-end
-
-local function _checkid(id)
-       return not not (id and #id == 32 and id:match("^[a-fA-F0-9]+$"))
-end
-
---- Write session data to a session file.
--- @param id   Session identifier
--- @param data Session data table
-function write(id, data)
-       if not sane() then
-               prepare()
-       end
-
-       assert(_checkid(id), "Security Exception: Session ID is invalid!")
-       assert(type(data) == "table", "Security Exception: Session data invalid!")
-
-       data.atime = luci.sys.uptime()
-
-       _write(id, luci.util.get_bytecode(data))
-end
-
---- Read a session and return its content.
--- @param id   Session identifier
--- @return             Session data table or nil if the given id is not found
-function read(id)
-       if not id or #id == 0 then
-               return nil
-       end
-
-       assert(_checkid(id), "Security Exception: Session ID is invalid!")
-
-       if not sane(sessionpath .. "/" .. id) then
-               return nil
-       end
-
-       local blob = _read(id)
-       local func = loadstring(blob)
-       setfenv(func, {})
-
-       local sess = func()
-       assert(type(sess) == "table", "Session data invalid!")
-
-       if sess.atime and sess.atime + sessiontime < luci.sys.uptime() then
-               kill(id)
-               return nil
-       end
-
-       -- refresh atime in session
-       write(id, sess)
-
-       return sess
-end
-
---- Check whether Session environment is sane.
--- @return Boolean status
-function sane(file)
-       return luci.sys.process.info("uid")
-                       == fs.stat(file or sessionpath, "uid")
-               and fs.stat(file or sessionpath, "modestr")
-                       == (file and "rw-------" or "rwx------")
-end
-
---- Kills a session
--- @param id   Session identifier
-function kill(id)
-       assert(_checkid(id), "Security Exception: Session ID is invalid!")
-       fs.unlink(sessionpath .. "/" .. id)
-end
-
---- Remove all expired session data files
-function reap()
-       if sane() then
-               local id
-               for id in nixio.fs.dir(sessionpath) do
-                       if _checkid(id) then
-                               -- reading the session will kill it if it is expired
-                               read(id)
-                       end
-               end
-       end
-end
diff --git a/libs/web/luasrc/template.lua b/libs/web/luasrc/template.lua
deleted file mode 100644 (file)
index 72127d1..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
---[[
-LuCI - 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 <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at 
-
-       http://www.apache.org/licenses/LICENSE-2.0 
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
-
-local util = require "luci.util"
-local config = require "luci.config"
-local tparser = require "luci.template.parser"
-
-local tostring, pairs, loadstring = tostring, pairs, loadstring
-local setmetatable, loadfile = setmetatable, loadfile
-local getfenv, setfenv, rawget = getfenv, setfenv, rawget
-local assert, type, error = assert, type, error
-
---- LuCI template library.
-module "luci.template"
-
-config.template = config.template or {}
-viewdir = config.template.viewdir or util.libpath() .. "/view"
-
-
--- Define the namespace for template modules
-context = util.threadlocal()
-
---- Render a certain template.
--- @param name         Template name
--- @param scope                Scope to assign to template (optional)
-function render(name, scope)
-       return Template(name):render(scope or getfenv(2))
-end
-
-
--- Template class
-Template = util.class()
-
--- Shared template cache to store templates in to avoid unnecessary reloading
-Template.cache = setmetatable({}, {__mode = "v"})
-
-
--- Constructor - Reads and compiles the template on-demand
-function Template.__init__(self, name) 
-
-       self.template = self.cache[name]
-       self.name = name
-       
-       -- Create a new namespace for this template
-       self.viewns = context.viewns
-       
-       -- If we have a cached template, skip compiling and loading
-       if not self.template then
-
-               -- Compile template
-               local err
-               local sourcefile = viewdir .. "/" .. name .. ".htm"
-
-               self.template, _, err = tparser.parse(sourcefile)
-
-               -- If we have no valid template throw error, otherwise cache the template
-               if not self.template then
-                       error("Failed to load template '" .. name .. "'.\n" ..
-                             "Error while parsing template '" .. sourcefile .. "':\n" ..
-                             (err or "Unknown syntax error"))
-               else
-                       self.cache[name] = self.template
-               end
-       end
-end
-
-
--- Renders a template
-function Template.render(self, scope)
-       scope = scope or getfenv(2)
-       
-       -- Put our predefined objects in the scope of the template
-       setfenv(self.template, setmetatable({}, {__index =
-               function(tbl, key)
-                       return rawget(tbl, key) or self.viewns[key] or scope[key]
-               end}))
-       
-       -- Now finally render the thing
-       local stat, err = util.copcall(self.template)
-       if not stat then
-               error("Failed to execute template '" .. self.name .. "'.\n" ..
-                     "A runtime error occured: " .. tostring(err or "(nil)"))
-       end
-end
diff --git a/libs/web/luasrc/view/cbi/apply_xhr.htm b/libs/web/luasrc/view/cbi/apply_xhr.htm
deleted file mode 100644 (file)
index 1814c93..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<% export("cbi_apply_xhr", function(id, configs, redirect) -%>
-<fieldset class="cbi-section" id="cbi-apply-<%=id%>">
-       <legend><%:Applying changes%></legend>
-       <script type="text/javascript">//<![CDATA[
-               var apply_xhr = new XHR();
-
-               apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "restart", table.concat(configs, ","))%>', null,
-                       function() {
-                               var checkfinish = function() {
-                                       apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "status")%>', null,
-                                               function(x) {
-                                                       if( x.responseText == 'finish' )
-                                                       {
-                                                               var e = document.getElementById('cbi-apply-<%=id%>-status');
-                                                               if( e )
-                                                               {
-                                                                       e.innerHTML = '<%:Configuration applied.%>';
-                                                                       window.setTimeout(function() {
-                                                                               e.parentNode.style.display = 'none';
-                                                                               <% if redirect then %>location.href='<%=redirect%>';<% end %>
-                                                                       }, 1000);
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               var e = document.getElementById('cbi-apply-<%=id%>-status');
-                                                               if( e && x.responseText ) e.innerHTML = x.responseText;
-
-                                                               window.setTimeout(checkfinish, 1000);
-                                                       }
-                                               }
-                                       );
-                               }
-
-                               window.setTimeout(checkfinish, 1000);
-                       }
-               );
-       //]]></script>
-
-       <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" />
-       <span id="cbi-apply-<%=id%>-status"><%:Waiting for changes to be applied...%></span>
-</fieldset>
-<%-    end) %>
diff --git a/libs/web/luasrc/view/cbi/browser.htm b/libs/web/luasrc/view/cbi/browser.htm
deleted file mode 100644 (file)
index e4a4077..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<% local v = self:cfgvalue(section) -%>
-<%+cbi/valueheader%>
-       <input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
-       <script type="text/javascript">
-cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=luci.dispatcher.build_url("admin", "filebrowser")%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
-       </script>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/button.htm b/libs/web/luasrc/view/cbi/button.htm
deleted file mode 100644 (file)
index 30f8ddf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<%+cbi/valueheader%>
-       <% if self:cfgvalue(section) ~= false then %>
-               <input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
-       <% else %>
-               -
-       <% end %>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/cell_valuefooter.htm b/libs/web/luasrc/view/cbi/cell_valuefooter.htm
deleted file mode 100644 (file)
index 220ebd4..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-</div>
-<div id="cbip-<%=self.config.."-"..section.."-"..self.option%>"></div>
-</td>
-
-<% if #self.deps > 0 then -%>
-       <script type="text/javascript">
-               <% for j, d in ipairs(self.deps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-               -%>
-                       <%-=string.format('"cbid.%s.%s.%s"', self.config, section, k) .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option%>");
-               <%- end %>
-       </script>
-<%- end %>
diff --git a/libs/web/luasrc/view/cbi/cell_valueheader.htm b/libs/web/luasrc/view/cbi/cell_valueheader.htm
deleted file mode 100644 (file)
index 9e2e145..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<td class="cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>">
-<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
diff --git a/libs/web/luasrc/view/cbi/compound.htm b/libs/web/luasrc/view/cbi/compound.htm
deleted file mode 100644 (file)
index 12d02bb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<%- self:render_children() %>
diff --git a/libs/web/luasrc/view/cbi/delegator.htm b/libs/web/luasrc/view/cbi/delegator.htm
deleted file mode 100644 (file)
index 4fd1926..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<%- self.active:render() %>
-       <div class="cbi-page-actions">
-               <input type="hidden" name="cbi.delg.current" value="<%=self.current%>" />
-<% for _, x in ipairs(self.chain) do %>
-               <input type="hidden" name="cbi.delg.path" value="<%=x%>" />
-<% end %>
-<% if not self.disallow_pageactions then %>
-<% if self.allow_finish and not self:get_next(self.current) then %>
-       <input class="cbi-button cbi-button-finish" type="submit" value="<%:Finish%>" />
-<% elseif self:get_next(self.current) then %>
-               <input class="cbi-button cbi-button-next" type="submit" value="<%:Next Â»%>" />
-<% end %>
-<% if self.allow_cancel then %>
-               <input class="cbi-button cbi-button-cancel" type="submit" name="cbi.cancel" value="<%:Cancel%>" />
-<% end %>
-<% if self.allow_reset then %>
-               <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
-<% end %>
-<% if self.allow_back and self:get_prev(self.current) then %>
-               <input class="cbi-button cbi-button-back" type="submit" name="cbi.delg.back" value="<%:« Back%>" />
-<% end %>
-<% end %>
-               <script type="text/javascript">cbi_d_update();</script>
-       </div>
diff --git a/libs/web/luasrc/view/cbi/dvalue.htm b/libs/web/luasrc/view/cbi/dvalue.htm
deleted file mode 100644 (file)
index 78e6f32..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<%+cbi/valueheader%>
-<% if self.href then %><a href="<%=self.href%>"><% end -%>
-       <%
-               local val = self:cfgvalue(section) or self.default or ""
-               if not self.rawhtml then
-                       write(pcdata(val))
-               else
-                       write(val)
-               end
-       %>
-<%- if self.href then %></a><%end%>
-<input type="hidden" id="<%=cbid%>" value="<%=pcdata(self:cfgvalue(section) or self.default or "")%>" />
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/dynlist.htm b/libs/web/luasrc/view/cbi/dynlist.htm
deleted file mode 100644 (file)
index fd626a4..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<%+cbi/valueheader%>
-<div>
-<%
-       local vals = self:cfgvalue(section) or {}
-       for i=1, #vals + 1 do
-               local val = vals[i]
-               if (val and #val > 0) or (i == 1) then
-%>
-       <input class="cbi-input-text" value="<%=pcdata(val)%>" onchange="cbi_d_update(this.id)" type="text"<%=
-               attr("id", cbid .. "." .. i) .. attr("name", cbid) .. ifattr(self.size, "size") ..
-               ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder)
-       %> /><br />
-<% end end %>
-</div>
-<script type="text/javascript">
-cbi_dynlist_init(
-       '<%=cbid%>', '<%=resource%>', '<%=self.datatype%>',
-       <%=tostring(self.optional or self.rmempty)%>
-       <%- if #self.keylist > 0 then -%>, [{
-               <%- for i, k in ipairs(self.keylist) do -%>
-                       <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
-                       <%-if i<#self.keylist then-%>,<%-end-%>
-               <%-     end     -%>
-       }, '<%: -- custom -- %>']<% end -%>);
-</script>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/filebrowser.htm b/libs/web/luasrc/view/cbi/filebrowser.htm
deleted file mode 100644 (file)
index a79beeb..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-    <title>Filebrowser - LuCI</title>
-       <style type="text/css">
-               #path, #listing {
-                       font-size: 85%;
-               }
-
-               ul {
-                       padding-left: 0;
-                       list-style-type: none;
-               }
-
-               li img {
-                       vertical-align: bottom;
-                       margin-right: 0.2em;
-               }
-       </style>
-
-       <script type="text/javascript">
-               function callback(path) {
-                       if( window.opener ) {
-                               var input = window.opener.document.getElementById('<%=luci.http.formvalue('field')%>');
-                               if( input ) {
-                                       input.value = path;
-                                       window.close();
-                               }
-                       }
-               }
-       </script>
-</head>
-<body>
-       <%
-               require("nixio.fs")
-               require("nixio.util")
-               require("luci.http")
-               require("luci.dispatcher")
-
-               local field   = luci.http.formvalue('field')
-               local request = luci.dispatcher.context.args
-               local path    = { '' }
-
-               for i = 1, #request do
-                       if request[i] ~= '..' and #request[i] > 0 then
-                               path[#path+1] = request[i]
-                       end
-               end
-
-               local filepath = table.concat( path, '/' )
-               local filestat = nixio.fs.stat( filepath )
-               local baseurl  = luci.dispatcher.build_url('admin', 'filebrowser')
-
-               if filestat and filestat.type == "reg" then
-                       table.remove( path, #path )
-                       filepath = table.concat( path, '/' ) .. '/'
-               elseif not ( filestat and filestat.type == "dir" ) then
-                       path     = { '' }
-                       filepath = '/'
-               else
-                       filepath = filepath .. '/'
-               end
-
-               local entries = nixio.util.consume((nixio.fs.dir(filepath)))
-       -%>
-    <div id="path">
-               Location:
-               <% for i, dir in ipairs(path) do %>
-                       <% if i == 1 then %>
-                               <a href="<%=baseurl%>?field=<%=field%>">(root)</a>
-                       <% elseif next(path, i) then %>
-                               <% baseurl = baseurl .. '/' .. dir %>
-                               / <a href="<%=baseurl%>?field=<%=field%>"><%=dir%></a>
-                       <% else %>
-                               <% baseurl = baseurl .. '/' .. dir %>
-                               / <%=dir%>
-                       <% end %>
-               <% end %>
-       </div>
-
-       <hr />
-
-       <div id="listing">
-               <ul>
-                       <% for _, e in luci.util.vspairs(entries) do
-                           local stat = nixio.fs.stat(filepath..e)
-                               if stat and stat.type == 'dir' then
-                       -%>
-                               <li class="dir">
-                                       <img src="<%=resource%>/cbi/folder.gif" alt="<%:Directory%>" />
-                                       <a href="<%=baseurl%>/<%=e%>?field=<%=field%>"><%=e%>/</a>
-                               </li>
-                       <% end end -%>
-
-                       <% for _, e in luci.util.vspairs(entries) do
-                           local stat = nixio.fs.stat(filepath..e)
-                               if stat and stat.type ~= 'dir' then
-                       -%>
-                               <li class="file">
-                                       <img src="<%=resource%>/cbi/file.gif" alt="<%:File%>" />
-                                       <a href="#" onclick="callback('<%=filepath..e%>')"><%=e%></a>
-                               </li>
-                       <% end end -%>
-               </ul>
-       </div>
-</body>
-</html>
diff --git a/libs/web/luasrc/view/cbi/firewall_zoneforwards.htm b/libs/web/luasrc/view/cbi/firewall_zoneforwards.htm
deleted file mode 100644 (file)
index 2a433b5..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<%+cbi/valueheader%>
-
-<%-
-       local utl = require "luci.util"
-       local fwm = require "luci.model.firewall".init()
-       local nwm = require "luci.model.network".init()
-
-       local zone, fwd, fz
-       local value = self:formvalue(section)
-       if not value or value == "-" then
-               value = self:cfgvalue(section) or self.default
-       end
-
-       local def  = fwm:get_defaults()
-       local zone = fwm:get_zone(value)
-       local empty = true
--%>
-
-<% if zone then %>
-<div style="white-space:nowrap">
-       <label class="zonebadge" style="background-color:<%=zone:get_color()%>">
-               <strong><%=zone:name()%>:</strong>
-               <%-
-                       local zempty = true
-                       for _, net in ipairs(zone:get_networks()) do
-                               net = nwm:get_network(net)
-                               if net then
-                                       zempty = false
-               -%>
-                       <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
-                       <%
-                               local nempty = true
-                               for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
-                                       nempty = false
-                        %>
-                               <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
-                       <% end %>
-                       <% if nempty then %><em><%:(empty)%></em><% end %>
-                       </span>
-               <%- end end -%>
-               <%- if zempty then %><em><%:(empty)%></em><% end -%>
-       </label>
-       &#160;&#8658;&#160;
-       <% for _, fwd in ipairs(zone:get_forwardings_by("src")) do
-               fz = fwd:dest_zone()
-               empty = false %>
-               <label class="zonebadge" style="background-color:<%=fz:get_color()%>">
-                       <strong><%=fz:name()%></strong>
-               </label>&#160;
-       <% end %>
-       <% if empty then %>
-               <label class="zonebadge zonebadge-empty">
-                       <strong><%=zone:forward():upper()%></strong>
-               </label>
-       <% end %>
-</div>
-<% end %>
-
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/firewall_zonelist.htm b/libs/web/luasrc/view/cbi/firewall_zonelist.htm
deleted file mode 100644 (file)
index 7973437..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<%+cbi/valueheader%>
-
-<%-
-       local utl = require "luci.util"
-       local fwm = require "luci.model.firewall".init()
-       local nwm = require "luci.model.network".init()
-
-       local zone, net, iface
-       local zones = fwm:get_zones()
-       local value = self:formvalue(section)
-       if not value or value == "-" then
-               value = self:cfgvalue(section) or self.default
-       end
-
-       local selected = false
-       local checked = { }
-
-       for value in utl.imatch(value) do
-               checked[value] = true
-       end
-
-       if not next(checked) then
-               checked[""] = true
-       end
--%>
-
-<ul style="margin:0; list-style-type:none; text-align:left">
-       <% if self.allowlocal then %>
-       <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> /> &#160;
-               <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
-                       <strong><%:Device%></strong>
-                       <% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
-               </label>
-       </li>
-       <% end %>
-       <% if self.allowany then %>
-       <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> /> &#160;
-               <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
-                       <strong><%:Any zone%></strong>
-                       <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
-               </label>
-       </li>
-       <% end %>
-       <%
-               for _, zone in utl.spairs(zones, function(a,b) return (zones[a]:name() < zones[b]:name()) end) do
-                       if zone:name() ~= self.exclude then
-                               selected = selected or (value == zone:name())
-       %>
-       <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> /> &#160;
-               <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
-                       <strong><%=zone:name()%>:</strong>
-                       <%
-                               local zempty = true
-                               for _, net in ipairs(zone:get_networks()) do
-                                       net = nwm:get_network(net)
-                                       if net then
-                                               zempty = false
-                       %>
-                               <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
-                               <%
-                                       local nempty = true
-                                       for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
-                                               nempty = false
-                                %>
-                                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
-                               <% end %>
-                               <% if nempty then %><em><%:(empty)%></em><% end %>
-                               </span>
-                       <% end end %>
-                       <% if zempty then %><em><%:(empty)%></em><% end %>
-               </label>
-       </li>
-       <% end end %>
-
-       <% if self.widget ~= "checkbox" and not self.nocreate then %>
-       <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> /> &#160;
-               <div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
-                       <em><%:unspecified -or- create:%>&#160;</em>
-                       <input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
-               </div>
-       </li>
-       <% end %>
-</ul>
-
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/footer.htm b/libs/web/luasrc/view/cbi/footer.htm
deleted file mode 100644 (file)
index 2c34028..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-       <%- if pageaction then -%>
-       <div class="cbi-page-actions">
-               <% if redirect then %>
-               <div style="float:left">
-                       <input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
-               </div>
-               <% end %>
-
-               <% if flow.skip then %>
-                       <input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
-               <% end %>
-               <% if not autoapply and not flow.hideapplybtn then %>
-                       <input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
-               <% end %>
-               <% if not flow.hidesavebtn then %>
-                       <input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
-               <% end %>
-               <% if not flow.hideresetbtn then %>
-                       <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
-               <% end %>
-
-               <script type="text/javascript">cbi_d_update();</script>
-       </div>
-       <%- end -%>
-</form>
-<%+footer%>
diff --git a/libs/web/luasrc/view/cbi/full_valuefooter.htm b/libs/web/luasrc/view/cbi/full_valuefooter.htm
deleted file mode 100644 (file)
index 4876fbc..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-               <% if self.description and #self.description > 0 then -%>
-                       <% if not luci.util.instanceof(self, luci.cbi.DynamicList) and (not luci.util.instanceof(self, luci.cbi.Flag) or self.orientation == "horizontal") then -%>
-                               <br />
-                       <%- end %>
-                       <div class="cbi-value-description">
-                               <span class="cbi-value-helpicon"><img src="<%=resource%>/cbi/help.gif" alt="<%:help%>" /></span>
-                               <%=self.description%>
-                       </div>
-               <%- end %>
-       <%- if self.title and #self.title > 0 then -%>
-       </div>
-       <%- end -%>
-</div>
-
-
-<% if #self.deps > 0 or #self.subdeps > 0 then -%>
-       <script type="text/javascript" id="cbip-<%=self.config.."-"..section.."-"..self.option%>">
-               <% for j, d in ipairs(self.subdeps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-                               local depk
-                               if k:find("!", 1, true) then
-                                       depk = string.format('"%s"', k)
-                               elseif k:find(".", 1, true) then
-                                       depk = string.format('"cbid.%s"', k)
-                               else
-                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
-                               end
-               -%>
-                       <%-= depk .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
-               <%- end %>
-               <% for j, d in ipairs(self.deps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-                               local depk
-                               if k:find("!", 1, true) then
-                                       depk = string.format('"%s"', k)
-                               elseif k:find(".", 1, true) then
-                                       depk = string.format('"cbid.%s"', k)
-                               else
-                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
-                               end
-               -%>
-                       <%-= depk .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
-               <%- end %>
-       </script>
-<%- end %>
diff --git a/libs/web/luasrc/view/cbi/full_valueheader.htm b/libs/web/luasrc/view/cbi/full_valueheader.htm
deleted file mode 100644 (file)
index aaf0854..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<div class="cbi-value<% if self.error and self.error[section] then %> cbi-value-error<% end %><% if self.last_child then %> cbi-value-last<% end %>" id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
-       <%- if self.title and #self.title > 0 then -%>
-       <label class="cbi-value-title"<%= attr("for", cbid) %>>
-       <%- if self.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=self.titleref%>"><%- end -%>
-               <%-=self.title-%>
-       <%- if self.titleref then -%></a><%- end -%>
-       </label>
-               <div class="cbi-value-field">
-       <%- end -%>
diff --git a/libs/web/luasrc/view/cbi/fvalue.htm b/libs/web/luasrc/view/cbi/fvalue.htm
deleted file mode 100644 (file)
index a1e0808..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<%+cbi/valueheader%>
-       <input type="hidden" value="1"<%=
-               attr("name", "cbi.cbe." .. self.config .. "." .. section .. "." .. self.option)
-       %> />
-       <input class="cbi-input-checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="checkbox"<%=
-               attr("id", cbid) .. attr("name", cbid) .. attr("value", self.enabled or 1) ..
-               ifattr((self:cfgvalue(section) or self.default) == self.enabled, "checked", "checked")
-       %> />
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/header.htm b/libs/web/luasrc/view/cbi/header.htm
deleted file mode 100644 (file)
index 2bddaba..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<%+header%>
-<form method="post" name="cbi" action="<%=REQUEST_URI%>" enctype="multipart/form-data" onreset="return cbi_validate_reset(this)" onsubmit="return cbi_validate_form(this, '<%:Some fields are invalid, cannot save values!%>')">
-       <div>
-               <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
-               <input type="hidden" name="cbi.submit" value="1" />
-               <input type="submit" value="<%:Save%>" class="hidden" />
-       </div>
diff --git a/libs/web/luasrc/view/cbi/lvalue.htm b/libs/web/luasrc/view/cbi/lvalue.htm
deleted file mode 100644 (file)
index 8cc086d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<%+cbi/valueheader%>
-<% if self.widget == "select" then %>
-       <select class="cbi-input-select" onchange="cbi_d_update(this.id)"<%= attr("id", cbid) .. attr("name", cbid) .. ifattr(self.size, "size") %>>
-       <% for i, key in pairs(self.keylist) do -%>
-               <option id="cbi-<%=self.config.."-"..section.."-"..self.option.."-"..key%>"<%= attr("value", key) .. ifattr(tostring(self:cfgvalue(section) or self.default) == key, "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
-       <%- end %>
-       </select>
-<% elseif self.widget == "radio" then
-       local c = 0
-       for i, key in pairs(self.keylist) do
-       c = c + 1
-%>
-       <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr((self:cfgvalue(section) or self.default) == key, "checked", "checked") %> />
-       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label>
-<% if c == self.size then c = 0 %><% if self.orientation == "horizontal" then %>&#160;<% else %><br /><% end %>
-<% end end %>
-<% end %>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/map.htm b/libs/web/luasrc/view/cbi/map.htm
deleted file mode 100644 (file)
index 053220d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<%- if firstmap and messages then local msg; for _, msg in ipairs(messages) do -%>
-       <div class="errorbox"><%=pcdata(msg)%></div>
-<%- end end -%>
-
-<%-+cbi/apply_xhr-%>
-
-<div class="cbi-map" id="cbi-<%=self.config%>">
-       <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
-       <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
-       <%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
-       <%- self:render_children() %>
-       <br />
-</div>
diff --git a/libs/web/luasrc/view/cbi/mvalue.htm b/libs/web/luasrc/view/cbi/mvalue.htm
deleted file mode 100644 (file)
index 6a0b388..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<% local v = self:valuelist(section) or {} -%>
-<%+cbi/valueheader%>
-<% if self.widget == "select" then %>
-       <select class="cbi-input-select" multiple="multiple" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. ifattr(self.size, "size") %>>
-       <% for i, key in pairs(self.keylist) do -%>
-               <option<%= attr("value", key) .. ifattr(luci.util.contains(v, key), "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
-       <%- end %>
-       </select>
-<% elseif self.widget == "checkbox" then
-       local c = 0;
-       for i, key in pairs(self.keylist) do
-       c = c + 1
-%>
-       <input class="cbi-input-checkbox" type="checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "checked", "checked") %> />
-       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label><br />
-<% if c == self.size then c = 0 %><br />
-<% end end %>
-<% end %>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/network_ifacelist.htm b/libs/web/luasrc/view/cbi/network_ifacelist.htm
deleted file mode 100644 (file)
index 643d849..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<%+cbi/valueheader%>
-
-<%-
-       local utl = require "luci.util"
-       local net = require "luci.model.network".init()
-       local cbeid = luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option
-
-       local iface
-       local ifaces = net:get_interfaces()
-       local value
-
-       if self.map:formvalue(cbeid) == "1" then
-               value = self:formvalue(section) or self.default or ""
-       else
-               value = self:cfgvalue(section) or self.default
-       end
-
-       local checked = { }
-
-       if value then
-               for value in utl.imatch(value) do
-                       checked[value] = true
-               end
-       else
-               local n = self.network and net:get_network(self.network)
-               if n then
-                       local i
-                       for _, i in ipairs(n:get_interfaces() or { n:get_interface() }) do
-                               checked[i:name()] = true
-                       end
-               end
-       end
--%>
-
-<input type="hidden" name="<%=cbeid%>" value="1" />
-<ul style="margin:0; list-style-type:none">
-       <% for _, iface in ipairs(ifaces) do
-            local link = iface:adminlink()
-         if (not self.nobridges  or not iface:is_bridge()) and
-                   (not self.noinactive or iface:is_up()) and
-                   iface:name() ~= self.exclude
-                then %>
-       <li>
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
-                       attr("type", self.widget or "radio") ..
-                       attr("id", cbid .. "." .. iface:name()) ..
-                       attr("name", cbid) .. attr("value", iface:name()) ..
-                       ifattr(checked[iface:name()], "checked", "checked")
-               %> /> &#160;
-               <label<%=attr("for", cbid .. "." .. iface:name())%>>
-                       <% if link then -%><a href="<%=link%>"><% end -%>
-                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
-                       <% if link then -%></a><% end -%>
-                       <%=pcdata(iface:get_i18n())%>
-                       <% local ns = iface:get_networks(); if #ns > 0 then %>(
-                               <%- local i, n; for i, n in ipairs(ns) do -%>
-                                       <%-= (i>1) and ', ' -%>
-                                       <a href="<%=n:adminlink()%>"><%=n:name()%></a>
-                               <%- end -%>
-                       )<% end %>
-               </label>
-       </li>
-       <% end end %>
-       <% if not self.nocreate then %>
-       <li>
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
-                       attr("type", self.widget or "radio") ..
-                       attr("id", cbid .. "_custom") ..
-                       attr("name", cbid) ..
-                       attr("value", " ")
-               %> /> &#160;
-               <label<%=attr("for", cbid .. "_custom")%>>
-                       <img title="<%:Custom Interface%>" style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/ethernet_disabled.png" />
-                       <%:Custom Interface%>:
-               </label>
-               <input type="text" style="width:50px" onfocus="document.getElementById('<%=cbid%>_custom').checked=true" onblur="var x=document.getElementById('<%=cbid%>_custom'); x.value=this.value; x.checked=true" />
-       </li>
-       <% end %>
-</ul>
-
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/network_netinfo.htm b/libs/web/luasrc/view/cbi/network_netinfo.htm
deleted file mode 100644 (file)
index 4fd8411..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<%+cbi/valueheader%>
-
-<%-
-       local value = self:formvalue(section)
-       if not value or value == "-" then
-               value = self:cfgvalue(section) or self.default
-       end
-
-       local nwm = require "luci.model.network".init()
-       local net = nwm:get_network(value)
--%>
-
-<% if net then %>
-<span class="ifacebadge"><%=net:name()%>:
-       <%
-               local empty = true
-               for _, iface in ipairs(net:get_interfaces() or { net:get_interface() }) do
-                       if not iface:is_bridge() then
-                               empty = false
-        %>
-               <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
-       <% end end %>
-       <% if empty then %><em><%:(no interfaces attached)%></em><% end %>
-</span>
-<% end %>
-
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/network_netlist.htm b/libs/web/luasrc/view/cbi/network_netlist.htm
deleted file mode 100644 (file)
index 7e23d14..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<%+cbi/valueheader%>
-
-<%-
-       local utl = require "luci.util"
-       local nwm = require "luci.model.network".init()
-
-       local net, iface
-       local networks = nwm:get_networks()
-       local value = self:formvalue(section)
-
-       self.cast = nil
-
-       if not value or value == "-" then
-               value = self:cfgvalue(section) or self.default
-       end
-
-       local checked = { }
-       for value in utl.imatch(value) do
-               checked[value] = true
-       end
--%>
-
-<ul style="margin:0; list-style-type:none; text-align:left">
-       <% for _, net in ipairs(networks) do
-              if (net:name() ~= "loopback") and
-                     (net:name() ~= self.exclude) and
-                     (not self.novirtual or not net:is_virtual())
-                  then %>
-       <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
-                       attr("type", self.widget or "radio") ..
-                       attr("id", cbid .. "." .. net:name()) ..
-                       attr("name", cbid) .. attr("value", net:name()) ..
-                       ifattr(checked[net:name()], "checked", "checked")
-               %> /> &#160;
-               <label<%=attr("for", cbid .. "." .. net:name())%>>
-                       <span class="ifacebadge"><%=net:name()%>:
-                               <%
-                                       local empty = true
-                                       for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
-                                               if not iface:is_bridge() then
-                                                       empty = false
-                                %>
-                                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
-                               <% end end %>
-                               <% if empty then %><em><%:(no interfaces attached)%></em><% end %>
-                       </span>
-               </label>
-       </li>
-       <% end end %>
-
-       <% if not self.nocreate then %>
-       <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not value and self.widget ~= "checkbox", "checked", "checked")%> /> &#160;
-               <div style="padding:0.5em; display:inline">
-                       <label<%=attr("for", cbid .. "_new")%>><em>
-                               <%- if self.widget == "checkbox" then -%>
-                                       <%:create:%>
-                               <%- else -%>
-                                       <%:unspecified -or- create:%>
-                               <%- end -%>&#160;</em></label>
-                       <input style="width:6em" type="text"<%=attr("name", cbid .. ".newnet")%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
-               </div>
-       </li>
-       <% elseif self.widget ~= "checkbox" and self.unspecified then %>
-       <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
-                       attr("type", self.widget or "radio") ..
-                       attr("id", cbid .. "_uns") ..
-                       attr("name", cbid) ..
-                       attr("value", "") ..
-                       ifattr(not value or #value == 0, "checked", "checked")
-               %> /> &#160;
-               <div style="padding:0.5em; display:inline">
-                       <label<%=attr("for", cbid .. "_uns")%>><em><%:unspecified%></em></label>
-               </div>
-       </li>
-       <% end %>
-</ul>
-
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/nsection.htm b/libs/web/luasrc/view/cbi/nsection.htm
deleted file mode 100644 (file)
index 95e7658..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<% if self:cfgvalue(self.section) then section = self.section %>
-       <fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=section%>">
-               <% if self.title and #self.title > 0 then -%>
-                       <legend><%=self.title%></legend>
-               <%- end %>
-               <% if self.description and #self.description > 0 then -%>
-                       <div class="cbi-section-descr"><%=self.description%></div>
-               <%- end %>
-               <% if self.addremove then -%>
-                       <div class="cbi-section-remove right">
-                               <input type="submit" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:Delete%>" />
-                       </div>
-               <%- end %>
-               <%+cbi/tabmenu%>
-               <div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
-                       <%+cbi/ucisection%>
-               </div>
-               <br />
-       </fieldset>
-<% elseif self.addremove then %>
-       <% if self.template_addremove then include(self.template_addremove) else -%>
-       <fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
-               <% if self.title and #self.title > 0 then -%>
-                       <legend><%=self.title%></legend>
-               <%- end %>
-               <div class="cbi-section-descr"><%=self.description%></div>
-               <input type="submit" class="cbi-button-add" name="cbi.cns.<%=self.config%>.<%=self.section%>" value="<%:Add%>" />
-       </fieldset>
-       <%- end %>
-<% end %>
-<!-- /nsection -->
diff --git a/libs/web/luasrc/view/cbi/nullsection.htm b/libs/web/luasrc/view/cbi/nullsection.htm
deleted file mode 100644 (file)
index bd48950..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<fieldset class="cbi-section">
-       <% if self.title and #self.title > 0 then -%>
-               <legend><%=self.title%></legend>
-       <%- end %>
-       <% if self.description and #self.description > 0 then -%>
-               <div class="cbi-section-descr"><%=self.description%></div>
-       <%- end %>
-       <div class="cbi-section-node" id="cbi-<%=self.config%>-<%=tostring(self):sub(8)%>">
-               <div>
-                       <% self:render_children(1, scope or {}) %>
-               </div>
-               <% if self.error and self.error[1] then -%>
-                       <div class="cbi-section-error">
-                               <ul><% for _, e in ipairs(self.error[1]) do -%>
-                                       <li>
-                                               <%- if e == "invalid" then -%>
-                                                       <%:One or more fields contain invalid values!%>
-                                               <%- elseif e == "missing" then -%>
-                                                       <%:One or more required fields have no value!%>
-                                               <%- else -%>
-                                                       <%=pcdata(e)%>
-                                               <%- end -%>
-                                       </li>
-                               <%- end %></ul>
-                       </div>
-               <%- end %>
-       </div>
-       <br />
-</fieldset>
-<%-
-       if type(self.hidden) == "table" then
-               for k, v in pairs(self.hidden) do
--%>
-       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
-<%-
-               end
-       end
-%>
diff --git a/libs/web/luasrc/view/cbi/simpleform.htm b/libs/web/luasrc/view/cbi/simpleform.htm
deleted file mode 100644 (file)
index 5216cd5..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<% if not self.embedded then %>
-<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
-       <div>
-               <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
-               <input type="hidden" name="cbi.submit" value="1" />
-       </div>
-<% end %>
-       <div class="cbi-map" id="cbi-<%=self.config%>">
-               <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
-               <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
-               <% self:render_children() %>
-               <br />
-       </div>
-<%- if self.message then %>
-       <div><%=self.message%></div>
-<%- end %>
-<%- if self.errmessage then %>
-       <div class="error"><%=self.errmessage%></div>
-<%- end %>
-<% if not self.embedded then %>
-       <div class="cbi-page-actions">
-<%-
-       if type(self.hidden) == "table" then
-               for k, v in pairs(self.hidden) do
--%>
-       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
-<%-
-               end
-       end
-%>
-<% if redirect then %>
-       <div style="float:left">
-               <input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
-       </div>
-<% end %>
-<%- if self.flow and self.flow.skip then %>
-       <input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
-<% end %>
-<%- if self.submit ~= false then %>
-       <input class="cbi-button cbi-button-save" type="submit" value="
-               <%- if not self.submit then -%><%-:Submit-%><%-else-%><%=self.submit%><%end-%>
-       " />
-<% end %>
-<%- if self.reset ~= false then %>
-       <input class="cbi-button cbi-button-reset" type="reset" value="
-               <%- if not self.reset then -%><%-:Reset-%><%-else-%><%=self.reset%><%end-%>
-       " />
-<% end %>
-<%- if self.cancel ~= false and self.on_cancel then %>
-       <input class="cbi-button cbi-button-reset" type="submit" name="cbi.cancel" value="
-               <%- if not self.cancel then -%><%-:Cancel-%><%-else-%><%=self.cancel%><%end-%>
-       " />
-<% end %>
-               <script type="text/javascript">cbi_d_update();</script>
-       </div>
-</form>
-<% end %>
diff --git a/libs/web/luasrc/view/cbi/tabcontainer.htm b/libs/web/luasrc/view/cbi/tabcontainer.htm
deleted file mode 100644 (file)
index 38c435d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<% for tab, data in pairs(self.tabs) do %>
-       <div class="cbi-tabcontainer" id="container.<%=self.config%>.<%=section%>.<%=tab%>"<% if tab ~= self.selected_tab then %> style="display:none"<% end %>>
-               <% if data.description then %><div class="cbi-tab-descr"><%=data.description%></div><% end %>
-               <% self:render_tab(tab, section, scope or {}) %>
-       </div>
-       <script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script>
-<% end %>
diff --git a/libs/web/luasrc/view/cbi/tabmenu.htm b/libs/web/luasrc/view/cbi/tabmenu.htm
deleted file mode 100644 (file)
index b96ac9c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<%- if self.tabs then %>
-       <ul class="cbi-tabmenu">
-       <%- self.selected_tab = luci.http.formvalue("tab." .. self.config .. "." .. section) %>
-       <%- for _, tab in ipairs(self.tab_names) do if #self.tabs[tab].childs > 0 then %>
-               <script type="text/javascript">cbi_c['container.<%=self.config%>.<%=section%>.<%=tab%>'] = <%=#self.tabs[tab].childs%>;</script>
-               <%- if not self.selected_tab then self.selected_tab = tab end %>
-               <li id="tab.<%=self.config%>.<%=section%>.<%=tab%>" class="cbi-tab<%=(tab == self.selected_tab) and '' or '-disabled'%>">
-                       <a onclick="this.blur(); return cbi_t_switch('<%=self.config%>.<%=section%>', '<%=tab%>')" href="<%=REQUEST_URI%>?tab.<%=self.config%>.<%=section%>=<%=tab%>"><%=self.tabs[tab].title%></a>
-                       <% if tab == self.selected_tab then %><input type="hidden" id="tab.<%=self.config%>.<%=section%>" name="tab.<%=self.config%>.<%=section%>" value="<%=tab%>" /><% end %>
-               </li>
-       <% end end -%>
-       </ul>
-<% end -%>
diff --git a/libs/web/luasrc/view/cbi/tblsection.htm b/libs/web/luasrc/view/cbi/tblsection.htm
deleted file mode 100644 (file)
index d928791..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-<%-
-local rowcnt = 1
-function rowstyle()
-       rowcnt = rowcnt + 1
-       return (rowcnt % 2) + 1
-end
-
-function width(o)
-       if o.width then
-               if type(o.width) == 'number' then
-                       return ' style="width:%dpx"' % o.width
-               end
-               return ' style="width:%s"' % o.width
-       end
-       return ''
-end
--%>
-
-<!-- tblsection -->
-<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
-       <% if self.title and #self.title > 0 then -%>
-               <legend><%=self.title%></legend>
-       <%- end %>
-       <%- if self.sortable then -%>
-               <input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
-       <%- end -%>
-       <div class="cbi-section-descr"><%=self.description%></div>
-       <div class="cbi-section-node">
-               <%- local count = 0 -%>
-               <table class="cbi-section-table">
-                       <tr class="cbi-section-table-titles">
-                       <%- if not self.anonymous then -%>
-                               <%- if self.sectionhead then -%>
-                                       <th class="cbi-section-table-cell"><%=self.sectionhead%></th>
-                               <%- else -%>
-                                       <th>&#160;</th>
-                               <%- end -%>
-                       <%- end -%>
-                       <%- for i, k in pairs(self.children) do if not k.optional then -%>
-                               <th class="cbi-section-table-cell"<%=width(k)%>>
-                               <%- if k.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%- end -%>
-                                       <%-=k.title-%>
-                               <%- if k.titleref then -%></a><%- end -%>
-                               </th>
-                       <%- count = count + 1; end; end; if self.sortable then -%>
-                               <th class="cbi-section-table-cell"><%:Sort%></th>
-                       <%- end; if self.extedit or self.addremove then -%>
-                               <th class="cbi-section-table-cell">&#160;</th>
-                       <%- count = count + 1; end -%>
-                       </tr>
-                       <tr class="cbi-section-table-descr">
-                       <%- if not self.anonymous then -%>
-                               <%- if self.sectiondesc then -%>
-                                       <th class="cbi-section-table-cell"><%=self.sectiondesc%></th>
-                               <%- else -%>
-                                       <th></th>
-                               <%- end -%>
-                       <%- end -%>
-                       <%- for i, k in pairs(self.children) do if not k.optional then -%>
-                               <th class="cbi-section-table-cell"<%=width(k)%>><%=k.description%></th>
-                       <%- end; end; if self.sortable then -%>
-                               <th class="cbi-section-table-cell"></th>
-                       <%- end; if self.extedit or self.addremove then -%>
-                               <th class="cbi-section-table-cell"></th>
-                       <%- end -%>
-                       </tr>
-                       <%- local isempty = true
-                           for i, k in ipairs(self:cfgsections()) do
-                                       section = k
-                                       isempty = false
-                                       scope = { valueheader = "cbi/cell_valueheader", valuefooter = "cbi/cell_valuefooter" }
-                       -%>
-                       <tr class="cbi-section-table-row<% if self.extedit or self.rowcolors then %> cbi-rowstyle-<%=rowstyle()%><% end %>" id="cbi-<%=self.config%>-<%=section%>">
-                               <% if not self.anonymous then -%>
-                                       <th><h3><%=(type(self.sectiontitle) == "function") and self:sectiontitle(section) or k%></h3></th>
-                               <%- end %>
-
-
-                               <%-
-                                       for k, node in ipairs(self.children) do
-                                               if not node.optional then
-                                                       node:render(section, scope or {})
-                                               end
-                                       end
-                               -%>
-
-                               <%- if self.sortable then -%>
-                                       <td class="cbi-section-table-cell">
-                                               <input class="cbi-button cbi-button-up" type="button" value=""  onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move up%>" title="<%:Move up%>" />
-                                               <input class="cbi-button cbi-button-down" type="button" value=""  onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move down%>" title="<%:Move down%>" />
-                                       </td>
-                               <%- end -%>
-
-                               <%- if self.extedit or self.addremove then -%>
-                                       <td class="cbi-section-table-cell">
-                                               <%- if self.extedit then -%>
-                                                       <input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
-                                                       <%- if type(self.extedit) == "string" then
-                                                       %> onclick="location.href='<%=self.extedit:format(section)%>'"
-                                                       <%- elseif type(self.extedit) == "function" then
-                                                       %> onclick="location.href='<%=self:extedit(section)%>'"
-                                                       <%- end 
-                                                       %> alt="<%:Edit%>" title="<%:Edit%>" />
-                                               <%- end; if self.addremove then %>
-                                                       <input class="cbi-button cbi-button-remove" type="submit" value="<%:Delete%>"  onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
-                                               <%- end -%>
-                                       </td>
-                               <%- end -%>
-                       </tr>
-                       <%- end -%>
-
-                       <%- if isempty then -%>
-                       <tr class="cbi-section-table-row">
-                               <td colspan="<%=count%>"><em><br /><%:This section contains no values yet%></em></td>
-                       </tr>
-                       <%- end -%>
-               </table>
-
-               <% if self.error then %>
-                       <div class="cbi-section-error">
-                               <ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
-                                       <li><%=pcdata(e):gsub("\n","<br />")%></li>
-                               <%- end end %></ul>
-                       </div>
-               <% end %>
-
-               <%- if self.addremove then -%>
-                       <% if self.template_addremove then include(self.template_addremove) else -%>
-                       <div class="cbi-section-create cbi-tblsection-create">
-                               <% if self.anonymous then %>
-                                       <input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
-                               <% else %>
-                                       <% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
-                                       <input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" />
-                                       <script type="text/javascript">cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');</script>
-                                       <input class="cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
-                                       <% if self.invalid_cts then -%>
-                                               <br /><%:Invalid%></div>
-                                       <%- end %>
-                               <% end %>
-                       </div>
-                       <%- end %>
-               <%- end -%>
-       </div>
-</fieldset>
-<!-- /tblsection -->
diff --git a/libs/web/luasrc/view/cbi/tsection.htm b/libs/web/luasrc/view/cbi/tsection.htm
deleted file mode 100644 (file)
index 087548b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
-       <% if self.title and #self.title > 0 then -%>
-               <legend><%=self.title%></legend>
-       <%- end %>
-       <div class="cbi-section-descr"><%=self.description%></div>
-       <% local isempty = true for i, k in ipairs(self:cfgsections()) do -%>
-               <% if self.addremove then -%>
-                       <div class="cbi-section-remove right">
-                               <input type="submit" name="cbi.rts.<%=self.config%>.<%=k%>" onclick="this.form.cbi_state='del-section'; return true" value="<%:Delete%>" class="cbi-button" />
-                       </div>
-               <%- end %>
-
-               <%- section = k; isempty = false -%>
-
-               <% if not self.anonymous then -%>
-                       <h3><%=section:upper()%></h3>
-               <%- end %>
-
-               <%+cbi/tabmenu%>
-
-               <fieldset class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
-                       <%+cbi/ucisection%>
-               </fieldset>
-               <br />
-       <%- end %>
-
-       <% if isempty then -%>
-               <em><%:This section contains no values yet%><br /><br /></em>
-       <%- end %>
-
-       <% if self.addremove then -%>
-               <% if self.template_addremove then include(self.template_addremove) else -%>
-               <div class="cbi-section-create">
-                       <% if self.anonymous then -%>
-                               <input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
-                       <%- else -%>
-                               <% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
-                               <input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" />
-                               <script type="text/javascript">cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');</script>
-                               <input type="submit" class="cbi-button cbi-button-add" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" />
-                               <% if self.invalid_cts then -%>
-                                       <br /><%:Invalid%></div>
-                               <%- end %>
-                       <%- end %>
-               </div>
-               <%- end %>
-       <%- end %>
-</fieldset>
diff --git a/libs/web/luasrc/view/cbi/tvalue.htm b/libs/web/luasrc/view/cbi/tvalue.htm
deleted file mode 100644 (file)
index fcf7a6c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<%+cbi/valueheader%>
-       <textarea class="cbi-input-textarea" <% if not self.size then %> style="width: 100%"<% else %> cols="<%=self.size%>"<% end %> onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. attr("id", cbid) .. ifattr(self.rows, "rows") .. ifattr(self.wrap, "wrap") %>>
-       <%-=pcdata(self:cfgvalue(section))-%>
-       </textarea>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/ucisection.htm b/libs/web/luasrc/view/cbi/ucisection.htm
deleted file mode 100644 (file)
index 3b69f12..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<%-
-       if type(self.hidden) == "table" then
-               for k, v in pairs(self.hidden) do
--%>
-       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
-<%-
-               end
-       end
-%>
-
-<% if self.tabs then %>
-       <%+cbi/tabcontainer%>
-<% else %>
-       <% self:render_children(section, scope or {}) %>
-<% end %>
-
-<% if self.error and self.error[section] then -%>
-       <div class="cbi-section-error">
-               <ul><% for _, e in ipairs(self.error[section]) do -%>
-                       <li>
-                               <%- if e == "invalid" then -%>
-                                       <%:One or more fields contain invalid values!%>
-                               <%- elseif e == "missing" then -%>
-                                       <%:One or more required fields have no value!%>
-                               <%- else -%>
-                                       <%=pcdata(e)%>
-                               <%- end -%>
-                       </li>
-               <%- end %></ul>
-       </div>
-<%- end %>
-
-<% if self.optionals[section] and #self.optionals[section] > 0 or self.dynamic then %>
-       <div class="cbi-optionals">
-               <% if self.dynamic then %>
-                       <input type="text" id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" />
-                       <% if self.optionals[section] and #self.optionals[section] > 0 then %>
-                       <script type="text/javascript">
-                               cbi_combobox_init('cbi.opt.<%=self.config%>.<%=section%>', {
-                               <%-
-                                       for i, val in pairs(self.optionals[section]) do
-                               -%>
-                                       <%-=string.format("%q", val.option) .. ":" .. string.format("%q", striptags(val.title))-%>
-                                       <%-if next(self.optionals[section], i) then-%>,<%-end-%>
-                               <%-
-                                       end
-                               -%>
-                               }, '', '<%-: -- custom -- -%>');
-                       </script>
-                       <% end %>
-               <% else %>
-               <select id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>">
-                       <option><%: -- Additional Field -- %></option>
-                       <% for key, val in pairs(self.optionals[section]) do -%>
-                               <option id="cbi-<%=self.config.."-"..section.."-"..val.option%>" value="<%=val.option%>"><%=striptags(val.title)%></option>
-                       <%- end %>
-               </select>
-               <script type="text/javascript"><% for key, val in pairs(self.optionals[section]) do %>
-                       <% if #val.deps > 0 then %><% for j, d in ipairs(val.deps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..val.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-               -%>
-                       <%-=string.format('"cbid.%s.%s.%s"', self.config, section, k) .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       });
-               <%- end %><% end %>
-               <% end %></script>
-       <% end %>
-               <input type="submit" class="cbi-button cbi-button-fieldadd" value="<%:Add%>" />
-       </div>
-<% end %>
diff --git a/libs/web/luasrc/view/cbi/upload.htm b/libs/web/luasrc/view/cbi/upload.htm
deleted file mode 100644 (file)
index 7770934..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<%
-       local t = require("luci.tools.webadmin")
-       local v = self:cfgvalue(section)
-       local s = v and nixio.fs.stat(v)
--%>
-<%+cbi/valueheader%>
-       <% if s then %>
-               <%:Uploaded File%> (<%=t.byte_format(s.size)%>)
-               <input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
-               <input class="cbi-button cbi-input-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
-       <% else %>
-               <input class="cbi-input-file" type="file"<%= attr("name", cbid) .. attr("id", cbid) %> />
-       <% end %>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/value.htm b/libs/web/luasrc/view/cbi/value.htm
deleted file mode 100644 (file)
index d1a7bea..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<%+cbi/valueheader%>
-       <input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
-               attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
-               ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder")
-       %> />
-       <% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
-       <% if #self.keylist > 0 or self.datatype then -%>
-       <script type="text/javascript">//<![CDATA[
-               <% if #self.keylist > 0 then -%>
-               cbi_combobox_init('<%=cbid%>', {
-               <%-
-                       for i, k in ipairs(self.keylist) do
-               -%>
-                       <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
-                       <%-if i<#self.keylist then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-               }, '<%- if not self.rmempty and not self.optional then -%>
-                       <%-: -- Please choose -- -%>
-                       <%- elseif self.placeholder then -%>
-                       <%-= pcdata(self.placeholder) -%>
-               <%- end -%>', '
-               <%- if self.combobox_manual then -%>
-                       <%-=self.combobox_manual-%>
-               <%- else -%>
-                       <%-: -- custom -- -%>
-               <%- end -%>');
-               <%- end %>
-               <% if self.datatype then -%>
-               cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("'", "\\'")%>');
-               <%- end %>
-       //]]></script>
-       <% end -%>
-<%+cbi/valuefooter%>
diff --git a/libs/web/luasrc/view/cbi/valuefooter.htm b/libs/web/luasrc/view/cbi/valuefooter.htm
deleted file mode 100644 (file)
index 805312e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<% include( valuefooter or "cbi/full_valuefooter" ) %>
diff --git a/libs/web/luasrc/view/cbi/valueheader.htm b/libs/web/luasrc/view/cbi/valueheader.htm
deleted file mode 100644 (file)
index 761a54a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<% include( valueheader or "cbi/full_valueheader" ) %>
diff --git a/libs/web/root/etc/config/luci b/libs/web/root/etc/config/luci
deleted file mode 100644 (file)
index c503a8f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-config core main
-       option lang auto
-       option mediaurlbase /luci-static/openwrt.org
-       option resourcebase /luci-static/resources
-       
-config extern flash_keep
-       option uci              "/etc/config/"
-       option dropbear "/etc/dropbear/"
-       option openvpn  "/etc/openvpn/"
-       option passwd   "/etc/passwd"
-       option opkg             "/etc/opkg.conf"
-       option firewall "/etc/firewall.user"
-       option uploads  "/lib/uci/upload/"
-       
-config internal languages
-       
-config internal sauth
-       option sessionpath "/tmp/luci-sessions"
-       option sessiontime 3600
-       
-config internal ccache
-       option enable 1
-               
-config internal themes
diff --git a/libs/web/root/lib/uci/upload/.gitignore b/libs/web/root/lib/uci/upload/.gitignore
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/libs/web/src/po2lmo.c b/libs/web/src/po2lmo.c
deleted file mode 100644 (file)
index 0da792b..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * lmo - Lua Machine Objects - PO to LMO conversion tool
- *
- *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_lmo.h"
-
-static void die(const char *msg)
-{
-       fprintf(stderr, "Error: %s\n", msg);
-       exit(1);
-}
-
-static void usage(const char *name)
-{
-       fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
-       exit(1);
-}
-
-static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
-{
-       if( fwrite(ptr, size, nmemb, stream) == 0 )
-               die("Failed to write stdout");
-}
-
-static int extract_string(const char *src, char *dest, int len)
-{
-       int pos = 0;
-       int esc = 0;
-       int off = -1;
-
-       for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
-       {
-               if( (off == -1) && (src[pos] == '"') )
-               {
-                       off = pos + 1;
-               }
-               else if( off >= 0 )
-               {
-                       if( esc == 1 )
-                       {
-                               switch (src[pos])
-                               {
-                               case '"':
-                               case '\\':
-                                       off++;
-                                       break;
-                               }
-                               dest[pos-off] = src[pos];
-                               esc = 0;
-                       }
-                       else if( src[pos] == '\\' )
-                       {
-                               dest[pos-off] = src[pos];
-                               esc = 1;
-                       }
-                       else if( src[pos] != '"' )
-                       {
-                               dest[pos-off] = src[pos];
-                       }
-                       else
-                       {
-                               dest[pos-off] = '\0';
-                               break;
-                       }
-               }
-       }
-
-       return (off > -1) ? strlen(dest) : -1;
-}
-
-static int cmp_index(const void *a, const void *b)
-{
-       uint32_t x = ((const lmo_entry_t *)a)->key_id;
-       uint32_t y = ((const lmo_entry_t *)b)->key_id;
-
-       if (x < y)
-               return -1;
-       else if (x > y)
-               return 1;
-
-       return 0;
-}
-
-static void print_uint32(uint32_t x, FILE *out)
-{
-       uint32_t y = htonl(x);
-       print(&y, sizeof(uint32_t), 1, out);
-}
-
-static void print_index(void *array, int n, FILE *out)
-{
-       lmo_entry_t *e;
-
-       qsort(array, n, sizeof(*e), cmp_index);
-
-       for (e = array; n > 0; n--, e++)
-       {
-               print_uint32(e->key_id, out);
-               print_uint32(e->val_id, out);
-               print_uint32(e->offset, out);
-               print_uint32(e->length, out);
-       }
-}
-
-int main(int argc, char *argv[])
-{
-       char line[4096];
-       char key[4096];
-       char val[4096];
-       char tmp[4096];
-       int state  = 0;
-       int offset = 0;
-       int length = 0;
-       int n_entries = 0;
-       void *array = NULL;
-       lmo_entry_t *entry = NULL;
-       uint32_t key_id, val_id;
-
-       FILE *in;
-       FILE *out;
-
-       if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
-               usage(argv[0]);
-
-       memset(line, 0, sizeof(key));
-       memset(key, 0, sizeof(val));
-       memset(val, 0, sizeof(val));
-
-       while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
-       {
-               if( state == 0 && strstr(line, "msgid \"") == line )
-               {
-                       switch(extract_string(line, key, sizeof(key)))
-                       {
-                               case -1:
-                                       die("Syntax error in msgid");
-                               case 0:
-                                       state = 1;
-                                       break;
-                               default:
-                                       state = 2;
-                       }
-               }
-               else if( state == 1 || state == 2 )
-               {
-                       if( strstr(line, "msgstr \"") == line || state == 2 )
-                       {
-                               switch(extract_string(line, val, sizeof(val)))
-                               {
-                                       case -1:
-                                               state = 4;
-                                               break;
-                                       default:
-                                               state = 3;
-                               }
-                       }
-                       else
-                       {
-                               switch(extract_string(line, tmp, sizeof(tmp)))
-                               {
-                                       case -1:
-                                               state = 2;
-                                               break;
-                                       default:
-                                               strcat(key, tmp);
-                               }
-                       }
-               }
-               else if( state == 3 )
-               {
-                       switch(extract_string(line, tmp, sizeof(tmp)))
-                       {
-                               case -1:
-                                       state = 4;
-                                       break;
-                               default:
-                                       strcat(val, tmp);
-                       }
-               }
-
-               if( state == 4 )
-               {
-                       if( strlen(key) > 0 && strlen(val) > 0 )
-                       {
-                               key_id = sfh_hash(key, strlen(key));
-                               val_id = sfh_hash(val, strlen(val));
-
-                               if( key_id != val_id )
-                               {
-                                       n_entries++;
-                                       array = realloc(array, n_entries * sizeof(lmo_entry_t));
-                                       entry = (lmo_entry_t *)array + n_entries - 1;
-
-                                       if (!array)
-                                               die("Out of memory");
-
-                                       entry->key_id = key_id;
-                                       entry->val_id = val_id;
-                                       entry->offset = offset;
-                                       entry->length = strlen(val);
-
-                                       length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
-
-                                       print(val, length, 1, out);
-                                       offset += length;
-                               }
-                       }
-
-                       state = 0;
-                       memset(key, 0, sizeof(key));
-                       memset(val, 0, sizeof(val));
-               }
-
-               memset(line, 0, sizeof(line));
-       }
-
-       print_index(array, n_entries, out);
-
-       if( offset > 0 )
-       {
-               print_uint32(offset, out);
-               fsync(fileno(out));
-               fclose(out);
-       }
-       else
-       {
-               fclose(out);
-               unlink(argv[2]);
-       }
-
-       fclose(in);
-       return(0);
-}
diff --git a/libs/web/src/template_lmo.c b/libs/web/src/template_lmo.c
deleted file mode 100644 (file)
index 27205a7..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Base functions
- *
- *   Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_lmo.h"
-
-/*
- * Hash function from http://www.azillionmonkeys.com/qed/hash.html
- * Copyright (C) 2004-2008 by Paul Hsieh
- */
-
-uint32_t sfh_hash(const char *data, int len)
-{
-       uint32_t hash = len, tmp;
-       int rem;
-
-       if (len <= 0 || data == NULL) return 0;
-
-       rem = len & 3;
-       len >>= 2;
-
-       /* Main loop */
-       for (;len > 0; len--) {
-               hash  += sfh_get16(data);
-               tmp    = (sfh_get16(data+2) << 11) ^ hash;
-               hash   = (hash << 16) ^ tmp;
-               data  += 2*sizeof(uint16_t);
-               hash  += hash >> 11;
-       }
-
-       /* Handle end cases */
-       switch (rem) {
-               case 3: hash += sfh_get16(data);
-                       hash ^= hash << 16;
-                       hash ^= data[sizeof(uint16_t)] << 18;
-                       hash += hash >> 11;
-                       break;
-               case 2: hash += sfh_get16(data);
-                       hash ^= hash << 11;
-                       hash += hash >> 17;
-                       break;
-               case 1: hash += *data;
-                       hash ^= hash << 10;
-                       hash += hash >> 1;
-       }
-
-       /* Force "avalanching" of final 127 bits */
-       hash ^= hash << 3;
-       hash += hash >> 5;
-       hash ^= hash << 4;
-       hash += hash >> 17;
-       hash ^= hash << 25;
-       hash += hash >> 6;
-
-       return hash;
-}
-
-uint32_t lmo_canon_hash(const char *str, int len)
-{
-       char res[4096];
-       char *ptr, prev;
-       int off;
-
-       if (!str || len >= sizeof(res))
-               return 0;
-
-       for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
-       {
-               if (isspace(*str))
-               {
-                       if (!isspace(prev))
-                               *ptr++ = ' ';
-               }
-               else
-               {
-                       *ptr++ = *str;
-               }
-       }
-
-       if ((ptr > res) && isspace(*(ptr-1)))
-               ptr--;
-
-       return sfh_hash(res, ptr - res);
-}
-
-lmo_archive_t * lmo_open(const char *file)
-{
-       int in = -1;
-       uint32_t idx_offset = 0;
-       struct stat s;
-
-       lmo_archive_t *ar = NULL;
-
-       if (stat(file, &s) == -1)
-               goto err;
-
-       if ((in = open(file, O_RDONLY)) == -1)
-               goto err;
-
-       if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
-       {
-               memset(ar, 0, sizeof(*ar));
-
-               ar->fd     = in;
-               ar->size = s.st_size;
-
-               fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
-
-               if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
-                       goto err;
-
-               idx_offset = ntohl(*((const uint32_t *)
-                                    (ar->mmap + ar->size - sizeof(uint32_t))));
-
-               if (idx_offset >= ar->size)
-                       goto err;
-
-               ar->index  = (lmo_entry_t *)(ar->mmap + idx_offset);
-               ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
-               ar->end    = ar->mmap + ar->size;
-
-               return ar;
-       }
-
-err:
-       if (in > -1)
-               close(in);
-
-       if (ar != NULL)
-       {
-               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
-                       munmap(ar->mmap, ar->size);
-
-               free(ar);
-       }
-
-       return NULL;
-}
-
-void lmo_close(lmo_archive_t *ar)
-{
-       if (ar != NULL)
-       {
-               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
-                       munmap(ar->mmap, ar->size);
-
-               close(ar->fd);
-               free(ar);
-
-               ar = NULL;
-       }
-}
-
-
-lmo_catalog_t *_lmo_catalogs = NULL;
-lmo_catalog_t *_lmo_active_catalog = NULL;
-
-int lmo_load_catalog(const char *lang, const char *dir)
-{
-       DIR *dh = NULL;
-       char pattern[16];
-       char path[PATH_MAX];
-       struct dirent *de = NULL;
-
-       lmo_archive_t *ar = NULL;
-       lmo_catalog_t *cat = NULL;
-
-       if (!lmo_change_catalog(lang))
-               return 0;
-
-       if (!dir || !(dh = opendir(dir)))
-               goto err;
-
-       if (!(cat = malloc(sizeof(*cat))))
-               goto err;
-
-       memset(cat, 0, sizeof(*cat));
-
-       snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
-       snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
-
-       while ((de = readdir(dh)) != NULL)
-       {
-               if (!fnmatch(pattern, de->d_name, 0))
-               {
-                       snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
-                       ar = lmo_open(path);
-
-                       if (ar)
-                       {
-                               ar->next = cat->archives;
-                               cat->archives = ar;
-                       }
-               }
-       }
-
-       closedir(dh);
-
-       cat->next = _lmo_catalogs;
-       _lmo_catalogs = cat;
-
-       if (!_lmo_active_catalog)
-               _lmo_active_catalog = cat;
-
-       return 0;
-
-err:
-       if (dh) closedir(dh);
-       if (cat) free(cat);
-
-       return -1;
-}
-
-int lmo_change_catalog(const char *lang)
-{
-       lmo_catalog_t *cat;
-
-       for (cat = _lmo_catalogs; cat; cat = cat->next)
-       {
-               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
-               {
-                       _lmo_active_catalog = cat;
-                       return 0;
-               }
-       }
-
-       return -1;
-}
-
-static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
-{
-       unsigned int m, l, r;
-       uint32_t k;
-
-       l = 0;
-       r = ar->length - 1;
-
-       while (1)
-       {
-               m = l + ((r - l) / 2);
-
-               if (r < l)
-                       break;
-
-               k = ntohl(ar->index[m].key_id);
-
-               if (k == hash)
-                       return &ar->index[m];
-
-               if (k > hash)
-               {
-                       if (!m)
-                               break;
-
-                       r = m - 1;
-               }
-               else
-               {
-                       l = m + 1;
-               }
-       }
-
-       return NULL;
-}
-
-int lmo_translate(const char *key, int keylen, char **out, int *outlen)
-{
-       uint32_t hash;
-       lmo_entry_t *e;
-       lmo_archive_t *ar;
-
-       if (!key || !_lmo_active_catalog)
-               return -2;
-
-       hash = lmo_canon_hash(key, keylen);
-
-       for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
-       {
-               if ((e = lmo_find_entry(ar, hash)) != NULL)
-               {
-                       *out = ar->mmap + ntohl(e->offset);
-                       *outlen = ntohl(e->length);
-                       return 0;
-               }
-       }
-
-       return -1;
-}
-
-void lmo_close_catalog(const char *lang)
-{
-       lmo_archive_t *ar, *next;
-       lmo_catalog_t *cat, *prev;
-
-       for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
-       {
-               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
-               {
-                       if (prev)
-                               prev->next = cat->next;
-                       else
-                               _lmo_catalogs = cat->next;
-
-                       for (ar = cat->archives; ar; ar = next)
-                       {
-                               next = ar->next;
-                               lmo_close(ar);
-                       }
-
-                       free(cat);
-                       break;
-               }
-       }
-}
diff --git a/libs/web/src/template_lmo.h b/libs/web/src/template_lmo.h
deleted file mode 100644 (file)
index 57f59aa..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * lmo - Lua Machine Objects - General header
- *
- *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef _TEMPLATE_LMO_H_
-#define _TEMPLATE_LMO_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <limits.h>
-
-#if (defined(__GNUC__) && defined(__i386__))
-#define sfh_get16(d) (*((const uint16_t *) (d)))
-#else
-#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
-                                          +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
-
-
-struct lmo_entry {
-       uint32_t key_id;
-       uint32_t val_id;
-       uint32_t offset;
-       uint32_t length;
-} __attribute__((packed));
-
-typedef struct lmo_entry lmo_entry_t;
-
-
-struct lmo_archive {
-       int         fd;
-       int             length;
-       uint32_t    size;
-       lmo_entry_t *index;
-       char        *mmap;
-       char            *end;
-       struct lmo_archive *next;
-};
-
-typedef struct lmo_archive lmo_archive_t;
-
-
-struct lmo_catalog {
-       char lang[6];
-       struct lmo_archive *archives;
-       struct lmo_catalog *next;
-};
-
-typedef struct lmo_catalog lmo_catalog_t;
-
-
-uint32_t sfh_hash(const char *data, int len);
-uint32_t lmo_canon_hash(const char *data, int len);
-
-lmo_archive_t * lmo_open(const char *file);
-void lmo_close(lmo_archive_t *ar);
-
-
-extern lmo_catalog_t *_lmo_catalogs;
-extern lmo_catalog_t *_lmo_active_catalog;
-
-int lmo_load_catalog(const char *lang, const char *dir);
-int lmo_change_catalog(const char *lang);
-int lmo_translate(const char *key, int keylen, char **out, int *outlen);
-void lmo_close_catalog(const char *lang);
-
-#endif
diff --git a/libs/web/src/template_lualib.c b/libs/web/src/template_lualib.c
deleted file mode 100644 (file)
index 0d43641..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * LuCI Template - Lua binding
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_lualib.h"
-
-int template_L_parse(lua_State *L)
-{
-       const char *file = luaL_checkstring(L, 1);
-       struct template_parser *parser = template_open(file);
-       int lua_status, rv;
-
-       if (!parser)
-       {
-               lua_pushnil(L);
-               lua_pushinteger(L, errno);
-               lua_pushstring(L, strerror(errno));
-               return 3;
-       }
-
-       lua_status = lua_load(L, template_reader, parser, file);
-
-       if (lua_status == 0)
-               rv = 1;
-       else
-               rv = template_error(L, parser);
-
-       template_close(parser);
-
-       return rv;
-}
-
-int template_L_utf8(lua_State *L)
-{
-       size_t len = 0;
-       const char *str = luaL_checklstring(L, 1, &len);
-       char *res = utf8(str, len);
-
-       if (res != NULL)
-       {
-               lua_pushstring(L, res);
-               free(res);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-int template_L_pcdata(lua_State *L)
-{
-       size_t len = 0;
-       const char *str = luaL_checklstring(L, 1, &len);
-       char *res = pcdata(str, len);
-
-       if (res != NULL)
-       {
-               lua_pushstring(L, res);
-               free(res);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-int template_L_striptags(lua_State *L)
-{
-       size_t len = 0;
-       const char *str = luaL_checklstring(L, 1, &len);
-       char *res = striptags(str, len);
-
-       if (res != NULL)
-       {
-               lua_pushstring(L, res);
-               free(res);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-static int template_L_load_catalog(lua_State *L) {
-       const char *lang = luaL_optstring(L, 1, "en");
-       const char *dir  = luaL_optstring(L, 2, NULL);
-       lua_pushboolean(L, !lmo_load_catalog(lang, dir));
-       return 1;
-}
-
-static int template_L_close_catalog(lua_State *L) {
-       const char *lang = luaL_optstring(L, 1, "en");
-       lmo_close_catalog(lang);
-       return 0;
-}
-
-static int template_L_change_catalog(lua_State *L) {
-       const char *lang = luaL_optstring(L, 1, "en");
-       lua_pushboolean(L, !lmo_change_catalog(lang));
-       return 1;
-}
-
-static int template_L_translate(lua_State *L) {
-       size_t len;
-       char *tr;
-       int trlen;
-       const char *key = luaL_checklstring(L, 1, &len);
-
-       switch (lmo_translate(key, len, &tr, &trlen))
-       {
-               case 0:
-                       lua_pushlstring(L, tr, trlen);
-                       return 1;
-
-               case -1:
-                       return 0;
-       }
-
-       lua_pushnil(L);
-       lua_pushstring(L, "no catalog loaded");
-       return 2;
-}
-
-static int template_L_hash(lua_State *L) {
-       size_t len;
-       const char *key = luaL_checklstring(L, 1, &len);
-       lua_pushinteger(L, sfh_hash(key, len));
-       return 1;
-}
-
-
-/* module table */
-static const luaL_reg R[] = {
-       { "parse",                              template_L_parse },
-       { "utf8",                               template_L_utf8 },
-       { "pcdata",                             template_L_pcdata },
-       { "striptags",                  template_L_striptags },
-       { "load_catalog",               template_L_load_catalog },
-       { "close_catalog",              template_L_close_catalog },
-       { "change_catalog",             template_L_change_catalog },
-       { "translate",                  template_L_translate },
-       { "hash",                               template_L_hash },
-       { NULL,                                 NULL }
-};
-
-LUALIB_API int luaopen_luci_template_parser(lua_State *L) {
-       luaL_register(L, TEMPLATE_LUALIB_META, R);
-       return 1;
-}
diff --git a/libs/web/src/template_lualib.h b/libs/web/src/template_lualib.h
deleted file mode 100644 (file)
index 1b659be..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * LuCI Template - Lua library header
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef _TEMPLATE_LUALIB_H_
-#define _TEMPLATE_LUALIB_H_
-
-#include "template_parser.h"
-#include "template_utils.h"
-#include "template_lmo.h"
-
-#define TEMPLATE_LUALIB_META  "template.parser"
-
-LUALIB_API int luaopen_luci_template_parser(lua_State *L);
-
-#endif
diff --git a/libs/web/src/template_parser.c b/libs/web/src/template_parser.c
deleted file mode 100644 (file)
index 6054451..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * LuCI Template - Parser implementation
- *
- *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_parser.h"
-#include "template_utils.h"
-#include "template_lmo.h"
-
-
-/* leading and trailing code for different types */
-const char *gen_code[9][2] = {
-       { NULL,                                 NULL                    },
-       { "write(\"",                   "\")"                   },
-       { NULL,                                 NULL                    },
-       { "write(tostring(",    " or \"\"))"    },
-       { "include(\"",                 "\")"                   },
-       { "write(\"",                   "\")"                   },
-       { "write(\"",                   "\")"                   },
-       { NULL,                                 " "                             },
-       { NULL,                                 NULL                    },
-};
-
-/* Simple strstr() like function that takes len arguments for both haystack and needle. */
-static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
-{
-       int match = 0;
-       int i, j;
-
-       for( i = 0; i < hslen; i++ )
-       {
-               if( haystack[i] == needle[0] )
-               {
-                       match = ((ndlen == 1) || ((i + ndlen) <= hslen));
-
-                       for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ )
-                       {
-                               if( haystack[i+j] != needle[j] )
-                               {
-                                       match = 0;
-                                       break;
-                               }
-                       }
-
-                       if( match )
-                               return &haystack[i];
-               }
-       }
-
-       return NULL;
-}
-
-struct template_parser * template_open(const char *file)
-{
-       struct stat s;
-       static struct template_parser *parser;
-
-       if (!(parser = malloc(sizeof(*parser))))
-               goto err;
-
-       memset(parser, 0, sizeof(*parser));
-       parser->fd = -1;
-       parser->file = file;
-
-       if (stat(file, &s))
-               goto err;
-
-       if ((parser->fd = open(file, O_RDONLY)) < 0)
-               goto err;
-
-       parser->size = s.st_size;
-       parser->mmap = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
-                                               parser->fd, 0);
-
-       if (parser->mmap != MAP_FAILED)
-       {
-               parser->off = parser->mmap;
-               parser->cur_chunk.type = T_TYPE_INIT;
-               parser->cur_chunk.s    = parser->mmap;
-               parser->cur_chunk.e    = parser->mmap;
-
-               return parser;
-       }
-
-err:
-       template_close(parser);
-       return NULL;
-}
-
-void template_close(struct template_parser *parser)
-{
-       if (!parser)
-               return;
-
-       if (parser->gc != NULL)
-               free(parser->gc);
-
-       if ((parser->mmap != NULL) && (parser->mmap != MAP_FAILED))
-               munmap(parser->mmap, parser->size);
-
-       if (parser->fd >= 0)
-               close(parser->fd);
-
-       free(parser);
-}
-
-void template_text(struct template_parser *parser, const char *e)
-{
-       const char *s = parser->off;
-
-       if (s < (parser->mmap + parser->size))
-       {
-               if (parser->strip_after)
-               {
-                       while ((s <= e) && isspace(*s))
-                               s++;
-               }
-
-               parser->cur_chunk.type = T_TYPE_TEXT;
-       }
-       else
-       {
-               parser->cur_chunk.type = T_TYPE_EOF;
-       }
-
-       parser->cur_chunk.line = parser->line;
-       parser->cur_chunk.s = s;
-       parser->cur_chunk.e = e;
-}
-
-void template_code(struct template_parser *parser, const char *e)
-{
-       const char *s = parser->off;
-
-       parser->strip_before = 0;
-       parser->strip_after = 0;
-
-       if (*s == '-')
-       {
-               parser->strip_before = 1;
-               for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
-       }
-
-       if (*(e-1) == '-')
-       {
-               parser->strip_after = 1;
-               for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
-       }
-
-       switch (*s)
-       {
-               /* comment */
-               case '#':
-                       s++;
-                       parser->cur_chunk.type = T_TYPE_COMMENT;
-                       break;
-
-               /* include */
-               case '+':
-                       s++;
-                       parser->cur_chunk.type = T_TYPE_INCLUDE;
-                       break;
-
-               /* translate */
-               case ':':
-                       s++;
-                       parser->cur_chunk.type = T_TYPE_I18N;
-                       break;
-
-               /* translate raw */
-               case '_':
-                       s++;
-                       parser->cur_chunk.type = T_TYPE_I18N_RAW;
-                       break;
-
-               /* expr */
-               case '=':
-                       s++;
-                       parser->cur_chunk.type = T_TYPE_EXPR;
-                       break;
-
-               /* code */
-               default:
-                       parser->cur_chunk.type = T_TYPE_CODE;
-                       break;
-       }
-
-       parser->cur_chunk.line = parser->line;
-       parser->cur_chunk.s = s;
-       parser->cur_chunk.e = e;
-}
-
-static const char *
-template_format_chunk(struct template_parser *parser, size_t *sz)
-{
-       const char *s, *p;
-       const char *head, *tail;
-       struct template_chunk *c = &parser->prv_chunk;
-       struct template_buffer *buf;
-
-       *sz = 0;
-       s = parser->gc = NULL;
-
-       if (parser->strip_before && c->type == T_TYPE_TEXT)
-       {
-               while ((c->e > c->s) && isspace(*(c->e - 1)))
-                       c->e--;
-       }
-
-       /* empty chunk */
-       if (c->s == c->e)
-       {
-               if (c->type == T_TYPE_EOF)
-               {
-                       *sz = 0;
-                       s = NULL;
-               }
-               else
-               {
-                       *sz = 1;
-                       s = " ";
-               }
-       }
-
-       /* format chunk */
-       else if ((buf = buf_init(c->e - c->s)) != NULL)
-       {
-               if ((head = gen_code[c->type][0]) != NULL)
-                       buf_append(buf, head, strlen(head));
-
-               switch (c->type)
-               {
-                       case T_TYPE_TEXT:
-                               luastr_escape(buf, c->s, c->e - c->s, 0);
-                               break;
-
-                       case T_TYPE_EXPR:
-                               buf_append(buf, c->s, c->e - c->s);
-                               for (p = c->s; p < c->e; p++)
-                                       parser->line += (*p == '\n');
-                               break;
-
-                       case T_TYPE_INCLUDE:
-                               luastr_escape(buf, c->s, c->e - c->s, 0);
-                               break;
-
-                       case T_TYPE_I18N:
-                               luastr_translate(buf, c->s, c->e - c->s, 1);
-                               break;
-
-                       case T_TYPE_I18N_RAW:
-                               luastr_translate(buf, c->s, c->e - c->s, 0);
-                               break;
-
-                       case T_TYPE_CODE:
-                               buf_append(buf, c->s, c->e - c->s);
-                               for (p = c->s; p < c->e; p++)
-                                       parser->line += (*p == '\n');
-                               break;
-               }
-
-               if ((tail = gen_code[c->type][1]) != NULL)
-                       buf_append(buf, tail, strlen(tail));
-
-               *sz = buf_length(buf);
-               s = parser->gc = buf_destroy(buf);
-
-               if (!*sz)
-               {
-                       *sz = 1;
-                       s = " ";
-               }
-       }
-
-       return s;
-}
-
-const char *template_reader(lua_State *L, void *ud, size_t *sz)
-{
-       struct template_parser *parser = ud;
-       int rem = parser->size - (parser->off - parser->mmap);
-       char *tag;
-
-       parser->prv_chunk = parser->cur_chunk;
-
-       /* free previous string */
-       if (parser->gc)
-       {
-               free(parser->gc);
-               parser->gc = NULL;
-       }
-
-       /* before tag */
-       if (!parser->in_expr)
-       {
-               if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL)
-               {
-                       template_text(parser, tag);
-                       parser->off = tag + 2;
-                       parser->in_expr = 1;
-               }
-               else
-               {
-                       template_text(parser, parser->mmap + parser->size);
-                       parser->off = parser->mmap + parser->size;
-               }
-       }
-
-       /* inside tag */
-       else
-       {
-               if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL)
-               {
-                       template_code(parser, tag);
-                       parser->off = tag + 2;
-                       parser->in_expr = 0;
-               }
-               else
-               {
-                       /* unexpected EOF */
-                       template_code(parser, parser->mmap + parser->size);
-
-                       *sz = 1;
-                       return "\033";
-               }
-       }
-
-       return template_format_chunk(parser, sz);
-}
-
-int template_error(lua_State *L, struct template_parser *parser)
-{
-       const char *err = luaL_checkstring(L, -1);
-       const char *off = parser->prv_chunk.s;
-       const char *ptr;
-       char msg[1024];
-       int line = 0;
-       int chunkline = 0;
-
-       if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL)
-       {
-               chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
-
-               while (*ptr)
-               {
-                       if (*ptr++ == ' ')
-                       {
-                               err = ptr;
-                               break;
-                       }
-               }
-       }
-
-       if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL)
-       {
-               off = parser->mmap + parser->size;
-               err = "'%>' expected before end of file";
-               chunkline = 0;
-       }
-
-       for (ptr = parser->mmap; ptr < off; ptr++)
-               if (*ptr == '\n')
-                       line++;
-
-       snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
-                        parser->file, line + chunkline, err ? err : "(unknown error)");
-
-       lua_pushnil(L);
-       lua_pushinteger(L, line + chunkline);
-       lua_pushstring(L, msg);
-
-       return 3;
-}
diff --git a/libs/web/src/template_parser.h b/libs/web/src/template_parser.h
deleted file mode 100644 (file)
index d1c6062..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * LuCI Template - Parser header
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef _TEMPLATE_PARSER_H_
-#define _TEMPLATE_PARSER_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-
-/* code types */
-#define T_TYPE_INIT                    0
-#define T_TYPE_TEXT                    1
-#define T_TYPE_COMMENT         2
-#define T_TYPE_EXPR                    3
-#define T_TYPE_INCLUDE                 4
-#define T_TYPE_I18N                    5
-#define T_TYPE_I18N_RAW                6
-#define T_TYPE_CODE                    7
-#define T_TYPE_EOF                     8
-
-
-struct template_chunk {
-       const char *s;
-       const char *e;
-       int type;
-       int line;
-};
-
-/* parser state */
-struct template_parser {
-       int fd;
-       uint32_t size;
-       char *mmap;
-       char *off;
-       char *gc;
-       int line;
-       int in_expr;
-       int strip_before;
-       int strip_after;
-       struct template_chunk prv_chunk;
-       struct template_chunk cur_chunk;
-       const char *file;
-};
-
-struct template_parser * template_open(const char *file);
-void template_close(struct template_parser *parser);
-
-const char *template_reader(lua_State *L, void *ud, size_t *sz);
-int template_error(lua_State *L, struct template_parser *parser);
-
-#endif
diff --git a/libs/web/src/template_utils.c b/libs/web/src/template_utils.c
deleted file mode 100644 (file)
index 80542bd..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * LuCI Template - Utility functions
- *
- *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#include "template_utils.h"
-#include "template_lmo.h"
-
-/* initialize a buffer object */
-struct template_buffer * buf_init(int size)
-{
-       struct template_buffer *buf;
-
-       if (size <= 0)
-               size = 1024;
-
-       buf = (struct template_buffer *)malloc(sizeof(struct template_buffer));
-
-       if (buf != NULL)
-       {
-               buf->fill = 0;
-               buf->size = size;
-               buf->data = malloc(buf->size);
-
-               if (buf->data != NULL)
-               {
-                       buf->dptr = buf->data;
-                       buf->data[0] = 0;
-
-                       return buf;
-               }
-
-               free(buf);
-       }
-
-       return NULL;
-}
-
-/* grow buffer */
-int buf_grow(struct template_buffer *buf, int size)
-{
-       unsigned int off = (buf->dptr - buf->data);
-       char *data;
-
-       if (size <= 0)
-               size = 1024;
-
-       data = realloc(buf->data, buf->size + size);
-
-       if (data != NULL)
-       {
-               buf->data  = data;
-               buf->dptr  = data + off;
-               buf->size += size;
-
-               return buf->size;
-       }
-
-       return 0;
-}
-
-/* put one char into buffer object */
-int buf_putchar(struct template_buffer *buf, char c)
-{
-       if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) )
-               return 0;
-
-       *(buf->dptr++) = c;
-       *(buf->dptr) = 0;
-
-       buf->fill++;
-       return 1;
-}
-
-/* append data to buffer */
-int buf_append(struct template_buffer *buf, const char *s, int len)
-{
-       if ((buf->fill + len + 1) >= buf->size)
-       {
-               if (!buf_grow(buf, len + 1))
-                       return 0;
-       }
-
-       memcpy(buf->dptr, s, len);
-       buf->fill += len;
-       buf->dptr += len;
-
-       *(buf->dptr) = 0;
-
-       return len;
-}
-
-/* read buffer length */
-int buf_length(struct template_buffer *buf)
-{
-       return buf->fill;
-}
-
-/* destroy buffer object and return pointer to data */
-char * buf_destroy(struct template_buffer *buf)
-{
-       char *data = buf->data;
-
-       free(buf);
-       return data;
-}
-
-
-/* calculate the number of expected continuation chars */
-static inline int mb_num_chars(unsigned char c)
-{
-       if ((c & 0xE0) == 0xC0)
-               return 2;
-       else if ((c & 0xF0) == 0xE0)
-               return 3;
-       else if ((c & 0xF8) == 0xF0)
-               return 4;
-       else if ((c & 0xFC) == 0xF8)
-               return 5;
-       else if ((c & 0xFE) == 0xFC)
-               return 6;
-
-       return 1;
-}
-
-/* test whether the given byte is a valid continuation char */
-static inline int mb_is_cont(unsigned char c)
-{
-       return ((c >= 0x80) && (c <= 0xBF));
-}
-
-/* test whether the byte sequence at the given pointer with the given
- * length is the shortest possible representation of the code point */
-static inline int mb_is_shortest(unsigned char *s, int n)
-{
-       switch (n)
-       {
-               case 2:
-                       /* 1100000x (10xxxxxx) */
-                       return !(((*s >> 1) == 0x60) &&
-                                        ((*(s+1) >> 6) == 0x02));
-
-               case 3:
-                       /* 11100000 100xxxxx (10xxxxxx) */
-                       return !((*s == 0xE0) &&
-                                        ((*(s+1) >> 5) == 0x04) &&
-                                        ((*(s+2) >> 6) == 0x02));
-
-               case 4:
-                       /* 11110000 1000xxxx (10xxxxxx 10xxxxxx) */
-                       return !((*s == 0xF0) &&
-                                        ((*(s+1) >> 4) == 0x08) &&
-                                        ((*(s+2) >> 6) == 0x02) &&
-                                        ((*(s+3) >> 6) == 0x02));
-
-               case 5:
-                       /* 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) */
-                       return !((*s == 0xF8) &&
-                                        ((*(s+1) >> 3) == 0x10) &&
-                                        ((*(s+2) >> 6) == 0x02) &&
-                                        ((*(s+3) >> 6) == 0x02) &&
-                                        ((*(s+4) >> 6) == 0x02));
-
-               case 6:
-                       /* 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) */
-                       return !((*s == 0xF8) &&
-                                        ((*(s+1) >> 2) == 0x20) &&
-                                        ((*(s+2) >> 6) == 0x02) &&
-                                        ((*(s+3) >> 6) == 0x02) &&
-                                        ((*(s+4) >> 6) == 0x02) &&
-                                        ((*(s+5) >> 6) == 0x02));
-       }
-
-       return 1;
-}
-
-/* test whether the byte sequence at the given pointer with the given
- * length is an UTF-16 surrogate */
-static inline int mb_is_surrogate(unsigned char *s, int n)
-{
-       return ((n == 3) && (*s == 0xED) && (*(s+1) >= 0xA0) && (*(s+1) <= 0xBF));
-}
-
-/* test whether the byte sequence at the given pointer with the given
- * length is an illegal UTF-8 code point */
-static inline int mb_is_illegal(unsigned char *s, int n)
-{
-       return ((n == 3) && (*s == 0xEF) && (*(s+1) == 0xBF) &&
-                       (*(s+2) >= 0xBE) && (*(s+2) <= 0xBF));
-}
-
-
-/* scan given source string, validate UTF-8 sequence and store result
- * in given buffer object */
-static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
-{
-       unsigned char *ptr = *s;
-       unsigned int o = 0, v, n;
-
-       /* ascii byte without null */
-       if ((*(ptr+0) >= 0x01) && (*(ptr+0) <= 0x7F))
-       {
-               if (!buf_putchar(buf, *ptr++))
-                       return 0;
-
-               o = 1;
-       }
-
-       /* multi byte sequence */
-       else if ((n = mb_num_chars(*ptr)) > 1)
-       {
-               /* count valid chars */
-               for (v = 1; (v <= n) && ((o+v) < l) && mb_is_cont(*(ptr+v)); v++);
-
-               switch (n)
-               {
-                       case 6:
-                       case 5:
-                               /* five and six byte sequences are always invalid */
-                               if (!buf_putchar(buf, '?'))
-                                       return 0;
-
-                               break;
-
-                       default:
-                               /* if the number of valid continuation bytes matches the
-                                * expected number and if the sequence is legal, copy
-                                * the bytes to the destination buffer */
-                               if ((v == n) && mb_is_shortest(ptr, n) &&
-                                       !mb_is_surrogate(ptr, n) && !mb_is_illegal(ptr, n))
-                               {
-                                       /* copy sequence */
-                                       if (!buf_append(buf, (char *)ptr, n))
-                                               return 0;
-                               }
-
-                               /* the found sequence is illegal, skip it */
-                               else
-                               {
-                                       /* invalid sequence */
-                                       if (!buf_putchar(buf, '?'))
-                                               return 0;
-                               }
-
-                               break;
-               }
-
-               /* advance beyound the last found valid continuation char */
-               o = v;
-               ptr += v;
-       }
-
-       /* invalid byte (0x00) */
-       else
-       {
-               if (!buf_putchar(buf, '?')) /* or 0xEF, 0xBF, 0xBD */
-                       return 0;
-
-               o = 1;
-               ptr++;
-       }
-
-       *s = ptr;
-       return o;
-}
-
-/* sanitize given string and replace all invalid UTF-8 sequences with "?" */
-char * utf8(const char *s, unsigned int l)
-{
-       struct template_buffer *buf = buf_init(l);
-       unsigned char *ptr = (unsigned char *)s;
-       unsigned int v, o;
-
-       if (!buf)
-               return NULL;
-
-       for (o = 0; o < l; o++)
-       {
-               /* ascii char */
-               if ((*ptr >= 0x01) && (*ptr <= 0x7F))
-               {
-                       if (!buf_putchar(buf, (char)*ptr++))
-                               break;
-               }
-
-               /* invalid byte or multi byte sequence */
-               else
-               {
-                       if (!(v = _validate_utf8(&ptr, l - o, buf)))
-                               break;
-
-                       o += (v - 1);
-               }
-       }
-
-       return buf_destroy(buf);
-}
-
-/* Sanitize given string and strip all invalid XML bytes
- * Validate UTF-8 sequences
- * Escape XML control chars */
-char * pcdata(const char *s, unsigned int l)
-{
-       struct template_buffer *buf = buf_init(l);
-       unsigned char *ptr = (unsigned char *)s;
-       unsigned int o, v;
-       char esq[8];
-       int esl;
-
-       if (!buf)
-               return NULL;
-
-       for (o = 0; o < l; o++)
-       {
-               /* Invalid XML bytes */
-               if (((*ptr >= 0x00) && (*ptr <= 0x08)) ||
-                   ((*ptr >= 0x0B) && (*ptr <= 0x0C)) ||
-                   ((*ptr >= 0x0E) && (*ptr <= 0x1F)) ||
-                   (*ptr == 0x7F))
-               {
-                       ptr++;
-               }
-
-               /* Escapes */
-               else if ((*ptr == 0x26) ||
-                        (*ptr == 0x27) ||
-                        (*ptr == 0x22) ||
-                        (*ptr == 0x3C) ||
-                        (*ptr == 0x3E))
-               {
-                       esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
-
-                       if (!buf_append(buf, esq, esl))
-                               break;
-
-                       ptr++;
-               }
-
-               /* ascii char */
-               else if (*ptr <= 0x7F)
-               {
-                       buf_putchar(buf, (char)*ptr++);
-               }
-
-               /* multi byte sequence */
-               else
-               {
-                       if (!(v = _validate_utf8(&ptr, l - o, buf)))
-                               break;
-
-                       o += (v - 1);
-               }
-       }
-
-       return buf_destroy(buf);
-}
-
-char * striptags(const char *s, unsigned int l)
-{
-       struct template_buffer *buf = buf_init(l);
-       unsigned char *ptr = (unsigned char *)s;
-       unsigned char *end = ptr + l;
-       unsigned char *tag;
-       unsigned char prev;
-       char esq[8];
-       int esl;
-
-       for (prev = ' '; ptr < end; ptr++)
-       {
-               if ((*ptr == '<') && ((ptr + 2) < end) &&
-                       ((*(ptr + 1) == '/') || isalpha(*(ptr + 1))))
-               {
-                       for (tag = ptr; tag < end; tag++)
-                       {
-                               if (*tag == '>')
-                               {
-                                       if (!isspace(prev))
-                                               buf_putchar(buf, ' ');
-
-                                       ptr = tag;
-                                       prev = ' ';
-                                       break;
-                               }
-                       }
-               }
-               else if (isspace(*ptr))
-               {
-                       if (!isspace(prev))
-                               buf_putchar(buf, *ptr);
-
-                       prev = *ptr;
-               }
-               else
-               {
-                       switch(*ptr)
-                       {
-                               case '"':
-                               case '\'':
-                               case '<':
-                               case '>':
-                               case '&':
-                                       esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
-                                       buf_append(buf, esq, esl);
-                                       break;
-
-                               default:
-                                       buf_putchar(buf, *ptr);
-                                       break;
-                       }
-
-                       prev = *ptr;
-               }
-       }
-
-       return buf_destroy(buf);
-}
-
-void luastr_escape(struct template_buffer *out, const char *s, unsigned int l,
-                                  int escape_xml)
-{
-       int esl;
-       char esq[8];
-       char *ptr;
-
-       for (ptr = (char *)s; ptr < (s + l); ptr++)
-       {
-               switch (*ptr)
-               {
-               case '\\':
-                       buf_append(out, "\\\\", 2);
-                       break;
-
-               case '"':
-                       if (escape_xml)
-                               buf_append(out, "&#34;", 5);
-                       else
-                               buf_append(out, "\\\"", 2);
-                       break;
-
-               case '\n':
-                       buf_append(out, "\\n", 2);
-                       break;
-
-               case '\'':
-               case '&':
-               case '<':
-               case '>':
-                       if (escape_xml)
-                       {
-                               esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
-                               buf_append(out, esq, esl);
-                               break;
-                       }
-
-               default:
-                       buf_putchar(out, *ptr);
-               }
-       }
-}
-
-void luastr_translate(struct template_buffer *out, const char *s, unsigned int l,
-                                         int escape_xml)
-{
-       char *tr;
-       int trlen;
-
-       switch (lmo_translate(s, l, &tr, &trlen))
-       {
-               case 0:
-                       luastr_escape(out, tr, trlen, escape_xml);
-                       break;
-
-               case -1:
-                       luastr_escape(out, s, l, escape_xml);
-                       break;
-
-               default:
-                       /* no catalog loaded */
-                       break;
-       }
-}
diff --git a/libs/web/src/template_utils.h b/libs/web/src/template_utils.h
deleted file mode 100644 (file)
index c54af75..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * LuCI Template - Utility header
- *
- *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-#ifndef _TEMPLATE_UTILS_H_
-#define _TEMPLATE_UTILS_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-
-/* buffer object */
-struct template_buffer {
-       char *data;
-       char *dptr;
-       unsigned int size;
-       unsigned int fill;
-};
-
-struct template_buffer * buf_init(int size);
-int buf_grow(struct template_buffer *buf, int size);
-int buf_putchar(struct template_buffer *buf, char c);
-int buf_append(struct template_buffer *buf, const char *s, int len);
-int buf_length(struct template_buffer *buf);
-char * buf_destroy(struct template_buffer *buf);
-
-char * utf8(const char *s, unsigned int l);
-char * pcdata(const char *s, unsigned int l);
-char * striptags(const char *s, unsigned int l);
-
-void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
-void luastr_translate(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
-
-#endif
diff --git a/libs/web/standalone.mk b/libs/web/standalone.mk
deleted file mode 100644 (file)
index 66a0e5a..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-LUAC = luac
-LUAC_OPTIONS = -s
-LUA_TARGET ?= source
-
-LUA_MODULEDIR = /usr/local/share/lua/5.1
-LUA_LIBRARYDIR = /usr/local/lib/lua/5.1
-
-OS ?= $(shell uname)
-
-LUA_SHLIBS = $(shell pkg-config --silence-errors --libs lua5.1 || pkg-config --silence-errors --libs lua-5.1 || pkg-config --silence-errors --libs lua)
-LUA_LIBS = $(if $(LUA_SHLIBS),$(LUA_SHLIBS),$(firstword $(wildcard /usr/lib/liblua.a /usr/local/lib/liblua.a /opt/local/lib/liblua.a)))
-LUA_CFLAGS = $(shell pkg-config --silence-errors --cflags lua5.1 || pkg-config --silence-errors --cflags lua-5.1 || pkg-config --silence-errors --cflags lua)
-
-CC = gcc
-AR = ar
-RANLIB = ranlib
-CFLAGS = -O2
-FPIC = -fPIC
-EXTRA_CFLAGS = --std=gnu99
-WFLAGS = -Wall -Werror -pedantic
-CPPFLAGS =
-COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(WFLAGS)
-ifeq ($(OS),Darwin)
-  SHLIB_FLAGS = -bundle -undefined dynamic_lookup
-else
-  SHLIB_FLAGS = -shared
-endif
-LINK = $(CC) $(LDFLAGS)
-
-.PHONY: all build compile luacompile luasource clean luaclean
-
-all: build
-
-build: luabuild gccbuild
-
-luabuild: lua$(LUA_TARGET)
-
-gccbuild: compile
-compile:
-
-clean: luaclean
-
-luasource:
-       mkdir -p dist$(LUA_MODULEDIR)
-       cp -pR root/* dist 2>/dev/null || true
-       cp -pR lua/* dist$(LUA_MODULEDIR) 2>/dev/null || true
-       for i in $$(find dist -name .svn); do rm -rf $$i || true; done
-
-luastrip: luasource
-       for i in $$(find dist -type f -name '*.lua'); do perl -e 'undef $$/; open( F, "< $$ARGV[0]" ) || die $$!; $$src = <F>; close F; $$src =~ s/--\[\[.*?\]\](--)?//gs; $$src =~ s/^\s*--.*?\n//gm; open( F, "> $$ARGV[0]" ) || die $$!; print F $$src; close F' $$i; done
-
-luacompile: luasource
-       for i in $$(find dist -name *.lua -not -name debug.lua); do $(LUAC) $(LUAC_OPTIONS) -o $$i $$i; done
-
-luaclean:
-       rm -rf dist
diff --git a/modules/admin-core/Makefile b/modules/admin-core/Makefile
deleted file mode 100644 (file)
index 81a96f6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
\ No newline at end of file
diff --git a/modules/admin-core/ipkg/postinst b/modules/admin-core/ipkg/postinst
deleted file mode 100755 (executable)
index a756b5b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-[ -n "${IPKG_INSTROOT}" ] || {
-       /etc/init.d/luci_fixtime enabled || /etc/init.d/luci_fixtime enable
-       /etc/init.d/luci_dhcp_migrate enabled || /etc/init.d/luci_dhcp_migrate enable
-       exit 0
-}
diff --git a/modules/admin-core/luasrc/controller/admin/servicectl.lua b/modules/admin-core/luasrc/controller/admin/servicectl.lua
deleted file mode 100644 (file)
index 753d2c7..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-]]--
-
-module("luci.controller.admin.servicectl", package.seeall)
-
-function index()
-       entry({"servicectl"}, alias("servicectl", "status")).sysauth = "root"
-       entry({"servicectl", "status"}, call("action_status")).leaf = true
-       entry({"servicectl", "restart"}, call("action_restart")).leaf = true
-end
-
-function action_status()
-       local data = nixio.fs.readfile("/var/run/luci-reload-status")
-       if data then
-               luci.http.write("/etc/config/")
-               luci.http.write(data)
-       else
-               luci.http.write("finish")
-       end
-end
-
-function action_restart(args)
-       local uci = require "luci.model.uci".cursor()
-       if args then
-               local service
-               local services = { }
-
-               for service in args:gmatch("[%w_-]+") do
-                       services[#services+1] = service
-               end
-
-               local command = uci:apply(services, true)
-               if nixio.fork() == 0 then
-                       local i = nixio.open("/dev/null", "r")
-                       local o = nixio.open("/dev/null", "w")
-
-                       nixio.dup(i, nixio.stdin)
-                       nixio.dup(o, nixio.stdout)
-
-                       i:close()
-                       o:close()
-
-                       nixio.exec("/bin/sh", unpack(command))
-               else
-                       luci.http.write("OK")
-                       os.exit(0)
-               end
-       end
-end
diff --git a/modules/admin-core/luasrc/tools/status.lua b/modules/admin-core/luasrc/tools/status.lua
deleted file mode 100644 (file)
index 27bc925..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-module("luci.tools.status", package.seeall)
-
-local uci = require "luci.model.uci".cursor()
-
-local function dhcp_leases_common(family)
-       local rv = { }
-       local nfs = require "nixio.fs"
-       local leasefile = "/var/dhcp.leases"
-
-       uci:foreach("dhcp", "dnsmasq",
-               function(s)
-                       if s.leasefile and nfs.access(s.leasefile) then
-                               leasefile = s.leasefile
-                               return false
-                       end
-               end)
-
-       local fd = io.open(leasefile, "r")
-       if fd then
-               while true do
-                       local ln = fd:read("*l")
-                       if not ln then
-                               break
-                       else
-                               local ts, mac, ip, name, duid = ln:match("^(%d+) (%S+) (%S+) (%S+) (%S+)")
-                               if ts and mac and ip and name and duid then
-                                       if family == 4 and not ip:match(":") then
-                                               rv[#rv+1] = {
-                                                       expires  = os.difftime(tonumber(ts) or 0, os.time()),
-                                                       macaddr  = mac,
-                                                       ipaddr   = ip,
-                                                       hostname = (name ~= "*") and name
-                                               }
-                                       elseif family == 6 and ip:match(":") then
-                                               rv[#rv+1] = {
-                                                       expires  = os.difftime(tonumber(ts) or 0, os.time()),
-                                                       ip6addr  = ip,
-                                                       duid     = (duid ~= "*") and duid,
-                                                       hostname = (name ~= "*") and name
-                                               }
-                                       end
-                               end
-                       end
-               end
-               fd:close()
-       end
-
-       local fd = io.open("/tmp/hosts/odhcpd", "r")
-       if fd then
-               while true do
-                       local ln = fd:read("*l")
-                       if not ln then
-                               break
-                       else
-                               local iface, duid, iaid, name, ts, id, length, ip = ln:match("^# (%S+) (%S+) (%S+) (%S+) (%d+) (%S+) (%S+) (.*)")
-                               if ip and iaid ~= "ipv4" and family == 6 then
-                                       rv[#rv+1] = {
-                                               expires  = os.difftime(tonumber(ts) or 0, os.time()),
-                                               duid     = duid,
-                                               ip6addr  = ip,
-                                               hostname = (name ~= "-") and name
-                                       }
-                               elseif ip and iaid == "ipv4" and family == 4 then
-                                       rv[#rv+1] = {
-                                               expires  = os.difftime(tonumber(ts) or 0, os.time()),
-                                               macaddr  = duid,
-                                               ipaddr   = ip,
-                                               hostname = (name ~= "-") and name
-                                       }
-                               end
-                       end
-               end
-               fd:close()
-       end
-
-       return rv
-end
-
-function dhcp_leases()
-       return dhcp_leases_common(4)
-end
-
-function dhcp6_leases()
-       return dhcp_leases_common(6)
-end
-
-function wifi_networks()
-       local rv = { }
-       local ntm = require "luci.model.network".init()
-
-       local dev
-       for _, dev in ipairs(ntm:get_wifidevs()) do
-               local rd = {
-                       up       = dev:is_up(),
-                       device   = dev:name(),
-                       name     = dev:get_i18n(),
-                       networks = { }
-               }
-
-               local net
-               for _, net in ipairs(dev:get_wifinets()) do
-                       rd.networks[#rd.networks+1] = {
-                               name       = net:shortname(),
-                               link       = net:adminlink(),
-                               up         = net:is_up(),
-                               mode       = net:active_mode(),
-                               ssid       = net:active_ssid(),
-                               bssid      = net:active_bssid(),
-                               encryption = net:active_encryption(),
-                               frequency  = net:frequency(),
-                               channel    = net:channel(),
-                               signal     = net:signal(),
-                               quality    = net:signal_percent(),
-                               noise      = net:noise(),
-                               bitrate    = net:bitrate(),
-                               ifname     = net:ifname(),
-                               assoclist  = net:assoclist(),
-                               country    = net:country(),
-                               txpower    = net:txpower(),
-                               txpoweroff = net:txpower_offset()
-                       }
-               end
-
-               rv[#rv+1] = rd
-       end
-
-       return rv
-end
-
-function wifi_network(id)
-       local ntm = require "luci.model.network".init()
-       local net = ntm:get_wifinet(id)
-       if net then
-               local dev = net:get_device()
-               if dev then
-                       return {
-                               id         = id,
-                               name       = net:shortname(),
-                               link       = net:adminlink(),
-                               up         = net:is_up(),
-                               mode       = net:active_mode(),
-                               ssid       = net:active_ssid(),
-                               bssid      = net:active_bssid(),
-                               encryption = net:active_encryption(),
-                               frequency  = net:frequency(),
-                               channel    = net:channel(),
-                               signal     = net:signal(),
-                               quality    = net:signal_percent(),
-                               noise      = net:noise(),
-                               bitrate    = net:bitrate(),
-                               ifname     = net:ifname(),
-                               assoclist  = net:assoclist(),
-                               country    = net:country(),
-                               txpower    = net:txpower(),
-                               txpoweroff = net:txpower_offset(),
-                               device     = {
-                                       up     = dev:is_up(),
-                                       device = dev:name(),
-                                       name   = dev:get_i18n()
-                               }
-                       }
-               end
-       end
-       return { }
-end
-
-function switch_status(devs)
-       local dev
-       local switches = { }
-       for dev in devs:gmatch("[^%s,]+") do
-               local ports = { }
-               local swc = io.popen("swconfig dev %q show" % dev, "r")
-               if swc then
-                       local l
-                       repeat
-                               l = swc:read("*l")
-                               if l then
-                                       local port, up = l:match("port:(%d+) link:(%w+)")
-                                       if port then
-                                               local speed  = l:match(" speed:(%d+)")
-                                               local duplex = l:match(" (%w+)-duplex")
-                                               local txflow = l:match(" (txflow)")
-                                               local rxflow = l:match(" (rxflow)")
-                                               local auto   = l:match(" (auto)")
-
-                                               ports[#ports+1] = {
-                                                       port   = tonumber(port) or 0,
-                                                       speed  = tonumber(speed) or 0,
-                                                       link   = (up == "up"),
-                                                       duplex = (duplex == "full"),
-                                                       rxflow = (not not rxflow),
-                                                       txflow = (not not txflow),
-                                                       auto   = (not not auto)
-                                               }
-                                       end
-                               end
-                       until not l
-                       swc:close()
-               end
-               switches[dev] = ports
-       end
-       return switches
-end
diff --git a/modules/admin-core/luasrc/tools/webadmin.lua b/modules/admin-core/luasrc/tools/webadmin.lua
deleted file mode 100644 (file)
index 0e09be9..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-]]--
-
-module("luci.tools.webadmin", package.seeall)
-local uci = require("luci.model.uci")
-require("luci.sys")
-require("luci.ip")
-
-function byte_format(byte)
-       local suff = {"B", "KB", "MB", "GB", "TB"}
-       for i=1, 5 do
-               if byte > 1024 and i < 5 then
-                       byte = byte / 1024
-               else
-                       return string.format("%.2f %s", byte, suff[i]) 
-               end 
-       end
-end
-
-function date_format(secs)
-       local suff = {"min", "h", "d"}
-       local mins = 0
-       local hour = 0
-       local days = 0
-       
-       secs = math.floor(secs)
-       if secs > 60 then
-               mins = math.floor(secs / 60)
-               secs = secs % 60
-       end
-       
-       if mins > 60 then
-               hour = math.floor(mins / 60)
-               mins = mins % 60
-       end
-       
-       if hour > 24 then
-               days = math.floor(hour / 24)
-               hour = hour % 24
-       end
-       
-       if days > 0 then
-               return string.format("%.0fd %02.0fh %02.0fmin %02.0fs", days, hour, mins, secs)
-       else
-               return string.format("%02.0fh %02.0fmin %02.0fs", hour, mins, secs)
-       end
-end
-
-function network_get_addresses(net)
-       local state = uci.cursor_state()
-       state:load("network")
-       local addr = {}
-       local ipv4 = state:get("network", net, "ipaddr")
-       local mav4 = state:get("network", net, "netmask")
-       local ipv6 = state:get("network", net, "ip6addr")
-       
-       if ipv4 and #ipv4 > 0 then
-               if mav4 and #mav4 == 0 then mav4 = nil end
-
-               ipv4 = luci.ip.IPv4(ipv4, mav4)
-               
-               if ipv4 then 
-                       table.insert(addr, ipv4:string())
-               end
-       end
-
-       if ipv6 then
-               table.insert(addr, ipv6)
-       end
-       
-       state:foreach("network", "alias",
-               function (section)
-                       if section.interface == net then
-                               if section.ipaddr and section.netmask then
-                                       local ipv4 = luci.ip.IPv4(section.ipaddr, section.netmask)
-                                       
-                                       if ipv4 then
-                                               table.insert(addr, ipv4:string())
-                                       end
-                               end
-                               
-                               if section.ip6addr then
-                                       table.insert(addr, section.ip6addr)
-                               end
-                       end
-               end
-       )
-       
-       return addr
-end
-
-function cbi_add_networks(field)
-       uci.cursor():foreach("network", "interface",
-               function (section)
-                       if section[".name"] ~= "loopback" then
-                               field:value(section[".name"])
-                       end
-               end
-       )
-       field.titleref = luci.dispatcher.build_url("admin", "network", "network")
-end
-
-function cbi_add_knownips(field)
-       for i, dataset in ipairs(luci.sys.net.arptable()) do
-               field:value(dataset["IP address"])
-       end
-end
-
-function network_get_zones(net)
-       local state = uci.cursor_state()
-       if not state:load("firewall") then
-               return nil
-       end
-       
-       local zones = {}
-       
-       state:foreach("firewall", "zone", 
-               function (section)
-                       local znet = section.network or section.name
-                       if luci.util.contains(luci.util.split(znet, " "), net) then
-                               table.insert(zones, section.name)
-                       end
-               end
-       )
-       
-       return zones
-end
-
-function firewall_find_zone(name)
-       local find
-       
-       luci.model.uci.cursor():foreach("firewall", "zone", 
-               function (section)
-                       if section.name == name then
-                               find = section[".name"]
-                       end
-               end
-       )
-       
-       return find
-end
-
-function iface_get_network(iface)
-       local state = uci.cursor_state()
-       state:load("network")
-       local net
-       
-       state:foreach("network", "interface",
-               function (section)
-                       local ifname = state:get(
-                               "network", section[".name"], "ifname"
-                       )
-                       
-                       if iface == ifname then
-                               net = section[".name"]
-                       end
-               end
-       )
-       
-       return net
-end
diff --git a/modules/admin-core/luasrc/view/error404.htm b/modules/admin-core/luasrc/view/error404.htm
deleted file mode 100644 (file)
index 813604d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
--%>
-<%+header%>
-<h2><a id="content" name="content">404 <%:Not Found%></a></h2>
-<p><%:Sorry, the object you requested was not found.%></p>
-<tt><%:Unable to dispatch%>: <%=luci.http.request.env.PATH_INFO%></tt>
-<%+footer%>
diff --git a/modules/admin-core/luasrc/view/error500.htm b/modules/admin-core/luasrc/view/error500.htm
deleted file mode 100644 (file)
index 14ba041..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
--%>
-<%+header%>
-<h2><a id="content" name="content">500 <%:Internal Server Error%></a></h2>
-<p><%:Sorry, the server encountered an unexpected error.%></p>
-<pre class="error500"><%=message%></pre>
-<%+footer%>
diff --git a/modules/admin-core/luasrc/view/footer.htm b/modules/admin-core/luasrc/view/footer.htm
deleted file mode 100644 (file)
index 6c6d214..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
--%>
-<% include("themes/" .. theme .. "/footer") %>
\ No newline at end of file
diff --git a/modules/admin-core/luasrc/view/header.htm b/modules/admin-core/luasrc/view/header.htm
deleted file mode 100644 (file)
index 77018b1..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
--%>
-
-<%
-       if not luci.dispatcher.context.template_header_sent then
-               include("themes/" .. theme .. "/header")
-               luci.dispatcher.context.template_header_sent = true
-       end
-%>
diff --git a/modules/admin-core/luasrc/view/indexer.htm b/modules/admin-core/luasrc/view/indexer.htm
deleted file mode 100644 (file)
index c628289..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-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
-
-$Id$
-
--%>
-<% include("themes/" .. theme .. "/indexer") %>
\ No newline at end of file
diff --git a/modules/admin-core/luasrc/view/sysauth.htm b/modules/admin-core/luasrc/view/sysauth.htm
deleted file mode 100644 (file)
index 7c39f0d..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<%#
-LuCI - Lua Configuration Interface
-Copyright 2008 Steven Barth <steven@midlink.org>
-Copyright 2008-2012 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
--%>
-
-<%+header%>
-
-<form method="post" action="<%=pcdata(luci.http.getenv("REQUEST_URI"))%>">
-       <div class="cbi-map">
-               <h2><a id="content" name="content"><%:Authorization Required%></a></h2>
-               <div class="cbi-map-descr">
-                       <%:Please enter your username and password.%>
-                       <%- if fuser then %>
-                       <div class="error"><%:Invalid username and/or password! Please try again.%></div>
-                       <br />
-                       <% end -%>
-               </div>
-               <fieldset class="cbi-section"><fieldset class="cbi-section-node">
-                       <div class="cbi-value">
-                               <label class="cbi-value-title"><%:Username%></label>
-                               <div class="cbi-value-field">
-                                       <input class="cbi-input-user" type="text" name="username" value="<%=duser%>" />
-                               </div>
-                       </div>
-                       <div class="cbi-value cbi-value-last">
-                               <label class="cbi-value-title"><%:Password%></label>
-                               <div class="cbi-value-field">
-                                       <input id="focus_password" class="cbi-input-password" type="password" name="password" />
-                               </div>
-                       </div>
-               </fieldset></fieldset>
-       </div>
-
-       <div>
-               <input type="submit" value="<%:Login%>" class="cbi-button cbi-button-apply" />
-               <input type="reset" value="<%:Reset%>" class="cbi-button cbi-button-reset" />
-       </div>
-</form>
-<script type="text/javascript">//<![CDATA[
-       var input = document.getElementById('focus_password');
-       if (input)
-               input.focus();
-//]]></script>
-
-<%
-local uci  = require "luci.model.uci".cursor()
-local fs  = require "nixio.fs"
-local https_key = uci:get("uhttpd", "main", "key")
-local https_port = uci:get("uhttpd", "main", "listen_https")
-if type(https_port) == "table" then
-       https_port = https_port[1]
-end
-
-if https_port and fs.access(https_key) then
-       https_port = https_port:match("(%d+)$")
-%>
-
-<script type="text/javascript">//<![CDATA[
-       if (document.location.protocol != 'https:') {
-               var url = 'https://' + window.location.hostname + ':' + '<%=https_port%>' + window.location.pathname;
-               var img=new Image;
-               img.onload=function(){window.location = url};
-               img.src='https://' + window.location.hostname + ':' + '<%=https_port%>' + '<%=resource%>/cbi/up.gif?' + Math.random();;
-               setTimeout(function(){
-                       img.src=''
-               }, 5000);
-       }
-//]]></script>
-
-<% end %>
-
-<%+footer%>
diff --git a/modules/admin-core/root/etc/init.d/luci_dhcp_migrate b/modules/admin-core/root/etc/init.d/luci_dhcp_migrate
deleted file mode 100755 (executable)
index 7fb4435..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh /etc/rc.common
-
-START=59
-
-boot() {
-       if [ -f /etc/config/luci_ethers ]; then
-               logger -t luci_dhcp_migrate "Migrating luci_ethers configuration ..."
-
-               lua -lluci.model.uci -e '
-                       x=luci.model.uci.cursor()
-                       x:foreach("luci_ethers", "static_lease",
-                       function(s)
-                               x:section("dhcp", "host", nil, {mac=s.macaddr, ip=s.ipaddr})
-                       end)
-                       x:save("dhcp")
-                       x:commit("dhcp")
-               '
-
-               rm -f /etc/config/luci_ethers
-       fi
-
-       if [ -f /etc/config/luci_hosts ]; then
-               logger -t luci_dhcp_migrate "Migrating luci_hosts configuration ..."
-
-               lua -lluci.model.uci -e '
-                       x=luci.model.uci.cursor()
-                       x:foreach("luci_hosts", "host",
-                       function(s)
-                               x:section("dhcp", "domain", nil, {name=s.hostname, ip=s.ipaddr})
-                       end)
-                       x:save("dhcp")
-                       x:commit("dhcp")
-               '
-
-               rm -f /etc/config/luci_hosts
-       fi
-}
-
-start() { :; }
-stop() { :; }
-
diff --git a/modules/admin-core/root/etc/init.d/luci_fixtime b/modules/admin-core/root/etc/init.d/luci_fixtime
deleted file mode 100755 (executable)
index 154a786..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh /etc/rc.common
-
-START=05
-STOP=95
-
-start() {
-       cat <<' EOF' | lua -l luci.fs -l luci.util -
-               if (os.time() < 1000000000) then
-                       os.execute('date -s ' .. os.date('%Y%m%d%H%M', luci.fs.mtime("/etc/init.d/luci_fixtime")))
-               end
-       EOF
-}
-
-stop() {
-       [[ -w /etc/init.d/luci_fixtime ]] && cat /dev/null >> /etc/init.d/luci_fixtime && touch /etc/init.d/luci_fixtime
-}
diff --git a/modules/admin-core/root/www/index.html b/modules/admin-core/root/www/index.html
deleted file mode 100644 (file)
index 0a7238b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
-</head>
-<body style="background-color: black">
-<a style="color: white; text-decoration: none" href="/cgi-bin/luci">LuCI - Lua Configuration Interface</a>
-</body>
-</html>
diff --git a/modules/base/Makefile b/modules/base/Makefile
new file mode 100644 (file)
index 0000000..ad16ec7
--- /dev/null
@@ -0,0 +1,45 @@
+ifneq (,$(wildcard ../../build/config.mk))
+include ../../build/config.mk
+include ../../build/module.mk
+include ../../build/gccconfig.mk
+else
+include standalone.mk
+endif
+
+TPL_LDFLAGS    =
+TPL_CFLAGS     =
+TPL_SO         = parser.so
+TPL_PO2LMO     = po2lmo
+TPL_PO2LMO_OBJ = src/po2lmo.o
+TPL_LMO_OBJ    = src/template_lmo.o
+TPL_COMMON_OBJ = src/template_parser.o src/template_utils.o
+TPL_LUALIB_OBJ = src/template_lualib.o
+
+%.o: %.c
+       $(COMPILE) $(TPL_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
+
+compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+       $(LINK) $(SHLIB_FLAGS) $(TPL_LDFLAGS) -o src/$(TPL_SO) \
+               $(TPL_COMMON_OBJ) $(TPL_LMO_OBJ) $(TPL_LUALIB_OBJ)
+       $(LINK) -o src/$(TPL_PO2LMO) \
+               $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+       mkdir -p dist$(LUCI_LIBRARYDIR)/template
+       cp src/$(TPL_SO) dist$(LUCI_LIBRARYDIR)/template/$(TPL_SO)
+
+install: build
+       cp -pR dist$(LUA_LIBRARYDIR)/* $(LUA_LIBRARYDIR)
+
+clean: build-clean
+
+build-clean:
+       rm -f src/*.o src/$(TPL_SO)
+
+host-compile: build-clean host-clean $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+       $(LINK) -o src/$(TPL_PO2LMO) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+
+host-install: host-compile
+       cp src/$(TPL_PO2LMO) ../../build/$(TPL_PO2LMO)
+
+host-clean:
+       rm -f src/$(TPL_PO2LMO)
+       rm -f ../../build/$(TPL_PO2LMO)
diff --git a/modules/base/htdocs/cgi-bin/luci b/modules/base/htdocs/cgi-bin/luci
new file mode 100755 (executable)
index 0000000..529d1d0
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/lua
+require "luci.cacheloader"
+require "luci.sgi.cgi"
+luci.dispatcher.indexcache = "/tmp/luci-indexcache"
+luci.sgi.cgi.run()
\ No newline at end of file
diff --git a/modules/base/htdocs/luci-static/resources/cbi.js b/modules/base/htdocs/luci-static/resources/cbi.js
new file mode 100644 (file)
index 0000000..02814a5
--- /dev/null
@@ -0,0 +1,1339 @@
+/*
+       LuCI - Lua Configuration Interface
+
+       Copyright 2008 Steven Barth <steven@midlink.org>
+       Copyright 2008-2012 Jo-Philipp Wich <xm@subsignal.org>
+
+       Licensed under the Apache License, Version 2.0 (the "License");
+       you may not use this file except in compliance with the License.
+       You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+*/
+
+var cbi_d = [];
+var cbi_t = [];
+var cbi_c = [];
+
+var cbi_validators = {
+
+       'integer': function()
+       {
+               return (this.match(/^-?[0-9]+$/) != null);
+       },
+
+       'uinteger': function()
+       {
+               return (cbi_validators.integer.apply(this) && (this >= 0));
+       },
+
+       'float': function()
+       {
+               return !isNaN(parseFloat(this));
+       },
+
+       'ufloat': function()
+       {
+               return (cbi_validators['float'].apply(this) && (this >= 0));
+       },
+
+       'ipaddr': function()
+       {
+               return cbi_validators.ip4addr.apply(this) ||
+                       cbi_validators.ip6addr.apply(this);
+       },
+
+       'ip4addr': function()
+       {
+               if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/))
+               {
+                       return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) &&
+                              (RegExp.$2 >= 0) && (RegExp.$2 <= 255) &&
+                              (RegExp.$3 >= 0) && (RegExp.$3 <= 255) &&
+                              (RegExp.$4 >= 0) && (RegExp.$4 <= 255) &&
+                              ((RegExp.$6.indexOf('.') < 0)
+                                 ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32))
+                                 : (cbi_validators.ip4addr.apply(RegExp.$6)))
+                       ;
+               }
+
+               return false;
+       },
+
+       'ip6addr': function()
+       {
+               if( this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) )
+               {
+                       if( !RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)) )
+                       {
+                               var addr = RegExp.$1;
+
+                               if( addr == '::' )
+                               {
+                                       return true;
+                               }
+
+                               if( addr.indexOf('.') > 0 )
+                               {
+                                       var off = addr.lastIndexOf(':');
+
+                                       if( !(off && cbi_validators.ip4addr.apply(addr.substr(off+1))) )
+                                               return false;
+
+                                       addr = addr.substr(0, off) + ':0:0';
+                               }
+
+                               if( addr.indexOf('::') >= 0 )
+                               {
+                                       var colons = 0;
+                                       var fill = '0';
+
+                                       for( var i = 1; i < (addr.length-1); i++ )
+                                               if( addr.charAt(i) == ':' )
+                                                       colons++;
+
+                                       if( colons > 7 )
+                                               return false;
+
+                                       for( var i = 0; i < (7 - colons); i++ )
+                                               fill += ':0';
+
+                                       if (addr.match(/^(.*?)::(.*?)$/))
+                                               addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill +
+                                                      (RegExp.$2 ? ':' + RegExp.$2 : '');
+                               }
+
+                               return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null);
+                       }
+               }
+
+               return false;
+       },
+
+       'port': function()
+       {
+               return cbi_validators.integer.apply(this) &&
+                       (this >= 0) && (this <= 65535);
+       },
+
+       'portrange': function()
+       {
+               if (this.match(/^(\d+)-(\d+)$/))
+               {
+                       var p1 = RegExp.$1;
+                       var p2 = RegExp.$2;
+
+                       return cbi_validators.port.apply(p1) &&
+                              cbi_validators.port.apply(p2) &&
+                              (parseInt(p1) <= parseInt(p2))
+                       ;
+               }
+               else
+               {
+                       return cbi_validators.port.apply(this);
+               }
+       },
+
+       'macaddr': function()
+       {
+               return (this.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null);
+       },
+
+       'host': function()
+       {
+               return cbi_validators.hostname.apply(this) ||
+                       cbi_validators.ipaddr.apply(this);
+       },
+
+       'hostname': function()
+       {
+               if (this.length <= 253)
+                       return (this.match(/^[a-zA-Z0-9]+$/) != null ||
+                               (this.match(/^[a-zA-Z0-9_][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$/) &&
+                                this.match(/[^0-9.]/)));
+
+               return false;
+       },
+
+       'network': function()
+       {
+               return cbi_validators.uciname.apply(this) ||
+                       cbi_validators.host.apply(this);
+       },
+
+       'wpakey': function()
+       {
+               var v = this;
+
+               if( v.length == 64 )
+                       return (v.match(/^[a-fA-F0-9]{64}$/) != null);
+               else
+                       return (v.length >= 8) && (v.length <= 63);
+       },
+
+       'wepkey': function()
+       {
+               var v = this;
+
+               if ( v.substr(0,2) == 's:' )
+                       v = v.substr(2);
+
+               if( (v.length == 10) || (v.length == 26) )
+                       return (v.match(/^[a-fA-F0-9]{10,26}$/) != null);
+               else
+                       return (v.length == 5) || (v.length == 13);
+       },
+
+       'uciname': function()
+       {
+               return (this.match(/^[a-zA-Z0-9_]+$/) != null);
+       },
+
+       'range': function(min, max)
+       {
+               var val = parseFloat(this);
+               if (!isNaN(min) && !isNaN(max) && !isNaN(val))
+                       return ((val >= min) && (val <= max));
+
+               return false;
+       },
+
+       'min': function(min)
+       {
+               var val = parseFloat(this);
+               if (!isNaN(min) && !isNaN(val))
+                       return (val >= min);
+
+               return false;
+       },
+
+       'max': function(max)
+       {
+               var val = parseFloat(this);
+               if (!isNaN(max) && !isNaN(val))
+                       return (val <= max);
+
+               return false;
+       },
+
+       'rangelength': function(min, max)
+       {
+               var val = '' + this;
+               if (!isNaN(min) && !isNaN(max))
+                       return ((val.length >= min) && (val.length <= max));
+
+               return false;
+       },
+
+       'minlength': function(min)
+       {
+               var val = '' + this;
+               if (!isNaN(min))
+                       return (val.length >= min);
+
+               return false;
+       },
+
+       'maxlength': function(max)
+       {
+               var val = '' + this;
+               if (!isNaN(max))
+                       return (val.length <= max);
+
+               return false;
+       },
+
+       'or': function()
+       {
+               for (var i = 0; i < arguments.length; i += 2)
+               {
+                       if (typeof arguments[i] != 'function')
+                       {
+                               if (arguments[i] == this)
+                                       return true;
+                               i--;
+                       }
+                       else if (arguments[i].apply(this, arguments[i+1]))
+                       {
+                               return true;
+                       }
+               }
+               return false;
+       },
+
+       'and': function()
+       {
+               for (var i = 0; i < arguments.length; i += 2)
+               {
+                       if (typeof arguments[i] != 'function')
+                       {
+                               if (arguments[i] != this)
+                                       return false;
+                               i--;
+                       }
+                       else if (!arguments[i].apply(this, arguments[i+1]))
+                       {
+                               return false;
+                       }
+               }
+               return true;
+       },
+
+       'neg': function()
+       {
+               return cbi_validators.or.apply(
+                       this.replace(/^[ \t]*![ \t]*/, ''), arguments);
+       },
+
+       'list': function(subvalidator, subargs)
+       {
+               if (typeof subvalidator != 'function')
+                       return false;
+
+               var tokens = this.match(/[^ \t]+/g);
+               for (var i = 0; i < tokens.length; i++)
+                       if (!subvalidator.apply(tokens[i], subargs))
+                               return false;
+
+               return true;
+       },
+       'phonedigit': function()
+       {
+               return (this.match(/^[0-9\*#!\.]+$/) != null);
+       }
+};
+
+
+function cbi_d_add(field, dep, next) {
+       var obj = document.getElementById(field);
+       if (obj) {
+               var entry
+               for (var i=0; i<cbi_d.length; i++) {
+                       if (cbi_d[i].id == field) {
+                               entry = cbi_d[i];
+                               break;
+                       }
+               }
+               if (!entry) {
+                       entry = {
+                               "node": obj,
+                               "id": field,
+                               "parent": obj.parentNode.id,
+                               "next": next,
+                               "deps": []
+                       };
+                       cbi_d.unshift(entry);
+               }
+               entry.deps.push(dep)
+       }
+}
+
+function cbi_d_checkvalue(target, ref) {
+       var t = document.getElementById(target);
+       var value;
+
+       if (!t) {
+               var tl = document.getElementsByName(target);
+
+               if( tl.length > 0 && (tl[0].type == 'radio' || tl[0].type == 'checkbox'))
+                       for( var i = 0; i < tl.length; i++ )
+                               if( tl[i].checked ) {
+                                       value = tl[i].value;
+                                       break;
+                               }
+
+               value = value ? value : "";
+       } else if (!t.value) {
+               value = "";
+       } else {
+               value = t.value;
+
+               if (t.type == "checkbox") {
+                       value = t.checked ? value : "";
+               }
+       }
+
+       return (value == ref)
+}
+
+function cbi_d_check(deps) {
+       var reverse;
+       var def = false;
+       for (var i=0; i<deps.length; i++) {
+               var istat = true;
+               reverse = false;
+               for (var j in deps[i]) {
+                       if (j == "!reverse") {
+                               reverse = true;
+                       } else if (j == "!default") {
+                               def = true;
+                               istat = false;
+                       } else {
+                               istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
+                       }
+               }
+               if (istat) {
+                       return !reverse;
+               }
+       }
+       return def;
+}
+
+function cbi_d_update() {
+       var state = false;
+       for (var i=0; i<cbi_d.length; i++) {
+               var entry = cbi_d[i];
+               var next  = document.getElementById(entry.next)
+               var node  = document.getElementById(entry.id)
+               var parent = document.getElementById(entry.parent)
+
+               if (node && node.parentNode && !cbi_d_check(entry.deps)) {
+                       node.parentNode.removeChild(node);
+                       state = true;
+                       if( entry.parent )
+                               cbi_c[entry.parent]--;
+               } else if ((!node || !node.parentNode) && cbi_d_check(entry.deps)) {
+                       if (!next) {
+                               parent.appendChild(entry.node);
+                       } else {
+                               next.parentNode.insertBefore(entry.node, next);
+                       }
+                       state = true;
+                       if( entry.parent )
+                               cbi_c[entry.parent]++;
+               }
+       }
+
+       if (entry && entry.parent) {
+               if (!cbi_t_update())
+                       cbi_tag_last(parent);
+       }
+
+       if (state) {
+               cbi_d_update();
+       }
+}
+
+function cbi_bind(obj, type, callback, mode) {
+       if (!obj.addEventListener) {
+               obj.attachEvent('on' + type,
+                       function(){
+                               var e = window.event;
+
+                               if (!e.target && e.srcElement)
+                                       e.target = e.srcElement;
+
+                               return !!callback(e);
+                       }
+               );
+       } else {
+               obj.addEventListener(type, callback, !!mode);
+       }
+       return obj;
+}
+
+function cbi_combobox(id, values, def, man) {
+       var selid = "cbi.combobox." + id;
+       if (document.getElementById(selid)) {
+               return
+       }
+
+       var obj = document.getElementById(id)
+       var sel = document.createElement("select");
+               sel.id = selid;
+               sel.className = obj.className.replace(/cbi-input-text/, 'cbi-input-select');
+
+       if (obj.nextSibling) {
+               obj.parentNode.insertBefore(sel, obj.nextSibling);
+       } else {
+               obj.parentNode.appendChild(sel);
+       }
+
+       var dt = obj.getAttribute('cbi_datatype');
+       var op = obj.getAttribute('cbi_optional');
+
+       if (dt)
+               cbi_validate_field(sel, op == 'true', dt);
+
+       if (!values[obj.value]) {
+               if (obj.value == "") {
+                       var optdef = document.createElement("option");
+                       optdef.value = "";
+                       optdef.appendChild(document.createTextNode(def));
+                       sel.appendChild(optdef);
+               } else {
+                       var opt = document.createElement("option");
+                       opt.value = obj.value;
+                       opt.selected = "selected";
+                       opt.appendChild(document.createTextNode(obj.value));
+                       sel.appendChild(opt);
+               }
+       }
+
+       for (var i in values) {
+               var opt = document.createElement("option");
+               opt.value = i;
+
+               if (obj.value == i) {
+                       opt.selected = "selected";
+               }
+
+               opt.appendChild(document.createTextNode(values[i]));
+               sel.appendChild(opt);
+       }
+
+       var optman = document.createElement("option");
+       optman.value = "";
+       optman.appendChild(document.createTextNode(man));
+       sel.appendChild(optman);
+
+       obj.style.display = "none";
+
+       cbi_bind(sel, "change", function() {
+               if (sel.selectedIndex == sel.options.length - 1) {
+                       obj.style.display = "inline";
+                       sel.parentNode.removeChild(sel);
+                       obj.focus();
+               } else {
+                       obj.value = sel.options[sel.selectedIndex].value;
+               }
+
+               try {
+                       cbi_d_update();
+               } catch (e) {
+                       //Do nothing
+               }
+       })
+
+       // Retrigger validation in select
+       sel.focus();
+       sel.blur();
+}
+
+function cbi_combobox_init(id, values, def, man) {
+       var obj = document.getElementById(id);
+       cbi_bind(obj, "blur", function() {
+               cbi_combobox(id, values, def, man)
+       });
+       cbi_combobox(id, values, def, man);
+}
+
+function cbi_filebrowser(id, url, defpath) {
+       var field   = document.getElementById(id);
+       var browser = window.open(
+               url + ( field.value || defpath || '' ) + '?field=' + id,
+               "luci_filebrowser", "width=300,height=400,left=100,top=200,scrollbars=yes"
+       );
+
+       browser.focus();
+}
+
+function cbi_browser_init(id, respath, url, defpath)
+{
+       function cbi_browser_btnclick(e) {
+               cbi_filebrowser(id, url, defpath);
+               return false;
+       }
+
+       var field = document.getElementById(id);
+
+       var btn = document.createElement('img');
+       btn.className = 'cbi-image-button';
+       btn.src = respath + '/cbi/folder.gif';
+       field.parentNode.insertBefore(btn, field.nextSibling);
+
+       cbi_bind(btn, 'click', cbi_browser_btnclick);
+}
+
+function cbi_dynlist_init(name, respath, datatype, optional, choices)
+{
+       var input0 = document.getElementsByName(name)[0];
+       var prefix = input0.name;
+       var parent = input0.parentNode;
+       var holder = input0.placeholder;
+
+       var values;
+
+       function cbi_dynlist_redraw(focus, add, del)
+       {
+               values = [ ];
+
+               while (parent.firstChild)
+               {
+                       var n = parent.firstChild;
+                       var i = parseInt(n.index);
+
+                       if (i != del)
+                       {
+                               if (n.nodeName.toLowerCase() == 'input')
+                                       values.push(n.value || '');
+                               else if (n.nodeName.toLowerCase() == 'select')
+                                       values[values.length-1] = n.options[n.selectedIndex].value;
+                       }
+
+                       parent.removeChild(n);
+               }
+
+               if (add >= 0)
+               {
+                       focus = add+1;
+                       values.splice(focus, 0, '');
+               }
+               else if (values.length == 0)
+               {
+                       focus = 0;
+                       values.push('');
+               }
+
+               for (var i = 0; i < values.length; i++)
+               {
+                       var t = document.createElement('input');
+                               t.id = prefix + '.' + (i+1);
+                               t.name = prefix;
+                               t.value = values[i];
+                               t.type = 'text';
+                               t.index = i;
+                               t.className = 'cbi-input-text';
+
+                       if (i == 0 && holder)
+                       {
+                               t.placeholder = holder;
+                       }
+
+                       var b = document.createElement('img');
+                               b.src = respath + ((i+1) < values.length ? '/cbi/remove.gif' : '/cbi/add.gif');
+                               b.className = 'cbi-image-button';
+
+                       parent.appendChild(t);
+                       parent.appendChild(b);
+                       parent.appendChild(document.createElement('br'));
+
+                       if (datatype)
+                       {
+                               cbi_validate_field(t.id, ((i+1) == values.length) || optional, datatype);
+                       }
+
+                       if (choices)
+                       {
+                               cbi_combobox_init(t.id, choices[0], '', choices[1]);
+                               t.nextSibling.index = i;
+
+                               cbi_bind(t.nextSibling, 'keydown',  cbi_dynlist_keydown);
+                               cbi_bind(t.nextSibling, 'keypress', cbi_dynlist_keypress);
+
+                               if (i == focus || -i == focus)
+                                       t.nextSibling.focus();
+                       }
+                       else
+                       {
+                               cbi_bind(t, 'keydown',  cbi_dynlist_keydown);
+                               cbi_bind(t, 'keypress', cbi_dynlist_keypress);
+
+                               if (i == focus)
+                               {
+                                       t.focus();
+                               }
+                               else if (-i == focus)
+                               {
+                                       t.focus();
+
+                                       /* force cursor to end */
+                                       var v = t.value;
+                                       t.value = ' '
+                                       t.value = v;
+                               }
+                       }
+
+                       cbi_bind(b, 'click', cbi_dynlist_btnclick);
+               }
+       }
+
+       function cbi_dynlist_keypress(ev)
+       {
+               ev = ev ? ev : window.event;
+
+               var se = ev.target ? ev.target : ev.srcElement;
+
+               if (se.nodeType == 3)
+                       se = se.parentNode;
+
+               switch (ev.keyCode)
+               {
+                       /* backspace, delete */
+                       case 8:
+                       case 46:
+                               if (se.value.length == 0)
+                               {
+                                       if (ev.preventDefault)
+                                               ev.preventDefault();
+
+                                       return false;
+                               }
+
+                               return true;
+
+                       /* enter, arrow up, arrow down */
+                       case 13:
+                       case 38:
+                       case 40:
+                               if (ev.preventDefault)
+                                       ev.preventDefault();
+
+                               return false;
+               }
+
+               return true;
+       }
+
+       function cbi_dynlist_keydown(ev)
+       {
+               ev = ev ? ev : window.event;
+
+               var se = ev.target ? ev.target : ev.srcElement;
+
+               if (se.nodeType == 3)
+                       se = se.parentNode;
+
+               var prev = se.previousSibling;
+               while (prev && prev.name != name)
+                       prev = prev.previousSibling;
+
+               var next = se.nextSibling;
+               while (next && next.name != name)
+                       next = next.nextSibling;
+
+               /* advance one further in combobox case */
+               if (next && next.nextSibling.name == name)
+                       next = next.nextSibling;
+
+               switch (ev.keyCode)
+               {
+                       /* backspace, delete */
+                       case 8:
+                       case 46:
+                               var del = (se.nodeName.toLowerCase() == 'select')
+                                       ? true : (se.value.length == 0);
+
+                               if (del)
+                               {
+                                       if (ev.preventDefault)
+                                               ev.preventDefault();
+
+                                       var focus = se.index;
+                                       if (ev.keyCode == 8)
+                                               focus = -focus+1;
+
+                                       cbi_dynlist_redraw(focus, -1, se.index);
+
+                                       return false;
+                               }
+
+                               break;
+
+                       /* enter */
+                       case 13:
+                               cbi_dynlist_redraw(-1, se.index, -1);
+                               break;
+
+                       /* arrow up */
+                       case 38:
+                               if (prev)
+                                       prev.focus();
+
+                               break;
+
+                       /* arrow down */
+                       case 40:
+                               if (next)
+                                       next.focus();
+
+                               break;
+               }
+
+               return true;
+       }
+
+       function cbi_dynlist_btnclick(ev)
+       {
+               ev = ev ? ev : window.event;
+
+               var se = ev.target ? ev.target : ev.srcElement;
+
+               if (se.src.indexOf('remove') > -1)
+               {
+                       se.previousSibling.value = '';
+
+                       cbi_dynlist_keydown({
+                               target:  se.previousSibling,
+                               keyCode: 8
+                       });
+               }
+               else
+               {
+                       cbi_dynlist_keydown({
+                               target:  se.previousSibling,
+                               keyCode: 13
+                       });
+               }
+
+               return false;
+       }
+
+       cbi_dynlist_redraw(NaN, -1, -1);
+}
+
+//Hijacks the CBI form to send via XHR (requires Prototype)
+function cbi_hijack_forms(layer, win, fail, load) {
+       var forms = layer.getElementsByTagName('form');
+       for (var i=0; i<forms.length; i++) {
+               $(forms[i]).observe('submit', function(event) {
+                       // Prevent the form from also submitting the regular way
+                       event.stop();
+
+                       // Submit via XHR
+                       event.element().request({
+                               onSuccess: win,
+                               onFailure: fail
+                       });
+
+                       if (load) {
+                               load();
+                       }
+               });
+       }
+}
+
+
+function cbi_t_add(section, tab) {
+       var t = document.getElementById('tab.' + section + '.' + tab);
+       var c = document.getElementById('container.' + section + '.' + tab);
+
+       if( t && c ) {
+               cbi_t[section] = (cbi_t[section] || [ ]);
+               cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
+       }
+}
+
+function cbi_t_switch(section, tab) {
+       if( cbi_t[section] && cbi_t[section][tab] ) {
+               var o = cbi_t[section][tab];
+               var h = document.getElementById('tab.' + section);
+               for( var tid in cbi_t[section] ) {
+                       var o2 = cbi_t[section][tid];
+                       if( o.tab.id != o2.tab.id ) {
+                               o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
+                               o2.container.style.display = 'none';
+                       }
+                       else {
+                               if(h) h.value = tab;
+                               o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
+                               o2.container.style.display = 'block';
+                       }
+               }
+       }
+       return false
+}
+
+function cbi_t_update() {
+       var hl_tabs = [ ];
+       var updated = false;
+
+       for( var sid in cbi_t )
+               for( var tid in cbi_t[sid] )
+               {
+                       if( cbi_c[cbi_t[sid][tid].cid] == 0 ) {
+                               cbi_t[sid][tid].tab.style.display = 'none';
+                       }
+                       else if( cbi_t[sid][tid].tab && cbi_t[sid][tid].tab.style.display == 'none' ) {
+                               cbi_t[sid][tid].tab.style.display = '';
+
+                               var t = cbi_t[sid][tid].tab;
+                               t.className += ' cbi-tab-highlighted';
+                               hl_tabs.push(t);
+                       }
+
+                       cbi_tag_last(cbi_t[sid][tid].container);
+                       updated = true;
+               }
+
+       if( hl_tabs.length > 0 )
+               window.setTimeout(function() {
+                       for( var i = 0; i < hl_tabs.length; i++ )
+                               hl_tabs[i].className = hl_tabs[i].className.replace(/ cbi-tab-highlighted/g, '');
+               }, 750);
+
+       return updated;
+}
+
+
+function cbi_validate_form(form, errmsg)
+{
+       /* if triggered by a section removal or addition, don't validate */
+       if( form.cbi_state == 'add-section' || form.cbi_state == 'del-section' )
+               return true;
+
+       if( form.cbi_validators )
+       {
+               for( var i = 0; i < form.cbi_validators.length; i++ )
+               {
+                       var validator = form.cbi_validators[i];
+                       if( !validator() && errmsg )
+                       {
+                               alert(errmsg);
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+function cbi_validate_reset(form)
+{
+       window.setTimeout(
+               function() { cbi_validate_form(form, null) }, 100
+       );
+
+       return true;
+}
+
+function cbi_validate_compile(code)
+{
+       var pos = 0;
+       var esc = false;
+       var depth = 0;
+       var stack = [ ];
+
+       code += ',';
+
+       for (var i = 0; i < code.length; i++)
+       {
+               if (esc)
+               {
+                       esc = false;
+                       continue;
+               }
+
+               switch (code.charCodeAt(i))
+               {
+               case 92:
+                       esc = true;
+                       break;
+
+               case 40:
+               case 44:
+                       if (depth <= 0)
+                       {
+                               if (pos < i)
+                               {
+                                       var label = code.substring(pos, i);
+                                               label = label.replace(/\\(.)/g, '$1');
+                                               label = label.replace(/^[ \t]+/g, '');
+                                               label = label.replace(/[ \t]+$/g, '');
+
+                                       if (label && !isNaN(label))
+                                       {
+                                               stack.push(parseFloat(label));
+                                       }
+                                       else if (label.match(/^(['"]).*\1$/))
+                                       {
+                                               stack.push(label.replace(/^(['"])(.*)\1$/, '$2'));
+                                       }
+                                       else if (typeof cbi_validators[label] == 'function')
+                                       {
+                                               stack.push(cbi_validators[label]);
+                                               stack.push(null);
+                                       }
+                                       else
+                                       {
+                                               throw "Syntax error, unhandled token '"+label+"'";
+                                       }
+                               }
+                               pos = i+1;
+                       }
+                       depth += (code.charCodeAt(i) == 40);
+                       break;
+
+               case 41:
+                       if (--depth <= 0)
+                       {
+                               if (typeof stack[stack.length-2] != 'function')
+                                       throw "Syntax error, argument list follows non-function";
+
+                               stack[stack.length-1] =
+                                       arguments.callee(code.substring(pos, i));
+
+                               pos = i+1;
+                       }
+                       break;
+               }
+       }
+
+       return stack;
+}
+
+function cbi_validate_field(cbid, optional, type)
+{
+       var field = (typeof cbid == "string") ? document.getElementById(cbid) : cbid;
+       var vstack; try { vstack = cbi_validate_compile(type); } catch(e) { };
+
+       if (field && vstack && typeof vstack[0] == "function")
+       {
+               var validator = function()
+               {
+                       // is not detached
+                       if( field.form )
+                       {
+                               field.className = field.className.replace(/ cbi-input-invalid/g, '');
+
+                               // validate value
+                               var value = (field.options && field.options.selectedIndex > -1)
+                                       ? field.options[field.options.selectedIndex].value : field.value;
+
+                               if (!(((value.length == 0) && optional) || vstack[0].apply(value, vstack[1])))
+                               {
+                                       // invalid
+                                       field.className += ' cbi-input-invalid';
+                                       return false;
+                               }
+                       }
+
+                       return true;
+               };
+
+               if( ! field.form.cbi_validators )
+                       field.form.cbi_validators = [ ];
+
+               field.form.cbi_validators.push(validator);
+
+               cbi_bind(field, "blur",  validator);
+               cbi_bind(field, "keyup", validator);
+
+               if (field.nodeName == 'SELECT')
+               {
+                       cbi_bind(field, "change", validator);
+                       cbi_bind(field, "click",  validator);
+               }
+
+               field.setAttribute("cbi_validate", validator);
+               field.setAttribute("cbi_datatype", type);
+               field.setAttribute("cbi_optional", (!!optional).toString());
+
+               validator();
+
+               var fcbox = document.getElementById('cbi.combobox.' + field.id);
+               if (fcbox)
+                       cbi_validate_field(fcbox, optional, type);
+       }
+}
+
+function cbi_row_swap(elem, up, store)
+{
+       var tr = elem.parentNode;
+       while (tr && tr.nodeName.toLowerCase() != 'tr')
+               tr = tr.parentNode;
+
+       if (!tr)
+               return false;
+
+       var table = tr.parentNode;
+       while (table && table.nodeName.toLowerCase() != 'table')
+               table = table.parentNode;
+
+       if (!table)
+               return false;
+
+       var s = up ? 3 : 2;
+       var e = up ? table.rows.length : table.rows.length - 1;
+
+       for (var idx = s; idx < e; idx++)
+       {
+               if (table.rows[idx] == tr)
+               {
+                       if (up)
+                               tr.parentNode.insertBefore(table.rows[idx], table.rows[idx-1]);
+                       else
+                               tr.parentNode.insertBefore(table.rows[idx+1], table.rows[idx]);
+
+                       break;
+               }
+       }
+
+       var ids = [ ];
+       for (idx = 2; idx < table.rows.length; idx++)
+       {
+               table.rows[idx].className = table.rows[idx].className.replace(
+                       /cbi-rowstyle-[12]/, 'cbi-rowstyle-' + (1 + (idx % 2))
+               );
+
+               if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/) )
+                       ids.push(RegExp.$1);
+       }
+
+       var input = document.getElementById(store);
+       if (input)
+               input.value = ids.join(' ');
+
+       return false;
+}
+
+function cbi_tag_last(container)
+{
+       var last;
+
+       for (var i = 0; i < container.childNodes.length; i++)
+       {
+               var c = container.childNodes[i];
+               if (c.nodeType == 1 && c.nodeName.toLowerCase() == 'div')
+               {
+                       c.className = c.className.replace(/ cbi-value-last$/, '');
+                       last = c;
+               }
+       }
+
+       if (last)
+       {
+               last.className += ' cbi-value-last';
+       }
+}
+
+String.prototype.serialize = function()
+{
+       var o = this;
+       switch(typeof(o))
+       {
+               case 'object':
+                       // null
+                       if( o == null )
+                       {
+                               return 'null';
+                       }
+
+                       // array
+                       else if( o.length )
+                       {
+                               var i, s = '';
+
+                               for( var i = 0; i < o.length; i++ )
+                                       s += (s ? ', ' : '') + String.serialize(o[i]);
+
+                               return '[ ' + s + ' ]';
+                       }
+
+                       // object
+                       else
+                       {
+                               var k, s = '';
+
+                               for( k in o )
+                                       s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
+
+                               return '{ ' + s + ' }';
+                       }
+
+                       break;
+
+               case 'string':
+                       // complex string
+                       if( o.match(/[^a-zA-Z0-9_,.: -]/) )
+                               return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
+
+                       // simple string
+                       else
+                               return '"' + o + '"';
+
+                       break;
+
+               default:
+                       return o.toString();
+       }
+}
+
+String.prototype.format = function()
+{
+       if (!RegExp)
+               return;
+
+       var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
+       var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
+
+       function esc(s, r) {
+               for( var i = 0; i < r.length; i += 2 )
+                       s = s.replace(r[i], r[i+1]);
+               return s;
+       }
+
+       var str = this;
+       var out = '';
+       var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
+       var a = b = [], numSubstitutions = 0, numMatches = 0;
+
+       while( a = re.exec(str) )
+       {
+               var m = a[1];
+               var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
+               var pPrecision = a[6], pType = a[7];
+
+               numMatches++;
+
+               if (pType == '%')
+               {
+                       subst = '%';
+               }
+               else
+               {
+                       if (numSubstitutions < arguments.length)
+                       {
+                               var param = arguments[numSubstitutions++];
+
+                               var pad = '';
+                               if (pPad && pPad.substr(0,1) == "'")
+                                       pad = leftpart.substr(1,1);
+                               else if (pPad)
+                                       pad = pPad;
+
+                               var justifyRight = true;
+                               if (pJustify && pJustify === "-")
+                                       justifyRight = false;
+
+                               var minLength = -1;
+                               if (pMinLength)
+                                       minLength = parseInt(pMinLength);
+
+                               var precision = -1;
+                               if (pPrecision && pType == 'f')
+                                       precision = parseInt(pPrecision.substring(1));
+
+                               var subst = param;
+
+                               switch(pType)
+                               {
+                                       case 'b':
+                                               subst = (parseInt(param) || 0).toString(2);
+                                               break;
+
+                                       case 'c':
+                                               subst = String.fromCharCode(parseInt(param) || 0);
+                                               break;
+
+                                       case 'd':
+                                               subst = (parseInt(param) || 0);
+                                               break;
+
+                                       case 'u':
+                                               subst = Math.abs(parseInt(param) || 0);
+                                               break;
+
+                                       case 'f':
+                                               subst = (precision > -1)
+                                                       ? ((parseFloat(param) || 0.0)).toFixed(precision)
+                                                       : (parseFloat(param) || 0.0);
+                                               break;
+
+                                       case 'o':
+                                               subst = (parseInt(param) || 0).toString(8);
+                                               break;
+
+                                       case 's':
+                                               subst = param;
+                                               break;
+
+                                       case 'x':
+                                               subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
+                                               break;
+
+                                       case 'X':
+                                               subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
+                                               break;
+
+                                       case 'h':
+                                               subst = esc(param, html_esc);
+                                               break;
+
+                                       case 'q':
+                                               subst = esc(param, quot_esc);
+                                               break;
+
+                                       case 'j':
+                                               subst = String.serialize(param);
+                                               break;
+
+                                       case 't':
+                                               var td = 0;
+                                               var th = 0;
+                                               var tm = 0;
+                                               var ts = (param || 0);
+
+                                               if (ts > 60) {
+                                                       tm = Math.floor(ts / 60);
+                                                       ts = (ts % 60);
+                                               }
+
+                                               if (tm > 60) {
+                                                       th = Math.floor(tm / 60);
+                                                       tm = (tm % 60);
+                                               }
+
+                                               if (th > 24) {
+                                                       td = Math.floor(th / 24);
+                                                       th = (th % 24);
+                                               }
+
+                                               subst = (td > 0)
+                                                       ? String.format('%dd %dh %dm %ds', td, th, tm, ts)
+                                                       : String.format('%dh %dm %ds', th, tm, ts);
+
+                                               break;
+
+                                       case 'm':
+                                               var mf = pMinLength ? parseInt(pMinLength) : 1000;
+                                               var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
+
+                                               var i = 0;
+                                               var val = parseFloat(param || 0);
+                                               var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
+
+                                               for (i = 0; (i < units.length) && (val > mf); i++)
+                                                       val /= mf;
+
+                                               subst = val.toFixed(pr) + ' ' + units[i];
+                                               break;
+                               }
+                       }
+               }
+
+               out += leftpart + subst;
+               str = str.substr(m.length);
+       }
+
+       return out + str;
+}
+
+String.prototype.nobr = function()
+{
+       return this.replace(/[\s\n]+/g, '&#160;');
+}
+
+String.serialize = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.serialize.apply(arguments[0], a);
+}
+
+String.format = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.format.apply(arguments[0], a);
+}
+
+String.nobr = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.nobr.apply(arguments[0], a);
+}
diff --git a/modules/base/htdocs/luci-static/resources/cbi/add.gif b/modules/base/htdocs/luci-static/resources/cbi/add.gif
new file mode 100644 (file)
index 0000000..0888abf
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/add.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/apply.gif b/modules/base/htdocs/luci-static/resources/cbi/apply.gif
new file mode 100644 (file)
index 0000000..82ae7ed
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/apply.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/arrow.gif b/modules/base/htdocs/luci-static/resources/cbi/arrow.gif
new file mode 100644 (file)
index 0000000..10d797e
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/arrow.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/down.gif b/modules/base/htdocs/luci-static/resources/cbi/down.gif
new file mode 100644 (file)
index 0000000..f0bb6a4
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/down.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/download.gif b/modules/base/htdocs/luci-static/resources/cbi/download.gif
new file mode 100644 (file)
index 0000000..f99a538
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/download.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/edit.gif b/modules/base/htdocs/luci-static/resources/cbi/edit.gif
new file mode 100644 (file)
index 0000000..7b02b6e
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/edit.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/fieldadd.gif b/modules/base/htdocs/luci-static/resources/cbi/fieldadd.gif
new file mode 100644 (file)
index 0000000..431ff64
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/fieldadd.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/file.gif b/modules/base/htdocs/luci-static/resources/cbi/file.gif
new file mode 100644 (file)
index 0000000..3b1217d
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/file.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/find.gif b/modules/base/htdocs/luci-static/resources/cbi/find.gif
new file mode 100644 (file)
index 0000000..9ae5e34
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/find.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/folder.gif b/modules/base/htdocs/luci-static/resources/cbi/folder.gif
new file mode 100644 (file)
index 0000000..22b583b
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/folder.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/help.gif b/modules/base/htdocs/luci-static/resources/cbi/help.gif
new file mode 100644 (file)
index 0000000..9dfa0e1
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/help.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/key.gif b/modules/base/htdocs/luci-static/resources/cbi/key.gif
new file mode 100644 (file)
index 0000000..e3853e5
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/key.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/link.gif b/modules/base/htdocs/luci-static/resources/cbi/link.gif
new file mode 100644 (file)
index 0000000..f0bb78d
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/link.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/reload.gif b/modules/base/htdocs/luci-static/resources/cbi/reload.gif
new file mode 100644 (file)
index 0000000..8268958
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/reload.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/remove.gif b/modules/base/htdocs/luci-static/resources/cbi/remove.gif
new file mode 100644 (file)
index 0000000..bf43a0a
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/remove.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/reset.gif b/modules/base/htdocs/luci-static/resources/cbi/reset.gif
new file mode 100644 (file)
index 0000000..c941c19
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/reset.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/save.gif b/modules/base/htdocs/luci-static/resources/cbi/save.gif
new file mode 100644 (file)
index 0000000..35e9499
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/save.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/up.gif b/modules/base/htdocs/luci-static/resources/cbi/up.gif
new file mode 100644 (file)
index 0000000..e823417
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/up.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/cbi/user.gif b/modules/base/htdocs/luci-static/resources/cbi/user.gif
new file mode 100644 (file)
index 0000000..dcb5c2a
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/cbi/user.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/bridge.png b/modules/base/htdocs/luci-static/resources/icons/bridge.png
new file mode 100644 (file)
index 0000000..4c163bf
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/bridge.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/bridge_disabled.png b/modules/base/htdocs/luci-static/resources/icons/bridge_disabled.png
new file mode 100644 (file)
index 0000000..0f367c5
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/bridge_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/encryption.png b/modules/base/htdocs/luci-static/resources/icons/encryption.png
new file mode 100644 (file)
index 0000000..41d2ba9
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/encryption.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/encryption_disabled.png b/modules/base/htdocs/luci-static/resources/icons/encryption_disabled.png
new file mode 100644 (file)
index 0000000..f2e05a4
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/encryption_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/ethernet.png b/modules/base/htdocs/luci-static/resources/icons/ethernet.png
new file mode 100644 (file)
index 0000000..a025381
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/ethernet.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/ethernet_disabled.png b/modules/base/htdocs/luci-static/resources/icons/ethernet_disabled.png
new file mode 100644 (file)
index 0000000..2bb02e4
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/ethernet_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/loading.gif b/modules/base/htdocs/luci-static/resources/icons/loading.gif
new file mode 100644 (file)
index 0000000..5bb90fd
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/loading.gif differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/port_down.png b/modules/base/htdocs/luci-static/resources/icons/port_down.png
new file mode 100644 (file)
index 0000000..25ea172
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/port_down.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/port_up.png b/modules/base/htdocs/luci-static/resources/icons/port_up.png
new file mode 100644 (file)
index 0000000..e063037
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/port_up.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-0-25.png b/modules/base/htdocs/luci-static/resources/icons/signal-0-25.png
new file mode 100644 (file)
index 0000000..2e5dff4
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-0-25.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-0.png b/modules/base/htdocs/luci-static/resources/icons/signal-0.png
new file mode 100644 (file)
index 0000000..114583a
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-0.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-25-50.png b/modules/base/htdocs/luci-static/resources/icons/signal-25-50.png
new file mode 100644 (file)
index 0000000..ee8cc4f
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-25-50.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-50-75.png b/modules/base/htdocs/luci-static/resources/icons/signal-50-75.png
new file mode 100644 (file)
index 0000000..26bcbf7
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-50-75.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-75-100.png b/modules/base/htdocs/luci-static/resources/icons/signal-75-100.png
new file mode 100644 (file)
index 0000000..5cffaa1
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-75-100.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/signal-none.png b/modules/base/htdocs/luci-static/resources/icons/signal-none.png
new file mode 100644 (file)
index 0000000..b77585c
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/signal-none.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/switch.png b/modules/base/htdocs/luci-static/resources/icons/switch.png
new file mode 100644 (file)
index 0000000..5c99ba5
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/switch.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/switch_disabled.png b/modules/base/htdocs/luci-static/resources/icons/switch_disabled.png
new file mode 100644 (file)
index 0000000..b8c84c8
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/switch_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/tunnel.png b/modules/base/htdocs/luci-static/resources/icons/tunnel.png
new file mode 100644 (file)
index 0000000..c5a09dd
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/tunnel.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/tunnel_disabled.png b/modules/base/htdocs/luci-static/resources/icons/tunnel_disabled.png
new file mode 100644 (file)
index 0000000..ad9856c
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/tunnel_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/vlan.png b/modules/base/htdocs/luci-static/resources/icons/vlan.png
new file mode 100644 (file)
index 0000000..5c99ba5
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/vlan.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/vlan_disabled.png b/modules/base/htdocs/luci-static/resources/icons/vlan_disabled.png
new file mode 100644 (file)
index 0000000..b8c84c8
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/vlan_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/wifi.png b/modules/base/htdocs/luci-static/resources/icons/wifi.png
new file mode 100644 (file)
index 0000000..528ce2b
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/wifi.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/wifi_big.png b/modules/base/htdocs/luci-static/resources/icons/wifi_big.png
new file mode 100644 (file)
index 0000000..d73a5e7
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/wifi_big.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png b/modules/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png
new file mode 100644 (file)
index 0000000..af93b37
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/icons/wifi_disabled.png b/modules/base/htdocs/luci-static/resources/icons/wifi_disabled.png
new file mode 100644 (file)
index 0000000..338a34f
Binary files /dev/null and b/modules/base/htdocs/luci-static/resources/icons/wifi_disabled.png differ
diff --git a/modules/base/htdocs/luci-static/resources/xhr.js b/modules/base/htdocs/luci-static/resources/xhr.js
new file mode 100644 (file)
index 0000000..701c12a
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * xhr.js - XMLHttpRequest helper class
+ * (c) 2008-2010 Jo-Philipp Wich
+ */
+
+XHR = function()
+{
+       this.reinit = function()
+       {
+               if (window.XMLHttpRequest) {
+                       this._xmlHttp = new XMLHttpRequest();
+               }
+               else if (window.ActiveXObject) {
+                       this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+               }
+               else {
+                       alert("xhr.js: XMLHttpRequest is not supported by this browser!");
+               }
+       }
+
+       this.busy = function() {
+               if (!this._xmlHttp)
+                       return false;
+
+               switch (this._xmlHttp.readyState)
+               {
+                       case 1:
+                       case 2:
+                       case 3:
+                               return true;
+
+                       default:
+                               return false;
+               }
+       }
+
+       this.abort = function() {
+               if (this.busy())
+                       this._xmlHttp.abort();
+       }
+
+       this.get = function(url,data,callback)
+       {
+               this.reinit();
+
+               var xhr  = this._xmlHttp;
+               var code = this._encode(data);
+
+               url = location.protocol + '//' + location.host + url;
+
+               if (code)
+                       if (url.substr(url.length-1,1) == '&')
+                               url += code;
+                       else
+                               url += '?' + code;
+
+               xhr.open('GET', url, true);
+
+               xhr.onreadystatechange = function()
+               {
+                       if (xhr.readyState == 4) {
+                               var json = null;
+                               if (xhr.getResponseHeader("Content-Type") == "application/json") {
+                                       try {
+                                               json = eval('(' + xhr.responseText + ')');
+                                       }
+                                       catch(e) {
+                                               json = null;
+                                       }
+                               }
+
+                               callback(xhr, json);
+                       }
+               }
+
+               xhr.send(null);
+       }
+
+       this.post = function(url,data,callback)
+       {
+               this.reinit();
+
+               var xhr  = this._xmlHttp;
+               var code = this._encode(data);
+
+               xhr.onreadystatechange = function()
+               {
+                       if (xhr.readyState == 4)
+                               callback(xhr);
+               }
+
+               xhr.open('POST', url, true);
+               xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+               xhr.setRequestHeader('Content-length', code.length);
+               xhr.setRequestHeader('Connection', 'close');
+               xhr.send(code);
+       }
+
+       this.cancel = function()
+       {
+               this._xmlHttp.onreadystatechange = function(){};
+               this._xmlHttp.abort();
+       }
+
+       this.send_form = function(form,callback,extra_values)
+       {
+               var code = '';
+
+               for (var i = 0; i < form.elements.length; i++)
+               {
+                       var e = form.elements[i];
+
+                       if (e.options)
+                       {
+                               code += (code ? '&' : '') +
+                                       form.elements[i].name + '=' + encodeURIComponent(
+                                               e.options[e.selectedIndex].value
+                                       );
+                       }
+                       else if (e.length)
+                       {
+                               for (var j = 0; j < e.length; j++)
+                                       if (e[j].name) {
+                                               code += (code ? '&' : '') +
+                                                       e[j].name + '=' + encodeURIComponent(e[j].value);
+                                       }
+                       }
+                       else
+                       {
+                               code += (code ? '&' : '') +
+                                       e.name + '=' + encodeURIComponent(e.value);
+                       }
+               }
+
+               if (typeof extra_values == 'object')
+                       for (var key in extra_values)
+                               code += (code ? '&' : '') +
+                                       key + '=' + encodeURIComponent(extra_values[key]);
+
+               return(
+                       (form.method == 'get')
+                               ? this.get(form.getAttribute('action'), code, callback)
+                               : this.post(form.getAttribute('action'), code, callback)
+               );
+       }
+
+       this._encode = function(obj)
+       {
+               obj = obj ? obj : { };
+               obj['_'] = Math.random();
+
+               if (typeof obj == 'object')
+               {
+                       var code = '';
+                       var self = this;
+
+                       for (var k in obj)
+                               code += (code ? '&' : '') +
+                                       k + '=' + encodeURIComponent(obj[k]);
+
+                       return code;
+               }
+
+               return obj;
+       }
+}
+
+XHR.get = function(url, data, callback)
+{
+       (new XHR()).get(url, data, callback);
+}
+
+XHR.poll = function(interval, url, data, callback)
+{
+       if (isNaN(interval) || interval < 1)
+               interval = 5;
+
+       if (!XHR._q)
+       {
+               XHR._t = 0;
+               XHR._q = [ ];
+               XHR._r = function() {
+                       for (var i = 0, e = XHR._q[0]; i < XHR._q.length; e = XHR._q[++i])
+                       {
+                               if (!(XHR._t % e.interval) && !e.xhr.busy())
+                                       e.xhr.get(e.url, e.data, e.callback);
+                       }
+
+                       XHR._t++;
+               };
+       }
+
+       XHR._q.push({
+               interval: interval,
+               callback: callback,
+               url:      url,
+               data:     data,
+               xhr:      new XHR()
+       });
+
+       XHR.run();
+}
+
+XHR.halt = function()
+{
+       if (XHR._i)
+       {
+               /* show & set poll indicator */
+               try {
+                       document.getElementById('xhr_poll_status').style.display = '';
+                       document.getElementById('xhr_poll_status_on').style.display = 'none';
+                       document.getElementById('xhr_poll_status_off').style.display = '';
+               } catch(e) { }
+
+               window.clearInterval(XHR._i);
+               XHR._i = null;
+       }
+}
+
+XHR.run = function()
+{
+       if (XHR._r && !XHR._i)
+       {
+               /* show & set poll indicator */
+               try {
+                       document.getElementById('xhr_poll_status').style.display = '';
+                       document.getElementById('xhr_poll_status_on').style.display = '';
+                       document.getElementById('xhr_poll_status_off').style.display = 'none';
+               } catch(e) { }
+
+               /* kick first round manually to prevent one second lag when setting up
+                * the poll interval */
+               XHR._r();
+               XHR._i = window.setInterval(XHR._r, 1000);
+       }
+}
+
+XHR.running = function()
+{
+       return !!(XHR._r && XHR._i);
+}
diff --git a/modules/base/luasrc/ccache.lua b/modules/base/luasrc/ccache.lua
new file mode 100644 (file)
index 0000000..56ccbc3
--- /dev/null
@@ -0,0 +1,87 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+]]--
+
+local io = require "io"
+local fs = require "nixio.fs"
+local util = require "luci.util"
+local nixio = require "nixio"
+local debug = require "debug"
+local string = require "string"
+local package = require "package"
+
+local type, loadfile = type, loadfile
+
+
+module "luci.ccache"
+
+function cache_ondemand(...)
+       if debug.getinfo(1, 'S').source ~= "=?" then
+               cache_enable(...)
+       end
+end
+
+function cache_enable(cachepath, mode)
+       cachepath = cachepath or "/tmp/luci-modulecache"
+       mode = mode or "r--r--r--"
+
+       local loader = package.loaders[2]
+       local uid    = nixio.getuid()
+
+       if not fs.stat(cachepath) then
+               fs.mkdir(cachepath)
+       end
+
+       local function _encode_filename(name)
+               local encoded = ""
+               for i=1, #name do
+                       encoded = encoded .. ("%2X" % string.byte(name, i))
+               end
+               return encoded
+       end
+
+       local function _load_sane(file)
+               local stat = fs.stat(file)
+               if stat and stat.uid == uid and stat.modestr == mode then
+                       return loadfile(file)
+               end
+       end
+
+       local function _write_sane(file, func)
+               if nixio.getuid() == uid then
+                       local fp = io.open(file, "w")
+                       if fp then
+                               fp:write(util.get_bytecode(func))
+                               fp:close()
+                               fs.chmod(file, mode)
+                       end
+               end
+       end
+
+       package.loaders[2] = function(mod)
+               local encoded = cachepath .. "/" .. _encode_filename(mod)
+               local modcons = _load_sane(encoded)
+               
+               if modcons then
+                       return modcons
+               end
+
+               -- No cachefile
+               modcons = loader(mod)
+               if type(modcons) == "function" then
+                       _write_sane(encoded, modcons)
+               end
+               return modcons
+       end
+end
diff --git a/modules/base/luasrc/controller/admin/servicectl.lua b/modules/base/luasrc/controller/admin/servicectl.lua
new file mode 100644 (file)
index 0000000..753d2c7
--- /dev/null
@@ -0,0 +1,60 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+module("luci.controller.admin.servicectl", package.seeall)
+
+function index()
+       entry({"servicectl"}, alias("servicectl", "status")).sysauth = "root"
+       entry({"servicectl", "status"}, call("action_status")).leaf = true
+       entry({"servicectl", "restart"}, call("action_restart")).leaf = true
+end
+
+function action_status()
+       local data = nixio.fs.readfile("/var/run/luci-reload-status")
+       if data then
+               luci.http.write("/etc/config/")
+               luci.http.write(data)
+       else
+               luci.http.write("finish")
+       end
+end
+
+function action_restart(args)
+       local uci = require "luci.model.uci".cursor()
+       if args then
+               local service
+               local services = { }
+
+               for service in args:gmatch("[%w_-]+") do
+                       services[#services+1] = service
+               end
+
+               local command = uci:apply(services, true)
+               if nixio.fork() == 0 then
+                       local i = nixio.open("/dev/null", "r")
+                       local o = nixio.open("/dev/null", "w")
+
+                       nixio.dup(i, nixio.stdin)
+                       nixio.dup(o, nixio.stdout)
+
+                       i:close()
+                       o:close()
+
+                       nixio.exec("/bin/sh", unpack(command))
+               else
+                       luci.http.write("OK")
+                       os.exit(0)
+               end
+       end
+end
diff --git a/modules/base/luasrc/debug.lua b/modules/base/luasrc/debug.lua
new file mode 100644 (file)
index 0000000..8ff1bb6
--- /dev/null
@@ -0,0 +1,37 @@
+local debug = require "debug"
+local io = require "io"
+local collectgarbage, floor = collectgarbage, math.floor
+
+module "luci.debug"
+__file__ = debug.getinfo(1, 'S').source:sub(2)
+
+-- Enables the memory tracer with given flags and returns a function to disable the tracer again
+function trap_memtrace(flags, dest)
+       flags = flags or "clr"
+       local tracefile = io.open(dest or "/tmp/memtrace", "w")
+       local peak = 0
+
+       local function trap(what, line)
+               local info = debug.getinfo(2, "Sn")
+               local size = floor(collectgarbage("count"))
+               if size > peak then
+                       peak = size
+               end
+               if tracefile then
+                       tracefile:write(
+                               "[", what, "] ", info.source, ":", (line or "?"), "\t",
+                               (info.namewhat or ""), "\t",
+                               (info.name or ""), "\t",
+                               size, " (", peak, ")\n"
+                       )
+               end
+       end
+
+       debug.sethook(trap, flags)
+
+       return function()
+               debug.sethook()
+               tracefile:close()
+       end
+end
+
diff --git a/modules/base/luasrc/fs.lua b/modules/base/luasrc/fs.lua
new file mode 100644 (file)
index 0000000..a81ff67
--- /dev/null
@@ -0,0 +1,244 @@
+--[[
+LuCI - Filesystem tools
+
+Description:
+A module offering often needed filesystem manipulation functions
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local io    = require "io"
+local os    = require "os"
+local ltn12 = require "luci.ltn12"
+local fs       = require "nixio.fs"
+local nutil = require "nixio.util"
+
+local type  = type
+
+--- LuCI filesystem library.
+module "luci.fs"
+
+--- Test for file access permission on given path.
+-- @class              function
+-- @name               access
+-- @param str  String value containing the path
+-- @return             Number containing the return code, 0 on sucess or nil on error
+-- @return             String containing the error description (if any)
+-- @return             Number containing the os specific errno (if any)
+access = fs.access
+
+--- Evaluate given shell glob pattern and return a table containing all matching
+-- file and directory entries.
+-- @class                      function
+-- @name                       glob
+-- @param filename     String containing the path of the file to read
+-- @return                     Table containing file and directory entries or nil if no matches
+-- @return                     String containing the error description (if no matches)
+-- @return                     Number containing the os specific errno (if no matches)
+function glob(...)
+       local iter, code, msg = fs.glob(...)
+       if iter then
+               return nutil.consume(iter)
+       else
+               return nil, code, msg
+       end
+end
+
+--- Checks wheather the given path exists and points to a regular file.
+-- @param filename     String containing the path of the file to test
+-- @return                     Boolean indicating wheather given path points to regular file
+function isfile(filename)
+       return fs.stat(filename, "type") == "reg"
+end
+
+--- Checks wheather the given path exists and points to a directory.
+-- @param dirname      String containing the path of the directory to test
+-- @return                     Boolean indicating wheather given path points to directory
+function isdirectory(dirname)
+       return fs.stat(dirname, "type") == "dir"
+end
+
+--- Read the whole content of the given file into memory.
+-- @param filename     String containing the path of the file to read
+-- @return                     String containing the file contents or nil on error
+-- @return                     String containing the error message on error
+readfile = fs.readfile
+
+--- Write the contents of given string to given file.
+-- @param filename     String containing the path of the file to read
+-- @param data         String containing the data to write
+-- @return                     Boolean containing true on success or nil on error
+-- @return                     String containing the error message on error
+writefile = fs.writefile
+
+--- Copies a file.
+-- @param source       Source file
+-- @param dest         Destination
+-- @return                     Boolean containing true on success or nil on error
+copy = fs.datacopy
+
+--- Renames a file.
+-- @param source       Source file
+-- @param dest         Destination
+-- @return                     Boolean containing true on success or nil on error
+rename = fs.move
+
+--- Get the last modification time of given file path in Unix epoch format.
+-- @param path String containing the path of the file or directory to read
+-- @return             Number containing the epoch time or nil on error
+-- @return             String containing the error description (if any)
+-- @return             Number containing the os specific errno (if any)
+function mtime(path)
+       return fs.stat(path, "mtime")
+end
+
+--- Set the last modification time  of given file path in Unix epoch format.
+-- @param path String containing the path of the file or directory to read
+-- @param mtime        Last modification timestamp
+-- @param atime Last accessed timestamp
+-- @return             0 in case of success nil on error
+-- @return             String containing the error description (if any)
+-- @return             Number containing the os specific errno (if any)
+function utime(path, mtime, atime)
+       return fs.utimes(path, atime, mtime)
+end
+
+--- Return the last element - usually the filename - from the given path with
+-- the directory component stripped.
+-- @class              function
+-- @name               basename
+-- @param path String containing the path to strip
+-- @return             String containing the base name of given path
+-- @see                        dirname
+basename = fs.basename
+
+--- Return the directory component of the given path with the last element
+-- stripped of.
+-- @class              function
+-- @name               dirname
+-- @param path String containing the path to strip
+-- @return             String containing the directory component of given path
+-- @see                        basename
+dirname = fs.dirname
+
+--- Return a table containing all entries of the specified directory.
+-- @class              function
+-- @name               dir
+-- @param path String containing the path of the directory to scan
+-- @return             Table containing file and directory entries or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+function dir(...)
+       local iter, code, msg = fs.dir(...)
+       if iter then
+               local t = nutil.consume(iter)
+               t[#t+1] = "."
+               t[#t+1] = ".."
+               return t
+       else
+               return nil, code, msg
+       end
+end
+
+--- Create a new directory, recursively on demand.
+-- @param path         String with the name or path of the directory to create
+-- @param recursive    Create multiple directory levels (optional, default is true)
+-- @return                     Number with the return code, 0 on sucess or nil on error
+-- @return                     String containing the error description on error
+-- @return                     Number containing the os specific errno on error
+function mkdir(path, recursive)
+       return recursive and fs.mkdirr(path) or fs.mkdir(path)
+end
+
+--- Remove the given empty directory.
+-- @class              function
+-- @name               rmdir
+-- @param path String containing the path of the directory to remove
+-- @return             Number with the return code, 0 on sucess or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+rmdir = fs.rmdir
+
+local stat_tr = {
+       reg = "regular",
+       dir = "directory",
+       lnk = "link",
+       chr = "character device",
+       blk = "block device",
+       fifo = "fifo",
+       sock = "socket"
+}
+--- Get information about given file or directory.
+-- @class              function
+-- @name               stat
+-- @param path String containing the path of the directory to query
+-- @return             Table containing file or directory properties or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+function stat(path, key)
+       local data, code, msg = fs.stat(path)
+       if data then
+               data.mode = data.modestr
+               data.type = stat_tr[data.type] or "?"
+       end
+       return key and data and data[key] or data, code, msg
+end
+
+--- Set permissions on given file or directory.
+-- @class              function
+-- @name               chmod
+-- @param path String containing the path of the directory
+-- @param perm String containing the permissions to set ([ugoa][+-][rwx])
+-- @return             Number with the return code, 0 on sucess or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+chmod = fs.chmod
+
+--- Create a hard- or symlink from given file (or directory) to specified target
+-- file (or directory) path.
+-- @class                      function
+-- @name                       link
+-- @param path1                String containing the source path to link
+-- @param path2                String containing the destination path for the link
+-- @param symlink      Boolean indicating wheather to create a symlink (optional)
+-- @return                     Number with the return code, 0 on sucess or nil on error
+-- @return                     String containing the error description on error
+-- @return                     Number containing the os specific errno on error
+function link(src, dest, sym)
+       return sym and fs.symlink(src, dest) or fs.link(src, dest)
+end
+
+--- Remove the given file.
+-- @class              function
+-- @name               unlink
+-- @param path String containing the path of the file to remove
+-- @return             Number with the return code, 0 on sucess or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+unlink = fs.unlink
+
+--- Retrieve target of given symlink.
+-- @class              function
+-- @name               readlink
+-- @param path String containing the path of the symlink to read
+-- @return             String containing the link target or nil on error
+-- @return             String containing the error description on error
+-- @return             Number containing the os specific errno on error
+readlink = fs.readlink
diff --git a/modules/base/luasrc/init.lua b/modules/base/luasrc/init.lua
new file mode 100644 (file)
index 0000000..4d66e86
--- /dev/null
@@ -0,0 +1,39 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Description:
+Main class
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local require = require
+
+-- Make sure that bitlib is loaded
+if not _G.bit then
+       _G.bit = require "bit"
+end
+
+module "luci"
+
+local v = require "luci.version"
+
+__version__ = v.luciversion or "trunk"
+__appname__ = v.luciname    or "LuCI"
diff --git a/modules/base/luasrc/ip.lua b/modules/base/luasrc/ip.lua
new file mode 100644 (file)
index 0000000..60ca090
--- /dev/null
@@ -0,0 +1,673 @@
+--[[
+
+LuCI ip calculation libarary
+(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+(c) 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+]]--
+
+--- LuCI IP calculation library.
+module( "luci.ip", package.seeall )
+
+require "nixio"
+local bit  = nixio.bit
+local util = require "luci.util"
+
+--- Boolean; true if system is little endian
+LITTLE_ENDIAN = not util.bigendian()
+
+--- Boolean; true if system is big endian
+BIG_ENDIAN    = not LITTLE_ENDIAN
+
+--- Specifier for IPv4 address family
+FAMILY_INET4  = 0x04
+
+--- Specifier for IPv6 address family
+FAMILY_INET6  = 0x06
+
+
+local function __bless(x)
+       return setmetatable( x, {
+               __index = luci.ip.cidr,
+               __add   = luci.ip.cidr.add,
+               __sub   = luci.ip.cidr.sub,
+               __lt    = luci.ip.cidr.lower,
+               __eq    = luci.ip.cidr.equal,
+               __le    =
+                       function(...)
+                               return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...)
+                       end
+       } )
+end
+
+local function __array16( x, family )
+       local list
+
+       if type(x) == "number" then
+               list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
+
+       elseif type(x) == "string" then
+               if x:find(":") then x = IPv6(x) else x = IPv4(x) end
+               if x then
+                       assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
+                       list = { unpack(x[2]) }
+               end
+
+       elseif type(x) == "table" and type(x[2]) == "table" then
+               assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
+               list = { unpack(x[2]) }
+
+       elseif type(x) == "table" then
+               list = { unpack(x) }
+       end
+
+       assert( list, "Invalid operand" )
+
+       return list
+end
+
+local function __mask16(bits)
+       return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
+end
+
+local function __not16(bits)
+       return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
+end
+
+local function __maxlen(family)
+       return ( family == FAMILY_INET4 ) and 32 or 128
+end
+
+local function __sublen(family)
+       return ( family == FAMILY_INET4 ) and 30 or 127
+end
+
+
+--- Convert given short value to network byte order on little endian hosts
+-- @param x    Unsigned integer value between 0x0000 and 0xFFFF
+-- @return     Byte-swapped value
+-- @see                htonl
+-- @see                ntohs
+function htons(x)
+       if LITTLE_ENDIAN then
+               return bit.bor(
+                       bit.rshift( x, 8 ),
+                       bit.band( bit.lshift( x, 8 ), 0xFF00 )
+               )
+       else
+               return x
+       end
+end
+
+--- Convert given long value to network byte order on little endian hosts
+-- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
+-- @return     Byte-swapped value
+-- @see                htons
+-- @see                ntohl
+function htonl(x)
+       if LITTLE_ENDIAN then
+               return bit.bor(
+                       bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ),
+                       htons( bit.rshift( x, 16 ) )
+               )
+       else
+               return x
+       end
+end
+
+--- Convert given short value to host byte order on little endian hosts
+-- @class      function
+-- @name       ntohs
+-- @param x    Unsigned integer value between 0x0000 and 0xFFFF
+-- @return     Byte-swapped value
+-- @see                htonl
+-- @see                ntohs
+ntohs = htons
+
+--- Convert given short value to host byte order on little endian hosts
+-- @class      function
+-- @name       ntohl
+-- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
+-- @return     Byte-swapped value
+-- @see                htons
+-- @see                ntohl
+ntohl = htonl
+
+
+--- Parse given IPv4 address in dotted quad or CIDR notation. If an optional
+-- netmask is given as second argument and the IP address is encoded in CIDR
+-- notation then the netmask parameter takes precedence. If neither a CIDR
+-- encoded prefix nor a netmask parameter is given, then a prefix length of
+-- 32 bit is assumed.
+-- @param address      IPv4 address in dotted quad or CIDR notation
+-- @param netmask      IPv4 netmask in dotted quad notation (optional)
+-- @return                     luci.ip.cidr instance or nil if given address was invalid
+-- @see                                IPv6
+-- @see                                Hex
+function IPv4(address, netmask)
+       address = address or "0.0.0.0/0"
+
+       local obj = __bless({ FAMILY_INET4 })
+
+       local data = {}
+       local prefix = address:match("/(.+)")
+       address = address:gsub("/.+","")
+       address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
+
+       if netmask then
+               prefix = obj:prefix(netmask)
+       elseif prefix then
+               prefix = tonumber(prefix)
+               if not prefix or prefix < 0 or prefix > 32 then return nil end
+       else
+               prefix = 32
+       end
+
+       local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
+
+       b1 = tonumber(b1)
+       b2 = tonumber(b2)
+       b3 = tonumber(b3)
+       b4 = tonumber(b4)
+
+       if b1 and b1 <= 255 and
+          b2 and b2 <= 255 and
+          b3 and b3 <= 255 and
+          b4 and b4 <= 255 and
+          prefix
+       then
+               table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 })
+               table.insert(obj, prefix)
+               return obj
+       end
+end
+
+--- Parse given IPv6 address in full, compressed, mixed or CIDR notation.
+-- If an optional netmask is given as second argument and the IP address is
+-- encoded in CIDR notation then the netmask parameter takes precedence.
+-- If neither a CIDR encoded prefix nor a netmask parameter is given, then a
+-- prefix length of 128 bit is assumed.
+-- @param address      IPv6 address in full/compressed/mixed or CIDR notation
+-- @param netmask      IPv6 netmask in full/compressed/mixed notation (optional)
+-- @return                     luci.ip.cidr instance or nil if given address was invalid
+-- @see                                IPv4
+-- @see                                Hex
+function IPv6(address, netmask)
+       address = address or "::/0"
+
+       local obj = __bless({ FAMILY_INET6 })
+
+       local data = {}
+       local prefix = address:match("/(.+)")
+       address = address:gsub("/.+","")
+       address = address:gsub("^%[(.*)%]$", "%1")
+
+       if netmask then
+               prefix = obj:prefix(netmask)
+       elseif prefix then
+               prefix = tonumber(prefix)
+               if not prefix or prefix < 0 or prefix > 128 then return nil end
+       else
+               prefix = 128
+       end
+
+       local borderl = address:sub(1, 1) == ":" and 2 or 1
+       local borderh, zeroh, chunk, block, i
+
+       if #address > 45 then return nil end
+
+       repeat
+               borderh = address:find(":", borderl, true)
+               if not borderh then break end
+
+               block = tonumber(address:sub(borderl, borderh - 1), 16)
+               if block and block <= 0xFFFF then
+                       data[#data+1] = block
+               else
+                       if zeroh or borderh - borderl > 1 then return nil end
+                       zeroh = #data + 1
+               end
+
+               borderl = borderh + 1
+       until #data == 7
+
+       chunk = address:sub(borderl)
+       if #chunk > 0 and #chunk <= 4 then
+               block = tonumber(chunk, 16)
+               if not block or block > 0xFFFF then return nil end
+
+               data[#data+1] = block
+       elseif #chunk > 4 then
+               if #data == 7 or #chunk > 15 then return nil end
+               borderl = 1
+               for i=1, 4 do
+                       borderh = chunk:find(".", borderl, true)
+                       if not borderh and i < 4 then return nil end
+                       borderh = borderh and borderh - 1
+
+                       block = tonumber(chunk:sub(borderl, borderh))
+                       if not block or block > 255 then return nil end
+
+                       if i == 1 or i == 3 then
+                               data[#data+1] = block * 256
+                       else
+                               data[#data] = data[#data] + block
+                       end
+
+                       borderl = borderh and borderh + 2
+               end
+       end
+
+       if zeroh then
+               if #data == 8 then return nil end
+               while #data < 8 do
+                       table.insert(data, zeroh, 0)
+               end
+       end
+
+       if #data == 8 and prefix then
+               table.insert(obj, data)
+               table.insert(obj, prefix)
+               return obj
+       end
+end
+
+--- Transform given hex-encoded value to luci.ip.cidr instance of specified
+-- address family.
+-- @param hex          String containing hex encoded value
+-- @param prefix       Prefix length of CIDR instance (optional, default is 32/128)
+-- @param family       Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6
+-- @param swap         Bool indicating whether to swap byteorder on low endian host
+-- @return                     luci.ip.cidr instance or nil if given value was invalid
+-- @see                                IPv4
+-- @see                                IPv6
+function Hex( hex, prefix, family, swap )
+       family = ( family ~= nil ) and family or FAMILY_INET4
+       swap   = ( swap   == nil ) and true   or swap
+       prefix = prefix or __maxlen(family)
+
+       local len  = __maxlen(family)
+       local tmp  = ""
+       local data = { }
+       local i
+
+       for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
+
+       if swap and LITTLE_ENDIAN then
+               for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end
+       else
+               tmp = tmp .. hex
+       end
+
+       hex = tmp
+
+       for i = 1, ( len / 4 ), 4 do
+               local n = tonumber( hex:sub( i, i+3 ), 16 )
+               if n then
+                       data[#data+1] = n
+               else
+                       return nil
+               end
+       end
+
+       return __bless({ family, data, prefix })
+end
+
+
+--- LuCI IP Library / CIDR instances
+-- @class      module
+-- @cstyle     instance
+-- @name       luci.ip.cidr
+cidr = util.class()
+
+--- Test whether the instance is a IPv4 address.
+-- @return     Boolean indicating a IPv4 address type
+-- @see                cidr.is6
+function cidr.is4( self )
+       return self[1] == FAMILY_INET4
+end
+
+--- Test whether this instance is an IPv4 RFC1918 private address
+-- @return     Boolean indicating whether this instance is an RFC1918 address
+function cidr.is4rfc1918( self )
+       if self[1] == FAMILY_INET4 then
+               return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
+                      ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
+                       (self[2][1] == 0xC0A8)
+       end
+       return false
+end
+
+--- Test whether this instance is an IPv4 link-local address (Zeroconf)
+-- @return     Boolean indicating whether this instance is IPv4 link-local
+function cidr.is4linklocal( self )
+       if self[1] == FAMILY_INET4 then
+               return (self[2][1] == 0xA9FE)
+       end
+       return false
+end
+
+--- Test whether the instance is a IPv6 address.
+-- @return     Boolean indicating a IPv6 address type
+-- @see                cidr.is4
+function cidr.is6( self )
+       return self[1] == FAMILY_INET6
+end
+
+--- Test whether this instance is an IPv6 link-local address
+-- @return     Boolean indicating whether this instance is IPv6 link-local
+function cidr.is6linklocal( self )
+       if self[1] == FAMILY_INET6 then
+               return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
+       end
+       return false
+end
+
+--- Return a corresponding string representation of the instance.
+-- If the prefix length is lower then the maximum possible prefix length for the
+-- corresponding address type then the address is returned in CIDR notation,
+-- otherwise the prefix will be left out.
+function cidr.string( self )
+       local str
+       if self:is4() then
+               str = string.format(
+                       "%d.%d.%d.%d",
+                       bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF),
+                       bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF)
+               )
+               if self[3] < 32 then
+                       str = str .. "/" .. self[3]
+               end
+       elseif self:is6() then
+               str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) )
+               if self[3] < 128 then
+                       str = str .. "/" .. self[3]
+               end
+       end
+       return str
+end
+
+--- Test whether the value of the instance is lower then the given address.
+-- This function will throw an exception if the given address has a different
+-- family than this instance.
+-- @param addr A luci.ip.cidr instance to compare against
+-- @return             Boolean indicating whether this instance is lower
+-- @see                        cidr.higher
+-- @see                        cidr.equal
+function cidr.lower( self, addr )
+       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+       local i
+       for i = 1, #self[2] do
+               if self[2][i] ~= addr[2][i] then
+                       return self[2][i] < addr[2][i]
+               end
+       end
+       return false
+end
+
+--- Test whether the value of the instance is higher then the given address.
+-- This function will throw an exception if the given address has a different
+-- family than this instance.
+-- @param addr A luci.ip.cidr instance to compare against
+-- @return             Boolean indicating whether this instance is higher
+-- @see                        cidr.lower
+-- @see                        cidr.equal
+function cidr.higher( self, addr )
+       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+       local i
+       for i = 1, #self[2] do
+               if self[2][i] ~= addr[2][i] then
+                       return self[2][i] > addr[2][i]
+               end
+       end
+       return false
+end
+
+--- Test whether the value of the instance is equal to the given address.
+-- This function will throw an exception if the given address is a different
+-- family than this instance.
+-- @param addr A luci.ip.cidr instance to compare against
+-- @return             Boolean indicating whether this instance is equal
+-- @see                        cidr.lower
+-- @see                        cidr.higher
+function cidr.equal( self, addr )
+       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+       local i
+       for i = 1, #self[2] do
+               if self[2][i] ~= addr[2][i] then
+                       return false
+               end
+       end
+       return true
+end
+
+--- Return the prefix length of this CIDR instance.
+-- @param mask Override instance prefix with given netmask (optional)
+-- @return             Prefix length in bit
+function cidr.prefix( self, mask )
+       local prefix = self[3]
+
+       if mask then
+               prefix = 0
+
+               local stop = false
+               local obj = type(mask) ~= "table"
+                       and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
+
+               if not obj then return nil end
+
+               local _, word
+               for _, word in ipairs(obj[2]) do
+                       if word == 0xFFFF then
+                               prefix = prefix + 16
+                       else
+                               local bitmask = bit.lshift(1, 15)
+                               while bit.band(word, bitmask) == bitmask do
+                                       prefix  = prefix + 1
+                                       bitmask = bit.lshift(1, 15 - (prefix % 16))
+                               end
+
+                               break
+                       end
+               end
+       end
+
+       return prefix
+end
+
+--- Return a corresponding CIDR representing the network address of this
+-- instance.
+-- @param bits Override prefix length of this instance (optional)
+-- @return             CIDR instance containing the network address
+-- @see                        cidr.host
+-- @see                        cidr.broadcast
+-- @see                        cidr.mask
+function cidr.network( self, bits )
+       local data = { }
+       bits = bits or self[3]
+
+       local i
+       for i = 1, math.floor( bits / 16 ) do
+               data[#data+1] = self[2][i]
+       end
+
+       if #data < #self[2] then
+               data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
+
+               for i = #data + 1, #self[2] do
+                       data[#data+1] = 0
+               end
+       end
+
+       return __bless({ self[1], data, __maxlen(self[1]) })
+end
+
+--- Return a corresponding CIDR representing the host address of this
+-- instance. This is intended to extract the host address from larger subnet.
+-- @return             CIDR instance containing the network address
+-- @see                        cidr.network
+-- @see                        cidr.broadcast
+-- @see                        cidr.mask
+function cidr.host( self )
+       return __bless({ self[1], self[2], __maxlen(self[1]) })
+end
+
+--- Return a corresponding CIDR representing the netmask of this instance.
+-- @param bits Override prefix length of this instance (optional)
+-- @return             CIDR instance containing the netmask
+-- @see                        cidr.network
+-- @see                        cidr.host
+-- @see                        cidr.broadcast
+function cidr.mask( self, bits )
+       local data = { }
+       bits = bits or self[3]
+
+       for i = 1, math.floor( bits / 16 ) do
+               data[#data+1] = 0xFFFF
+       end
+
+       if #data < #self[2] then
+               data[#data+1] = __mask16(bits)
+
+               for i = #data + 1, #self[2] do
+                       data[#data+1] = 0
+               end
+       end
+
+       return __bless({ self[1], data, __maxlen(self[1]) })
+end
+
+--- Return CIDR containing the broadcast address of this instance.
+-- @return             CIDR instance containing the netmask, always nil for IPv6
+-- @see                        cidr.network
+-- @see                        cidr.host
+-- @see                        cidr.mask
+function cidr.broadcast( self )
+       -- IPv6 has no broadcast addresses (XXX: assert() instead?)
+       if self[1] == FAMILY_INET4 then
+               local data   = { unpack(self[2]) }
+               local offset = math.floor( self[3] / 16 ) + 1
+
+               if offset <= #data then
+                       data[offset] = bit.bor( data[offset], __not16(self[3]) )
+                       for i = offset + 1, #data do data[i] = 0xFFFF end
+
+                       return __bless({ self[1], data, __maxlen(self[1]) })
+               end
+       end
+end
+
+--- Test whether this instance fully contains the given CIDR instance.
+-- @param addr CIDR instance to test against
+-- @return             Boolean indicating whether this instance contains the given CIDR
+function cidr.contains( self, addr )
+       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+
+       if self:prefix() <= addr:prefix() then
+               return self:network() == addr:network(self:prefix())
+       end
+
+       return false
+end
+
+--- Add specified amount of hosts to this instance.
+-- @param amount       Number of hosts to add to this instance
+-- @param inplace      Boolen indicating whether to alter values inplace (optional)
+-- @return                     CIDR representing the new address or nil on overflow error
+-- @see                                cidr.sub
+function cidr.add( self, amount, inplace )
+       local pos
+       local data   = { unpack(self[2]) }
+       local shorts = __array16( amount, self[1] )
+
+       for pos = #data, 1, -1 do
+               local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
+               if ( data[pos] + add ) > 0xFFFF then
+                       data[pos] = ( data[pos] + add ) % 0xFFFF
+                       if pos > 1 then
+                               data[pos-1] = data[pos-1] + ( add - data[pos] )
+                       else
+                               return nil
+                       end
+               else
+                       data[pos] = data[pos] + add
+               end
+       end
+
+       if inplace then
+               self[2] = data
+               return self
+       else
+               return __bless({ self[1], data, self[3] })
+       end
+end
+
+--- Substract specified amount of hosts from this instance.
+-- @param amount       Number of hosts to substract from this instance
+-- @param inplace      Boolen indicating whether to alter values inplace (optional)
+-- @return                     CIDR representing the new address or nil on underflow error
+-- @see                                cidr.add
+function cidr.sub( self, amount, inplace )
+       local pos
+       local data   = { unpack(self[2]) }
+       local shorts = __array16( amount, self[1] )
+
+       for pos = #data, 1, -1 do
+               local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
+               if ( data[pos] - sub ) < 0 then
+                       data[pos] = ( sub - data[pos] ) % 0xFFFF
+                       if pos > 1 then
+                               data[pos-1] = data[pos-1] - ( sub + data[pos] )
+                       else
+                               return nil
+                       end
+               else
+                       data[pos] = data[pos] - sub
+               end
+       end
+
+       if inplace then
+               self[2] = data
+               return self
+       else
+               return __bless({ self[1], data, self[3] })
+       end
+end
+
+--- Return CIDR containing the lowest available host address within this subnet.
+-- @return             CIDR containing the host address, nil if subnet is too small
+-- @see                        cidr.maxhost
+function cidr.minhost( self )
+       if self[3] <= __sublen(self[1]) then
+               -- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
+               return self:network():add(1, true)
+       end
+end
+
+--- Return CIDR containing the highest available host address within the subnet.
+-- @return             CIDR containing the host address, nil if subnet is too small
+-- @see                        cidr.minhost
+function cidr.maxhost( self )
+       if self[3] <= __sublen(self[1]) then
+               local i
+               local data   = { unpack(self[2]) }
+               local offset = math.floor( self[3] / 16 ) + 1
+
+               data[offset] = bit.bor( data[offset], __not16(self[3]) )
+               for i = offset + 1, #data do data[i] = 0xFFFF end
+               data = __bless({ self[1], data, __maxlen(self[1]) })
+
+               -- Last address in reserved for Broadcast Address in IPv4
+               if data[1] == FAMILY_INET4 then data:sub(1, true) end
+
+               return data
+       end
+end
diff --git a/modules/base/luasrc/ltn12.lua b/modules/base/luasrc/ltn12.lua
new file mode 100644 (file)
index 0000000..9371290
--- /dev/null
@@ -0,0 +1,391 @@
+--[[
+LuaSocket 2.0.2 license
+Copyright ï¿½ 2004-2007 Diego Nehab
+
+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.
+]]--
+--[[
+       Changes made by LuCI project:
+               * Renamed to luci.ltn12 to avoid collisions with luasocket
+               * Added inline documentation
+]]--
+-----------------------------------------------------------------------------
+-- LTN12 - Filters, sources, sinks and pumps.
+-- LuaSocket toolkit.
+-- Author: Diego Nehab
+-- RCS ID: $Id$
+-----------------------------------------------------------------------------
+
+-----------------------------------------------------------------------------
+-- Declare module
+-----------------------------------------------------------------------------
+local string = require("string")
+local table = require("table")
+local base = _G
+
+--- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
+-- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
+module("luci.ltn12")
+
+filter = {}
+source = {}
+sink = {}
+pump = {}
+
+-- 2048 seems to be better in windows...
+BLOCKSIZE = 2048
+_VERSION = "LTN12 1.0.1"
+
+-----------------------------------------------------------------------------
+-- Filter stuff
+-----------------------------------------------------------------------------
+
+--- LTN12 Filter constructors
+-- @class module
+-- @name luci.ltn12.filter
+
+--- Return a high level filter that cycles a low-level filter
+-- by passing it each chunk and updating a context between calls. 
+-- @param low   Low-level filter
+-- @param ctx   Context
+-- @param extra Extra argument passed to the low-level filter
+-- @return LTN12 filter
+function filter.cycle(low, ctx, extra)
+    base.assert(low)
+    return function(chunk)
+        local ret
+        ret, ctx = low(ctx, chunk, extra)
+        return ret
+    end
+end
+
+--- Chain a bunch of filters together.
+-- (thanks to Wim Couwenberg)
+-- @param ... filters to be chained
+-- @return LTN12 filter
+function filter.chain(...)
+    local n = table.getn(arg)
+    local top, index = 1, 1
+    local retry = ""
+    return function(chunk)
+        retry = chunk and retry
+        while true do
+            if index == top then
+                chunk = arg[index](chunk)
+                if chunk == "" or top == n then return chunk
+                elseif chunk then index = index + 1
+                else
+                    top = top+1
+                    index = top
+                end
+            else
+                chunk = arg[index](chunk or "")
+                if chunk == "" then
+                    index = index - 1
+                    chunk = retry
+                elseif chunk then
+                    if index == n then return chunk
+                    else index = index + 1 end
+                else base.error("filter returned inappropriate nil") end
+            end
+        end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Source stuff
+-----------------------------------------------------------------------------
+
+--- LTN12 Source constructors
+-- @class module
+-- @name luci.ltn12.source
+
+-- create an empty source
+local function empty()
+    return nil
+end
+
+--- Create an empty source.
+-- @return LTN12 source
+function source.empty()
+    return empty
+end
+
+--- Return a source that just outputs an error.
+-- @param err Error object
+-- @return LTN12 source
+function source.error(err)
+    return function()
+        return nil, err
+    end
+end
+
+--- Create a file source.
+-- @param handle File handle ready for reading
+-- @param io_err IO error object
+-- @return LTN12 source
+function source.file(handle, io_err)
+    if handle then
+        return function()
+            local chunk = handle:read(BLOCKSIZE)
+            if not chunk then handle:close() end
+            return chunk
+        end
+    else return source.error(io_err or "unable to open file") end
+end
+
+--- Turn a fancy source into a simple source.
+-- @param src fancy source
+-- @return LTN12 source
+function source.simplify(src)
+    base.assert(src)
+    return function()
+        local chunk, err_or_new = src()
+        src = err_or_new or src
+        if not chunk then return nil, err_or_new
+        else return chunk end
+    end
+end
+
+--- Create a string source.
+-- @param s Data
+-- @return LTN12 source
+function source.string(s)
+    if s then
+        local i = 1
+        return function()
+            local chunk = string.sub(s, i, i+BLOCKSIZE-1)
+            i = i + BLOCKSIZE
+            if chunk ~= "" then return chunk
+            else return nil end
+        end
+    else return source.empty() end
+end
+
+--- Creates rewindable source.
+-- @param src LTN12 source to be made rewindable
+-- @return LTN12 source
+function source.rewind(src)
+    base.assert(src)
+    local t = {}
+    return function(chunk)
+        if not chunk then
+            chunk = table.remove(t)
+            if not chunk then return src()
+            else return chunk end
+        else
+            t[#t+1] = chunk
+        end
+    end
+end
+
+--- Chain a source and a filter together.
+-- @param src LTN12 source
+-- @param f LTN12 filter
+-- @return LTN12 source
+function source.chain(src, f)
+    base.assert(src and f)
+    local last_in, last_out = "", ""
+    local state = "feeding"
+    local err
+    return function()
+        if not last_out then
+            base.error('source is empty!', 2)
+        end
+        while true do
+            if state == "feeding" then
+                last_in, err = src()
+                if err then return nil, err end
+                last_out = f(last_in)
+                if not last_out then
+                    if last_in then
+                        base.error('filter returned inappropriate nil')
+                    else
+                        return nil
+                    end
+                elseif last_out ~= "" then
+                    state = "eating"
+                    if last_in then last_in = "" end
+                    return last_out
+                end
+            else
+                last_out = f(last_in)
+                if last_out == "" then
+                    if last_in == "" then
+                        state = "feeding"
+                    else
+                        base.error('filter returned ""')
+                    end
+                elseif not last_out then
+                    if last_in then
+                        base.error('filter returned inappropriate nil')
+                    else
+                        return nil
+                    end
+                else
+                    return last_out
+                end
+            end
+        end
+    end
+end
+
+--- Create a source that produces contents of several sources.
+-- Sources will be used one after the other, as if they were concatenated
+-- (thanks to Wim Couwenberg)
+-- @param ... LTN12 sources
+-- @return LTN12 source
+function source.cat(...)
+    local src = table.remove(arg, 1)
+    return function()
+        while src do
+            local chunk, err = src()
+            if chunk then return chunk end
+            if err then return nil, err end
+            src = table.remove(arg, 1)
+        end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Sink stuff
+-----------------------------------------------------------------------------
+
+--- LTN12 sink constructors
+-- @class module
+-- @name luci.ltn12.sink
+
+--- Create a sink that stores into a table.
+-- @param t output table to store into
+-- @return LTN12 sink
+function sink.table(t)
+    t = t or {}
+    local f = function(chunk, err)
+        if chunk then t[#t+1] = chunk end
+        return 1
+    end
+    return f, t
+end
+
+--- Turn a fancy sink into a simple sink.
+-- @param snk fancy sink
+-- @return LTN12 sink
+function sink.simplify(snk)
+    base.assert(snk)
+    return function(chunk, err)
+        local ret, err_or_new = snk(chunk, err)
+        if not ret then return nil, err_or_new end
+        snk = err_or_new or snk
+        return 1
+    end
+end
+
+--- Create a file sink.
+-- @param handle file handle to write to
+-- @param io_err IO error
+-- @return LTN12 sink
+function sink.file(handle, io_err)
+    if handle then
+        return function(chunk, err)
+            if not chunk then
+                handle:close()
+                return 1
+            else return handle:write(chunk) end
+        end
+    else return sink.error(io_err or "unable to open file") end
+end
+
+-- creates a sink that discards data
+local function null()
+    return 1
+end
+
+--- Create a sink that discards data.
+-- @return LTN12 sink
+function sink.null()
+    return null
+end
+
+--- Create a sink that just returns an error.
+-- @param err Error object
+-- @return LTN12 sink
+function sink.error(err)
+    return function()
+        return nil, err
+    end
+end
+
+--- Chain a sink with a filter.
+-- @param f LTN12 filter
+-- @param snk LTN12 sink
+-- @return LTN12 sink
+function sink.chain(f, snk)
+    base.assert(f and snk)
+    return function(chunk, err)
+        if chunk ~= "" then
+            local filtered = f(chunk)
+            local done = chunk and ""
+            while true do
+                local ret, snkerr = snk(filtered, err)
+                if not ret then return nil, snkerr end
+                if filtered == done then return 1 end
+                filtered = f(done)
+            end
+        else return 1 end
+    end
+end
+
+-----------------------------------------------------------------------------
+-- Pump stuff
+-----------------------------------------------------------------------------
+
+--- LTN12 pump functions
+-- @class module
+-- @name luci.ltn12.pump
+
+--- Pump one chunk from the source to the sink.
+-- @param src LTN12 source
+-- @param snk LTN12 sink
+-- @return Chunk of data or nil if an error occured
+-- @return Error object
+function pump.step(src, snk)
+    local chunk, src_err = src()
+    local ret, snk_err = snk(chunk, src_err)
+    if chunk and ret then return 1
+    else return nil, src_err or snk_err end
+end
+
+--- Pump all data from a source to a sink, using a step function.
+-- @param src LTN12 source
+-- @param snk LTN12 sink
+-- @param step step function (optional)
+-- @return 1 if the operation succeeded otherwise nil
+-- @return Error object
+function pump.all(src, snk, step)
+    base.assert(src and snk)
+    step = step or pump.step
+    while true do
+        local ret, err = step(src, snk)
+        if not ret then
+            if err then return nil, err
+            else return 1 end
+        end
+    end
+end
+
diff --git a/modules/base/luasrc/luasrc/cacheloader.lua b/modules/base/luasrc/luasrc/cacheloader.lua
new file mode 100644 (file)
index 0000000..942c4b7
--- /dev/null
@@ -0,0 +1,23 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+]]--
+
+local config = require "luci.config"
+local ccache = require "luci.ccache"
+
+module "luci.cacheloader"
+
+if config.ccache and config.ccache.enable == "1" then
+       ccache.cache_ondemand()
+end
\ No newline at end of file
diff --git a/modules/base/luasrc/luasrc/cbi.lua b/modules/base/luasrc/luasrc/cbi.lua
new file mode 100644 (file)
index 0000000..ae570b1
--- /dev/null
@@ -0,0 +1,1850 @@
+--[[
+LuCI - Configuration Bind Interface
+
+Description:
+Offers an interface for binding configuration values to certain
+data types. Supports value and range validation and basic dependencies.
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+module("luci.cbi", package.seeall)
+
+require("luci.template")
+local util = require("luci.util")
+require("luci.http")
+
+
+--local event      = require "luci.sys.event"
+local fs         = require("nixio.fs")
+local uci        = require("luci.model.uci")
+local datatypes  = require("luci.cbi.datatypes")
+local class      = util.class
+local instanceof = util.instanceof
+
+FORM_NODATA  =  0
+FORM_PROCEED =  0
+FORM_VALID   =  1
+FORM_DONE       =  1
+FORM_INVALID = -1
+FORM_CHANGED =  2
+FORM_SKIP    =  4
+
+AUTO = true
+
+CREATE_PREFIX = "cbi.cts."
+REMOVE_PREFIX = "cbi.rts."
+RESORT_PREFIX = "cbi.sts."
+FEXIST_PREFIX = "cbi.cbe."
+
+-- Loads a CBI map from given file, creating an environment and returns it
+function load(cbimap, ...)
+       local fs   = require "nixio.fs"
+       local i18n = require "luci.i18n"
+       require("luci.config")
+       require("luci.util")
+
+       local upldir = "/lib/uci/upload/"
+       local cbidir = luci.util.libpath() .. "/model/cbi/"
+       local func, err
+
+       if fs.access(cbidir..cbimap..".lua") then
+               func, err = loadfile(cbidir..cbimap..".lua")
+       elseif fs.access(cbimap) then
+               func, err = loadfile(cbimap)
+       else
+               func, err = nil, "Model '" .. cbimap .. "' not found!"
+       end
+
+       assert(func, err)
+
+       local env = {
+               translate=i18n.translate,
+               translatef=i18n.translatef,
+               arg={...}
+       }
+
+       setfenv(func, setmetatable(env, {__index =
+               function(tbl, key)
+                       return rawget(tbl, key) or _M[key] or _G[key]
+               end}))
+
+       local maps       = { func() }
+       local uploads    = { }
+       local has_upload = false
+
+       for i, map in ipairs(maps) do
+               if not instanceof(map, Node) then
+                       error("CBI map returns no valid map object!")
+                       return nil
+               else
+                       map:prepare()
+                       if map.upload_fields then
+                               has_upload = true
+                               for _, field in ipairs(map.upload_fields) do
+                                       uploads[
+                                               field.config .. '.' ..
+                                               (field.section.sectiontype or '1') .. '.' ..
+                                               field.option
+                                       ] = true
+                               end
+                       end
+               end
+       end
+
+       if has_upload then
+               local uci = luci.model.uci.cursor()
+               local prm = luci.http.context.request.message.params
+               local fd, cbid
+
+               luci.http.setfilehandler(
+                       function( field, chunk, eof )
+                               if not field then return end
+                               if field.name and not cbid then
+                                       local c, s, o = field.name:gmatch(
+                                               "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
+                                       )()
+
+                                       if c and s and o then
+                                               local t = uci:get( c, s ) or s
+                                               if uploads[c.."."..t.."."..o] then
+                                                       local path = upldir .. field.name
+                                                       fd = io.open(path, "w")
+                                                       if fd then
+                                                               cbid = field.name
+                                                               prm[cbid] = path
+                                                       end
+                                               end
+                                       end
+                               end
+
+                               if field.name == cbid and fd then
+                                       fd:write(chunk)
+                               end
+
+                               if eof and fd then
+                                       fd:close()
+                                       fd   = nil
+                                       cbid = nil
+                               end
+                       end
+               )
+       end
+
+       return maps
+end
+
+--
+-- Compile a datatype specification into a parse tree for evaluation later on
+--
+local cdt_cache = { }
+
+function compile_datatype(code)
+       local i
+       local pos = 0
+       local esc = false
+       local depth = 0
+       local stack = { }
+
+       for i = 1, #code+1 do
+               local byte = code:byte(i) or 44
+               if esc then
+                       esc = false
+               elseif byte == 92 then
+                       esc = true
+               elseif byte == 40 or byte == 44 then
+                       if depth <= 0 then
+                               if pos < i then
+                                       local label = code:sub(pos, i-1)
+                                               :gsub("\\(.)", "%1")
+                                               :gsub("^%s+", "")
+                                               :gsub("%s+$", "")
+
+                                       if #label > 0 and tonumber(label) then
+                                               stack[#stack+1] = tonumber(label)
+                                       elseif label:match("^'.*'$") or label:match('^".*"$') then
+                                               stack[#stack+1] = label:gsub("[\"'](.*)[\"']", "%1")
+                                       elseif type(datatypes[label]) == "function" then
+                                               stack[#stack+1] = datatypes[label]
+                                               stack[#stack+1] = { }
+                                       else
+                                               error("Datatype error, bad token %q" % label)
+                                       end
+                               end
+                               pos = i + 1
+                       end
+                       depth = depth + (byte == 40 and 1 or 0)
+               elseif byte == 41 then
+                       depth = depth - 1
+                       if depth <= 0 then
+                               if type(stack[#stack-1]) ~= "function" then
+                                       error("Datatype error, argument list follows non-function")
+                               end
+                               stack[#stack] = compile_datatype(code:sub(pos, i-1))
+                               pos = i + 1
+                       end
+               end
+       end
+
+       return stack
+end
+
+function verify_datatype(dt, value)
+       if dt and #dt > 0 then
+               if not cdt_cache[dt] then
+                       local c = compile_datatype(dt)
+                       if c and type(c[1]) == "function" then
+                               cdt_cache[dt] = c
+                       else
+                               error("Datatype error, not a function expression")
+                       end
+               end
+               if cdt_cache[dt] then
+                       return cdt_cache[dt][1](value, unpack(cdt_cache[dt][2]))
+               end
+       end
+       return true
+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
+
+-- hook helper
+function Node._run_hook(self, hook)
+       if type(self[hook]) == "function" then
+               return self[hook](self)
+       end
+end
+
+function Node._run_hooks(self, ...)
+       local f
+       local r = false
+       for _, f in ipairs(arg) do
+               if type(self[f]) == "function" then
+                       self[f](self)
+                       r = true
+               end
+       end
+       return r
+end
+
+-- Prepare nodes
+function Node.prepare(self, ...)
+       for k, child in ipairs(self.children) do
+               child:prepare(...)
+       end
+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, scope)
+       scope = scope or {}
+       scope.self = self
+
+       luci.template.render(self.template, scope)
+end
+
+-- Render the children
+function Node.render_children(self, ...)
+       local k, node
+       for k, node in ipairs(self.children) do
+               node.last_child = (k == #self.children)
+               node:render(...)
+       end
+end
+
+
+--[[
+A simple template element
+]]--
+Template = class(Node)
+
+function Template.__init__(self, template)
+       Node.__init__(self)
+       self.template = template
+end
+
+function Template.render(self)
+       luci.template.render(self.template, {self=self})
+end
+
+function Template.parse(self, readinput)
+       self.readinput = (readinput ~= false)
+       return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
+end
+
+
+--[[
+Map - A map describing a configuration file
+]]--
+Map = class(Node)
+
+function Map.__init__(self, config, ...)
+       Node.__init__(self, ...)
+
+       self.config = config
+       self.parsechain = {self.config}
+       self.template = "cbi/map"
+       self.apply_on_parse = nil
+       self.readinput = true
+       self.proceed = false
+       self.flow = {}
+
+       self.uci = uci.cursor()
+       self.save = true
+
+       self.changed = false
+
+       if not self.uci:load(self.config) then
+               error("Unable to read UCI data: " .. self.config)
+       end
+end
+
+function Map.formvalue(self, key)
+       return self.readinput and luci.http.formvalue(key)
+end
+
+function Map.formvaluetable(self, key)
+       return self.readinput and luci.http.formvaluetable(key) or {}
+end
+
+function Map.get_scheme(self, sectiontype, option)
+       if not option then
+               return self.scheme and self.scheme.sections[sectiontype]
+       else
+               return self.scheme and self.scheme.variables[sectiontype]
+                and self.scheme.variables[sectiontype][option]
+       end
+end
+
+function Map.submitstate(self)
+       return self:formvalue("cbi.submit")
+end
+
+-- Chain foreign config
+function Map.chain(self, config)
+       table.insert(self.parsechain, config)
+end
+
+function Map.state_handler(self, state)
+       return state
+end
+
+-- Use optimized UCI writing
+function Map.parse(self, readinput, ...)
+       self.readinput = (readinput ~= false)
+       self:_run_hooks("on_parse")
+
+       if self:formvalue("cbi.skip") then
+               self.state = FORM_SKIP
+               return self:state_handler(self.state)
+       end
+
+       Node.parse(self, ...)
+
+       if self.save then
+               self:_run_hooks("on_save", "on_before_save")
+               for i, config in ipairs(self.parsechain) do
+                       self.uci:save(config)
+               end
+               self:_run_hooks("on_after_save")
+               if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
+                       self:_run_hooks("on_before_commit")
+                       for i, config in ipairs(self.parsechain) do
+                               self.uci:commit(config)
+
+                               -- Refresh data because commit changes section names
+                               self.uci:load(config)
+                       end
+                       self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
+                       if self.apply_on_parse then
+                               self.uci:apply(self.parsechain)
+                               self:_run_hooks("on_apply", "on_after_apply")
+                       else
+                               -- This is evaluated by the dispatcher and delegated to the
+                               -- template which in turn fires XHR to perform the actual
+                               -- apply actions.
+                               self.apply_needed = true
+                       end
+
+                       -- Reparse sections
+                       Node.parse(self, true)
+
+               end
+               for i, config in ipairs(self.parsechain) do
+                       self.uci:unload(config)
+               end
+               if type(self.commit_handler) == "function" then
+                       self:commit_handler(self:submitstate())
+               end
+       end
+
+       if self:submitstate() then
+               if not self.save then
+                       self.state = FORM_INVALID
+               elseif self.proceed then
+                       self.state = FORM_PROCEED
+               else
+                       self.state = self.changed and FORM_CHANGED or FORM_VALID
+               end
+       else
+               self.state = FORM_NODATA
+       end
+
+       return self:state_handler(self.state)
+end
+
+function Map.render(self, ...)
+       self:_run_hooks("on_init")
+       Node.render(self, ...)
+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)
+       return self.uci:add(self.config, sectiontype)
+end
+
+-- UCI set
+function Map.set(self, section, option, value)
+       if type(value) ~= "table" or #value > 0 then
+               if option then
+                       return self.uci:set(self.config, section, option, value)
+               else
+                       return self.uci:set(self.config, section, value)
+               end
+       else
+               return Map.del(self, section, option)
+       end
+end
+
+-- UCI del
+function Map.del(self, section, option)
+       if option then
+               return self.uci:delete(self.config, section, option)
+       else
+               return self.uci:delete(self.config, section)
+       end
+end
+
+-- UCI get
+function Map.get(self, section, option)
+       if not section then
+               return self.uci:get_all(self.config)
+       elseif option then
+               return self.uci:get(self.config, section, option)
+       else
+               return self.uci:get_all(self.config, section)
+       end
+end
+
+--[[
+Compound - Container
+]]--
+Compound = class(Node)
+
+function Compound.__init__(self, ...)
+       Node.__init__(self)
+       self.template = "cbi/compound"
+       self.children = {...}
+end
+
+function Compound.populate_delegator(self, delegator)
+       for _, v in ipairs(self.children) do
+               v.delegator = delegator
+       end
+end
+
+function Compound.parse(self, ...)
+       local cstate, state = 0
+
+       for k, child in ipairs(self.children) do
+               cstate = child:parse(...)
+               state = (not state or cstate < state) and cstate or state
+       end
+
+       return state
+end
+
+
+--[[
+Delegator - Node controller
+]]--
+Delegator = class(Node)
+function Delegator.__init__(self, ...)
+       Node.__init__(self, ...)
+       self.nodes = {}
+       self.defaultpath = {}
+       self.pageaction = false
+       self.readinput = true
+       self.allow_reset = false
+       self.allow_cancel = false
+       self.allow_back = false
+       self.allow_finish = false
+       self.template = "cbi/delegator"
+end
+
+function Delegator.set(self, name, node)
+       assert(not self.nodes[name], "Duplicate entry")
+
+       self.nodes[name] = node
+end
+
+function Delegator.add(self, name, node)
+       node = self:set(name, node)
+       self.defaultpath[#self.defaultpath+1] = name
+end
+
+function Delegator.insert_after(self, name, after)
+       local n = #self.chain + 1
+       for k, v in ipairs(self.chain) do
+               if v == after then
+                       n = k + 1
+                       break
+               end
+       end
+       table.insert(self.chain, n, name)
+end
+
+function Delegator.set_route(self, ...)
+       local n, chain, route = 0, self.chain, {...}
+       for i = 1, #chain do
+               if chain[i] == self.current then
+                       n = i
+                       break
+               end
+       end
+       for i = 1, #route do
+               n = n + 1
+               chain[n] = route[i]
+       end
+       for i = n + 1, #chain do
+               chain[i] = nil
+       end
+end
+
+function Delegator.get(self, name)
+       local node = self.nodes[name]
+
+       if type(node) == "string" then
+               node = load(node, name)
+       end
+
+       if type(node) == "table" and getmetatable(node) == nil then
+               node = Compound(unpack(node))
+       end
+
+       return node
+end
+
+function Delegator.parse(self, ...)
+       if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
+               if self:_run_hooks("on_cancel") then
+                       return FORM_DONE
+               end
+       end
+
+       if not Map.formvalue(self, "cbi.delg.current") then
+               self:_run_hooks("on_init")
+       end
+
+       local newcurrent
+       self.chain = self.chain or self:get_chain()
+       self.current = self.current or self:get_active()
+       self.active = self.active or self:get(self.current)
+       assert(self.active, "Invalid state")
+
+       local stat = FORM_DONE
+       if type(self.active) ~= "function" then
+               self.active:populate_delegator(self)
+               stat = self.active:parse()
+       else
+               self:active()
+       end
+
+       if stat > FORM_PROCEED then
+               if Map.formvalue(self, "cbi.delg.back") then
+                       newcurrent = self:get_prev(self.current)
+               else
+                       newcurrent = self:get_next(self.current)
+               end
+       elseif stat < FORM_PROCEED then
+               return stat
+       end
+
+
+       if not Map.formvalue(self, "cbi.submit") then
+               return FORM_NODATA
+       elseif stat > FORM_PROCEED
+       and (not newcurrent or not self:get(newcurrent)) then
+               return self:_run_hook("on_done") or FORM_DONE
+       else
+               self.current = newcurrent or self.current
+               self.active = self:get(self.current)
+               if type(self.active) ~= "function" then
+                       self.active:populate_delegator(self)
+                       local stat = self.active:parse(false)
+                       if stat == FORM_SKIP then
+                               return self:parse(...)
+                       else
+                               return FORM_PROCEED
+                       end
+               else
+                       return self:parse(...)
+               end
+       end
+end
+
+function Delegator.get_next(self, state)
+       for k, v in ipairs(self.chain) do
+               if v == state then
+                       return self.chain[k+1]
+               end
+       end
+end
+
+function Delegator.get_prev(self, state)
+       for k, v in ipairs(self.chain) do
+               if v == state then
+                       return self.chain[k-1]
+               end
+       end
+end
+
+function Delegator.get_chain(self)
+       local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
+       return type(x) == "table" and x or {x}
+end
+
+function Delegator.get_active(self)
+       return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
+end
+
+--[[
+Page - A simple node
+]]--
+
+Page = class(Node)
+Page.__init__ = Node.__init__
+Page.parse    = function() end
+
+
+--[[
+SimpleForm - A Simple non-UCI form
+]]--
+SimpleForm = class(Node)
+
+function SimpleForm.__init__(self, config, title, description, data)
+       Node.__init__(self, title, description)
+       self.config = config
+       self.data = data or {}
+       self.template = "cbi/simpleform"
+       self.dorender = true
+       self.pageaction = false
+       self.readinput = true
+end
+
+SimpleForm.formvalue = Map.formvalue
+SimpleForm.formvaluetable = Map.formvaluetable
+
+function SimpleForm.parse(self, readinput, ...)
+       self.readinput = (readinput ~= false)
+
+       if self:formvalue("cbi.skip") then
+               return FORM_SKIP
+       end
+
+       if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
+               return FORM_DONE
+       end
+
+       if self:submitstate() then
+               Node.parse(self, 1, ...)
+       end
+
+       local valid = true
+       for k, j in ipairs(self.children) do
+               for i, v in ipairs(j.children) do
+                       valid = valid
+                        and (not v.tag_missing or not v.tag_missing[1])
+                        and (not v.tag_invalid or not v.tag_invalid[1])
+                        and (not v.error)
+               end
+       end
+
+       local state =
+               not self:submitstate() and FORM_NODATA
+               or valid and FORM_VALID
+               or FORM_INVALID
+
+       self.dorender = not self.handle
+       if self.handle then
+               local nrender, nstate = self:handle(state, self.data)
+               self.dorender = self.dorender or (nrender ~= false)
+               state = nstate or state
+       end
+       return state
+end
+
+function SimpleForm.render(self, ...)
+       if self.dorender then
+               Node.render(self, ...)
+       end
+end
+
+function SimpleForm.submitstate(self)
+       return self:formvalue("cbi.submit")
+end
+
+function SimpleForm.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
+
+-- Creates a child field
+function SimpleForm.field(self, class, ...)
+       local section
+       for k, v in ipairs(self.children) do
+               if instanceof(v, SimpleSection) then
+                       section = v
+                       break
+               end
+       end
+       if not section then
+               section = self:section(SimpleSection)
+       end
+
+       if instanceof(class, AbstractValue) then
+               local obj  = class(self, section, ...)
+               obj.track_missing = true
+               section:append(obj)
+               return obj
+       else
+               error("class must be a descendent of AbstractValue")
+       end
+end
+
+function SimpleForm.set(self, section, option, value)
+       self.data[option] = value
+end
+
+
+function SimpleForm.del(self, section, option)
+       self.data[option] = nil
+end
+
+
+function SimpleForm.get(self, section, option)
+       return self.data[option]
+end
+
+
+function SimpleForm.get_scheme()
+       return nil
+end
+
+
+Form = class(SimpleForm)
+
+function Form.__init__(self, ...)
+       SimpleForm.__init__(self, ...)
+       self.embedded = true
+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.defaults = {}
+       self.fields = {}
+       self.tag_error = {}
+       self.tag_invalid = {}
+       self.tag_deperror = {}
+       self.changed = false
+
+       self.optional = true
+       self.addremove = false
+       self.dynamic = false
+end
+
+-- Define a tab for the section
+function AbstractSection.tab(self, tab, title, desc)
+       self.tabs      = self.tabs      or { }
+       self.tab_names = self.tab_names or { }
+
+       self.tab_names[#self.tab_names+1] = tab
+       self.tabs[tab] = {
+               title       = title,
+               description = desc,
+               childs      = { }
+       }
+end
+
+-- Check whether the section has tabs
+function AbstractSection.has_tabs(self)
+       return (self.tabs ~= nil) and (next(self.tabs) ~= nil)
+end
+
+-- Appends a new option
+function AbstractSection.option(self, class, option, ...)
+       if instanceof(class, AbstractValue) then
+               local obj  = class(self.map, self, option, ...)
+               self:append(obj)
+               self.fields[option] = obj
+               return obj
+       elseif class == true then
+               error("No valid class was given and autodetection failed.")
+       else
+               error("class must be a descendant of AbstractValue")
+       end
+end
+
+-- Appends a new tabbed option
+function AbstractSection.taboption(self, tab, ...)
+
+       assert(tab and self.tabs and self.tabs[tab],
+               "Cannot assign option to not existing tab %q" % tostring(tab))
+
+       local l = self.tabs[tab].childs
+       local o = AbstractSection.option(self, ...)
+
+       if o then l[#l+1] = o end
+
+       return o
+end
+
+-- Render a single tab
+function AbstractSection.render_tab(self, tab, ...)
+
+       assert(tab and self.tabs and self.tabs[tab],
+               "Cannot render not existing tab %q" % tostring(tab))
+
+       local k, node
+       for k, node in ipairs(self.tabs[tab].childs) do
+               node.last_child = (k == #self.tabs[tab].childs)
+               node:render(...)
+       end
+end
+
+-- Parse optional options
+function AbstractSection.parse_optionals(self, section)
+       if not self.optional then
+               return
+       end
+
+       self.optionals[section] = {}
+
+       local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
+       for k,v in ipairs(self.children) do
+               if v.optional and not v:cfgvalue(section) and not self:has_tabs() then
+                       if field == v.option then
+                               field = nil
+                               self.map.proceed = true
+                       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  = luci.util.clone(self:cfgvalue(section))
+       local form = self.map: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.map.proceed = true
+                       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
+
+-- Push events
+function AbstractSection.push_events(self)
+       --luci.util.append(self.map.events, self.events)
+       self.map.changed = true
+end
+
+-- Removes the section
+function AbstractSection.remove(self, section)
+       self.map.proceed = true
+       return self.map:del(section)
+end
+
+-- Creates the section
+function AbstractSection.create(self, section)
+       local stat
+
+       if section then
+               stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
+       else
+               section = self.map:add(self.sectiontype)
+               stat = section
+       end
+
+       if stat then
+               for k,v in pairs(self.children) do
+                       if v.default then
+                               self.map:set(section, v.option, v.default)
+                       end
+               end
+
+               for k,v in pairs(self.defaults) do
+                       self.map:set(section, k, v)
+               end
+       end
+
+       self.map.proceed = true
+
+       return stat
+end
+
+
+SimpleSection = class(AbstractSection)
+
+function SimpleSection.__init__(self, form, ...)
+       AbstractSection.__init__(self, form, nil, ...)
+       self.template = "cbi/nullsection"
+end
+
+
+Table = class(AbstractSection)
+
+function Table.__init__(self, form, data, ...)
+       local datasource = {}
+       local tself = self
+       datasource.config = "table"
+       self.data = data or {}
+
+       datasource.formvalue = Map.formvalue
+       datasource.formvaluetable = Map.formvaluetable
+       datasource.readinput = true
+
+       function datasource.get(self, section, option)
+               return tself.data[section] and tself.data[section][option]
+       end
+
+       function datasource.submitstate(self)
+               return Map.formvalue(self, "cbi.submit")
+       end
+
+       function datasource.del(...)
+               return true
+       end
+
+       function datasource.get_scheme()
+               return nil
+       end
+
+       AbstractSection.__init__(self, datasource, "table", ...)
+       self.template = "cbi/tblsection"
+       self.rowcolors = true
+       self.anonymous = true
+end
+
+function Table.parse(self, readinput)
+       self.map.readinput = (readinput ~= false)
+       for i, k in ipairs(self:cfgsections()) do
+               if self.map:submitstate() then
+                       Node.parse(self, k)
+               end
+       end
+end
+
+function Table.cfgsections(self)
+       local sections = {}
+
+       for i, v in luci.util.kspairs(self.data) do
+               table.insert(sections, i)
+       end
+
+       return sections
+end
+
+function Table.update(self, data)
+       self.data = data
+end
+
+
+
+--[[
+NamedSection - A fixed configuration section defined by its name
+]]--
+NamedSection = class(AbstractSection)
+
+function NamedSection.__init__(self, map, section, stype, ...)
+       AbstractSection.__init__(self, map, stype, ...)
+
+       -- Defaults
+       self.addremove = false
+       self.template = "cbi/nsection"
+       self.section = section
+end
+
+function NamedSection.parse(self, novld)
+       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 self.map:formvalue("cbi.rns."..path) and self:remove(s) then
+                               self:push_events()
+                               return
+                       end
+               else           -- Create and apply default values
+                       if self.map:formvalue("cbi.cns."..path) then
+                               self:create(s)
+                               return
+                       end
+               end
+       end
+
+       if active then
+               AbstractSection.parse_dynamic(self, s)
+               if self.map:submitstate() then
+                       Node.parse(self, s)
+               end
+               AbstractSection.parse_optionals(self, s)
+
+               if self.changed then
+                       self:push_events()
+               end
+       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, map, type, ...)
+       AbstractSection.__init__(self, map, type, ...)
+
+       self.template = "cbi/tsection"
+       self.deps = {}
+       self.anonymous = false
+end
+
+-- Return all matching UCI sections for this TypedSection
+function TypedSection.cfgsections(self)
+       local sections = {}
+       self.map.uci:foreach(self.map.config, self.sectiontype,
+               function (section)
+                       if self:checkscope(section[".name"]) then
+                               table.insert(sections, section[".name"])
+                       end
+               end)
+
+       return sections
+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
+
+function TypedSection.parse(self, novld)
+       if self.addremove then
+               -- Remove
+               local crval = REMOVE_PREFIX .. self.config
+               local name = self.map:formvaluetable(crval)
+               for k,v in pairs(name) do
+                       if k:sub(-2) == ".x" then
+                               k = k:sub(1, #k - 2)
+                       end
+                       if self:cfgvalue(k) and self:checkscope(k) then
+                               self:remove(k)
+                       end
+               end
+       end
+
+       local co
+       for i, k in ipairs(self:cfgsections()) do
+               AbstractSection.parse_dynamic(self, k)
+               if self.map:submitstate() then
+                       Node.parse(self, k, novld)
+               end
+               AbstractSection.parse_optionals(self, k)
+       end
+
+       if self.addremove then
+               -- Create
+               local created
+               local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
+               local origin, name = next(self.map:formvaluetable(crval))
+               if self.anonymous then
+                       if name then
+                               created = self:create(nil, origin)
+                       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 > 0 then
+                                       created = self:create(name, origin) and name
+                                       if not created then
+                                               self.invalid_cts = true
+                                       end
+                               end
+                       end
+               end
+
+               if created then
+                       AbstractSection.parse_optionals(self, created)
+               end
+       end
+
+       if self.sortable then
+               local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
+               local order = self.map:formvalue(stval)
+               if order and #order > 0 then
+                       local sid
+                       local num = 0
+                       for sid in util.imatch(order) do
+                               self.map.uci:reorder(self.config, sid, num)
+                               num = num + 1
+                       end
+                       self.changed = (num > 0)
+               end
+       end
+
+       if created or self.changed then
+               self:push_events()
+       end
+end
+
+-- Verifies scope of sections
+function TypedSection.checkscope(self, section)
+       -- Check if we are not excluded
+       if self.filter and not self:filter(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, section, option, ...)
+       Node.__init__(self, ...)
+       self.section = section
+       self.option  = option
+       self.map     = map
+       self.config  = map.config
+       self.tag_invalid = {}
+       self.tag_missing = {}
+       self.tag_reqerror = {}
+       self.tag_error = {}
+       self.deps = {}
+       self.subdeps = {}
+       --self.cast = "string"
+
+       self.track_missing = false
+       self.rmempty   = true
+       self.default   = nil
+       self.size      = nil
+       self.optional  = false
+end
+
+function AbstractValue.prepare(self)
+       self.cast = self.cast or "string"
+end
+
+-- Add a dependencie to another section field
+function AbstractValue.depends(self, field, value)
+       local deps
+       if type(field) == "string" then
+               deps = {}
+               deps[field] = value
+       else
+               deps = field
+       end
+
+       table.insert(self.deps, {deps=deps, add=""})
+end
+
+-- Generates the unique CBID
+function AbstractValue.cbid(self, section)
+       return "cbid."..self.map.config.."."..section.."."..self.option
+end
+
+-- Return whether this object should be created
+function AbstractValue.formcreated(self, section)
+       local key = "cbi.opt."..self.config.."."..section
+       return (self.map:formvalue(key) == self.option)
+end
+
+-- Returns the formvalue for this object
+function AbstractValue.formvalue(self, section)
+       return self.map:formvalue(self:cbid(section))
+end
+
+function AbstractValue.additional(self, value)
+       self.optional = value
+end
+
+function AbstractValue.mandatory(self, value)
+       self.rmempty = not value
+end
+
+function AbstractValue.add_error(self, section, type, msg)
+       self.error = self.error or { }
+       self.error[section] = msg or type
+
+       self.section.error = self.section.error or { }
+       self.section.error[section] = self.section.error[section] or { }
+       table.insert(self.section.error[section], msg or type)
+
+       if type == "invalid" then
+               self.tag_invalid[section] = true
+       elseif type == "missing" then
+               self.tag_missing[section] = true
+       end
+
+       self.tag_error[section] = true
+       self.map.save = false
+end
+
+function AbstractValue.parse(self, section, novld)
+       local fvalue = self:formvalue(section)
+       local cvalue = self:cfgvalue(section)
+
+       -- If favlue and cvalue are both tables and have the same content
+       -- make them identical
+       if type(fvalue) == "table" and type(cvalue) == "table" then
+               local equal = #fvalue == #cvalue
+               if equal then
+                       for i=1, #fvalue do
+                               if cvalue[i] ~= fvalue[i] then
+                                       equal = false
+                               end
+                       end
+               end
+               if equal then
+                       fvalue = cvalue
+               end
+       end
+
+       if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
+               local val_err
+               fvalue, val_err = self:validate(fvalue, section)
+               fvalue = self:transform(fvalue)
+
+               if not fvalue and not novld then
+                       self:add_error(section, "invalid", val_err)
+               end
+
+               if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
+                       if self:write(section, fvalue) then
+                               -- Push events
+                               self.section.changed = true
+                               --luci.util.append(self.map.events, self.events)
+                       end
+               end
+       else                                                    -- Unset the UCI or error
+               if self.rmempty or self.optional then
+                       if self:remove(section) then
+                               -- Push events
+                               self.section.changed = true
+                               --luci.util.append(self.map.events, self.events)
+                       end
+               elseif cvalue ~= fvalue and not novld then
+                       -- trigger validator with nil value to get custom user error msg.
+                       local _, val_err = self:validate(nil, section)
+                       self:add_error(section, "missing", val_err)
+               end
+       end
+end
+
+-- Render if this value exists or if it is mandatory
+function AbstractValue.render(self, s, scope)
+       if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
+               scope = scope or {}
+               scope.section = s
+               scope.cbid    = self:cbid(s)
+               Node.render(self, scope)
+       end
+end
+
+-- Return the UCI value of this object
+function AbstractValue.cfgvalue(self, section)
+       local value
+       if self.tag_error[section] then
+               value = self:formvalue(section)
+       else
+               value = self.map:get(section, self.option)
+       end
+
+       if not value then
+               return nil
+       elseif not self.cast or self.cast == type(value) then
+               return value
+       elseif self.cast == "string" then
+               if type(value) == "table" then
+                       return value[1]
+               end
+       elseif self.cast == "table" then
+               return { value }
+       end
+end
+
+-- Validate the form value
+function AbstractValue.validate(self, value)
+       if self.datatype and value then
+               if type(value) == "table" then
+                       local v
+                       for _, v in ipairs(value) do
+                               if v and #v > 0 and not verify_datatype(self.datatype, v) then
+                                       return nil
+                               end
+                       end
+               else
+                       if not verify_datatype(self.datatype, value) then
+                               return nil
+                       end
+               end
+       end
+
+       return value
+end
+
+AbstractValue.transform = AbstractValue.validate
+
+
+-- 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
+]]--
+Value = class(AbstractValue)
+
+function Value.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template  = "cbi/value"
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function Value.reset_values(self)
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function Value.value(self, key, val)
+       val = val or key
+       table.insert(self.keylist, tostring(key))
+       table.insert(self.vallist, tostring(val))
+end
+
+
+-- DummyValue - This does nothing except being there
+DummyValue = class(AbstractValue)
+
+function DummyValue.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template = "cbi/dvalue"
+       self.value = nil
+end
+
+function DummyValue.cfgvalue(self, section)
+       local value
+       if self.value then
+               if type(self.value) == "function" then
+                       value = self:value(section)
+               else
+                       value = self.value
+               end
+       else
+               value = AbstractValue.cfgvalue(self, section)
+       end
+       return value
+end
+
+function DummyValue.parse(self)
+
+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"
+       self.default  = self.disabled
+end
+
+-- A flag can only have two states: set or unset
+function Flag.parse(self, section)
+       local fexists = self.map:formvalue(
+               FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
+
+       if fexists then
+               local fvalue = self:formvalue(section) and self.enabled or self.disabled
+               if fvalue ~= self.default or (not self.optional and not self.rmempty) then
+                       self:write(section, fvalue)
+               else
+                       self:remove(section)
+               end
+       else
+               self:remove(section)
+       end
+end
+
+function Flag.cfgvalue(self, section)
+       return AbstractValue.cfgvalue(self, section) or self.default
+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.reset_values(self)
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function ListValue.value(self, key, val, ...)
+       if luci.util.contains(self.keylist, key) then
+               return
+       end
+
+       val = val or key
+       table.insert(self.keylist, tostring(key))
+       table.insert(self.vallist, tostring(val))
+
+       for i, deps in ipairs({...}) do
+               self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
+       end
+end
+
+function ListValue.validate(self, val)
+       if luci.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.render(self, ...)
+       if self.widget == "select" and not self.size then
+               self.size = #self.vallist
+       end
+
+       AbstractValue.render(self, ...)
+end
+
+function MultiValue.reset_values(self)
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function MultiValue.value(self, key, val)
+       if luci.util.contains(self.keylist, key) then
+               return
+       end
+
+       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 luci.util.split(val, self.delimiter)
+end
+
+function MultiValue.validate(self, val)
+       val = (type(val) == "table") and val or {val}
+
+       local result
+
+       for i, value in ipairs(val) do
+               if luci.util.contains(self.keylist, value) then
+                       result = result and (result .. self.delimiter .. value) or value
+               end
+       end
+
+       return result
+end
+
+
+StaticList = class(MultiValue)
+
+function StaticList.__init__(self, ...)
+       MultiValue.__init__(self, ...)
+       self.cast = "table"
+       self.valuelist = self.cfgvalue
+
+       if not self.override_scheme
+        and self.map:get_scheme(self.section.sectiontype, self.option) then
+               local vs = self.map:get_scheme(self.section.sectiontype, self.option)
+               if self.value and vs.values and not self.override_values then
+                       for k, v in pairs(vs.values) do
+                               self:value(k, v)
+                       end
+               end
+       end
+end
+
+function StaticList.validate(self, value)
+       value = (type(value) == "table") and value or {value}
+
+       local valid = {}
+       for i, v in ipairs(value) do
+               if luci.util.contains(self.keylist, v) then
+                       table.insert(valid, v)
+               end
+       end
+       return valid
+end
+
+
+DynamicList = class(AbstractValue)
+
+function DynamicList.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template  = "cbi/dynlist"
+       self.cast = "table"
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function DynamicList.reset_values(self)
+       self.keylist = {}
+       self.vallist = {}
+end
+
+function DynamicList.value(self, key, val)
+       val = val or key
+       table.insert(self.keylist, tostring(key))
+       table.insert(self.vallist, tostring(val))
+end
+
+function DynamicList.write(self, section, value)
+       local t = { }
+
+       if type(value) == "table" then
+               local x
+               for _, x in ipairs(value) do
+                       if x and #x > 0 then
+                               t[#t+1] = x
+                       end
+               end
+       else
+               t = { value }
+       end
+
+       if self.cast == "string" then
+               value = table.concat(t, " ")
+       else
+               value = t
+       end
+
+       return AbstractValue.write(self, section, value)
+end
+
+function DynamicList.cfgvalue(self, section)
+       local value = AbstractValue.cfgvalue(self, section)
+
+       if type(value) == "string" then
+               local x
+               local t = { }
+               for x in value:gmatch("%S+") do
+                       if #x > 0 then
+                               t[#t+1] = x
+                       end
+               end
+               value = t
+       end
+
+       return value
+end
+
+function DynamicList.formvalue(self, section)
+       local value = AbstractValue.formvalue(self, section)
+
+       if type(value) == "string" then
+               if self.cast == "string" then
+                       local x
+                       local t = { }
+                       for x in value:gmatch("%S+") do
+                               t[#t+1] = x
+                       end
+                       value = t
+               else
+                       value = { value }
+               end
+       end
+
+       return value
+end
+
+
+--[[
+TextValue - A multi-line value
+       rows:   Rows
+]]--
+TextValue = class(AbstractValue)
+
+function TextValue.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template  = "cbi/tvalue"
+end
+
+--[[
+Button
+]]--
+Button = class(AbstractValue)
+
+function Button.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template  = "cbi/button"
+       self.inputstyle = nil
+       self.rmempty = true
+end
+
+
+FileUpload = class(AbstractValue)
+
+function FileUpload.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template = "cbi/upload"
+       if not self.map.upload_fields then
+               self.map.upload_fields = { self }
+       else
+               self.map.upload_fields[#self.map.upload_fields+1] = self
+       end
+end
+
+function FileUpload.formcreated(self, section)
+       return AbstractValue.formcreated(self, section) or
+               self.map:formvalue("cbi.rlf."..section.."."..self.option) or
+               self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
+end
+
+function FileUpload.cfgvalue(self, section)
+       local val = AbstractValue.cfgvalue(self, section)
+       if val and fs.access(val) then
+               return val
+       end
+       return nil
+end
+
+function FileUpload.formvalue(self, section)
+       local val = AbstractValue.formvalue(self, section)
+       if val then
+               if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
+                  not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
+               then
+                       return val
+               end
+               fs.unlink(val)
+               self.value = nil
+       end
+       return nil
+end
+
+function FileUpload.remove(self, section)
+       local val = AbstractValue.formvalue(self, section)
+       if val and fs.access(val) then fs.unlink(val) end
+       return AbstractValue.remove(self, section)
+end
+
+
+FileBrowser = class(AbstractValue)
+
+function FileBrowser.__init__(self, ...)
+       AbstractValue.__init__(self, ...)
+       self.template = "cbi/browser"
+end
diff --git a/modules/base/luasrc/luasrc/cbi/datatypes.lua b/modules/base/luasrc/luasrc/cbi/datatypes.lua
new file mode 100644 (file)
index 0000000..c5f4ec0
--- /dev/null
@@ -0,0 +1,345 @@
+--[[
+
+LuCI - Configuration Bind Interface - Datatype Tests
+(c) 2010 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+]]--
+
+local fs = require "nixio.fs"
+local ip = require "luci.ip"
+local math = require "math"
+local util = require "luci.util"
+local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
+
+
+module "luci.cbi.datatypes"
+
+
+_M['or'] = function(v, ...)
+       local i
+       for i = 1, select('#', ...), 2 do
+               local f = select(i, ...)
+               local a = select(i+1, ...)
+               if type(f) ~= "function" then
+                       if f == v then
+                               return true
+                       end
+                       i = i - 1
+               elseif f(v, unpack(a)) then
+                       return true
+               end
+       end
+       return false
+end
+
+_M['and'] = function(v, ...)
+       local i
+       for i = 1, select('#', ...), 2 do
+               local f = select(i, ...)
+               local a = select(i+1, ...)
+               if type(f) ~= "function" then
+                       if f ~= v then
+                               return false
+                       end
+                       i = i - 1
+               elseif not f(v, unpack(a)) then
+                       return false
+               end
+       end
+       return true
+end
+
+function neg(v, ...)
+       return _M['or'](v:gsub("^%s*!%s*", ""), ...)
+end
+
+function list(v, subvalidator, subargs)
+       if type(subvalidator) ~= "function" then
+               return false
+       end
+       local token
+       for token in v:gmatch("%S+") do
+               if not subvalidator(token, unpack(subargs)) then
+                       return false
+               end
+       end
+       return true
+end
+
+function bool(val)
+       if val == "1" or val == "yes" or val == "on" or val == "true" then
+               return true
+       elseif val == "0" or val == "no" or val == "off" or val == "false" then
+               return true
+       elseif val == "" or val == nil then
+               return true
+       end
+
+       return false
+end
+
+function uinteger(val)
+       local n = tonumber(val)
+       if n ~= nil and math.floor(n) == n and n >= 0 then
+               return true
+       end
+
+       return false
+end
+
+function integer(val)
+       local n = tonumber(val)
+       if n ~= nil and math.floor(n) == n then
+               return true
+       end
+
+       return false
+end
+
+function ufloat(val)
+       local n = tonumber(val)
+       return ( n ~= nil and n >= 0 )
+end
+
+function float(val)
+       return ( tonumber(val) ~= nil )
+end
+
+function ipaddr(val)
+       return ip4addr(val) or ip6addr(val)
+end
+
+function ip4addr(val)
+       if val then
+               return ip.IPv4(val) and true or false
+       end
+
+       return false
+end
+
+function ip4prefix(val)
+       val = tonumber(val)
+       return ( val and val >= 0 and val <= 32 )
+end
+
+function ip6addr(val)
+       if val then
+               return ip.IPv6(val) and true or false
+       end
+
+       return false
+end
+
+function ip6prefix(val)
+       val = tonumber(val)
+       return ( val and val >= 0 and val <= 128 )
+end
+
+function port(val)
+       val = tonumber(val)
+       return ( val and val >= 0 and val <= 65535 )
+end
+
+function portrange(val)
+       local p1, p2 = val:match("^(%d+)%-(%d+)$")
+       if p1 and p2 and port(p1) and port(p2) then
+               return true
+       else
+               return port(val)
+       end
+end
+
+function macaddr(val)
+       if val and val:match(
+               "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
+                "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
+       ) then
+               local parts = util.split( val, ":" )
+
+               for i = 1,6 do
+                       parts[i] = tonumber( parts[i], 16 )
+                       if parts[i] < 0 or parts[i] > 255 then
+                               return false
+                       end
+               end
+
+               return true
+       end
+
+       return false
+end
+
+function hostname(val)
+       if val and (#val < 254) and (
+          val:match("^[a-zA-Z_]+$") or
+          (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
+           val:match("[^0-9%.]"))
+       ) then
+               return true
+       end
+       return false
+end
+
+function host(val)
+       return hostname(val) or ipaddr(val)
+end
+
+function network(val)
+       return uciname(val) or host(val)
+end
+
+function wpakey(val)
+       if #val == 64 then
+               return (val:match("^[a-fA-F0-9]+$") ~= nil)
+       else
+               return (#val >= 8) and (#val <= 63)
+       end
+end
+
+function wepkey(val)
+       if val:sub(1, 2) == "s:" then
+               val = val:sub(3)
+       end
+
+       if (#val == 10) or (#val == 26) then
+               return (val:match("^[a-fA-F0-9]+$") ~= nil)
+       else
+               return (#val == 5) or (#val == 13)
+       end
+end
+
+function string(val)
+       return true             -- Everything qualifies as valid string
+end
+
+function directory( val, seen )
+       local s = fs.stat(val)
+       seen = seen or { }
+
+       if s and not seen[s.ino] then
+               seen[s.ino] = true
+               if s.type == "dir" then
+                       return true
+               elseif s.type == "lnk" then
+                       return directory( fs.readlink(val), seen )
+               end
+       end
+
+       return false
+end
+
+function file( val, seen )
+       local s = fs.stat(val)
+       seen = seen or { }
+
+       if s and not seen[s.ino] then
+               seen[s.ino] = true
+               if s.type == "reg" then
+                       return true
+               elseif s.type == "lnk" then
+                       return file( fs.readlink(val), seen )
+               end
+       end
+
+       return false
+end
+
+function device( val, seen )
+       local s = fs.stat(val)
+       seen = seen or { }
+
+       if s and not seen[s.ino] then
+               seen[s.ino] = true
+               if s.type == "chr" or s.type == "blk" then
+                       return true
+               elseif s.type == "lnk" then
+                       return device( fs.readlink(val), seen )
+               end
+       end
+
+       return false
+end
+
+function uciname(val)
+       return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
+end
+
+function range(val, min, max)
+       val = tonumber(val)
+       min = tonumber(min)
+       max = tonumber(max)
+
+       if val ~= nil and min ~= nil and max ~= nil then
+               return ((val >= min) and (val <= max))
+       end
+
+       return false
+end
+
+function min(val, min)
+       val = tonumber(val)
+       min = tonumber(min)
+
+       if val ~= nil and min ~= nil then
+               return (val >= min)
+       end
+
+       return false
+end
+
+function max(val, max)
+       val = tonumber(val)
+       max = tonumber(max)
+
+       if val ~= nil and max ~= nil then
+               return (val <= max)
+       end
+
+       return false
+end
+
+function rangelength(val, min, max)
+       val = tostring(val)
+       min = tonumber(min)
+       max = tonumber(max)
+
+       if val ~= nil and min ~= nil and max ~= nil then
+               return ((#val >= min) and (#val <= max))
+       end
+
+       return false
+end
+
+function minlength(val, min)
+       val = tostring(val)
+       min = tonumber(min)
+
+       if val ~= nil and min ~= nil then
+               return (#val >= min)
+       end
+
+       return false
+end
+
+function maxlength(val, max)
+       val = tostring(val)
+       max = tonumber(max)
+
+       if val ~= nil and max ~= nil then
+               return (#val <= max)
+       end
+
+       return false
+end
+
+function phonedigit(val)
+       return (val:match("^[0-9\*#!%.]+$") ~= nil)
+end
diff --git a/modules/base/luasrc/luasrc/config.lua b/modules/base/luasrc/luasrc/config.lua
new file mode 100644 (file)
index 0000000..53db82b
--- /dev/null
@@ -0,0 +1,42 @@
+--[[
+LuCI - Configuration
+
+Description:
+Some LuCI configuration values read from uci file "luci"
+
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at 
+
+       http://www.apache.org/licenses/LICENSE-2.0 
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local util = require "luci.util"
+module("luci.config",
+               function(m)
+                       if pcall(require, "luci.model.uci") then
+                               local config = util.threadlocal()
+                               setmetatable(m, {
+                                       __index = function(tbl, key)
+                                               if not config[key] then
+                                                       config[key] = luci.model.uci.cursor():get_all("luci", key)
+                                               end
+                                               return config[key]
+                                       end
+                               })
+                       end
+               end)
diff --git a/modules/base/luasrc/luasrc/dispatcher.lua b/modules/base/luasrc/luasrc/dispatcher.lua
new file mode 100644 (file)
index 0000000..9e5b78d
--- /dev/null
@@ -0,0 +1,959 @@
+--[[
+LuCI - Dispatcher
+
+Description:
+The request dispatcher and module dispatcher generators
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+--- LuCI web dispatcher.
+local fs = require "nixio.fs"
+local sys = require "luci.sys"
+local init = require "luci.init"
+local util = require "luci.util"
+local http = require "luci.http"
+local nixio = require "nixio", require "nixio.util"
+
+module("luci.dispatcher", package.seeall)
+context = util.threadlocal()
+uci = require "luci.model.uci"
+i18n = require "luci.i18n"
+_M.fs = fs
+
+authenticator = {}
+
+-- Index table
+local index = nil
+
+-- Fastindex
+local fi
+
+
+--- Build the URL relative to the server webroot from given virtual path.
+-- @param ...  Virtual path
+-- @return             Relative URL
+function build_url(...)
+       local path = {...}
+       local url = { http.getenv("SCRIPT_NAME") or "" }
+
+       local k, v
+       for k, v in pairs(context.urltoken) do
+               url[#url+1] = "/;"
+               url[#url+1] = http.urlencode(k)
+               url[#url+1] = "="
+               url[#url+1] = http.urlencode(v)
+       end
+
+       local p
+       for _, p in ipairs(path) do
+               if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
+                       url[#url+1] = "/"
+                       url[#url+1] = p
+               end
+       end
+
+       return table.concat(url, "")
+end
+
+--- Check whether a dispatch node shall be visible
+-- @param node Dispatch node
+-- @return             Boolean indicating whether the node should be visible
+function node_visible(node)
+   if node then
+         return not (
+                (not node.title or #node.title == 0) or
+                (not node.target or node.hidden == true) or
+                (type(node.target) == "table" and node.target.type == "firstchild" and
+                 (type(node.nodes) ~= "table" or not next(node.nodes)))
+         )
+   end
+   return false
+end
+
+--- Return a sorted table of visible childs within a given node
+-- @param node Dispatch node
+-- @return             Ordered table of child node names
+function node_childs(node)
+       local rv = { }
+       if node then
+               local k, v
+               for k, v in util.spairs(node.nodes,
+                       function(a, b)
+                               return (node.nodes[a].order or 100)
+                                    < (node.nodes[b].order or 100)
+                       end)
+               do
+                       if node_visible(v) then
+                               rv[#rv+1] = k
+                       end
+               end
+       end
+       return rv
+end
+
+
+--- Send a 404 error code and render the "error404" template if available.
+-- @param message      Custom error message (optional)
+-- @return                     false
+function error404(message)
+       luci.http.status(404, "Not Found")
+       message = message or "Not Found"
+
+       require("luci.template")
+       if not luci.util.copcall(luci.template.render, "error404") then
+               luci.http.prepare_content("text/plain")
+               luci.http.write(message)
+       end
+       return false
+end
+
+--- Send a 500 error code and render the "error500" template if available.
+-- @param message      Custom error message (optional)#
+-- @return                     false
+function error500(message)
+       luci.util.perror(message)
+       if not context.template_header_sent then
+               luci.http.status(500, "Internal Server Error")
+               luci.http.prepare_content("text/plain")
+               luci.http.write(message)
+       else
+               require("luci.template")
+               if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
+                       luci.http.prepare_content("text/plain")
+                       luci.http.write(message)
+               end
+       end
+       return false
+end
+
+function authenticator.htmlauth(validator, accs, default)
+       local user = luci.http.formvalue("username")
+       local pass = luci.http.formvalue("password")
+
+       if user and validator(user, pass) then
+               return user
+       end
+
+       require("luci.i18n")
+       require("luci.template")
+       context.path = {}
+       luci.template.render("sysauth", {duser=default, fuser=user})
+       return false
+
+end
+
+--- Dispatch an HTTP request.
+-- @param request      LuCI HTTP Request object
+function httpdispatch(request, prefix)
+       luci.http.context.request = request
+
+       local r = {}
+       context.request = r
+       context.urltoken = {}
+
+       local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
+
+       if prefix then
+               for _, node in ipairs(prefix) do
+                       r[#r+1] = node
+               end
+       end
+
+       local tokensok = true
+       for node in pathinfo:gmatch("[^/]+") do
+               local tkey, tval
+               if tokensok then
+                       tkey, tval = node:match(";(%w+)=([a-fA-F0-9]*)")
+               end
+               if tkey then
+                       context.urltoken[tkey] = tval
+               else
+                       tokensok = false
+                       r[#r+1] = node
+               end
+       end
+
+       local stat, err = util.coxpcall(function()
+               dispatch(context.request)
+       end, error500)
+
+       luci.http.close()
+
+       --context._disable_memtrace()
+end
+
+--- Dispatches a LuCI virtual path.
+-- @param request      Virtual path
+function dispatch(request)
+       --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
+       local ctx = context
+       ctx.path = request
+
+       local conf = require "luci.config"
+       assert(conf.main,
+               "/etc/config/luci seems to be corrupt, unable to find section 'main'")
+
+       local lang = conf.main.lang or "auto"
+       if lang == "auto" then
+               local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
+               for lpat in aclang:gmatch("[%w-]+") do
+                       lpat = lpat and lpat:gsub("-", "_")
+                       if conf.languages[lpat] then
+                               lang = lpat
+                               break
+                       end
+               end
+       end
+       require "luci.i18n".setlanguage(lang)
+
+       local c = ctx.tree
+       local stat
+       if not c then
+               c = createtree()
+       end
+
+       local track = {}
+       local args = {}
+       ctx.args = args
+       ctx.requestargs = ctx.requestargs or args
+       local n
+       local token = ctx.urltoken
+       local preq = {}
+       local freq = {}
+
+       for i, s in ipairs(request) do
+               preq[#preq+1] = s
+               freq[#freq+1] = s
+               c = c.nodes[s]
+               n = i
+               if not c then
+                       break
+               end
+
+               util.update(track, c)
+
+               if c.leaf then
+                       break
+               end
+       end
+
+       if c and c.leaf then
+               for j=n+1, #request do
+                       args[#args+1] = request[j]
+                       freq[#freq+1] = request[j]
+               end
+       end
+
+       ctx.requestpath = ctx.requestpath or freq
+       ctx.path = preq
+
+       if track.i18n then
+               i18n.loadc(track.i18n)
+       end
+
+       -- Init template engine
+       if (c and c.index) or not track.notemplate then
+               local tpl = require("luci.template")
+               local media = track.mediaurlbase or luci.config.main.mediaurlbase
+               if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
+                       media = nil
+                       for name, theme in pairs(luci.config.themes) do
+                               if name:sub(1,1) ~= "." and pcall(tpl.Template,
+                                "themes/%s/header" % fs.basename(theme)) then
+                                       media = theme
+                               end
+                       end
+                       assert(media, "No valid theme found")
+               end
+
+               local function _ifattr(cond, key, val)
+                       if cond then
+                               local env = getfenv(3)
+                               local scope = (type(env.self) == "table") and env.self
+                               return string.format(
+                                       ' %s="%s"', tostring(key),
+                                       luci.util.pcdata(tostring( val
+                                        or (type(env[key]) ~= "function" and env[key])
+                                        or (scope and type(scope[key]) ~= "function" and scope[key])
+                                        or "" ))
+                               )
+                       else
+                               return ''
+                       end
+               end
+
+               tpl.context.viewns = setmetatable({
+                  write       = luci.http.write;
+                  include     = function(name) tpl.Template(name):render(getfenv(2)) end;
+                  translate   = i18n.translate;
+                  translatef  = i18n.translatef;
+                  export      = function(k, v) if tpl.context.viewns[k] == nil then tpl.context.viewns[k] = v end end;
+                  striptags   = util.striptags;
+                  pcdata      = util.pcdata;
+                  media       = media;
+                  theme       = fs.basename(media);
+                  resource    = luci.config.main.resourcebase;
+                  ifattr      = function(...) return _ifattr(...) end;
+                  attr        = function(...) return _ifattr(true, ...) end;
+               }, {__index=function(table, key)
+                       if key == "controller" then
+                               return build_url()
+                       elseif key == "REQUEST_URI" then
+                               return build_url(unpack(ctx.requestpath))
+                       else
+                               return rawget(table, key) or _G[key]
+                       end
+               end})
+       end
+
+       track.dependent = (track.dependent ~= false)
+       assert(not track.dependent or not track.auto,
+               "Access Violation\nThe page at '" .. table.concat(request, "/") .. "/' " ..
+               "has no parent node so the access to this location has been denied.\n" ..
+               "This is a software bug, please report this message at " ..
+               "http://luci.subsignal.org/trac/newticket"
+       )
+
+       if track.sysauth then
+               local sauth = require "luci.sauth"
+
+               local authen = type(track.sysauth_authenticator) == "function"
+                and track.sysauth_authenticator
+                or authenticator[track.sysauth_authenticator]
+
+               local def  = (type(track.sysauth) == "string") and track.sysauth
+               local accs = def and {track.sysauth} or track.sysauth
+               local sess = ctx.authsession
+               local verifytoken = false
+               if not sess then
+                       sess = luci.http.getcookie("sysauth")
+                       sess = sess and sess:match("^[a-f0-9]*$")
+                       verifytoken = true
+               end
+
+               local sdat = sauth.read(sess)
+               local user
+
+               if sdat then
+                       if not verifytoken or ctx.urltoken.stok == sdat.token then
+                               user = sdat.user
+                       end
+               else
+                       local eu = http.getenv("HTTP_AUTH_USER")
+                       local ep = http.getenv("HTTP_AUTH_PASS")
+                       if eu and ep and luci.sys.user.checkpasswd(eu, ep) then
+                               authen = function() return eu end
+                       end
+               end
+
+               if not util.contains(accs, user) then
+                       if authen then
+                               ctx.urltoken.stok = nil
+                               local user, sess = authen(luci.sys.user.checkpasswd, accs, def)
+                               if not user or not util.contains(accs, user) then
+                                       return
+                               else
+                                       local sid = sess or luci.sys.uniqueid(16)
+                                       if not sess then
+                                               local token = luci.sys.uniqueid(16)
+                                               sauth.reap()
+                                               sauth.write(sid, {
+                                                       user=user,
+                                                       token=token,
+                                                       secret=luci.sys.uniqueid(16)
+                                               })
+                                               ctx.urltoken.stok = token
+                                       end
+                                       luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path="..build_url())
+                                       ctx.authsession = sid
+                                       ctx.authuser = user
+                               end
+                       else
+                               luci.http.status(403, "Forbidden")
+                               return
+                       end
+               else
+                       ctx.authsession = sess
+                       ctx.authuser = user
+               end
+       end
+
+       if track.setgroup then
+               luci.sys.process.setgroup(track.setgroup)
+       end
+
+       if track.setuser then
+               luci.sys.process.setuser(track.setuser)
+       end
+
+       local target = nil
+       if c then
+               if type(c.target) == "function" then
+                       target = c.target
+               elseif type(c.target) == "table" then
+                       target = c.target.target
+               end
+       end
+
+       if c and (c.index or type(target) == "function") then
+               ctx.dispatched = c
+               ctx.requested = ctx.requested or ctx.dispatched
+       end
+
+       if c and c.index then
+               local tpl = require "luci.template"
+
+               if util.copcall(tpl.render, "indexer", {}) then
+                       return true
+               end
+       end
+
+       if type(target) == "function" then
+               util.copcall(function()
+                       local oldenv = getfenv(target)
+                       local module = require(c.module)
+                       local env = setmetatable({}, {__index=
+
+                       function(tbl, key)
+                               return rawget(tbl, key) or module[key] or oldenv[key]
+                       end})
+
+                       setfenv(target, env)
+               end)
+
+               local ok, err
+               if type(c.target) == "table" then
+                       ok, err = util.copcall(target, c.target, unpack(args))
+               else
+                       ok, err = util.copcall(target, unpack(args))
+               end
+               assert(ok,
+                      "Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
+                      " dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
+                      "The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
+       else
+               local root = node()
+               if not root or not root.target then
+                       error404("No root node was registered, this usually happens if no module was installed.\n" ..
+                                "Install luci-mod-admin-full and retry. " ..
+                                "If the module is already installed, try removing the /tmp/luci-indexcache file.")
+               else
+                       error404("No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
+                                "If this url belongs to an extension, make sure it is properly installed.\n" ..
+                                "If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
+               end
+       end
+end
+
+--- Generate the dispatching index using the best possible strategy.
+function createindex()
+       local path = luci.util.libpath() .. "/controller/"
+       local suff = { ".lua", ".lua.gz" }
+
+       if luci.util.copcall(require, "luci.fastindex") then
+               createindex_fastindex(path, suff)
+       else
+               createindex_plain(path, suff)
+       end
+end
+
+--- Generate the dispatching index using the fastindex C-indexer.
+-- @param path         Controller base directory
+-- @param suffixes     Controller file suffixes
+function createindex_fastindex(path, suffixes)
+       index = {}
+
+       if not fi then
+               fi = luci.fastindex.new("index")
+               for _, suffix in ipairs(suffixes) do
+                       fi.add(path .. "*" .. suffix)
+                       fi.add(path .. "*/*" .. suffix)
+               end
+       end
+       fi.scan()
+
+       for k, v in pairs(fi.indexes) do
+               index[v[2]] = v[1]
+       end
+end
+
+--- Generate the dispatching index using the native file-cache based strategy.
+-- @param path         Controller base directory
+-- @param suffixes     Controller file suffixes
+function createindex_plain(path, suffixes)
+       local controllers = { }
+       for _, suffix in ipairs(suffixes) do
+               nixio.util.consume((fs.glob(path .. "*" .. suffix)), controllers)
+               nixio.util.consume((fs.glob(path .. "*/*" .. suffix)), controllers)
+       end
+
+       if indexcache then
+               local cachedate = fs.stat(indexcache, "mtime")
+               if cachedate then
+                       local realdate = 0
+                       for _, obj in ipairs(controllers) do
+                               local omtime = fs.stat(obj, "mtime")
+                               realdate = (omtime and omtime > realdate) and omtime or realdate
+                       end
+
+                       if cachedate > realdate then
+                               assert(
+                                       sys.process.info("uid") == fs.stat(indexcache, "uid")
+                                       and fs.stat(indexcache, "modestr") == "rw-------",
+                                       "Fatal: Indexcache is not sane!"
+                               )
+
+                               index = loadfile(indexcache)()
+                               return index
+                       end
+               end
+       end
+
+       index = {}
+
+       for i,c in ipairs(controllers) do
+               local modname = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
+               for _, suffix in ipairs(suffixes) do
+                       modname = modname:gsub(suffix.."$", "")
+               end
+
+               local mod = require(modname)
+               assert(mod ~= true,
+                      "Invalid controller file found\n" ..
+                      "The file '" .. c .. "' contains an invalid module line.\n" ..
+                      "Please verify whether the module name is set to '" .. modname ..
+                      "' - It must correspond to the file path!")
+
+               local idx = mod.index
+               assert(type(idx) == "function",
+                      "Invalid controller file found\n" ..
+                      "The file '" .. c .. "' contains no index() function.\n" ..
+                      "Please make sure that the controller contains a valid " ..
+                      "index function and verify the spelling!")
+
+               index[modname] = idx
+       end
+
+       if indexcache then
+               local f = nixio.open(indexcache, "w", 600)
+               f:writeall(util.get_bytecode(index))
+               f:close()
+       end
+end
+
+--- Create the dispatching tree from the index.
+-- Build the index before if it does not exist yet.
+function createtree()
+       if not index then
+               createindex()
+       end
+
+       local ctx  = context
+       local tree = {nodes={}, inreq=true}
+       local modi = {}
+
+       ctx.treecache = setmetatable({}, {__mode="v"})
+       ctx.tree = tree
+       ctx.modifiers = modi
+
+       -- Load default translation
+       require "luci.i18n".loadc("base")
+
+       local scope = setmetatable({}, {__index = luci.dispatcher})
+
+       for k, v in pairs(index) do
+               scope._NAME = k
+               setfenv(v, scope)
+               v()
+       end
+
+       local function modisort(a,b)
+               return modi[a].order < modi[b].order
+       end
+
+       for _, v in util.spairs(modi, modisort) do
+               scope._NAME = v.module
+               setfenv(v.func, scope)
+               v.func()
+       end
+
+       return tree
+end
+
+--- Register a tree modifier.
+-- @param      func    Modifier function
+-- @param      order   Modifier order value (optional)
+function modifier(func, order)
+       context.modifiers[#context.modifiers+1] = {
+               func = func,
+               order = order or 0,
+               module
+                       = getfenv(2)._NAME
+       }
+end
+
+--- Clone a node of the dispatching tree to another position.
+-- @param      path    Virtual path destination
+-- @param      clone   Virtual path source
+-- @param      title   Destination node title (optional)
+-- @param      order   Destination node order value (optional)
+-- @return                     Dispatching tree node
+function assign(path, clone, title, order)
+       local obj  = node(unpack(path))
+       obj.nodes  = nil
+       obj.module = nil
+
+       obj.title = title
+       obj.order = order
+
+       setmetatable(obj, {__index = _create_node(clone)})
+
+       return obj
+end
+
+--- Create a new dispatching node and define common parameters.
+-- @param      path    Virtual path
+-- @param      target  Target function to call when dispatched.
+-- @param      title   Destination node title
+-- @param      order   Destination node order value (optional)
+-- @return                     Dispatching tree node
+function entry(path, target, title, order)
+       local c = node(unpack(path))
+
+       c.target = target
+       c.title  = title
+       c.order  = order
+       c.module = getfenv(2)._NAME
+
+       return c
+end
+
+--- Fetch or create a dispatching node without setting the target module or
+-- enabling the node.
+-- @param      ...             Virtual path
+-- @return                     Dispatching tree node
+function get(...)
+       return _create_node({...})
+end
+
+--- Fetch or create a new dispatching node.
+-- @param      ...             Virtual path
+-- @return                     Dispatching tree node
+function node(...)
+       local c = _create_node({...})
+
+       c.module = getfenv(2)._NAME
+       c.auto = nil
+
+       return c
+end
+
+function _create_node(path)
+       if #path == 0 then
+               return context.tree
+       end
+
+       local name = table.concat(path, ".")
+       local c = context.treecache[name]
+
+       if not c then
+               local last = table.remove(path)
+               local parent = _create_node(path)
+
+               c = {nodes={}, auto=true}
+               -- the node is "in request" if the request path matches
+               -- at least up to the length of the node path
+               if parent.inreq and context.path[#path+1] == last then
+                 c.inreq = true
+               end
+               parent.nodes[last] = c
+               context.treecache[name] = c
+       end
+       return c
+end
+
+-- Subdispatchers --
+
+function _firstchild()
+   local path = { unpack(context.path) }
+   local name = table.concat(path, ".")
+   local node = context.treecache[name]
+
+   local lowest
+   if node and node.nodes and next(node.nodes) then
+         local k, v
+         for k, v in pairs(node.nodes) do
+                if not lowest or
+                       (v.order or 100) < (node.nodes[lowest].order or 100)
+                then
+                       lowest = k
+                end
+         end
+   end
+
+   assert(lowest ~= nil,
+                 "The requested node contains no childs, unable to redispatch")
+
+   path[#path+1] = lowest
+   dispatch(path)
+end
+
+--- Alias the first (lowest order) page automatically
+function firstchild()
+   return { type = "firstchild", target = _firstchild }
+end
+
+--- Create a redirect to another dispatching node.
+-- @param      ...             Virtual path destination
+function alias(...)
+       local req = {...}
+       return function(...)
+               for _, r in ipairs({...}) do
+                       req[#req+1] = r
+               end
+
+               dispatch(req)
+       end
+end
+
+--- Rewrite the first x path values of the request.
+-- @param      n               Number of path values to replace
+-- @param      ...             Virtual path to replace removed path values with
+function rewrite(n, ...)
+       local req = {...}
+       return function(...)
+               local dispatched = util.clone(context.dispatched)
+
+               for i=1,n do
+                       table.remove(dispatched, 1)
+               end
+
+               for i, r in ipairs(req) do
+                       table.insert(dispatched, i, r)
+               end
+
+               for _, r in ipairs({...}) do
+                       dispatched[#dispatched+1] = r
+               end
+
+               dispatch(dispatched)
+       end
+end
+
+
+local function _call(self, ...)
+       local func = getfenv()[self.name]
+       assert(func ~= nil,
+              'Cannot resolve function "' .. self.name .. '". Is it misspelled or local?')
+
+       assert(type(func) == "function",
+              'The symbol "' .. self.name .. '" does not refer to a function but data ' ..
+              'of type "' .. type(func) .. '".')
+
+       if #self.argv > 0 then
+               return func(unpack(self.argv), ...)
+       else
+               return func(...)
+       end
+end
+
+--- Create a function-call dispatching target.
+-- @param      name    Target function of local controller
+-- @param      ...             Additional parameters passed to the function
+function call(name, ...)
+       return {type = "call", argv = {...}, name = name, target = _call}
+end
+
+
+local _template = function(self, ...)
+       require "luci.template".render(self.view)
+end
+
+--- Create a template render dispatching target.
+-- @param      name    Template to be rendered
+function template(name)
+       return {type = "template", view = name, target = _template}
+end
+
+
+local function _cbi(self, ...)
+       local cbi = require "luci.cbi"
+       local tpl = require "luci.template"
+       local http = require "luci.http"
+
+       local config = self.config or {}
+       local maps = cbi.load(self.model, ...)
+
+       local state = nil
+
+       for i, res in ipairs(maps) do
+               res.flow = config
+               local cstate = res:parse()
+               if cstate and (not state or cstate < state) then
+                       state = cstate
+               end
+       end
+
+       local function _resolve_path(path)
+               return type(path) == "table" and build_url(unpack(path)) or path
+       end
+
+       if config.on_valid_to and state and state > 0 and state < 2 then
+               http.redirect(_resolve_path(config.on_valid_to))
+               return
+       end
+
+       if config.on_changed_to and state and state > 1 then
+               http.redirect(_resolve_path(config.on_changed_to))
+               return
+       end
+
+       if config.on_success_to and state and state > 0 then
+               http.redirect(_resolve_path(config.on_success_to))
+               return
+       end
+
+       if config.state_handler then
+               if not config.state_handler(state, maps) then
+                       return
+               end
+       end
+
+       http.header("X-CBI-State", state or 0)
+
+       if not config.noheader then
+               tpl.render("cbi/header", {state = state})
+       end
+
+       local redirect
+       local messages
+       local applymap   = false
+       local pageaction = true
+       local parsechain = { }
+
+       for i, res in ipairs(maps) do
+               if res.apply_needed and res.parsechain then
+                       local c
+                       for _, c in ipairs(res.parsechain) do
+                               parsechain[#parsechain+1] = c
+                       end
+                       applymap = true
+               end
+
+               if res.redirect then
+                       redirect = redirect or res.redirect
+               end
+
+               if res.pageaction == false then
+                       pageaction = false
+               end
+
+               if res.message then
+                       messages = messages or { }
+                       messages[#messages+1] = res.message
+               end
+       end
+
+       for i, res in ipairs(maps) do
+               res:render({
+                       firstmap   = (i == 1),
+                       applymap   = applymap,
+                       redirect   = redirect,
+                       messages   = messages,
+                       pageaction = pageaction,
+                       parsechain = parsechain
+               })
+       end
+
+       if not config.nofooter then
+               tpl.render("cbi/footer", {
+                       flow       = config,
+                       pageaction = pageaction,
+                       redirect   = redirect,
+                       state      = state,
+                       autoapply  = config.autoapply
+               })
+       end
+end
+
+--- Create a CBI model dispatching target.
+-- @param      model   CBI model to be rendered
+function cbi(model, config)
+       return {type = "cbi", config = config, model = model, target = _cbi}
+end
+
+
+local function _arcombine(self, ...)
+       local argv = {...}
+       local target = #argv > 0 and self.targets[2] or self.targets[1]
+       setfenv(target.target, self.env)
+       target:target(unpack(argv))
+end
+
+--- Create a combined dispatching target for non argv and argv requests.
+-- @param trg1 Overview Target
+-- @param trg2 Detail Target
+function arcombine(trg1, trg2)
+       return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}}
+end
+
+
+local function _form(self, ...)
+       local cbi = require "luci.cbi"
+       local tpl = require "luci.template"
+       local http = require "luci.http"
+
+       local maps = luci.cbi.load(self.model, ...)
+       local state = nil
+
+       for i, res in ipairs(maps) do
+               local cstate = res:parse()
+               if cstate and (not state or cstate < state) then
+                       state = cstate
+               end
+       end
+
+       http.header("X-CBI-State", state or 0)
+       tpl.render("header")
+       for i, res in ipairs(maps) do
+               res:render()
+       end
+       tpl.render("footer")
+end
+
+--- Create a CBI form model dispatching target.
+-- @param      model   CBI form model tpo be rendered
+function form(model)
+       return {type = "cbi", model = model, target = _form}
+end
+
+--- Access the luci.i18n translate() api.
+-- @class  function
+-- @name   translate
+-- @param  text    Text to translate
+translate = i18n.translate
+
+--- No-op function used to mark translation entries for menu labels.
+-- This function does not actually translate the given argument but
+-- is used by build/i18n-scan.pl to find translatable entries.
+function _(text)
+       return text
+end
diff --git a/modules/base/luasrc/luasrc/http.lua b/modules/base/luasrc/luasrc/http.lua
new file mode 100644 (file)
index 0000000..c53307a
--- /dev/null
@@ -0,0 +1,344 @@
+--[[
+LuCI - HTTP-Interaction
+
+Description:
+HTTP-Header manipulator and form variable preprocessor
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local ltn12 = require "luci.ltn12"
+local protocol = require "luci.http.protocol"
+local util  = require "luci.util"
+local string = require "string"
+local coroutine = require "coroutine"
+local table = require "table"
+
+local ipairs, pairs, next, type, tostring, error =
+       ipairs, pairs, next, type, tostring, error
+
+--- LuCI Web Framework high-level HTTP functions.
+module "luci.http"
+
+context = util.threadlocal()
+
+Request = util.class()
+function Request.__init__(self, env, sourcein, sinkerr)
+       self.input = sourcein
+       self.error = sinkerr
+
+
+       -- File handler
+       self.filehandler = function() end
+
+       -- HTTP-Message table
+       self.message = {
+               env = env,
+               headers = {},
+               params = protocol.urldecode_params(env.QUERY_STRING or ""),
+       }
+
+       self.parsed_input = false
+end
+
+function Request.formvalue(self, name, noparse)
+       if not noparse and not self.parsed_input then
+               self:_parse_input()
+       end
+
+       if name then
+               return self.message.params[name]
+       else
+               return self.message.params
+       end
+end
+
+function Request.formvaluetable(self, prefix)
+       local vals = {}
+       prefix = prefix and prefix .. "." or "."
+
+       if not self.parsed_input then
+               self:_parse_input()
+       end
+
+       local void = self.message.params[nil]
+       for k, v in pairs(self.message.params) do
+               if k:find(prefix, 1, true) == 1 then
+                       vals[k:sub(#prefix + 1)] = tostring(v)
+               end
+       end
+
+       return vals
+end
+
+function Request.content(self)
+       if not self.parsed_input then
+               self:_parse_input()
+       end
+
+       return self.message.content, self.message.content_length
+end
+
+function Request.getcookie(self, name)
+  local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
+  local p = ";" .. name .. "=(.-);"
+  local i, j, value = c:find(p)
+  return value and urldecode(value)
+end
+
+function Request.getenv(self, name)
+       if name then
+               return self.message.env[name]
+       else
+               return self.message.env
+       end
+end
+
+function Request.setfilehandler(self, callback)
+       self.filehandler = callback
+end
+
+function Request._parse_input(self)
+       protocol.parse_message_body(
+                self.input,
+                self.message,
+                self.filehandler
+       )
+       self.parsed_input = true
+end
+
+--- Close the HTTP-Connection.
+function close()
+       if not context.eoh then
+               context.eoh = true
+               coroutine.yield(3)
+       end
+
+       if not context.closed then
+               context.closed = true
+               coroutine.yield(5)
+       end
+end
+
+--- Return the request content if the request was of unknown type.
+-- @return     HTTP request body
+-- @return     HTTP request body length
+function content()
+       return context.request:content()
+end
+
+--- Get a certain HTTP input value or a table of all input values.
+-- @param name         Name of the GET or POST variable to fetch
+-- @param noparse      Don't parse POST data before getting the value
+-- @return                     HTTP input value or table of all input value
+function formvalue(name, noparse)
+       return context.request:formvalue(name, noparse)
+end
+
+--- Get a table of all HTTP input values with a certain prefix.
+-- @param prefix       Prefix
+-- @return                     Table of all HTTP input values with given prefix
+function formvaluetable(prefix)
+       return context.request:formvaluetable(prefix)
+end
+
+--- Get the value of a certain HTTP-Cookie.
+-- @param name         Cookie Name
+-- @return                     String containing cookie data
+function getcookie(name)
+       return context.request:getcookie(name)
+end
+
+--- Get the value of a certain HTTP environment variable
+-- or the environment table itself.
+-- @param name         Environment variable
+-- @return                     HTTP environment value or environment table
+function getenv(name)
+       return context.request:getenv(name)
+end
+
+--- Set a handler function for incoming user file uploads.
+-- @param callback     Handler function
+function setfilehandler(callback)
+       return context.request:setfilehandler(callback)
+end
+
+--- Send a HTTP-Header.
+-- @param key  Header key
+-- @param value Header value
+function header(key, value)
+       if not context.headers then
+               context.headers = {}
+       end
+       context.headers[key:lower()] = value
+       coroutine.yield(2, key, value)
+end
+
+--- Set the mime type of following content data.
+-- @param mime Mimetype of following content
+function prepare_content(mime)
+       if not context.headers or not context.headers["content-type"] then
+               if mime == "application/xhtml+xml" then
+                       if not getenv("HTTP_ACCEPT") or
+                         not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
+                               mime = "text/html; charset=UTF-8"
+                       end
+                       header("Vary", "Accept")
+               end
+               header("Content-Type", mime)
+       end
+end
+
+--- Get the RAW HTTP input source
+-- @return     HTTP LTN12 source
+function source()
+       return context.request.input
+end
+
+--- Set the HTTP status code and status message.
+-- @param code         Status code
+-- @param message      Status message
+function status(code, message)
+       code = code or 200
+       message = message or "OK"
+       context.status = code
+       coroutine.yield(1, code, message)
+end
+
+--- Send a chunk of content data to the client.
+-- This function is as a valid LTN12 sink.
+-- If the content chunk is nil this function will automatically invoke close.
+-- @param content      Content chunk
+-- @param src_err      Error object from source (optional)
+-- @see close
+function write(content, src_err)
+       if not content then
+               if src_err then
+                       error(src_err)
+               else
+                       close()
+               end
+               return true
+       elseif #content == 0 then
+               return true
+       else
+               if not context.eoh then
+                       if not context.status then
+                               status()
+                       end
+                       if not context.headers or not context.headers["content-type"] then
+                               header("Content-Type", "text/html; charset=utf-8")
+                       end
+                       if not context.headers["cache-control"] then
+                               header("Cache-Control", "no-cache")
+                               header("Expires", "0")
+                       end
+
+
+                       context.eoh = true
+                       coroutine.yield(3)
+               end
+               coroutine.yield(4, content)
+               return true
+       end
+end
+
+--- Splice data from a filedescriptor to the client.
+-- @param fp   File descriptor
+-- @param size Bytes to splice (optional)
+function splice(fd, size)
+       coroutine.yield(6, fd, size)
+end
+
+--- Redirects the client to a new URL and closes the connection.
+-- @param url  Target URL
+function redirect(url)
+       status(302, "Found")
+       header("Location", url)
+       close()
+end
+
+--- Create a querystring out of a table of key - value pairs.
+-- @param table                Query string source table
+-- @return                     Encoded HTTP query string
+function build_querystring(q)
+       local s = { "?" }
+
+       for k, v in pairs(q) do
+               if #s > 1 then s[#s+1] = "&" end
+
+               s[#s+1] = urldecode(k)
+               s[#s+1] = "="
+               s[#s+1] = urldecode(v)
+       end
+
+       return table.concat(s, "")
+end
+
+--- Return the URL-decoded equivalent of a string.
+-- @param str          URL-encoded string
+-- @param no_plus      Don't decode + to " "
+-- @return                     URL-decoded string
+-- @see urlencode
+urldecode = protocol.urldecode
+
+--- Return the URL-encoded equivalent of a string.
+-- @param str          Source string
+-- @return                     URL-encoded string
+-- @see urldecode
+urlencode = protocol.urlencode
+
+--- Send the given data as JSON encoded string.
+-- @param data         Data to send
+function write_json(x)
+       if x == nil then
+               write("null")
+       elseif type(x) == "table" then
+               local k, v
+               if type(next(x)) == "number" then
+                       write("[ ")
+                       for k, v in ipairs(x) do
+                               write_json(v)
+                               if next(x, k) then
+                                       write(", ")
+                               end
+                       end
+                       write(" ]")
+               else
+                       write("{ ")
+                       for k, v in pairs(x) do
+                       write("%q: " % k)
+                               write_json(v)
+                               if next(x, k) then
+                                       write(", ")
+                               end
+                       end
+                       write(" }")
+               end
+       elseif type(x) == "number" or type(x) == "boolean" then
+               if (x ~= x) then
+                       -- NaN is the only value that doesn't equal to itself.
+                       write("Number.NaN")
+               else
+                       write(tostring(x))
+               end
+       else
+               write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
+                       return '\\u%04x' % c:byte(1)
+               end))
+       end
+end
diff --git a/modules/base/luasrc/luasrc/http/protocol.lua b/modules/base/luasrc/luasrc/http/protocol.lua
new file mode 100644 (file)
index 0000000..0d41550
--- /dev/null
@@ -0,0 +1,688 @@
+--[[
+
+HTTP protocol implementation for LuCI
+(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+]]--
+
+--- LuCI http protocol class.
+-- This class contains several functions useful for http message- and content
+-- decoding and to retrive form data from raw http messages.
+module("luci.http.protocol", package.seeall)
+
+local ltn12 = require("luci.ltn12")
+
+HTTP_MAX_CONTENT      = 1024*8         -- 8 kB maximum content size
+
+--- Decode an urlencoded string - optionally without decoding
+-- the "+" sign to " " - and return the decoded string.
+-- @param str          Input string in x-www-urlencoded format
+-- @param no_plus      Don't decode "+" signs to spaces
+-- @return                     The decoded string
+-- @see                                urlencode
+function urldecode( str, no_plus )
+
+       local function __chrdec( hex )
+               return string.char( tonumber( hex, 16 ) )
+       end
+
+       if type(str) == "string" then
+               if not no_plus then
+                       str = str:gsub( "+", " " )
+               end
+
+               str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
+       end
+
+       return str
+end
+
+--- Extract and split urlencoded data pairs, separated bei either "&" or ";"
+-- from given url or string. Returns a table with urldecoded values.
+-- Simple parameters are stored as string values associated with the parameter
+-- name within the table. Parameters with multiple values are stored as array
+-- containing the corresponding values.
+-- @param url  The url or string which contains x-www-urlencoded form data
+-- @param tbl  Use the given table for storing values (optional)
+-- @return             Table containing the urldecoded parameters
+-- @see                        urlencode_params
+function urldecode_params( url, tbl )
+
+       local params = tbl or { }
+
+       if url:find("?") then
+               url = url:gsub( "^.+%?([^?]+)", "%1" )
+       end
+
+       for pair in url:gmatch( "[^&;]+" ) do
+
+               -- find key and value
+               local key = urldecode( pair:match("^([^=]+)")     )
+               local val = urldecode( pair:match("^[^=]+=(.+)$") )
+
+               -- store
+               if type(key) == "string" and key:len() > 0 then
+                       if type(val) ~= "string" then val = "" end
+
+                       if not params[key] then
+                               params[key] = val
+                       elseif type(params[key]) ~= "table" then
+                               params[key] = { params[key], val }
+                       else
+                               table.insert( params[key], val )
+                       end
+               end
+       end
+
+       return params
+end
+
+--- Encode given string to x-www-urlencoded format.
+-- @param str  String to encode
+-- @return             String containing the encoded data
+-- @see                        urldecode
+function urlencode( str )
+
+       local function __chrenc( chr )
+               return string.format(
+                       "%%%02x", string.byte( chr )
+               )
+       end
+
+       if type(str) == "string" then
+               str = str:gsub(
+                       "([^a-zA-Z0-9$_%-%.%+!*'(),])",
+                       __chrenc
+               )
+       end
+
+       return str
+end
+
+--- Encode each key-value-pair in given table to x-www-urlencoded format,
+-- separated by "&". Tables are encoded as parameters with multiple values by
+-- repeating the parameter name with each value.
+-- @param tbl  Table with the values
+-- @return             String containing encoded values
+-- @see                        urldecode_params
+function urlencode_params( tbl )
+       local enc = ""
+
+       for k, v in pairs(tbl) do
+               if type(v) == "table" then
+                       for i, v2 in ipairs(v) do
+                               enc = enc .. ( #enc > 0 and "&" or "" ) ..
+                                       urlencode(k) .. "=" .. urlencode(v2)
+                       end
+               else
+                       enc = enc .. ( #enc > 0 and "&" or "" ) ..
+                               urlencode(k) .. "=" .. urlencode(v)
+               end
+       end
+
+       return enc
+end
+
+-- (Internal function)
+-- Initialize given parameter and coerce string into table when the parameter
+-- already exists.
+-- @param tbl  Table where parameter should be created
+-- @param key  Parameter name
+-- @return             Always nil
+local function __initval( tbl, key )
+       if tbl[key] == nil then
+               tbl[key] = ""
+       elseif type(tbl[key]) == "string" then
+               tbl[key] = { tbl[key], "" }
+       else
+               table.insert( tbl[key], "" )
+       end
+end
+
+-- (Internal function)
+-- Append given data to given parameter, either by extending the string value
+-- or by appending it to the last string in the parameter's value table.
+-- @param tbl  Table containing the previously initialized parameter value
+-- @param key  Parameter name
+-- @param chunk        String containing the data to append
+-- @return             Always nil
+-- @see                        __initval
+local function __appendval( tbl, key, chunk )
+       if type(tbl[key]) == "table" then
+               tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
+       else
+               tbl[key] = tbl[key] .. chunk
+       end
+end
+
+-- (Internal function)
+-- Finish the value of given parameter, either by transforming the string value
+-- or - in the case of multi value parameters - the last element in the
+-- associated values table.
+-- @param tbl          Table containing the previously initialized parameter value
+-- @param key          Parameter name
+-- @param handler      Function which transforms the parameter value
+-- @return                     Always nil
+-- @see                                __initval
+-- @see                                __appendval
+local function __finishval( tbl, key, handler )
+       if handler then
+               if type(tbl[key]) == "table" then
+                       tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
+               else
+                       tbl[key] = handler( tbl[key] )
+               end
+       end
+end
+
+
+-- Table of our process states
+local process_states = { }
+
+-- Extract "magic", the first line of a http message.
+-- Extracts the message type ("get", "post" or "response"), the requested uri
+-- or the status code if the line descripes a http response.
+process_states['magic'] = function( msg, chunk, err )
+
+       if chunk ~= nil then
+               -- ignore empty lines before request
+               if #chunk == 0 then
+                       return true, nil
+               end
+
+               -- Is it a request?
+               local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
+
+               -- Yup, it is
+               if method then
+
+                       msg.type           = "request"
+                       msg.request_method = method:lower()
+                       msg.request_uri    = uri
+                       msg.http_version   = tonumber( http_ver )
+                       msg.headers        = { }
+
+                       -- We're done, next state is header parsing
+                       return true, function( chunk )
+                               return process_states['headers']( msg, chunk )
+                       end
+
+               -- Is it a response?
+               else
+
+                       local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
+
+                       -- Is a response
+                       if code then
+
+                               msg.type           = "response"
+                               msg.status_code    = code
+                               msg.status_message = message
+                               msg.http_version   = tonumber( http_ver )
+                               msg.headers        = { }
+
+                               -- We're done, next state is header parsing
+                               return true, function( chunk )
+                                       return process_states['headers']( msg, chunk )
+                               end
+                       end
+               end
+       end
+
+       -- Can't handle it
+       return nil, "Invalid HTTP message magic"
+end
+
+
+-- Extract headers from given string.
+process_states['headers'] = function( msg, chunk )
+
+       if chunk ~= nil then
+
+               -- Look for a valid header format
+               local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" )
+
+               if type(hdr) == "string" and hdr:len() > 0 and
+                  type(val) == "string" and val:len() > 0
+               then
+                       msg.headers[hdr] = val
+
+                       -- Valid header line, proceed
+                       return true, nil
+
+               elseif #chunk == 0 then
+                       -- Empty line, we won't accept data anymore
+                       return false, nil
+               else
+                       -- Junk data
+                       return nil, "Invalid HTTP header received"
+               end
+       else
+               return nil, "Unexpected EOF"
+       end
+end
+
+
+--- Creates a ltn12 source from the given socket. The source will return it's
+-- data line by line with the trailing \r\n stripped of.
+-- @param sock Readable network socket
+-- @return             Ltn12 source function
+function header_source( sock )
+       return ltn12.source.simplify( function()
+
+               local chunk, err, part = sock:receive("*l")
+
+               -- Line too long
+               if chunk == nil then
+                       if err ~= "timeout" then
+                               return nil, part
+                                       and "Line exceeds maximum allowed length"
+                                       or  "Unexpected EOF"
+                       else
+                               return nil, err
+                       end
+
+               -- Line ok
+               elseif chunk ~= nil then
+
+                       -- Strip trailing CR
+                       chunk = chunk:gsub("\r$","")
+
+                       return chunk, nil
+               end
+       end )
+end
+
+--- Decode a mime encoded http message body with multipart/form-data
+-- Content-Type. Stores all extracted data associated with its parameter name
+-- in the params table withing the given message object. Multiple parameter
+-- values are stored as tables, ordinary ones as strings.
+-- If an optional file callback function is given then it is feeded with the
+-- file contents chunk by chunk and only the extracted file name is stored
+-- within the params table. The callback function will be called subsequently
+-- with three arguments:
+--  o Table containing decoded (name, file) and raw (headers) mime header data
+--  o String value containing a chunk of the file data
+--  o Boolean which indicates wheather the current chunk is the last one (eof)
+-- @param src          Ltn12 source function
+-- @param msg          HTTP message object
+-- @param filecb       File callback function (optional)
+-- @return                     Value indicating successful operation (not nil means "ok")
+-- @return                     String containing the error if unsuccessful
+-- @see                                parse_message_header
+function mimedecode_message_body( src, msg, filecb )
+
+       if msg and msg.env.CONTENT_TYPE then
+               msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
+       end
+
+       if not msg.mime_boundary then
+               return nil, "Invalid Content-Type found"
+       end
+
+
+       local tlen   = 0
+       local inhdr  = false
+       local field  = nil
+       local store  = nil
+       local lchunk = nil
+
+       local function parse_headers( chunk, field )
+
+               local stat
+               repeat
+                       chunk, stat = chunk:gsub(
+                               "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
+                               function(k,v)
+                                       field.headers[k] = v
+                                       return ""
+                               end
+                       )
+               until stat == 0
+
+               chunk, stat = chunk:gsub("^\r\n","")
+
+               -- End of headers
+               if stat > 0 then
+                       if field.headers["Content-Disposition"] then
+                               if field.headers["Content-Disposition"]:match("^form%-data; ") then
+                                       field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
+                                       field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
+                               end
+                       end
+
+                       if not field.headers["Content-Type"] then
+                               field.headers["Content-Type"] = "text/plain"
+                       end
+
+                       if field.name and field.file and filecb then
+                               __initval( msg.params, field.name )
+                               __appendval( msg.params, field.name, field.file )
+
+                               store = filecb
+                       elseif field.name then
+                               __initval( msg.params, field.name )
+
+                               store = function( hdr, buf, eof )
+                                       __appendval( msg.params, field.name, buf )
+                               end
+                       else
+                               store = nil
+                       end
+
+                       return chunk, true
+               end
+
+               return chunk, false
+       end
+
+       local function snk( chunk )
+
+               tlen = tlen + ( chunk and #chunk or 0 )
+
+               if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
+                       return nil, "Message body size exceeds Content-Length"
+               end
+
+               if chunk and not lchunk then
+                       lchunk = "\r\n" .. chunk
+
+               elseif lchunk then
+                       local data = lchunk .. ( chunk or "" )
+                       local spos, epos, found
+
+                       repeat
+                               spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
+
+                               if not spos then
+                                       spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
+                               end
+
+
+                               if spos then
+                                       local predata = data:sub( 1, spos - 1 )
+
+                                       if inhdr then
+                                               predata, eof = parse_headers( predata, field )
+
+                                               if not eof then
+                                                       return nil, "Invalid MIME section header"
+                                               elseif not field.name then
+                                                       return nil, "Invalid Content-Disposition header"
+                                               end
+                                       end
+
+                                       if store then
+                                               store( field, predata, true )
+                                       end
+
+
+                                       field = { headers = { } }
+                                       found = found or true
+
+                                       data, eof = parse_headers( data:sub( epos + 1, #data ), field )
+                                       inhdr = not eof
+                               end
+                       until not spos
+
+                       if found then
+                               -- We found at least some boundary. Save
+                               -- the unparsed remaining data for the
+                               -- next chunk.
+                               lchunk, data = data, nil
+                       else
+                               -- There was a complete chunk without a boundary. Parse it as headers or
+                               -- append it as data, depending on our current state.
+                               if inhdr then
+                                       lchunk, eof = parse_headers( data, field )
+                                       inhdr = not eof
+                               else
+                                       -- We're inside data, so append the data. Note that we only append
+                                       -- lchunk, not all of data, since there is a chance that chunk
+                                       -- contains half a boundary. Assuming that each chunk is at least the
+                                       -- boundary in size, this should prevent problems
+                                       store( field, lchunk, false )
+                                       lchunk, chunk = chunk, nil
+                               end
+                       end
+               end
+
+               return true
+       end
+
+       return ltn12.pump.all( src, snk )
+end
+
+--- Decode an urlencoded http message body with application/x-www-urlencoded
+-- Content-Type. Stores all extracted data associated with its parameter name
+-- in the params table withing the given message object. Multiple parameter
+-- values are stored as tables, ordinary ones as strings.
+-- @param src  Ltn12 source function
+-- @param msg  HTTP message object
+-- @return             Value indicating successful operation (not nil means "ok")
+-- @return             String containing the error if unsuccessful
+-- @see                        parse_message_header
+function urldecode_message_body( src, msg )
+
+       local tlen   = 0
+       local lchunk = nil
+
+       local function snk( chunk )
+
+               tlen = tlen + ( chunk and #chunk or 0 )
+
+               if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
+                       return nil, "Message body size exceeds Content-Length"
+               elseif tlen > HTTP_MAX_CONTENT then
+                       return nil, "Message body size exceeds maximum allowed length"
+               end
+
+               if not lchunk and chunk then
+                       lchunk = chunk
+
+               elseif lchunk then
+                       local data = lchunk .. ( chunk or "&" )
+                       local spos, epos
+
+                       repeat
+                               spos, epos = data:find("^.-[;&]")
+
+                               if spos then
+                                       local pair = data:sub( spos, epos - 1 )
+                                       local key  = pair:match("^(.-)=")
+                                       local val  = pair:match("=([^%s]*)%s*$")
+
+                                       if key and #key > 0 then
+                                               __initval( msg.params, key )
+                                               __appendval( msg.params, key, val )
+                                               __finishval( msg.params, key, urldecode )
+                                       end
+
+                                       data = data:sub( epos + 1, #data )
+                               end
+                       until not spos
+
+                       lchunk = data
+               end
+
+               return true
+       end
+
+       return ltn12.pump.all( src, snk )
+end
+
+--- Try to extract an http message header including information like protocol
+-- version, message headers and resulting CGI environment variables from the
+-- given ltn12 source.
+-- @param src  Ltn12 source function
+-- @return             HTTP message object
+-- @see                        parse_message_body
+function parse_message_header( src )
+
+       local ok   = true
+       local msg  = { }
+
+       local sink = ltn12.sink.simplify(
+               function( chunk )
+                       return process_states['magic']( msg, chunk )
+               end
+       )
+
+       -- Pump input data...
+       while ok do
+
+               -- get data
+               ok, err = ltn12.pump.step( src, sink )
+
+               -- error
+               if not ok and err then
+                       return nil, err
+
+               -- eof
+               elseif not ok then
+
+                       -- Process get parameters
+                       if ( msg.request_method == "get" or msg.request_method == "post" ) and
+                          msg.request_uri:match("?")
+                       then
+                               msg.params = urldecode_params( msg.request_uri )
+                       else
+                               msg.params = { }
+                       end
+
+                       -- Populate common environment variables
+                       msg.env = {
+                               CONTENT_LENGTH    = msg.headers['Content-Length'];
+                               CONTENT_TYPE      = msg.headers['Content-Type'] or msg.headers['Content-type'];
+                               REQUEST_METHOD    = msg.request_method:upper();
+                               REQUEST_URI       = msg.request_uri;
+                               SCRIPT_NAME       = msg.request_uri:gsub("?.+$","");
+                               SCRIPT_FILENAME   = "";         -- XXX implement me
+                               SERVER_PROTOCOL   = "HTTP/" .. string.format("%.1f", msg.http_version);
+                               QUERY_STRING      = msg.request_uri:match("?")
+                                       and msg.request_uri:gsub("^.+?","") or ""
+                       }
+
+                       -- Populate HTTP_* environment variables
+                       for i, hdr in ipairs( {
+                               'Accept',
+                               'Accept-Charset',
+                               'Accept-Encoding',
+                               'Accept-Language',
+                               'Connection',
+                               'Cookie',
+                               'Host',
+                               'Referer',
+                               'User-Agent',
+                       } ) do
+                               local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
+                               local val = msg.headers[hdr]
+
+                               msg.env[var] = val
+                       end
+               end
+       end
+
+       return msg
+end
+
+--- Try to extract and decode a http message body from the given ltn12 source.
+-- This function will examine the Content-Type within the given message object
+-- to select the appropriate content decoder.
+-- Currently the application/x-www-urlencoded and application/form-data
+-- mime types are supported. If the encountered content encoding can't be
+-- handled then the whole message body will be stored unaltered as "content"
+-- property within the given message object.
+-- @param src          Ltn12 source function
+-- @param msg          HTTP message object
+-- @param filecb       File data callback (optional, see mimedecode_message_body())
+-- @return                     Value indicating successful operation (not nil means "ok")
+-- @return                     String containing the error if unsuccessful
+-- @see                                parse_message_header
+function parse_message_body( src, msg, filecb )
+       -- Is it multipart/mime ?
+       if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
+          msg.env.CONTENT_TYPE:match("^multipart/form%-data")
+       then
+
+               return mimedecode_message_body( src, msg, filecb )
+
+       -- Is it application/x-www-form-urlencoded ?
+       elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
+              msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
+       then
+               return urldecode_message_body( src, msg, filecb )
+
+
+       -- Unhandled encoding
+       -- If a file callback is given then feed it chunk by chunk, else
+       -- store whole buffer in message.content
+       else
+
+               local sink
+
+               -- If we have a file callback then feed it
+               if type(filecb) == "function" then
+                       sink = filecb
+
+               -- ... else append to .content
+               else
+                       msg.content = ""
+                       msg.content_length = 0
+
+                       sink = function( chunk, err )
+                               if chunk then
+                                       if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
+                                               msg.content        = msg.content        .. chunk
+                                               msg.content_length = msg.content_length + #chunk
+                                               return true
+                                       else
+                                               return nil, "POST data exceeds maximum allowed length"
+                                       end
+                               end
+                               return true
+                       end
+               end
+
+               -- Pump data...
+               while true do
+                       local ok, err = ltn12.pump.step( src, sink )
+
+                       if not ok and err then
+                               return nil, err
+                       elseif not err then
+                               return true
+                       end
+               end
+
+               return true
+       end
+end
+
+--- Table containing human readable messages for several http status codes.
+-- @class table
+statusmsg = {
+       [200] = "OK",
+       [206] = "Partial Content",
+       [301] = "Moved Permanently",
+       [302] = "Found",
+       [304] = "Not Modified",
+       [400] = "Bad Request",
+       [403] = "Forbidden",
+       [404] = "Not Found",
+       [405] = "Method Not Allowed",
+       [408] = "Request Time-out",
+       [411] = "Length Required",
+       [412] = "Precondition Failed",
+       [416] = "Requested range not satisfiable",
+       [500] = "Internal Server Error",
+       [503] = "Server Unavailable",
+}
diff --git a/modules/base/luasrc/luasrc/http/protocol/conditionals.lua b/modules/base/luasrc/luasrc/http/protocol/conditionals.lua
new file mode 100644 (file)
index 0000000..75e1f7b
--- /dev/null
@@ -0,0 +1,153 @@
+--[[
+
+HTTP protocol implementation for LuCI - RFC2616 / 14.19, 14.24 - 14.28
+(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+]]--
+
+--- LuCI http protocol implementation - HTTP/1.1 bits.
+-- This class provides basic ETag handling and implements most of the
+-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
+module("luci.http.protocol.conditionals", package.seeall)
+
+local date = require("luci.http.protocol.date")
+
+
+--- Implement 14.19 / ETag.
+-- @param stat A file.stat structure
+-- @return             String containing the generated tag suitable for ETag headers
+function mk_etag( stat )
+       if stat ~= nil then
+               return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
+       end
+end
+
+--- 14.24 / If-Match
+-- Test whether the given message object contains an "If-Match" header and
+-- compare it against the given stat object.
+-- @param req  HTTP request message object
+-- @param stat A file.stat object
+-- @return             Boolean indicating whether the precondition is ok
+-- @return             Alternative status code if the precondition failed
+function if_match( req, stat )
+       local h    = req.headers
+       local etag = mk_etag( stat )
+
+       -- Check for matching resource
+       if type(h['If-Match']) == "string" then
+               for ent in h['If-Match']:gmatch("([^, ]+)") do
+                       if ( ent == '*' or ent == etag ) and stat ~= nil then
+                               return true
+                       end
+               end
+
+               return false, 412
+       end
+
+       return true
+end
+
+--- 14.25 / If-Modified-Since
+-- Test whether the given message object contains an "If-Modified-Since" header
+-- and compare it against the given stat object.
+-- @param req  HTTP request message object
+-- @param stat A file.stat object
+-- @return             Boolean indicating whether the precondition is ok
+-- @return             Alternative status code if the precondition failed
+-- @return             Table containing extra HTTP headers if the precondition failed
+function if_modified_since( req, stat )
+       local h = req.headers
+
+       -- Compare mtimes
+       if type(h['If-Modified-Since']) == "string" then
+               local since = date.to_unix( h['If-Modified-Since'] )
+
+               if stat == nil or since < stat.mtime then
+                       return true
+               end
+
+               return false, 304, {
+                       ["ETag"]          = mk_etag( stat );
+                       ["Date"]          = date.to_http( os.time() );
+                       ["Last-Modified"] = date.to_http( stat.mtime )
+               }
+       end
+
+       return true
+end
+
+--- 14.26 / If-None-Match
+-- Test whether the given message object contains an "If-None-Match" header and
+-- compare it against the given stat object.
+-- @param req  HTTP request message object
+-- @param stat A file.stat object
+-- @return             Boolean indicating whether the precondition is ok
+-- @return             Alternative status code if the precondition failed
+-- @return             Table containing extra HTTP headers if the precondition failed
+function if_none_match( req, stat )
+       local h      = req.headers
+       local etag   = mk_etag( stat )
+       local method = req.env and req.env.REQUEST_METHOD or "GET"
+
+       -- Check for matching resource
+       if type(h['If-None-Match']) == "string" then
+               for ent in h['If-None-Match']:gmatch("([^, ]+)") do
+                       if ( ent == '*' or ent == etag ) and stat ~= nil then
+                               if method == "GET" or method == "HEAD" then
+                                       return false, 304, {
+                                               ["ETag"]          = etag;
+                                               ["Date"]          = date.to_http( os.time() );
+                                               ["Last-Modified"] = date.to_http( stat.mtime )
+                                       }
+                               else
+                                       return false, 412
+                               end
+                       end
+               end
+       end
+
+       return true
+end
+
+--- 14.27 / If-Range
+-- The If-Range header is currently not implemented due to the lack of general
+-- byte range stuff in luci.http.protocol . This function will always return
+-- false, 412 to indicate a failed precondition.
+-- @param req  HTTP request message object
+-- @param stat A file.stat object
+-- @return             Boolean indicating whether the precondition is ok
+-- @return             Alternative status code if the precondition failed
+function if_range( req, stat )
+       -- Sorry, no subranges (yet)
+       return false, 412
+end
+
+--- 14.28 / If-Unmodified-Since
+-- Test whether the given message object contains an "If-Unmodified-Since"
+-- header and compare it against the given stat object.
+-- @param req  HTTP request message object
+-- @param stat A file.stat object
+-- @return             Boolean indicating whether the precondition is ok
+-- @return             Alternative status code if the precondition failed
+function if_unmodified_since( req, stat )
+       local h = req.headers
+
+       -- Compare mtimes
+       if type(h['If-Unmodified-Since']) == "string" then
+               local since = date.to_unix( h['If-Unmodified-Since'] )
+
+               if stat ~= nil and since <= stat.mtime then
+                       return false, 412
+               end
+       end
+
+       return true
+end
diff --git a/modules/base/luasrc/luasrc/http/protocol/date.lua b/modules/base/luasrc/luasrc/http/protocol/date.lua
new file mode 100644 (file)
index 0000000..83d11e2
--- /dev/null
@@ -0,0 +1,115 @@
+--[[
+
+HTTP protocol implementation for LuCI - date handling
+(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+]]--
+
+--- LuCI http protocol implementation - date helper class.
+-- This class contains functions to parse, compare and format http dates.
+module("luci.http.protocol.date", package.seeall)
+
+require("luci.sys.zoneinfo")
+
+
+MONTHS = {
+       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+       "Sep", "Oct", "Nov", "Dec"
+}
+
+--- Return the time offset in seconds between the UTC and given time zone.
+-- @param tz   Symbolic or numeric timezone specifier
+-- @return             Time offset to UTC in seconds
+function tz_offset(tz)
+
+       if type(tz) == "string" then
+
+               -- check for a numeric identifier
+               local s, v = tz:match("([%+%-])([0-9]+)")
+               if s == '+' then s = 1 else s = -1 end
+               if v then v = tonumber(v) end
+
+               if s and v then
+                       return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
+
+               -- lookup symbolic tz
+               elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
+                       return luci.sys.zoneinfo.OFFSET[tz:lower()]
+               end
+
+       end
+
+       -- bad luck
+       return 0
+end
+
+--- Parse given HTTP date string and convert it to unix epoch time.
+-- @param data String containing the date
+-- @return             Unix epoch time
+function to_unix(date)
+
+       local wd, day, mon, yr, hr, min, sec, tz = date:match(
+               "([A-Z][a-z][a-z]), ([0-9]+) " ..
+               "([A-Z][a-z][a-z]) ([0-9]+) " ..
+               "([0-9]+):([0-9]+):([0-9]+) " ..
+               "([A-Z0-9%+%-]+)"
+       )
+
+       if day and mon and yr and hr and min and sec then
+               -- find month
+               local month = 1
+               for i = 1, 12 do
+                       if MONTHS[i] == mon then
+                               month = i
+                               break
+                       end
+               end
+
+               -- convert to epoch time
+               return tz_offset(tz) + os.time( {
+                       year  = yr,
+                       month = month,
+                       day   = day,
+                       hour  = hr,
+                       min   = min,
+                       sec   = sec
+               } )
+       end
+
+       return 0
+end
+
+--- Convert the given unix epoch time to valid HTTP date string.
+-- @param time Unix epoch time
+-- @return             String containing the formatted date
+function to_http(time)
+       return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
+end
+
+--- Compare two dates which can either be unix epoch times or HTTP date strings.
+-- @param d1   The first date or epoch time to compare
+-- @param d2   The first date or epoch time to compare
+-- @return             -1  -  if d1 is lower then d2
+-- @return             0   -  if both dates are equal
+-- @return             1   -  if d1 is higher then d2
+function compare(d1, d2)
+
+       if d1:match("[^0-9]") then d1 = to_unix(d1) end
+       if d2:match("[^0-9]") then d2 = to_unix(d2) end
+
+       if d1 == d2 then
+               return 0
+       elseif d1 < d2 then
+               return -1
+       else
+               return 1
+       end
+end
diff --git a/modules/base/luasrc/luasrc/http/protocol/mime.lua b/modules/base/luasrc/luasrc/http/protocol/mime.lua
new file mode 100644 (file)
index 0000000..c878160
--- /dev/null
@@ -0,0 +1,99 @@
+--[[
+
+HTTP protocol implementation for LuCI - mime handling
+(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+]]--
+
+--- LuCI http protocol implementation - mime helper class.
+-- This class provides functions to guess mime types from file extensions and
+-- vice versa.
+module("luci.http.protocol.mime", package.seeall)
+
+require("luci.util")
+
+--- MIME mapping table containg extension - mimetype relations.
+-- @class table
+MIME_TYPES = {
+    ["txt"]   = "text/plain";
+    ["js"]    = "text/javascript";
+    ["css"]   = "text/css";
+    ["htm"]   = "text/html";
+    ["html"]  = "text/html";
+    ["patch"] = "text/x-patch";
+    ["c"]     = "text/x-csrc";
+    ["h"]     = "text/x-chdr";
+    ["o"]     = "text/x-object";
+    ["ko"]    = "text/x-object";
+
+    ["bmp"]   = "image/bmp";
+    ["gif"]   = "image/gif";
+    ["png"]   = "image/png";
+    ["jpg"]   = "image/jpeg";
+    ["jpeg"]  = "image/jpeg";
+    ["svg"]   = "image/svg+xml";
+
+    ["zip"]   = "application/zip";
+    ["pdf"]   = "application/pdf";
+    ["xml"]   = "application/xml";
+    ["xsl"]   = "application/xml";
+    ["doc"]   = "application/msword";
+    ["ppt"]   = "application/vnd.ms-powerpoint";
+    ["xls"]   = "application/vnd.ms-excel";
+    ["odt"]   = "application/vnd.oasis.opendocument.text";
+    ["odp"]   = "application/vnd.oasis.opendocument.presentation";
+    ["pl"]    = "application/x-perl";
+    ["sh"]    = "application/x-shellscript";
+    ["php"]   = "application/x-php";
+    ["deb"]   = "application/x-deb";
+    ["iso"]   = "application/x-cd-image";
+    ["tgz"]   = "application/x-compressed-tar";
+
+    ["mp3"]   = "audio/mpeg";
+    ["ogg"]   = "audio/x-vorbis+ogg";
+    ["wav"]   = "audio/x-wav";
+
+    ["mpg"]   = "video/mpeg";
+    ["mpeg"]  = "video/mpeg";
+    ["avi"]   = "video/x-msvideo";
+}
+
+--- Extract extension from a filename and return corresponding mime-type or
+-- "application/octet-stream" if the extension is unknown.
+-- @param filename     The filename for which the mime type is guessed
+-- @return                     String containign the determined mime type
+function to_mime(filename)
+       if type(filename) == "string" then
+               local ext = filename:match("[^%.]+$")
+
+               if ext and MIME_TYPES[ext:lower()] then
+                       return MIME_TYPES[ext:lower()]
+               end
+       end
+
+       return "application/octet-stream"
+end
+
+--- Return corresponding extension for a given mime type or nil if the
+-- given mime-type is unknown.
+-- @param mimetype     The mimetype to retrieve the extension from
+-- @return                     String with the extension or nil for unknown type
+function to_ext(mimetype)
+       if type(mimetype) == "string" then
+               for ext, type in luci.util.kspairs( MIME_TYPES ) do
+                       if type == mimetype then
+                               return ext
+                       end
+               end
+       end
+
+       return nil
+end
diff --git a/modules/base/luasrc/luasrc/i18n.lua b/modules/base/luasrc/luasrc/i18n.lua
new file mode 100644 (file)
index 0000000..545a8ae
--- /dev/null
@@ -0,0 +1,104 @@
+--[[
+LuCI - Internationalisation
+
+Description:
+A very minimalistic but yet effective internationalisation module
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+--- LuCI translation library.
+module("luci.i18n", package.seeall)
+require("luci.util")
+
+local tparser = require "luci.template.parser"
+
+table   = {}
+i18ndir = luci.util.libpath() .. "/i18n/"
+loaded  = {}
+context = luci.util.threadlocal()
+default = "en"
+
+--- Clear the translation table.
+function clear()
+end
+
+--- Load a translation and copy its data into the translation table.
+-- @param file Language file
+-- @param lang Two-letter language code
+-- @param force        Force reload even if already loaded (optional)
+-- @return             Success status
+function load(file, lang, force)
+end
+
+--- Load a translation file using the default translation language.
+-- Alternatively load the translation of the fallback language.
+-- @param file Language file
+-- @param force        Force reload even if already loaded (optional)
+function loadc(file, force)
+end
+
+--- Set the context default translation language.
+-- @param lang Two-letter language code
+function setlanguage(lang)
+       context.lang   = lang:gsub("_", "-")
+       context.parent = (context.lang:match("^([a-z][a-z])_"))
+       if not tparser.load_catalog(context.lang, i18ndir) then
+               if context.parent then
+                       tparser.load_catalog(context.parent, i18ndir)
+                       return context.parent
+               end
+       end
+       return context.lang
+end
+
+--- Return the translated value for a specific translation key.
+-- @param key  Default translation text
+-- @return             Translated string
+function translate(key)
+       return tparser.translate(key) or key
+end
+
+--- Return the translated value for a specific translation key and use it as sprintf pattern.
+-- @param key          Default translation text
+-- @param ...          Format parameters
+-- @return                     Translated and formatted string
+function translatef(key, ...)
+       return tostring(translate(key)):format(...)
+end
+
+--- Return the translated value for a specific translation key
+-- and ensure that the returned value is a Lua string value.
+-- This is the same as calling <code>tostring(translate(...))</code>
+-- @param key          Default translation text
+-- @return                     Translated string
+function string(key)
+       return tostring(translate(key))
+end
+
+--- Return the translated value for a specific translation key and use it as sprintf pattern.
+-- Ensure that the returned value is a Lua string value.
+-- This is the same as calling <code>tostring(translatef(...))</code>
+-- @param key          Default translation text
+-- @param ...          Format parameters
+-- @return                     Translated and formatted string
+function stringf(key, ...)
+       return tostring(translate(key)):format(...)
+end
diff --git a/modules/base/luasrc/luasrc/sauth.lua b/modules/base/luasrc/luasrc/sauth.lua
new file mode 100644 (file)
index 0000000..32f172d
--- /dev/null
@@ -0,0 +1,127 @@
+--[[
+
+Session authentication
+(c) 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+]]--
+
+--- LuCI session library.
+module("luci.sauth", package.seeall)
+require("luci.util")
+require("luci.sys")
+require("luci.config")
+local nixio = require "nixio", require "nixio.util"
+local fs = require "nixio.fs"
+
+
+luci.config.sauth = luci.config.sauth or {}
+sessionpath = luci.config.sauth.sessionpath
+sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
+
+--- Prepare session storage by creating the session directory.
+function prepare()
+       fs.mkdir(sessionpath, 700)
+       if not sane() then
+               error("Security Exception: Session path is not sane!")
+       end
+end
+
+local function _read(id)
+       local blob = fs.readfile(sessionpath .. "/" .. id)
+       return blob
+end
+
+local function _write(id, data)
+       local f = nixio.open(sessionpath .. "/" .. id, "w", 600)
+       f:writeall(data)
+       f:close()
+end
+
+local function _checkid(id)
+       return not not (id and #id == 32 and id:match("^[a-fA-F0-9]+$"))
+end
+
+--- Write session data to a session file.
+-- @param id   Session identifier
+-- @param data Session data table
+function write(id, data)
+       if not sane() then
+               prepare()
+       end
+
+       assert(_checkid(id), "Security Exception: Session ID is invalid!")
+       assert(type(data) == "table", "Security Exception: Session data invalid!")
+
+       data.atime = luci.sys.uptime()
+
+       _write(id, luci.util.get_bytecode(data))
+end
+
+--- Read a session and return its content.
+-- @param id   Session identifier
+-- @return             Session data table or nil if the given id is not found
+function read(id)
+       if not id or #id == 0 then
+               return nil
+       end
+
+       assert(_checkid(id), "Security Exception: Session ID is invalid!")
+
+       if not sane(sessionpath .. "/" .. id) then
+               return nil
+       end
+
+       local blob = _read(id)
+       local func = loadstring(blob)
+       setfenv(func, {})
+
+       local sess = func()
+       assert(type(sess) == "table", "Session data invalid!")
+
+       if sess.atime and sess.atime + sessiontime < luci.sys.uptime() then
+               kill(id)
+               return nil
+       end
+
+       -- refresh atime in session
+       write(id, sess)
+
+       return sess
+end
+
+--- Check whether Session environment is sane.
+-- @return Boolean status
+function sane(file)
+       return luci.sys.process.info("uid")
+                       == fs.stat(file or sessionpath, "uid")
+               and fs.stat(file or sessionpath, "modestr")
+                       == (file and "rw-------" or "rwx------")
+end
+
+--- Kills a session
+-- @param id   Session identifier
+function kill(id)
+       assert(_checkid(id), "Security Exception: Session ID is invalid!")
+       fs.unlink(sessionpath .. "/" .. id)
+end
+
+--- Remove all expired session data files
+function reap()
+       if sane() then
+               local id
+               for id in nixio.fs.dir(sessionpath) do
+                       if _checkid(id) then
+                               -- reading the session will kill it if it is expired
+                               read(id)
+                       end
+               end
+       end
+end
diff --git a/modules/base/luasrc/luasrc/template.lua b/modules/base/luasrc/luasrc/template.lua
new file mode 100644 (file)
index 0000000..72127d1
--- /dev/null
@@ -0,0 +1,107 @@
+--[[
+LuCI - 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 <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at 
+
+       http://www.apache.org/licenses/LICENSE-2.0 
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local util = require "luci.util"
+local config = require "luci.config"
+local tparser = require "luci.template.parser"
+
+local tostring, pairs, loadstring = tostring, pairs, loadstring
+local setmetatable, loadfile = setmetatable, loadfile
+local getfenv, setfenv, rawget = getfenv, setfenv, rawget
+local assert, type, error = assert, type, error
+
+--- LuCI template library.
+module "luci.template"
+
+config.template = config.template or {}
+viewdir = config.template.viewdir or util.libpath() .. "/view"
+
+
+-- Define the namespace for template modules
+context = util.threadlocal()
+
+--- Render a certain template.
+-- @param name         Template name
+-- @param scope                Scope to assign to template (optional)
+function render(name, scope)
+       return Template(name):render(scope or getfenv(2))
+end
+
+
+-- Template class
+Template = util.class()
+
+-- Shared template cache to store templates in to avoid unnecessary reloading
+Template.cache = setmetatable({}, {__mode = "v"})
+
+
+-- Constructor - Reads and compiles the template on-demand
+function Template.__init__(self, name) 
+
+       self.template = self.cache[name]
+       self.name = name
+       
+       -- Create a new namespace for this template
+       self.viewns = context.viewns
+       
+       -- If we have a cached template, skip compiling and loading
+       if not self.template then
+
+               -- Compile template
+               local err
+               local sourcefile = viewdir .. "/" .. name .. ".htm"
+
+               self.template, _, err = tparser.parse(sourcefile)
+
+               -- If we have no valid template throw error, otherwise cache the template
+               if not self.template then
+                       error("Failed to load template '" .. name .. "'.\n" ..
+                             "Error while parsing template '" .. sourcefile .. "':\n" ..
+                             (err or "Unknown syntax error"))
+               else
+                       self.cache[name] = self.template
+               end
+       end
+end
+
+
+-- Renders a template
+function Template.render(self, scope)
+       scope = scope or getfenv(2)
+       
+       -- Put our predefined objects in the scope of the template
+       setfenv(self.template, setmetatable({}, {__index =
+               function(tbl, key)
+                       return rawget(tbl, key) or self.viewns[key] or scope[key]
+               end}))
+       
+       -- Now finally render the thing
+       local stat, err = util.copcall(self.template)
+       if not stat then
+               error("Failed to execute template '" .. self.name .. "'.\n" ..
+                     "A runtime error occured: " .. tostring(err or "(nil)"))
+       end
+end
diff --git a/modules/base/luasrc/luasrc/view/cbi/apply_xhr.htm b/modules/base/luasrc/luasrc/view/cbi/apply_xhr.htm
new file mode 100644 (file)
index 0000000..1814c93
--- /dev/null
@@ -0,0 +1,43 @@
+<% export("cbi_apply_xhr", function(id, configs, redirect) -%>
+<fieldset class="cbi-section" id="cbi-apply-<%=id%>">
+       <legend><%:Applying changes%></legend>
+       <script type="text/javascript">//<![CDATA[
+               var apply_xhr = new XHR();
+
+               apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "restart", table.concat(configs, ","))%>', null,
+                       function() {
+                               var checkfinish = function() {
+                                       apply_xhr.get('<%=luci.dispatcher.build_url("servicectl", "status")%>', null,
+                                               function(x) {
+                                                       if( x.responseText == 'finish' )
+                                                       {
+                                                               var e = document.getElementById('cbi-apply-<%=id%>-status');
+                                                               if( e )
+                                                               {
+                                                                       e.innerHTML = '<%:Configuration applied.%>';
+                                                                       window.setTimeout(function() {
+                                                                               e.parentNode.style.display = 'none';
+                                                                               <% if redirect then %>location.href='<%=redirect%>';<% end %>
+                                                                       }, 1000);
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               var e = document.getElementById('cbi-apply-<%=id%>-status');
+                                                               if( e && x.responseText ) e.innerHTML = x.responseText;
+
+                                                               window.setTimeout(checkfinish, 1000);
+                                                       }
+                                               }
+                                       );
+                               }
+
+                               window.setTimeout(checkfinish, 1000);
+                       }
+               );
+       //]]></script>
+
+       <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" />
+       <span id="cbi-apply-<%=id%>-status"><%:Waiting for changes to be applied...%></span>
+</fieldset>
+<%-    end) %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/browser.htm b/modules/base/luasrc/luasrc/view/cbi/browser.htm
new file mode 100644 (file)
index 0000000..e4a4077
--- /dev/null
@@ -0,0 +1,7 @@
+<% local v = self:cfgvalue(section) -%>
+<%+cbi/valueheader%>
+       <input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
+       <script type="text/javascript">
+cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=luci.dispatcher.build_url("admin", "filebrowser")%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
+       </script>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/button.htm b/modules/base/luasrc/luasrc/view/cbi/button.htm
new file mode 100644 (file)
index 0000000..30f8ddf
--- /dev/null
@@ -0,0 +1,7 @@
+<%+cbi/valueheader%>
+       <% if self:cfgvalue(section) ~= false then %>
+               <input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
+       <% else %>
+               -
+       <% end %>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/cell_valuefooter.htm b/modules/base/luasrc/luasrc/view/cbi/cell_valuefooter.htm
new file mode 100644 (file)
index 0000000..220ebd4
--- /dev/null
@@ -0,0 +1,20 @@
+</div>
+<div id="cbip-<%=self.config.."-"..section.."-"..self.option%>"></div>
+</td>
+
+<% if #self.deps > 0 then -%>
+       <script type="text/javascript">
+               <% for j, d in ipairs(self.deps) do -%>
+                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
+               <%-
+                       for k,v in pairs(d.deps) do
+               -%>
+                       <%-=string.format('"cbid.%s.%s.%s"', self.config, section, k) .. ":" .. string.format("%q", v)-%>
+                       <%-if next(d.deps, k) then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+                       }, "cbip-<%=self.config.."-"..section.."-"..self.option%>");
+               <%- end %>
+       </script>
+<%- end %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/cell_valueheader.htm b/modules/base/luasrc/luasrc/view/cbi/cell_valueheader.htm
new file mode 100644 (file)
index 0000000..9e2e145
--- /dev/null
@@ -0,0 +1,2 @@
+<td class="cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>">
+<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
diff --git a/modules/base/luasrc/luasrc/view/cbi/compound.htm b/modules/base/luasrc/luasrc/view/cbi/compound.htm
new file mode 100644 (file)
index 0000000..12d02bb
--- /dev/null
@@ -0,0 +1 @@
+<%- self:render_children() %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/delegator.htm b/modules/base/luasrc/luasrc/view/cbi/delegator.htm
new file mode 100644 (file)
index 0000000..4fd1926
--- /dev/null
@@ -0,0 +1,24 @@
+<%- self.active:render() %>
+       <div class="cbi-page-actions">
+               <input type="hidden" name="cbi.delg.current" value="<%=self.current%>" />
+<% for _, x in ipairs(self.chain) do %>
+               <input type="hidden" name="cbi.delg.path" value="<%=x%>" />
+<% end %>
+<% if not self.disallow_pageactions then %>
+<% if self.allow_finish and not self:get_next(self.current) then %>
+       <input class="cbi-button cbi-button-finish" type="submit" value="<%:Finish%>" />
+<% elseif self:get_next(self.current) then %>
+               <input class="cbi-button cbi-button-next" type="submit" value="<%:Next Â»%>" />
+<% end %>
+<% if self.allow_cancel then %>
+               <input class="cbi-button cbi-button-cancel" type="submit" name="cbi.cancel" value="<%:Cancel%>" />
+<% end %>
+<% if self.allow_reset then %>
+               <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
+<% end %>
+<% if self.allow_back and self:get_prev(self.current) then %>
+               <input class="cbi-button cbi-button-back" type="submit" name="cbi.delg.back" value="<%:« Back%>" />
+<% end %>
+<% end %>
+               <script type="text/javascript">cbi_d_update();</script>
+       </div>
diff --git a/modules/base/luasrc/luasrc/view/cbi/dvalue.htm b/modules/base/luasrc/luasrc/view/cbi/dvalue.htm
new file mode 100644 (file)
index 0000000..78e6f32
--- /dev/null
@@ -0,0 +1,13 @@
+<%+cbi/valueheader%>
+<% if self.href then %><a href="<%=self.href%>"><% end -%>
+       <%
+               local val = self:cfgvalue(section) or self.default or ""
+               if not self.rawhtml then
+                       write(pcdata(val))
+               else
+                       write(val)
+               end
+       %>
+<%- if self.href then %></a><%end%>
+<input type="hidden" id="<%=cbid%>" value="<%=pcdata(self:cfgvalue(section) or self.default or "")%>" />
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/dynlist.htm b/modules/base/luasrc/luasrc/view/cbi/dynlist.htm
new file mode 100644 (file)
index 0000000..fd626a4
--- /dev/null
@@ -0,0 +1,26 @@
+<%+cbi/valueheader%>
+<div>
+<%
+       local vals = self:cfgvalue(section) or {}
+       for i=1, #vals + 1 do
+               local val = vals[i]
+               if (val and #val > 0) or (i == 1) then
+%>
+       <input class="cbi-input-text" value="<%=pcdata(val)%>" onchange="cbi_d_update(this.id)" type="text"<%=
+               attr("id", cbid .. "." .. i) .. attr("name", cbid) .. ifattr(self.size, "size") ..
+               ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder)
+       %> /><br />
+<% end end %>
+</div>
+<script type="text/javascript">
+cbi_dynlist_init(
+       '<%=cbid%>', '<%=resource%>', '<%=self.datatype%>',
+       <%=tostring(self.optional or self.rmempty)%>
+       <%- if #self.keylist > 0 then -%>, [{
+               <%- for i, k in ipairs(self.keylist) do -%>
+                       <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
+                       <%-if i<#self.keylist then-%>,<%-end-%>
+               <%-     end     -%>
+       }, '<%: -- custom -- %>']<% end -%>);
+</script>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/filebrowser.htm b/modules/base/luasrc/luasrc/view/cbi/filebrowser.htm
new file mode 100644 (file)
index 0000000..a79beeb
--- /dev/null
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>Filebrowser - LuCI</title>
+       <style type="text/css">
+               #path, #listing {
+                       font-size: 85%;
+               }
+
+               ul {
+                       padding-left: 0;
+                       list-style-type: none;
+               }
+
+               li img {
+                       vertical-align: bottom;
+                       margin-right: 0.2em;
+               }
+       </style>
+
+       <script type="text/javascript">
+               function callback(path) {
+                       if( window.opener ) {
+                               var input = window.opener.document.getElementById('<%=luci.http.formvalue('field')%>');
+                               if( input ) {
+                                       input.value = path;
+                                       window.close();
+                               }
+                       }
+               }
+       </script>
+</head>
+<body>
+       <%
+               require("nixio.fs")
+               require("nixio.util")
+               require("luci.http")
+               require("luci.dispatcher")
+
+               local field   = luci.http.formvalue('field')
+               local request = luci.dispatcher.context.args
+               local path    = { '' }
+
+               for i = 1, #request do
+                       if request[i] ~= '..' and #request[i] > 0 then
+                               path[#path+1] = request[i]
+                       end
+               end
+
+               local filepath = table.concat( path, '/' )
+               local filestat = nixio.fs.stat( filepath )
+               local baseurl  = luci.dispatcher.build_url('admin', 'filebrowser')
+
+               if filestat and filestat.type == "reg" then
+                       table.remove( path, #path )
+                       filepath = table.concat( path, '/' ) .. '/'
+               elseif not ( filestat and filestat.type == "dir" ) then
+                       path     = { '' }
+                       filepath = '/'
+               else
+                       filepath = filepath .. '/'
+               end
+
+               local entries = nixio.util.consume((nixio.fs.dir(filepath)))
+       -%>
+    <div id="path">
+               Location:
+               <% for i, dir in ipairs(path) do %>
+                       <% if i == 1 then %>
+                               <a href="<%=baseurl%>?field=<%=field%>">(root)</a>
+                       <% elseif next(path, i) then %>
+                               <% baseurl = baseurl .. '/' .. dir %>
+                               / <a href="<%=baseurl%>?field=<%=field%>"><%=dir%></a>
+                       <% else %>
+                               <% baseurl = baseurl .. '/' .. dir %>
+                               / <%=dir%>
+                       <% end %>
+               <% end %>
+       </div>
+
+       <hr />
+
+       <div id="listing">
+               <ul>
+                       <% for _, e in luci.util.vspairs(entries) do
+                           local stat = nixio.fs.stat(filepath..e)
+                               if stat and stat.type == 'dir' then
+                       -%>
+                               <li class="dir">
+                                       <img src="<%=resource%>/cbi/folder.gif" alt="<%:Directory%>" />
+                                       <a href="<%=baseurl%>/<%=e%>?field=<%=field%>"><%=e%>/</a>
+                               </li>
+                       <% end end -%>
+
+                       <% for _, e in luci.util.vspairs(entries) do
+                           local stat = nixio.fs.stat(filepath..e)
+                               if stat and stat.type ~= 'dir' then
+                       -%>
+                               <li class="file">
+                                       <img src="<%=resource%>/cbi/file.gif" alt="<%:File%>" />
+                                       <a href="#" onclick="callback('<%=filepath..e%>')"><%=e%></a>
+                               </li>
+                       <% end end -%>
+               </ul>
+       </div>
+</body>
+</html>
diff --git a/modules/base/luasrc/luasrc/view/cbi/firewall_zoneforwards.htm b/modules/base/luasrc/luasrc/view/cbi/firewall_zoneforwards.htm
new file mode 100644 (file)
index 0000000..2a433b5
--- /dev/null
@@ -0,0 +1,59 @@
+<%+cbi/valueheader%>
+
+<%-
+       local utl = require "luci.util"
+       local fwm = require "luci.model.firewall".init()
+       local nwm = require "luci.model.network".init()
+
+       local zone, fwd, fz
+       local value = self:formvalue(section)
+       if not value or value == "-" then
+               value = self:cfgvalue(section) or self.default
+       end
+
+       local def  = fwm:get_defaults()
+       local zone = fwm:get_zone(value)
+       local empty = true
+-%>
+
+<% if zone then %>
+<div style="white-space:nowrap">
+       <label class="zonebadge" style="background-color:<%=zone:get_color()%>">
+               <strong><%=zone:name()%>:</strong>
+               <%-
+                       local zempty = true
+                       for _, net in ipairs(zone:get_networks()) do
+                               net = nwm:get_network(net)
+                               if net then
+                                       zempty = false
+               -%>
+                       <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
+                       <%
+                               local nempty = true
+                               for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
+                                       nempty = false
+                        %>
+                               <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+                       <% end %>
+                       <% if nempty then %><em><%:(empty)%></em><% end %>
+                       </span>
+               <%- end end -%>
+               <%- if zempty then %><em><%:(empty)%></em><% end -%>
+       </label>
+       &#160;&#8658;&#160;
+       <% for _, fwd in ipairs(zone:get_forwardings_by("src")) do
+               fz = fwd:dest_zone()
+               empty = false %>
+               <label class="zonebadge" style="background-color:<%=fz:get_color()%>">
+                       <strong><%=fz:name()%></strong>
+               </label>&#160;
+       <% end %>
+       <% if empty then %>
+               <label class="zonebadge zonebadge-empty">
+                       <strong><%=zone:forward():upper()%></strong>
+               </label>
+       <% end %>
+</div>
+<% end %>
+
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/firewall_zonelist.htm b/modules/base/luasrc/luasrc/view/cbi/firewall_zonelist.htm
new file mode 100644 (file)
index 0000000..7973437
--- /dev/null
@@ -0,0 +1,89 @@
+<%+cbi/valueheader%>
+
+<%-
+       local utl = require "luci.util"
+       local fwm = require "luci.model.firewall".init()
+       local nwm = require "luci.model.network".init()
+
+       local zone, net, iface
+       local zones = fwm:get_zones()
+       local value = self:formvalue(section)
+       if not value or value == "-" then
+               value = self:cfgvalue(section) or self.default
+       end
+
+       local selected = false
+       local checked = { }
+
+       for value in utl.imatch(value) do
+               checked[value] = true
+       end
+
+       if not next(checked) then
+               checked[""] = true
+       end
+-%>
+
+<ul style="margin:0; list-style-type:none; text-align:left">
+       <% if self.allowlocal then %>
+       <li style="padding:0.5em">
+               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> /> &#160;
+               <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+                       <strong><%:Device%></strong>
+                       <% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
+               </label>
+       </li>
+       <% end %>
+       <% if self.allowany then %>
+       <li style="padding:0.5em">
+               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> /> &#160;
+               <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+                       <strong><%:Any zone%></strong>
+                       <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
+               </label>
+       </li>
+       <% end %>
+       <%
+               for _, zone in utl.spairs(zones, function(a,b) return (zones[a]:name() < zones[b]:name()) end) do
+                       if zone:name() ~= self.exclude then
+                               selected = selected or (value == zone:name())
+       %>
+       <li style="padding:0.5em">
+               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> /> &#160;
+               <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
+                       <strong><%=zone:name()%>:</strong>
+                       <%
+                               local zempty = true
+                               for _, net in ipairs(zone:get_networks()) do
+                                       net = nwm:get_network(net)
+                                       if net then
+                                               zempty = false
+                       %>
+                               <span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
+                               <%
+                                       local nempty = true
+                                       for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
+                                               nempty = false
+                                %>
+                                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+                               <% end %>
+                               <% if nempty then %><em><%:(empty)%></em><% end %>
+                               </span>
+                       <% end end %>
+                       <% if zempty then %><em><%:(empty)%></em><% end %>
+               </label>
+       </li>
+       <% end end %>
+
+       <% if self.widget ~= "checkbox" and not self.nocreate then %>
+       <li style="padding:0.5em">
+               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> /> &#160;
+               <div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
+                       <em><%:unspecified -or- create:%>&#160;</em>
+                       <input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
+               </div>
+       </li>
+       <% end %>
+</ul>
+
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/footer.htm b/modules/base/luasrc/luasrc/view/cbi/footer.htm
new file mode 100644 (file)
index 0000000..2c34028
--- /dev/null
@@ -0,0 +1,26 @@
+       <%- if pageaction then -%>
+       <div class="cbi-page-actions">
+               <% if redirect then %>
+               <div style="float:left">
+                       <input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
+               </div>
+               <% end %>
+
+               <% if flow.skip then %>
+                       <input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
+               <% end %>
+               <% if not autoapply and not flow.hideapplybtn then %>
+                       <input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
+               <% end %>
+               <% if not flow.hidesavebtn then %>
+                       <input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
+               <% end %>
+               <% if not flow.hideresetbtn then %>
+                       <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
+               <% end %>
+
+               <script type="text/javascript">cbi_d_update();</script>
+       </div>
+       <%- end -%>
+</form>
+<%+footer%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/full_valuefooter.htm b/modules/base/luasrc/luasrc/view/cbi/full_valuefooter.htm
new file mode 100644 (file)
index 0000000..4876fbc
--- /dev/null
@@ -0,0 +1,59 @@
+               <% if self.description and #self.description > 0 then -%>
+                       <% if not luci.util.instanceof(self, luci.cbi.DynamicList) and (not luci.util.instanceof(self, luci.cbi.Flag) or self.orientation == "horizontal") then -%>
+                               <br />
+                       <%- end %>
+                       <div class="cbi-value-description">
+                               <span class="cbi-value-helpicon"><img src="<%=resource%>/cbi/help.gif" alt="<%:help%>" /></span>
+                               <%=self.description%>
+                       </div>
+               <%- end %>
+       <%- if self.title and #self.title > 0 then -%>
+       </div>
+       <%- end -%>
+</div>
+
+
+<% if #self.deps > 0 or #self.subdeps > 0 then -%>
+       <script type="text/javascript" id="cbip-<%=self.config.."-"..section.."-"..self.option%>">
+               <% for j, d in ipairs(self.subdeps) do -%>
+                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
+               <%-
+                       for k,v in pairs(d.deps) do
+                               local depk
+                               if k:find("!", 1, true) then
+                                       depk = string.format('"%s"', k)
+                               elseif k:find(".", 1, true) then
+                                       depk = string.format('"cbid.%s"', k)
+                               else
+                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
+                               end
+               -%>
+                       <%-= depk .. ":" .. string.format("%q", v)-%>
+                       <%-if next(d.deps, k) then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
+               <%- end %>
+               <% for j, d in ipairs(self.deps) do -%>
+                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
+               <%-
+                       for k,v in pairs(d.deps) do
+                               local depk
+                               if k:find("!", 1, true) then
+                                       depk = string.format('"%s"', k)
+                               elseif k:find(".", 1, true) then
+                                       depk = string.format('"cbid.%s"', k)
+                               else
+                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
+                               end
+               -%>
+                       <%-= depk .. ":" .. string.format("%q", v)-%>
+                       <%-if next(d.deps, k) then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
+               <%- end %>
+       </script>
+<%- end %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/full_valueheader.htm b/modules/base/luasrc/luasrc/view/cbi/full_valueheader.htm
new file mode 100644 (file)
index 0000000..aaf0854
--- /dev/null
@@ -0,0 +1,9 @@
+<div class="cbi-value<% if self.error and self.error[section] then %> cbi-value-error<% end %><% if self.last_child then %> cbi-value-last<% end %>" id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
+       <%- if self.title and #self.title > 0 then -%>
+       <label class="cbi-value-title"<%= attr("for", cbid) %>>
+       <%- if self.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=self.titleref%>"><%- end -%>
+               <%-=self.title-%>
+       <%- if self.titleref then -%></a><%- end -%>
+       </label>
+               <div class="cbi-value-field">
+       <%- end -%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/fvalue.htm b/modules/base/luasrc/luasrc/view/cbi/fvalue.htm
new file mode 100644 (file)
index 0000000..a1e0808
--- /dev/null
@@ -0,0 +1,9 @@
+<%+cbi/valueheader%>
+       <input type="hidden" value="1"<%=
+               attr("name", "cbi.cbe." .. self.config .. "." .. section .. "." .. self.option)
+       %> />
+       <input class="cbi-input-checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="checkbox"<%=
+               attr("id", cbid) .. attr("name", cbid) .. attr("value", self.enabled or 1) ..
+               ifattr((self:cfgvalue(section) or self.default) == self.enabled, "checked", "checked")
+       %> />
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/header.htm b/modules/base/luasrc/luasrc/view/cbi/header.htm
new file mode 100644 (file)
index 0000000..2bddaba
--- /dev/null
@@ -0,0 +1,7 @@
+<%+header%>
+<form method="post" name="cbi" action="<%=REQUEST_URI%>" enctype="multipart/form-data" onreset="return cbi_validate_reset(this)" onsubmit="return cbi_validate_form(this, '<%:Some fields are invalid, cannot save values!%>')">
+       <div>
+               <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+               <input type="hidden" name="cbi.submit" value="1" />
+               <input type="submit" value="<%:Save%>" class="hidden" />
+       </div>
diff --git a/modules/base/luasrc/luasrc/view/cbi/lvalue.htm b/modules/base/luasrc/luasrc/view/cbi/lvalue.htm
new file mode 100644 (file)
index 0000000..8cc086d
--- /dev/null
@@ -0,0 +1,18 @@
+<%+cbi/valueheader%>
+<% if self.widget == "select" then %>
+       <select class="cbi-input-select" onchange="cbi_d_update(this.id)"<%= attr("id", cbid) .. attr("name", cbid) .. ifattr(self.size, "size") %>>
+       <% for i, key in pairs(self.keylist) do -%>
+               <option id="cbi-<%=self.config.."-"..section.."-"..self.option.."-"..key%>"<%= attr("value", key) .. ifattr(tostring(self:cfgvalue(section) or self.default) == key, "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
+       <%- end %>
+       </select>
+<% elseif self.widget == "radio" then
+       local c = 0
+       for i, key in pairs(self.keylist) do
+       c = c + 1
+%>
+       <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr((self:cfgvalue(section) or self.default) == key, "checked", "checked") %> />
+       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label>
+<% if c == self.size then c = 0 %><% if self.orientation == "horizontal" then %>&#160;<% else %><br /><% end %>
+<% end end %>
+<% end %>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/map.htm b/modules/base/luasrc/luasrc/view/cbi/map.htm
new file mode 100644 (file)
index 0000000..053220d
--- /dev/null
@@ -0,0 +1,13 @@
+<%- if firstmap and messages then local msg; for _, msg in ipairs(messages) do -%>
+       <div class="errorbox"><%=pcdata(msg)%></div>
+<%- end end -%>
+
+<%-+cbi/apply_xhr-%>
+
+<div class="cbi-map" id="cbi-<%=self.config%>">
+       <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
+       <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
+       <%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
+       <%- self:render_children() %>
+       <br />
+</div>
diff --git a/modules/base/luasrc/luasrc/view/cbi/mvalue.htm b/modules/base/luasrc/luasrc/view/cbi/mvalue.htm
new file mode 100644 (file)
index 0000000..6a0b388
--- /dev/null
@@ -0,0 +1,19 @@
+<% local v = self:valuelist(section) or {} -%>
+<%+cbi/valueheader%>
+<% if self.widget == "select" then %>
+       <select class="cbi-input-select" multiple="multiple" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. ifattr(self.size, "size") %>>
+       <% for i, key in pairs(self.keylist) do -%>
+               <option<%= attr("value", key) .. ifattr(luci.util.contains(v, key), "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
+       <%- end %>
+       </select>
+<% elseif self.widget == "checkbox" then
+       local c = 0;
+       for i, key in pairs(self.keylist) do
+       c = c + 1
+%>
+       <input class="cbi-input-checkbox" type="checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "checked", "checked") %> />
+       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label><br />
+<% if c == self.size then c = 0 %><br />
+<% end end %>
+<% end %>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/network_ifacelist.htm b/modules/base/luasrc/luasrc/view/cbi/network_ifacelist.htm
new file mode 100644 (file)
index 0000000..643d849
--- /dev/null
@@ -0,0 +1,81 @@
+<%+cbi/valueheader%>
+
+<%-
+       local utl = require "luci.util"
+       local net = require "luci.model.network".init()
+       local cbeid = luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option
+
+       local iface
+       local ifaces = net:get_interfaces()
+       local value
+
+       if self.map:formvalue(cbeid) == "1" then
+               value = self:formvalue(section) or self.default or ""
+       else
+               value = self:cfgvalue(section) or self.default
+       end
+
+       local checked = { }
+
+       if value then
+               for value in utl.imatch(value) do
+                       checked[value] = true
+               end
+       else
+               local n = self.network and net:get_network(self.network)
+               if n then
+                       local i
+                       for _, i in ipairs(n:get_interfaces() or { n:get_interface() }) do
+                               checked[i:name()] = true
+                       end
+               end
+       end
+-%>
+
+<input type="hidden" name="<%=cbeid%>" value="1" />
+<ul style="margin:0; list-style-type:none">
+       <% for _, iface in ipairs(ifaces) do
+            local link = iface:adminlink()
+         if (not self.nobridges  or not iface:is_bridge()) and
+                   (not self.noinactive or iface:is_up()) and
+                   iface:name() ~= self.exclude
+                then %>
+       <li>
+               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+                       attr("type", self.widget or "radio") ..
+                       attr("id", cbid .. "." .. iface:name()) ..
+                       attr("name", cbid) .. attr("value", iface:name()) ..
+                       ifattr(checked[iface:name()], "checked", "checked")
+               %> /> &#160;
+               <label<%=attr("for", cbid .. "." .. iface:name())%>>
+                       <% if link then -%><a href="<%=link%>"><% end -%>
+                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+                       <% if link then -%></a><% end -%>
+                       <%=pcdata(iface:get_i18n())%>
+                       <% local ns = iface:get_networks(); if #ns > 0 then %>(
+                               <%- local i, n; for i, n in ipairs(ns) do -%>
+                                       <%-= (i>1) and ', ' -%>
+                                       <a href="<%=n:adminlink()%>"><%=n:name()%></a>
+                               <%- end -%>
+                       )<% end %>
+               </label>
+       </li>
+       <% end end %>
+       <% if not self.nocreate then %>
+       <li>
+               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+                       attr("type", self.widget or "radio") ..
+                       attr("id", cbid .. "_custom") ..
+                       attr("name", cbid) ..
+                       attr("value", " ")
+               %> /> &#160;
+               <label<%=attr("for", cbid .. "_custom")%>>
+                       <img title="<%:Custom Interface%>" style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/ethernet_disabled.png" />
+                       <%:Custom Interface%>:
+               </label>
+               <input type="text" style="width:50px" onfocus="document.getElementById('<%=cbid%>_custom').checked=true" onblur="var x=document.getElementById('<%=cbid%>_custom'); x.value=this.value; x.checked=true" />
+       </li>
+       <% end %>
+</ul>
+
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/network_netinfo.htm b/modules/base/luasrc/luasrc/view/cbi/network_netinfo.htm
new file mode 100644 (file)
index 0000000..4fd8411
--- /dev/null
@@ -0,0 +1,27 @@
+<%+cbi/valueheader%>
+
+<%-
+       local value = self:formvalue(section)
+       if not value or value == "-" then
+               value = self:cfgvalue(section) or self.default
+       end
+
+       local nwm = require "luci.model.network".init()
+       local net = nwm:get_network(value)
+-%>
+
+<% if net then %>
+<span class="ifacebadge"><%=net:name()%>:
+       <%
+               local empty = true
+               for _, iface in ipairs(net:get_interfaces() or { net:get_interface() }) do
+                       if not iface:is_bridge() then
+                               empty = false
+        %>
+               <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+       <% end end %>
+       <% if empty then %><em><%:(no interfaces attached)%></em><% end %>
+</span>
+<% end %>
+
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/network_netlist.htm b/modules/base/luasrc/luasrc/view/cbi/network_netlist.htm
new file mode 100644 (file)
index 0000000..7e23d14
--- /dev/null
@@ -0,0 +1,81 @@
+<%+cbi/valueheader%>
+
+<%-
+       local utl = require "luci.util"
+       local nwm = require "luci.model.network".init()
+
+       local net, iface
+       local networks = nwm:get_networks()
+       local value = self:formvalue(section)
+
+       self.cast = nil
+
+       if not value or value == "-" then
+               value = self:cfgvalue(section) or self.default
+       end
+
+       local checked = { }
+       for value in utl.imatch(value) do
+               checked[value] = true
+       end
+-%>
+
+<ul style="margin:0; list-style-type:none; text-align:left">
+       <% for _, net in ipairs(networks) do
+              if (net:name() ~= "loopback") and
+                     (net:name() ~= self.exclude) and
+                     (not self.novirtual or not net:is_virtual())
+                  then %>
+       <li style="padding:0.25em 0">
+               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+                       attr("type", self.widget or "radio") ..
+                       attr("id", cbid .. "." .. net:name()) ..
+                       attr("name", cbid) .. attr("value", net:name()) ..
+                       ifattr(checked[net:name()], "checked", "checked")
+               %> /> &#160;
+               <label<%=attr("for", cbid .. "." .. net:name())%>>
+                       <span class="ifacebadge"><%=net:name()%>:
+                               <%
+                                       local empty = true
+                                       for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
+                                               if not iface:is_bridge() then
+                                                       empty = false
+                                %>
+                                       <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+                               <% end end %>
+                               <% if empty then %><em><%:(no interfaces attached)%></em><% end %>
+                       </span>
+               </label>
+       </li>
+       <% end end %>
+
+       <% if not self.nocreate then %>
+       <li style="padding:0.25em 0">
+               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not value and self.widget ~= "checkbox", "checked", "checked")%> /> &#160;
+               <div style="padding:0.5em; display:inline">
+                       <label<%=attr("for", cbid .. "_new")%>><em>
+                               <%- if self.widget == "checkbox" then -%>
+                                       <%:create:%>
+                               <%- else -%>
+                                       <%:unspecified -or- create:%>
+                               <%- end -%>&#160;</em></label>
+                       <input style="width:6em" type="text"<%=attr("name", cbid .. ".newnet")%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
+               </div>
+       </li>
+       <% elseif self.widget ~= "checkbox" and self.unspecified then %>
+       <li style="padding:0.25em 0">
+               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+                       attr("type", self.widget or "radio") ..
+                       attr("id", cbid .. "_uns") ..
+                       attr("name", cbid) ..
+                       attr("value", "") ..
+                       ifattr(not value or #value == 0, "checked", "checked")
+               %> /> &#160;
+               <div style="padding:0.5em; display:inline">
+                       <label<%=attr("for", cbid .. "_uns")%>><em><%:unspecified%></em></label>
+               </div>
+       </li>
+       <% end %>
+</ul>
+
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/nsection.htm b/modules/base/luasrc/luasrc/view/cbi/nsection.htm
new file mode 100644 (file)
index 0000000..95e7658
--- /dev/null
@@ -0,0 +1,31 @@
+<% if self:cfgvalue(self.section) then section = self.section %>
+       <fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=section%>">
+               <% if self.title and #self.title > 0 then -%>
+                       <legend><%=self.title%></legend>
+               <%- end %>
+               <% if self.description and #self.description > 0 then -%>
+                       <div class="cbi-section-descr"><%=self.description%></div>
+               <%- end %>
+               <% if self.addremove then -%>
+                       <div class="cbi-section-remove right">
+                               <input type="submit" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:Delete%>" />
+                       </div>
+               <%- end %>
+               <%+cbi/tabmenu%>
+               <div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
+                       <%+cbi/ucisection%>
+               </div>
+               <br />
+       </fieldset>
+<% elseif self.addremove then %>
+       <% if self.template_addremove then include(self.template_addremove) else -%>
+       <fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
+               <% if self.title and #self.title > 0 then -%>
+                       <legend><%=self.title%></legend>
+               <%- end %>
+               <div class="cbi-section-descr"><%=self.description%></div>
+               <input type="submit" class="cbi-button-add" name="cbi.cns.<%=self.config%>.<%=self.section%>" value="<%:Add%>" />
+       </fieldset>
+       <%- end %>
+<% end %>
+<!-- /nsection -->
diff --git a/modules/base/luasrc/luasrc/view/cbi/nullsection.htm b/modules/base/luasrc/luasrc/view/cbi/nullsection.htm
new file mode 100644 (file)
index 0000000..bd48950
--- /dev/null
@@ -0,0 +1,38 @@
+<fieldset class="cbi-section">
+       <% if self.title and #self.title > 0 then -%>
+               <legend><%=self.title%></legend>
+       <%- end %>
+       <% if self.description and #self.description > 0 then -%>
+               <div class="cbi-section-descr"><%=self.description%></div>
+       <%- end %>
+       <div class="cbi-section-node" id="cbi-<%=self.config%>-<%=tostring(self):sub(8)%>">
+               <div>
+                       <% self:render_children(1, scope or {}) %>
+               </div>
+               <% if self.error and self.error[1] then -%>
+                       <div class="cbi-section-error">
+                               <ul><% for _, e in ipairs(self.error[1]) do -%>
+                                       <li>
+                                               <%- if e == "invalid" then -%>
+                                                       <%:One or more fields contain invalid values!%>
+                                               <%- elseif e == "missing" then -%>
+                                                       <%:One or more required fields have no value!%>
+                                               <%- else -%>
+                                                       <%=pcdata(e)%>
+                                               <%- end -%>
+                                       </li>
+                               <%- end %></ul>
+                       </div>
+               <%- end %>
+       </div>
+       <br />
+</fieldset>
+<%-
+       if type(self.hidden) == "table" then
+               for k, v in pairs(self.hidden) do
+-%>
+       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
+<%-
+               end
+       end
+%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/simpleform.htm b/modules/base/luasrc/luasrc/view/cbi/simpleform.htm
new file mode 100644 (file)
index 0000000..5216cd5
--- /dev/null
@@ -0,0 +1,57 @@
+<% if not self.embedded then %>
+<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
+       <div>
+               <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+               <input type="hidden" name="cbi.submit" value="1" />
+       </div>
+<% end %>
+       <div class="cbi-map" id="cbi-<%=self.config%>">
+               <% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
+               <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
+               <% self:render_children() %>
+               <br />
+       </div>
+<%- if self.message then %>
+       <div><%=self.message%></div>
+<%- end %>
+<%- if self.errmessage then %>
+       <div class="error"><%=self.errmessage%></div>
+<%- end %>
+<% if not self.embedded then %>
+       <div class="cbi-page-actions">
+<%-
+       if type(self.hidden) == "table" then
+               for k, v in pairs(self.hidden) do
+-%>
+       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
+<%-
+               end
+       end
+%>
+<% if redirect then %>
+       <div style="float:left">
+               <input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
+       </div>
+<% end %>
+<%- if self.flow and self.flow.skip then %>
+       <input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
+<% end %>
+<%- if self.submit ~= false then %>
+       <input class="cbi-button cbi-button-save" type="submit" value="
+               <%- if not self.submit then -%><%-:Submit-%><%-else-%><%=self.submit%><%end-%>
+       " />
+<% end %>
+<%- if self.reset ~= false then %>
+       <input class="cbi-button cbi-button-reset" type="reset" value="
+               <%- if not self.reset then -%><%-:Reset-%><%-else-%><%=self.reset%><%end-%>
+       " />
+<% end %>
+<%- if self.cancel ~= false and self.on_cancel then %>
+       <input class="cbi-button cbi-button-reset" type="submit" name="cbi.cancel" value="
+               <%- if not self.cancel then -%><%-:Cancel-%><%-else-%><%=self.cancel%><%end-%>
+       " />
+<% end %>
+               <script type="text/javascript">cbi_d_update();</script>
+       </div>
+</form>
+<% end %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/tabcontainer.htm b/modules/base/luasrc/luasrc/view/cbi/tabcontainer.htm
new file mode 100644 (file)
index 0000000..38c435d
--- /dev/null
@@ -0,0 +1,7 @@
+<% for tab, data in pairs(self.tabs) do %>
+       <div class="cbi-tabcontainer" id="container.<%=self.config%>.<%=section%>.<%=tab%>"<% if tab ~= self.selected_tab then %> style="display:none"<% end %>>
+               <% if data.description then %><div class="cbi-tab-descr"><%=data.description%></div><% end %>
+               <% self:render_tab(tab, section, scope or {}) %>
+       </div>
+       <script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script>
+<% end %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/tabmenu.htm b/modules/base/luasrc/luasrc/view/cbi/tabmenu.htm
new file mode 100644 (file)
index 0000000..b96ac9c
--- /dev/null
@@ -0,0 +1,13 @@
+<%- if self.tabs then %>
+       <ul class="cbi-tabmenu">
+       <%- self.selected_tab = luci.http.formvalue("tab." .. self.config .. "." .. section) %>
+       <%- for _, tab in ipairs(self.tab_names) do if #self.tabs[tab].childs > 0 then %>
+               <script type="text/javascript">cbi_c['container.<%=self.config%>.<%=section%>.<%=tab%>'] = <%=#self.tabs[tab].childs%>;</script>
+               <%- if not self.selected_tab then self.selected_tab = tab end %>
+               <li id="tab.<%=self.config%>.<%=section%>.<%=tab%>" class="cbi-tab<%=(tab == self.selected_tab) and '' or '-disabled'%>">
+                       <a onclick="this.blur(); return cbi_t_switch('<%=self.config%>.<%=section%>', '<%=tab%>')" href="<%=REQUEST_URI%>?tab.<%=self.config%>.<%=section%>=<%=tab%>"><%=self.tabs[tab].title%></a>
+                       <% if tab == self.selected_tab then %><input type="hidden" id="tab.<%=self.config%>.<%=section%>" name="tab.<%=self.config%>.<%=section%>" value="<%=tab%>" /><% end %>
+               </li>
+       <% end end -%>
+       </ul>
+<% end -%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/tblsection.htm b/modules/base/luasrc/luasrc/view/cbi/tblsection.htm
new file mode 100644 (file)
index 0000000..d928791
--- /dev/null
@@ -0,0 +1,146 @@
+<%-
+local rowcnt = 1
+function rowstyle()
+       rowcnt = rowcnt + 1
+       return (rowcnt % 2) + 1
+end
+
+function width(o)
+       if o.width then
+               if type(o.width) == 'number' then
+                       return ' style="width:%dpx"' % o.width
+               end
+               return ' style="width:%s"' % o.width
+       end
+       return ''
+end
+-%>
+
+<!-- tblsection -->
+<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
+       <% if self.title and #self.title > 0 then -%>
+               <legend><%=self.title%></legend>
+       <%- end %>
+       <%- if self.sortable then -%>
+               <input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
+       <%- end -%>
+       <div class="cbi-section-descr"><%=self.description%></div>
+       <div class="cbi-section-node">
+               <%- local count = 0 -%>
+               <table class="cbi-section-table">
+                       <tr class="cbi-section-table-titles">
+                       <%- if not self.anonymous then -%>
+                               <%- if self.sectionhead then -%>
+                                       <th class="cbi-section-table-cell"><%=self.sectionhead%></th>
+                               <%- else -%>
+                                       <th>&#160;</th>
+                               <%- end -%>
+                       <%- end -%>
+                       <%- for i, k in pairs(self.children) do if not k.optional then -%>
+                               <th class="cbi-section-table-cell"<%=width(k)%>>
+                               <%- if k.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%- end -%>
+                                       <%-=k.title-%>
+                               <%- if k.titleref then -%></a><%- end -%>
+                               </th>
+                       <%- count = count + 1; end; end; if self.sortable then -%>
+                               <th class="cbi-section-table-cell"><%:Sort%></th>
+                       <%- end; if self.extedit or self.addremove then -%>
+                               <th class="cbi-section-table-cell">&#160;</th>
+                       <%- count = count + 1; end -%>
+                       </tr>
+                       <tr class="cbi-section-table-descr">
+                       <%- if not self.anonymous then -%>
+                               <%- if self.sectiondesc then -%>
+                                       <th class="cbi-section-table-cell"><%=self.sectiondesc%></th>
+                               <%- else -%>
+                                       <th></th>
+                               <%- end -%>
+                       <%- end -%>
+                       <%- for i, k in pairs(self.children) do if not k.optional then -%>
+                               <th class="cbi-section-table-cell"<%=width(k)%>><%=k.description%></th>
+                       <%- end; end; if self.sortable then -%>
+                               <th class="cbi-section-table-cell"></th>
+                       <%- end; if self.extedit or self.addremove then -%>
+                               <th class="cbi-section-table-cell"></th>
+                       <%- end -%>
+                       </tr>
+                       <%- local isempty = true
+                           for i, k in ipairs(self:cfgsections()) do
+                                       section = k
+                                       isempty = false
+                                       scope = { valueheader = "cbi/cell_valueheader", valuefooter = "cbi/cell_valuefooter" }
+                       -%>
+                       <tr class="cbi-section-table-row<% if self.extedit or self.rowcolors then %> cbi-rowstyle-<%=rowstyle()%><% end %>" id="cbi-<%=self.config%>-<%=section%>">
+                               <% if not self.anonymous then -%>
+                                       <th><h3><%=(type(self.sectiontitle) == "function") and self:sectiontitle(section) or k%></h3></th>
+                               <%- end %>
+
+
+                               <%-
+                                       for k, node in ipairs(self.children) do
+                                               if not node.optional then
+                                                       node:render(section, scope or {})
+                                               end
+                                       end
+                               -%>
+
+                               <%- if self.sortable then -%>
+                                       <td class="cbi-section-table-cell">
+                                               <input class="cbi-button cbi-button-up" type="button" value=""  onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move up%>" title="<%:Move up%>" />
+                                               <input class="cbi-button cbi-button-down" type="button" value=""  onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move down%>" title="<%:Move down%>" />
+                                       </td>
+                               <%- end -%>
+
+                               <%- if self.extedit or self.addremove then -%>
+                                       <td class="cbi-section-table-cell">
+                                               <%- if self.extedit then -%>
+                                                       <input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
+                                                       <%- if type(self.extedit) == "string" then
+                                                       %> onclick="location.href='<%=self.extedit:format(section)%>'"
+                                                       <%- elseif type(self.extedit) == "function" then
+                                                       %> onclick="location.href='<%=self:extedit(section)%>'"
+                                                       <%- end 
+                                                       %> alt="<%:Edit%>" title="<%:Edit%>" />
+                                               <%- end; if self.addremove then %>
+                                                       <input class="cbi-button cbi-button-remove" type="submit" value="<%:Delete%>"  onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
+                                               <%- end -%>
+                                       </td>
+                               <%- end -%>
+                       </tr>
+                       <%- end -%>
+
+                       <%- if isempty then -%>
+                       <tr class="cbi-section-table-row">
+                               <td colspan="<%=count%>"><em><br /><%:This section contains no values yet%></em></td>
+                       </tr>
+                       <%- end -%>
+               </table>
+
+               <% if self.error then %>
+                       <div class="cbi-section-error">
+                               <ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
+                                       <li><%=pcdata(e):gsub("\n","<br />")%></li>
+                               <%- end end %></ul>
+                       </div>
+               <% end %>
+
+               <%- if self.addremove then -%>
+                       <% if self.template_addremove then include(self.template_addremove) else -%>
+                       <div class="cbi-section-create cbi-tblsection-create">
+                               <% if self.anonymous then %>
+                                       <input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
+                               <% else %>
+                                       <% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
+                                       <input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" />
+                                       <script type="text/javascript">cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');</script>
+                                       <input class="cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
+                                       <% if self.invalid_cts then -%>
+                                               <br /><%:Invalid%></div>
+                                       <%- end %>
+                               <% end %>
+                       </div>
+                       <%- end %>
+               <%- end -%>
+       </div>
+</fieldset>
+<!-- /tblsection -->
diff --git a/modules/base/luasrc/luasrc/view/cbi/tsection.htm b/modules/base/luasrc/luasrc/view/cbi/tsection.htm
new file mode 100644 (file)
index 0000000..087548b
--- /dev/null
@@ -0,0 +1,48 @@
+<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
+       <% if self.title and #self.title > 0 then -%>
+               <legend><%=self.title%></legend>
+       <%- end %>
+       <div class="cbi-section-descr"><%=self.description%></div>
+       <% local isempty = true for i, k in ipairs(self:cfgsections()) do -%>
+               <% if self.addremove then -%>
+                       <div class="cbi-section-remove right">
+                               <input type="submit" name="cbi.rts.<%=self.config%>.<%=k%>" onclick="this.form.cbi_state='del-section'; return true" value="<%:Delete%>" class="cbi-button" />
+                       </div>
+               <%- end %>
+
+               <%- section = k; isempty = false -%>
+
+               <% if not self.anonymous then -%>
+                       <h3><%=section:upper()%></h3>
+               <%- end %>
+
+               <%+cbi/tabmenu%>
+
+               <fieldset class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
+                       <%+cbi/ucisection%>
+               </fieldset>
+               <br />
+       <%- end %>
+
+       <% if isempty then -%>
+               <em><%:This section contains no values yet%><br /><br /></em>
+       <%- end %>
+
+       <% if self.addremove then -%>
+               <% if self.template_addremove then include(self.template_addremove) else -%>
+               <div class="cbi-section-create">
+                       <% if self.anonymous then -%>
+                               <input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
+                       <%- else -%>
+                               <% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
+                               <input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" />
+                               <script type="text/javascript">cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');</script>
+                               <input type="submit" class="cbi-button cbi-button-add" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" />
+                               <% if self.invalid_cts then -%>
+                                       <br /><%:Invalid%></div>
+                               <%- end %>
+                       <%- end %>
+               </div>
+               <%- end %>
+       <%- end %>
+</fieldset>
diff --git a/modules/base/luasrc/luasrc/view/cbi/tvalue.htm b/modules/base/luasrc/luasrc/view/cbi/tvalue.htm
new file mode 100644 (file)
index 0000000..fcf7a6c
--- /dev/null
@@ -0,0 +1,5 @@
+<%+cbi/valueheader%>
+       <textarea class="cbi-input-textarea" <% if not self.size then %> style="width: 100%"<% else %> cols="<%=self.size%>"<% end %> onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. attr("id", cbid) .. ifattr(self.rows, "rows") .. ifattr(self.wrap, "wrap") %>>
+       <%-=pcdata(self:cfgvalue(section))-%>
+       </textarea>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/ucisection.htm b/modules/base/luasrc/luasrc/view/cbi/ucisection.htm
new file mode 100644 (file)
index 0000000..3b69f12
--- /dev/null
@@ -0,0 +1,75 @@
+<%-
+       if type(self.hidden) == "table" then
+               for k, v in pairs(self.hidden) do
+-%>
+       <input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
+<%-
+               end
+       end
+%>
+
+<% if self.tabs then %>
+       <%+cbi/tabcontainer%>
+<% else %>
+       <% self:render_children(section, scope or {}) %>
+<% end %>
+
+<% if self.error and self.error[section] then -%>
+       <div class="cbi-section-error">
+               <ul><% for _, e in ipairs(self.error[section]) do -%>
+                       <li>
+                               <%- if e == "invalid" then -%>
+                                       <%:One or more fields contain invalid values!%>
+                               <%- elseif e == "missing" then -%>
+                                       <%:One or more required fields have no value!%>
+                               <%- else -%>
+                                       <%=pcdata(e)%>
+                               <%- end -%>
+                       </li>
+               <%- end %></ul>
+       </div>
+<%- end %>
+
+<% if self.optionals[section] and #self.optionals[section] > 0 or self.dynamic then %>
+       <div class="cbi-optionals">
+               <% if self.dynamic then %>
+                       <input type="text" id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" />
+                       <% if self.optionals[section] and #self.optionals[section] > 0 then %>
+                       <script type="text/javascript">
+                               cbi_combobox_init('cbi.opt.<%=self.config%>.<%=section%>', {
+                               <%-
+                                       for i, val in pairs(self.optionals[section]) do
+                               -%>
+                                       <%-=string.format("%q", val.option) .. ":" .. string.format("%q", striptags(val.title))-%>
+                                       <%-if next(self.optionals[section], i) then-%>,<%-end-%>
+                               <%-
+                                       end
+                               -%>
+                               }, '', '<%-: -- custom -- -%>');
+                       </script>
+                       <% end %>
+               <% else %>
+               <select id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>">
+                       <option><%: -- Additional Field -- %></option>
+                       <% for key, val in pairs(self.optionals[section]) do -%>
+                               <option id="cbi-<%=self.config.."-"..section.."-"..val.option%>" value="<%=val.option%>"><%=striptags(val.title)%></option>
+                       <%- end %>
+               </select>
+               <script type="text/javascript"><% for key, val in pairs(self.optionals[section]) do %>
+                       <% if #val.deps > 0 then %><% for j, d in ipairs(val.deps) do -%>
+                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..val.option..d.add%>", {
+               <%-
+                       for k,v in pairs(d.deps) do
+               -%>
+                       <%-=string.format('"cbid.%s.%s.%s"', self.config, section, k) .. ":" .. string.format("%q", v)-%>
+                       <%-if next(d.deps, k) then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+                       });
+               <%- end %><% end %>
+               <% end %></script>
+       <% end %>
+               <input type="submit" class="cbi-button cbi-button-fieldadd" value="<%:Add%>" />
+       </div>
+<% end %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/upload.htm b/modules/base/luasrc/luasrc/view/cbi/upload.htm
new file mode 100644 (file)
index 0000000..7770934
--- /dev/null
@@ -0,0 +1,14 @@
+<%
+       local t = require("luci.tools.webadmin")
+       local v = self:cfgvalue(section)
+       local s = v and nixio.fs.stat(v)
+-%>
+<%+cbi/valueheader%>
+       <% if s then %>
+               <%:Uploaded File%> (<%=t.byte_format(s.size)%>)
+               <input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
+               <input class="cbi-button cbi-input-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
+       <% else %>
+               <input class="cbi-input-file" type="file"<%= attr("name", cbid) .. attr("id", cbid) %> />
+       <% end %>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/value.htm b/modules/base/luasrc/luasrc/view/cbi/value.htm
new file mode 100644 (file)
index 0000000..d1a7bea
--- /dev/null
@@ -0,0 +1,35 @@
+<%+cbi/valueheader%>
+       <input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
+               attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
+               ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder")
+       %> />
+       <% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
+       <% if #self.keylist > 0 or self.datatype then -%>
+       <script type="text/javascript">//<![CDATA[
+               <% if #self.keylist > 0 then -%>
+               cbi_combobox_init('<%=cbid%>', {
+               <%-
+                       for i, k in ipairs(self.keylist) do
+               -%>
+                       <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
+                       <%-if i<#self.keylist then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+               }, '<%- if not self.rmempty and not self.optional then -%>
+                       <%-: -- Please choose -- -%>
+                       <%- elseif self.placeholder then -%>
+                       <%-= pcdata(self.placeholder) -%>
+               <%- end -%>', '
+               <%- if self.combobox_manual then -%>
+                       <%-=self.combobox_manual-%>
+               <%- else -%>
+                       <%-: -- custom -- -%>
+               <%- end -%>');
+               <%- end %>
+               <% if self.datatype then -%>
+               cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("'", "\\'")%>');
+               <%- end %>
+       //]]></script>
+       <% end -%>
+<%+cbi/valuefooter%>
diff --git a/modules/base/luasrc/luasrc/view/cbi/valuefooter.htm b/modules/base/luasrc/luasrc/view/cbi/valuefooter.htm
new file mode 100644 (file)
index 0000000..805312e
--- /dev/null
@@ -0,0 +1 @@
+<% include( valuefooter or "cbi/full_valuefooter" ) %>
diff --git a/modules/base/luasrc/luasrc/view/cbi/valueheader.htm b/modules/base/luasrc/luasrc/view/cbi/valueheader.htm
new file mode 100644 (file)
index 0000000..761a54a
--- /dev/null
@@ -0,0 +1 @@
+<% include( valueheader or "cbi/full_valueheader" ) %>
diff --git a/modules/base/luasrc/model/cbi/admin_network/proto_dhcp.lua b/modules/base/luasrc/model/cbi/admin_network/proto_dhcp.lua
new file mode 100644 (file)
index 0000000..fe3fec6
--- /dev/null
@@ -0,0 +1,76 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+]]--
+
+local map, section, net = ...
+local ifc = net:get_interface()
+
+local hostname, accept_ra, send_rs
+local bcast, defaultroute, peerdns, dns, metric, clientid, vendorclass
+
+
+hostname = section:taboption("general", Value, "hostname",
+       translate("Hostname to send when requesting DHCP"))
+
+hostname.placeholder = luci.sys.hostname()
+hostname.datatype    = "hostname"
+
+
+bcast = section:taboption("advanced", Flag, "broadcast",
+       translate("Use broadcast flag"),
+       translate("Required for certain ISPs, e.g. Charter with DOCSIS 3"))
+
+bcast.default = bcast.disabled
+
+
+defaultroute = section:taboption("advanced", Flag, "defaultroute",
+       translate("Use default gateway"),
+       translate("If unchecked, no default route is configured"))
+
+defaultroute.default = defaultroute.enabled
+
+
+peerdns = section:taboption("advanced", Flag, "peerdns",
+       translate("Use DNS servers advertised by peer"),
+       translate("If unchecked, the advertised DNS server addresses are ignored"))
+
+peerdns.default = peerdns.enabled
+
+
+dns = section:taboption("advanced", DynamicList, "dns",
+       translate("Use custom DNS servers"))
+
+dns:depends("peerdns", "")
+dns.datatype = "ipaddr"
+dns.cast     = "string"
+
+
+metric = section:taboption("advanced", Value, "metric",
+       translate("Use gateway metric"))
+
+metric.placeholder = "0"
+metric.datatype    = "uinteger"
+
+
+clientid = section:taboption("advanced", Value, "clientid",
+       translate("Client ID to send when requesting DHCP"))
+
+
+vendorclass = section:taboption("advanced", Value, "vendorid",
+       translate("Vendor Class to send when requesting DHCP"))
+
+
+luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
+
+
+mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
+mtu.placeholder = "1500"
+mtu.datatype    = "max(9200)"
diff --git a/modules/base/luasrc/model/cbi/admin_network/proto_none.lua b/modules/base/luasrc/model/cbi/admin_network/proto_none.lua
new file mode 100644 (file)
index 0000000..0e34b67
--- /dev/null
@@ -0,0 +1,13 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+]]--
+
+local map, section, net = ...
diff --git a/modules/base/luasrc/model/cbi/admin_network/proto_static.lua b/modules/base/luasrc/model/cbi/admin_network/proto_static.lua
new file mode 100644 (file)
index 0000000..338c0b7
--- /dev/null
@@ -0,0 +1,90 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+]]--
+
+local map, section, net = ...
+local ifc = net:get_interface()
+
+local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
+local mtu, metric
+
+
+ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
+ipaddr.datatype = "ip4addr"
+
+
+netmask = section:taboption("general", Value, "netmask",
+       translate("IPv4 netmask"))
+
+netmask.datatype = "ip4addr"
+netmask:value("255.255.255.0")
+netmask:value("255.255.0.0")
+netmask:value("255.0.0.0")
+
+
+gateway = section:taboption("general", Value, "gateway", translate("IPv4 gateway"))
+gateway.datatype = "ip4addr"
+
+
+broadcast = section:taboption("general", Value, "broadcast", translate("IPv4 broadcast"))
+broadcast.datatype = "ip4addr"
+
+
+dns = section:taboption("general", DynamicList, "dns",
+       translate("Use custom DNS servers"))
+
+dns.datatype = "ipaddr"
+dns.cast     = "string"
+
+
+if luci.model.network:has_ipv6() then
+
+       local ip6assign = section:taboption("general", Value, "ip6assign", translate("IPv6 assignment length"),
+               translate("Assign a part of given length of every public IPv6-prefix to this interface"))
+       ip6assign:value("", translate("disabled"))
+       ip6assign:value("64")
+       ip6assign.datatype = "max(64)"
+
+       local ip6hint = section:taboption("general", Value, "ip6hint", translate("IPv6 assignment hint"),
+               translate("Assign prefix parts using this hexadecimal subprefix ID for this interface."))
+       for i=33,64 do ip6hint:depends("ip6assign", i) end
+
+       ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
+       ip6addr.datatype = "ip6addr"
+       ip6addr:depends("ip6assign", "")
+
+
+       ip6gw = section:taboption("general", Value, "ip6gw", translate("IPv6 gateway"))
+       ip6gw.datatype = "ip6addr"
+       ip6gw:depends("ip6assign", "")
+
+
+       local ip6prefix = s:taboption("general", Value, "ip6prefix", translate("IPv6 routed prefix"),
+               translate("Public prefix routed to this device for distribution to clients."))
+       ip6prefix.datatype = "ip6addr"
+       ip6prefix:depends("ip6assign", "")
+
+end
+
+
+luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
+
+
+mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
+mtu.placeholder = "1500"
+mtu.datatype    = "max(9200)"
+
+
+metric = section:taboption("advanced", Value, "metric",
+       translate("Use gateway metric"))
+
+metric.placeholder = "0"
+metric.datatype    = "uinteger"
diff --git a/modules/base/luasrc/model/firewall.lua b/modules/base/luasrc/model/firewall.lua
new file mode 100644 (file)
index 0000000..a9f6fdb
--- /dev/null
@@ -0,0 +1,582 @@
+--[[
+LuCI - Firewall model
+
+Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local type, pairs, ipairs, table, luci, math
+       = type, pairs, ipairs, table, luci, math
+
+local tpl = require "luci.template.parser"
+local utl = require "luci.util"
+local uci = require "luci.model.uci"
+
+module "luci.model.firewall"
+
+
+local uci_r, uci_s
+
+function _valid_id(x)
+       return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$"))
+end
+
+function _get(c, s, o)
+       return uci_r:get(c, s, o)
+end
+
+function _set(c, s, o, v)
+       if v ~= nil then
+               if type(v) == "boolean" then v = v and "1" or "0" end
+               return uci_r:set(c, s, o, v)
+       else
+               return uci_r:delete(c, s, o)
+       end
+end
+
+
+function init(cursor)
+       uci_r = cursor or uci_r or uci.cursor()
+       uci_s = uci_r:substate()
+
+       return _M
+end
+
+function save(self, ...)
+       uci_r:save(...)
+       uci_r:load(...)
+end
+
+function commit(self, ...)
+       uci_r:commit(...)
+       uci_r:load(...)
+end
+
+function get_defaults()
+       return defaults()
+end
+
+function new_zone(self)
+       local name = "newzone"
+       local count = 1
+
+       while self:get_zone(name) do
+               count = count + 1
+               name = "newzone%d" % count
+       end
+
+       return self:add_zone(name)
+end
+
+function add_zone(self, n)
+       if _valid_id(n) and not self:get_zone(n) then
+               local d = defaults()
+               local z = uci_r:section("firewall", "zone", nil, {
+                       name    = n,
+                       network = " ",
+                       input   = d:input()   or "DROP",
+                       forward = d:forward() or "DROP",
+                       output  = d:output()  or "DROP"
+               })
+
+               return z and zone(z)
+       end
+end
+
+function get_zone(self, n)
+       if uci_r:get("firewall", n) == "zone" then
+               return zone(n)
+       else
+               local z
+               uci_r:foreach("firewall", "zone",
+                       function(s)
+                               if n and s.name == n then
+                                       z = s['.name']
+                                       return false
+                               end
+                       end)
+               return z and zone(z)
+       end
+end
+
+function get_zones(self)
+       local zones = { }
+       local znl = { }
+
+       uci_r:foreach("firewall", "zone",
+               function(s)
+                       if s.name then
+                               znl[s.name] = zone(s['.name'])
+                       end
+               end)
+
+       local z
+       for z in utl.kspairs(znl) do
+               zones[#zones+1] = znl[z]
+       end
+
+       return zones
+end
+
+function get_zone_by_network(self, net)
+       local z
+
+       uci_r:foreach("firewall", "zone",
+               function(s)
+                       if s.name and net then
+                               local n
+                               for n in utl.imatch(s.network or s.name) do
+                                       if n == net then
+                                               z = s['.name']
+                                               return false
+                                       end
+                               end
+                       end
+               end)
+
+       return z and zone(z)
+end
+
+function del_zone(self, n)
+       local r = false
+
+       if uci_r:get("firewall", n) == "zone" then
+               local z = uci_r:get("firewall", n, "name")
+               r = uci_r:delete("firewall", n)
+               n = z
+       else
+               uci_r:foreach("firewall", "zone",
+                       function(s)
+                               if n and s.name == n then
+                                       r = uci_r:delete("firewall", s['.name'])
+                                       return false
+                               end
+                       end)
+       end
+
+       if r then
+               uci_r:foreach("firewall", "rule",
+                       function(s)
+                               if s.src == n or s.dest == n then
+                                       uci_r:delete("firewall", s['.name'])
+                               end
+                       end)
+
+               uci_r:foreach("firewall", "redirect",
+                       function(s)
+                               if s.src == n or s.dest == n then
+                                       uci_r:delete("firewall", s['.name'])
+                               end
+                       end)
+
+               uci_r:foreach("firewall", "forwarding",
+                       function(s)
+                               if s.src == n or s.dest == n then
+                                       uci_r:delete("firewall", s['.name'])
+                               end
+                       end)
+       end
+
+       return r
+end
+
+function rename_zone(self, old, new)
+       local r = false
+
+       if _valid_id(new) and not self:get_zone(new) then
+               uci_r:foreach("firewall", "zone",
+                       function(s)
+                               if old and s.name == old then
+                                       if not s.network then
+                                               uci_r:set("firewall", s['.name'], "network", old)
+                                       end
+                                       uci_r:set("firewall", s['.name'], "name", new)
+                                       r = true
+                                       return false
+                               end
+                       end)
+
+               if r then
+                       uci_r:foreach("firewall", "rule",
+                               function(s)
+                                       if s.src == old then
+                                               uci_r:set("firewall", s['.name'], "src", new)
+                                       end
+                                       if s.dest == old then
+                                               uci_r:set("firewall", s['.name'], "dest", new)
+                                       end
+                               end)
+
+                       uci_r:foreach("firewall", "redirect",
+                               function(s)
+                                       if s.src == old then
+                                               uci_r:set("firewall", s['.name'], "src", new)
+                                       end
+                                       if s.dest == old then
+                                               uci_r:set("firewall", s['.name'], "dest", new)
+                                       end
+                               end)
+
+                       uci_r:foreach("firewall", "forwarding",
+                               function(s)
+                                       if s.src == old then
+                                               uci_r:set("firewall", s['.name'], "src", new)
+                                       end
+                                       if s.dest == old then
+                                               uci_r:set("firewall", s['.name'], "dest", new)
+                                       end
+                               end)
+               end
+       end
+
+       return r
+end
+
+function del_network(self, net)
+       local z
+       if net then
+               for _, z in ipairs(self:get_zones()) do
+                       z:del_network(net)
+               end
+       end
+end
+
+
+defaults = utl.class()
+function defaults.__init__(self)
+       uci_r:foreach("firewall", "defaults",
+               function(s)
+                       self.sid  = s['.name']
+                       return false
+               end)
+
+       self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { })
+end
+
+function defaults.get(self, opt)
+       return _get("firewall", self.sid, opt)
+end
+
+function defaults.set(self, opt, val)
+       return _set("firewall", self.sid, opt, val)
+end
+
+function defaults.syn_flood(self)
+       return (self:get("syn_flood") == "1")
+end
+
+function defaults.drop_invalid(self)
+       return (self:get("drop_invalid") == "1")
+end
+
+function defaults.input(self)
+       return self:get("input") or "DROP"
+end
+
+function defaults.forward(self)
+       return self:get("forward") or "DROP"
+end
+
+function defaults.output(self)
+       return self:get("output") or "DROP"
+end
+
+
+zone = utl.class()
+function zone.__init__(self, z)
+       if uci_r:get("firewall", z) == "zone" then
+               self.sid  = z
+               self.data = uci_r:get_all("firewall", z)
+       else
+               uci_r:foreach("firewall", "zone",
+                       function(s)
+                               if s.name == z then
+                                       self.sid  = s['.name']
+                                       self.data = s
+                                       return false
+                               end
+                       end)
+       end
+end
+
+function zone.get(self, opt)
+       return _get("firewall", self.sid, opt)
+end
+
+function zone.set(self, opt, val)
+       return _set("firewall", self.sid, opt, val)
+end
+
+function zone.masq(self)
+       return (self:get("masq") == "1")
+end
+
+function zone.name(self)
+       return self:get("name")
+end
+
+function zone.network(self)
+       return self:get("network")
+end
+
+function zone.input(self)
+       return self:get("input") or defaults():input() or "DROP"
+end
+
+function zone.forward(self)
+       return self:get("forward") or defaults():forward() or "DROP"
+end
+
+function zone.output(self)
+       return self:get("output") or defaults():output() or "DROP"
+end
+
+function zone.add_network(self, net)
+       if uci_r:get("network", net) == "interface" then
+               local nets = { }
+
+               local n
+               for n in utl.imatch(self:get("network") or self:get("name")) do
+                       if n ~= net then
+                               nets[#nets+1] = n
+                       end
+               end
+
+               nets[#nets+1] = net
+
+               _M:del_network(net)
+               self:set("network", table.concat(nets, " "))
+       end
+end
+
+function zone.del_network(self, net)
+       local nets = { }
+
+       local n
+       for n in utl.imatch(self:get("network") or self:get("name")) do
+               if n ~= net then
+                       nets[#nets+1] = n
+               end
+       end
+
+       if #nets > 0 then
+               self:set("network", table.concat(nets, " "))
+       else
+               self:set("network", " ")
+       end
+end
+
+function zone.get_networks(self)
+       local nets = { }
+
+       local n
+       for n in utl.imatch(self:get("network") or self:get("name")) do
+               nets[#nets+1] = n
+       end
+
+       return nets
+end
+
+function zone.clear_networks(self)
+       self:set("network", " ")
+end
+
+function zone.get_forwardings_by(self, what)
+       local name = self:name()
+       local forwards = { }
+
+       uci_r:foreach("firewall", "forwarding",
+               function(s)
+                       if s.src and s.dest and s[what] == name then
+                               forwards[#forwards+1] = forwarding(s['.name'])
+                       end
+               end)
+
+       return forwards
+end
+
+function zone.add_forwarding_to(self, dest)
+       local exist, forward
+
+       for _, forward in ipairs(self:get_forwardings_by('src')) do
+               if forward:dest() == dest then
+                       exist = true
+                       break
+               end
+       end
+
+       if not exist and dest ~= self:name() and _valid_id(dest) then
+               local s = uci_r:section("firewall", "forwarding", nil, {
+                       src     = self:name(),
+                       dest    = dest
+               })
+
+               return s and forwarding(s)
+       end
+end
+
+function zone.add_forwarding_from(self, src)
+       local exist, forward
+
+       for _, forward in ipairs(self:get_forwardings_by('dest')) do
+               if forward:src() == src then
+                       exist = true
+                       break
+               end
+       end
+
+       if not exist and src ~= self:name() and _valid_id(src) then
+               local s = uci_r:section("firewall", "forwarding", nil, {
+                       src     = src,
+                       dest    = self:name()
+               })
+
+               return s and forwarding(s)
+       end
+end
+
+function zone.del_forwardings_by(self, what)
+       local name = self:name()
+
+       uci_r:delete_all("firewall", "forwarding",
+               function(s)
+                       return (s.src and s.dest and s[what] == name)
+               end)
+end
+
+function zone.add_redirect(self, options)
+       options = options or { }
+       options.src = self:name()
+
+       local s = uci_r:section("firewall", "redirect", nil, options)
+       return s and redirect(s)
+end
+
+function zone.add_rule(self, options)
+       options = options or { }
+       options.src = self:name()
+
+       local s = uci_r:section("firewall", "rule", nil, options)
+       return s and rule(s)
+end
+
+function zone.get_color(self)
+       if self and self:name() == "lan" then
+               return "#90f090"
+       elseif self and self:name() == "wan" then
+               return "#f09090"
+       elseif self then
+               math.randomseed(tpl.hash(self:name()))
+
+               local r   = math.random(128)
+               local g   = math.random(128)
+               local min = 0
+               local max = 128
+
+               if ( r + g ) < 128 then
+                       min = 128 - r - g
+               else
+                       max = 255 - r - g
+               end
+
+               local b = min + math.floor( math.random() * ( max - min ) )
+
+               return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
+       else
+               return "#eeeeee"
+       end
+end
+
+
+forwarding = utl.class()
+function forwarding.__init__(self, f)
+       self.sid = f
+end
+
+function forwarding.src(self)
+       return uci_r:get("firewall", self.sid, "src")
+end
+
+function forwarding.dest(self)
+       return uci_r:get("firewall", self.sid, "dest")
+end
+
+function forwarding.src_zone(self)
+       return zone(self:src())
+end
+
+function forwarding.dest_zone(self)
+       return zone(self:dest())
+end
+
+
+rule = utl.class()
+function rule.__init__(self, f)
+       self.sid = f
+end
+
+function rule.get(self, opt)
+       return _get("firewall", self.sid, opt)
+end
+
+function rule.set(self, opt, val)
+       return _set("firewall", self.sid, opt, val)
+end
+
+function rule.src(self)
+       return uci_r:get("firewall", self.sid, "src")
+end
+
+function rule.dest(self)
+       return uci_r:get("firewall", self.sid, "dest")
+end
+
+function rule.src_zone(self)
+       return zone(self:src())
+end
+
+function rule.dest_zone(self)
+       return zone(self:dest())
+end
+
+
+redirect = utl.class()
+function redirect.__init__(self, f)
+       self.sid = f
+end
+
+function redirect.get(self, opt)
+       return _get("firewall", self.sid, opt)
+end
+
+function redirect.set(self, opt, val)
+       return _set("firewall", self.sid, opt, val)
+end
+
+function redirect.src(self)
+       return uci_r:get("firewall", self.sid, "src")
+end
+
+function redirect.dest(self)
+       return uci_r:get("firewall", self.sid, "dest")
+end
+
+function redirect.src_zone(self)
+       return zone(self:src())
+end
+
+function redirect.dest_zone(self)
+       return zone(self:dest())
+end
diff --git a/modules/base/luasrc/model/ipkg.lua b/modules/base/luasrc/model/ipkg.lua
new file mode 100644 (file)
index 0000000..c927e71
--- /dev/null
@@ -0,0 +1,239 @@
+--[[
+LuCI - Lua Configuration Interface
+
+(c) 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
+(c) 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+local os   = require "os"
+local io   = require "io"
+local fs   = require "nixio.fs"
+local util = require "luci.util"
+
+local type  = type
+local pairs = pairs
+local error = error
+local table = table
+
+local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
+local icfg = "/etc/opkg.conf"
+
+--- LuCI OPKG call abstraction library
+module "luci.model.ipkg"
+
+
+-- Internal action function
+local function _action(cmd, ...)
+       local pkg = ""
+       for k, v in pairs({...}) do
+               pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
+       end
+
+       local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg }
+       local r = os.execute(c)
+       local e = fs.readfile("/tmp/opkg.stderr")
+       local o = fs.readfile("/tmp/opkg.stdout")
+
+       fs.unlink("/tmp/opkg.stderr")
+       fs.unlink("/tmp/opkg.stdout")
+
+       return r, o or "", e or ""
+end
+
+-- Internal parser function
+local function _parselist(rawdata)
+       if type(rawdata) ~= "function" then
+               error("OPKG: Invalid rawdata given")
+       end
+
+       local data = {}
+       local c = {}
+       local l = nil
+
+       for line in rawdata do
+               if line:sub(1, 1) ~= " " then
+                       local key, val = line:match("(.-): ?(.*)%s*")
+
+                       if key and val then
+                               if key == "Package" then
+                                       c = {Package = val}
+                                       data[val] = c
+                               elseif key == "Status" then
+                                       c.Status = {}
+                                       for j in val:gmatch("([^ ]+)") do
+                                               c.Status[j] = true
+                                       end
+                               else
+                                       c[key] = val
+                               end
+                               l = key
+                       end
+               else
+                       -- Multi-line field
+                       c[l] = c[l] .. "\n" .. line
+               end
+       end
+
+       return data
+end
+
+-- Internal lookup function
+local function _lookup(act, pkg)
+       local cmd = ipkg .. " " .. act
+       if pkg then
+               cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'"
+       end
+
+       -- OPKG sometimes kills the whole machine because it sucks
+       -- Therefore we have to use a sucky approach too and use
+       -- tmpfiles instead of directly reading the output
+       local tmpfile = os.tmpname()
+       os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile))
+
+       local data = _parselist(io.lines(tmpfile))
+       os.remove(tmpfile)
+       return data
+end
+
+
+--- Return information about installed and available packages.
+-- @param pkg Limit output to a (set of) packages
+-- @return Table containing package information
+function info(pkg)
+       return _lookup("info", pkg)
+end
+
+--- Return the package status of one or more packages.
+-- @param pkg Limit output to a (set of) packages
+-- @return Table containing package status information
+function status(pkg)
+       return _lookup("status", pkg)
+end
+
+--- Install one or more packages.
+-- @param ... List of packages to install
+-- @return Boolean indicating the status of the action
+-- @return OPKG return code, STDOUT and STDERR
+function install(...)
+       return _action("install", ...)
+end
+
+--- Determine whether a given package is installed.
+-- @param pkg Package
+-- @return Boolean
+function installed(pkg)
+       local p = status(pkg)[pkg]
+       return (p and p.Status and p.Status.installed)
+end
+
+--- Remove one or more packages.
+-- @param ... List of packages to install
+-- @return Boolean indicating the status of the action
+-- @return OPKG return code, STDOUT and STDERR
+function remove(...)
+       return _action("remove", ...)
+end
+
+--- Update package lists.
+-- @return Boolean indicating the status of the action
+-- @return OPKG return code, STDOUT and STDERR
+function update()
+       return _action("update")
+end
+
+--- Upgrades all installed packages.
+-- @return Boolean indicating the status of the action
+-- @return OPKG return code, STDOUT and STDERR
+function upgrade()
+       return _action("upgrade")
+end
+
+-- List helper
+function _list(action, pat, cb)
+       local fd = io.popen(ipkg .. " " .. action ..
+               (pat and (" '%s'" % pat:gsub("'", "")) or ""))
+
+       if fd then
+               local name, version, desc
+               while true do
+                       local line = fd:read("*l")
+                       if not line then break end
+
+                       name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
+
+                       if not name then
+                               name, version = line:match("^(.-) %- (.+)")
+                               desc = ""
+                       end
+
+                       cb(name, version, desc)
+
+                       name    = nil
+                       version = nil
+                       desc    = nil
+               end
+
+               fd:close()
+       end
+end
+
+--- List all packages known to opkg.
+-- @param pat  Only find packages matching this pattern, nil lists all packages
+-- @param cb   Callback function invoked for each package, receives name, version and description as arguments
+-- @return     nothing
+function list_all(pat, cb)
+       _list("list", pat, cb)
+end
+
+--- List installed packages.
+-- @param pat  Only find packages matching this pattern, nil lists all packages
+-- @param cb   Callback function invoked for each package, receives name, version and description as arguments
+-- @return     nothing
+function list_installed(pat, cb)
+       _list("list_installed", pat, cb)
+end
+
+--- Find packages that match the given pattern.
+-- @param pat  Find packages whose names or descriptions match this pattern, nil results in zero results
+-- @param cb   Callback function invoked for each patckage, receives name, version and description as arguments
+-- @return     nothing
+function find(pat, cb)
+       _list("find", pat, cb)
+end
+
+
+--- Determines the overlay root used by opkg.
+-- @return             String containing the directory path of the overlay root.
+function overlay_root()
+       local od = "/"
+       local fd = io.open(icfg, "r")
+
+       if fd then
+               local ln
+
+               repeat
+                       ln = fd:read("*l")
+                       if ln and ln:match("^%s*option%s+overlay_root%s+") then
+                               od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
+
+                               local s = fs.stat(od)
+                               if not s or s.type ~= "dir" then
+                                       od = "/"
+                               end
+
+                               break
+                       end
+               until not ln
+
+               fd:close()
+       end
+
+       return od
+end
diff --git a/modules/base/luasrc/model/network.lua b/modules/base/luasrc/model/network.lua
new file mode 100644 (file)
index 0000000..a409621
--- /dev/null
@@ -0,0 +1,1584 @@
+--[[
+LuCI - Network model
+
+Copyright 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local type, next, pairs, ipairs, loadfile, table
+       = type, next, pairs, ipairs, loadfile, table
+
+local tonumber, tostring, math = tonumber, tostring, math
+
+local require = require
+
+local bus = require "ubus"
+local nxo = require "nixio"
+local nfs = require "nixio.fs"
+local ipc = require "luci.ip"
+local sys = require "luci.sys"
+local utl = require "luci.util"
+local dsp = require "luci.dispatcher"
+local uci = require "luci.model.uci"
+local lng = require "luci.i18n"
+
+module "luci.model.network"
+
+
+IFACE_PATTERNS_VIRTUAL  = { }
+IFACE_PATTERNS_IGNORE   = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^lo$" }
+IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
+
+
+protocol = utl.class()
+
+local _protocols = { }
+
+local _interfaces, _bridge, _switch, _tunnel
+local _ubus, _ubusnetcache, _ubusdevcache, _ubuswificache
+local _uci_real, _uci_state
+
+function _filter(c, s, o, r)
+       local val = _uci_real:get(c, s, o)
+       if val then
+               local l = { }
+               if type(val) == "string" then
+                       for val in val:gmatch("%S+") do
+                               if val ~= r then
+                                       l[#l+1] = val
+                               end
+                       end
+                       if #l > 0 then
+                               _uci_real:set(c, s, o, table.concat(l, " "))
+                       else
+                               _uci_real:delete(c, s, o)
+                       end
+               elseif type(val) == "table" then
+                       for _, val in ipairs(val) do
+                               if val ~= r then
+                                       l[#l+1] = val
+                               end
+                       end
+                       if #l > 0 then
+                               _uci_real:set(c, s, o, l)
+                       else
+                               _uci_real:delete(c, s, o)
+                       end
+               end
+       end
+end
+
+function _append(c, s, o, a)
+       local val = _uci_real:get(c, s, o) or ""
+       if type(val) == "string" then
+               local l = { }
+               for val in val:gmatch("%S+") do
+                       if val ~= a then
+                               l[#l+1] = val
+                       end
+               end
+               l[#l+1] = a
+               _uci_real:set(c, s, o, table.concat(l, " "))
+       elseif type(val) == "table" then
+               local l = { }
+               for _, val in ipairs(val) do
+                       if val ~= a then
+                               l[#l+1] = val
+                       end
+               end
+               l[#l+1] = a
+               _uci_real:set(c, s, o, l)
+       end
+end
+
+function _stror(s1, s2)
+       if not s1 or #s1 == 0 then
+               return s2 and #s2 > 0 and s2
+       else
+               return s1
+       end
+end
+
+function _get(c, s, o)
+       return _uci_real:get(c, s, o)
+end
+
+function _set(c, s, o, v)
+       if v ~= nil then
+               if type(v) == "boolean" then v = v and "1" or "0" end
+               return _uci_real:set(c, s, o, v)
+       else
+               return _uci_real:delete(c, s, o)
+       end
+end
+
+function _wifi_iface(x)
+       local _, p
+       for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
+               if x:match(p) then
+                       return true
+               end
+       end
+       return false
+end
+
+function _wifi_state(key, val, field)
+       if not next(_ubuswificache) then
+               _ubuswificache = _ubus:call("network.wireless", "status", {}) or {}
+       end
+
+       local radio, radiostate
+       for radio, radiostate in pairs(_ubuswificache) do
+               local ifc, ifcstate
+               for ifc, ifcstate in pairs(radiostate.interfaces) do
+                       if ifcstate[key] == val then
+                               return ifcstate[field]
+                       end
+               end
+       end
+end
+
+function _wifi_lookup(ifn)
+       -- got a radio#.network# pseudo iface, locate the corresponding section
+       local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
+       if radio and ifnidx then
+               local sid = nil
+               local num = 0
+
+               ifnidx = tonumber(ifnidx)
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               if s.device == radio then
+                                       num = num + 1
+                                       if num == ifnidx then
+                                               sid = s['.name']
+                                               return false
+                                       end
+                               end
+                       end)
+
+               return sid
+
+       -- looks like wifi, try to locate the section via state vars
+       elseif _wifi_iface(ifn) then
+               local sid = _wifi_state("ifname", ifn, "section")
+               if not sid then
+                       _uci_state:foreach("wireless", "wifi-iface",
+                               function(s)
+                                       if s.ifname == ifn then
+                                               sid = s['.name']
+                                               return false
+                                       end
+                               end)
+               end
+
+               return sid
+       end
+end
+
+function _iface_virtual(x)
+       local _, p
+       for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
+               if x:match(p) then
+                       return true
+               end
+       end
+       return false
+end
+
+function _iface_ignore(x)
+       local _, p
+       for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
+               if x:match(p) then
+                       return true
+               end
+       end
+       return _iface_virtual(x)
+end
+
+
+function init(cursor)
+       _uci_real  = cursor or _uci_real or uci.cursor()
+       _uci_state = _uci_real:substate()
+
+       _interfaces = { }
+       _bridge     = { }
+       _switch     = { }
+       _tunnel     = { }
+
+       _ubus          = bus.connect()
+       _ubusnetcache  = { }
+       _ubusdevcache  = { }
+       _ubuswificache = { }
+
+       -- read interface information
+       local n, i
+       for n, i in ipairs(nxo.getifaddrs()) do
+               local name = i.name:match("[^:]+")
+               local prnt = name:match("^([^%.]+)%.")
+
+               if _iface_virtual(name) then
+                       _tunnel[name] = true
+               end
+
+               if _tunnel[name] or not _iface_ignore(name) then
+                       _interfaces[name] = _interfaces[name] or {
+                               idx      = i.ifindex or n,
+                               name     = name,
+                               rawname  = i.name,
+                               flags    = { },
+                               ipaddrs  = { },
+                               ip6addrs = { }
+                       }
+
+                       if prnt then
+                               _switch[name] = true
+                               _switch[prnt] = true
+                       end
+
+                       if i.family == "packet" then
+                               _interfaces[name].flags   = i.flags
+                               _interfaces[name].stats   = i.data
+                               _interfaces[name].macaddr = i.addr
+                       elseif i.family == "inet" then
+                               _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
+                       elseif i.family == "inet6" then
+                               _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
+                       end
+               end
+       end
+
+       -- read bridge informaton
+       local b, l
+       for l in utl.execi("brctl show") do
+               if not l:match("STP") then
+                       local r = utl.split(l, "%s+", nil, true)
+                       if #r == 4 then
+                               b = {
+                                       name    = r[1],
+                                       id      = r[2],
+                                       stp     = r[3] == "yes",
+                                       ifnames = { _interfaces[r[4]] }
+                               }
+                               if b.ifnames[1] then
+                                       b.ifnames[1].bridge = b
+                               end
+                               _bridge[r[1]] = b
+                       elseif b then
+                               b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
+                               b.ifnames[#b.ifnames].bridge = b
+                       end
+               end
+       end
+
+       return _M
+end
+
+function save(self, ...)
+       _uci_real:save(...)
+       _uci_real:load(...)
+end
+
+function commit(self, ...)
+       _uci_real:commit(...)
+       _uci_real:load(...)
+end
+
+function ifnameof(self, x)
+       if utl.instanceof(x, interface) then
+               return x:name()
+       elseif utl.instanceof(x, protocol) then
+               return x:ifname()
+       elseif type(x) == "string" then
+               return x:match("^[^:]+")
+       end
+end
+
+function get_protocol(self, protoname, netname)
+       local v = _protocols[protoname]
+       if v then
+               return v(netname or "__dummy__")
+       end
+end
+
+function get_protocols(self)
+       local p = { }
+       local _, v
+       for _, v in ipairs(_protocols) do
+               p[#p+1] = v("__dummy__")
+       end
+       return p
+end
+
+function register_protocol(self, protoname)
+       local proto = utl.class(protocol)
+
+       function proto.__init__(self, name)
+               self.sid = name
+       end
+
+       function proto.proto(self)
+               return protoname
+       end
+
+       _protocols[#_protocols+1] = proto
+       _protocols[protoname]     = proto
+
+       return proto
+end
+
+function register_pattern_virtual(self, pat)
+       IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
+end
+
+
+function has_ipv6(self)
+       return nfs.access("/proc/net/ipv6_route")
+end
+
+function add_network(self, n, options)
+       local oldnet = self:get_network(n)
+       if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
+               if _uci_real:section("network", "interface", n, options) then
+                       return network(n)
+               end
+       elseif oldnet and oldnet:is_empty() then
+               if options then
+                       local k, v
+                       for k, v in pairs(options) do
+                               oldnet:set(k, v)
+                       end
+               end
+               return oldnet
+       end
+end
+
+function get_network(self, n)
+       if n and _uci_real:get("network", n) == "interface" then
+               return network(n)
+       end
+end
+
+function get_networks(self)
+       local nets = { }
+       local nls = { }
+
+       _uci_real:foreach("network", "interface",
+               function(s)
+                       nls[s['.name']] = network(s['.name'])
+               end)
+
+       local n
+       for n in utl.kspairs(nls) do
+               nets[#nets+1] = nls[n]
+       end
+
+       return nets
+end
+
+function del_network(self, n)
+       local r = _uci_real:delete("network", n)
+       if r then
+               _uci_real:delete_all("network", "alias",
+                       function(s) return (s.interface == n) end)
+
+               _uci_real:delete_all("network", "route",
+                       function(s) return (s.interface == n) end)
+
+               _uci_real:delete_all("network", "route6",
+                       function(s) return (s.interface == n) end)
+
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               local net
+                               local rest = { }
+                               for net in utl.imatch(s.network) do
+                                       if net ~= n then
+                                               rest[#rest+1] = net
+                                       end
+                               end
+                               if #rest > 0 then
+                                       _uci_real:set("wireless", s['.name'], "network",
+                                                     table.concat(rest, " "))
+                               else
+                                       _uci_real:delete("wireless", s['.name'], "network")
+                               end
+                       end)
+       end
+       return r
+end
+
+function rename_network(self, old, new)
+       local r
+       if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
+               r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
+
+               if r then
+                       _uci_real:foreach("network", "alias",
+                               function(s)
+                                       if s.interface == old then
+                                               _uci_real:set("network", s['.name'], "interface", new)
+                                       end
+                               end)
+
+                       _uci_real:foreach("network", "route",
+                               function(s)
+                                       if s.interface == old then
+                                               _uci_real:set("network", s['.name'], "interface", new)
+                                       end
+                               end)
+
+                       _uci_real:foreach("network", "route6",
+                               function(s)
+                                       if s.interface == old then
+                                               _uci_real:set("network", s['.name'], "interface", new)
+                                       end
+                               end)
+
+                       _uci_real:foreach("wireless", "wifi-iface",
+                               function(s)
+                                       local net
+                                       local list = { }
+                                       for net in utl.imatch(s.network) do
+                                               if net == old then
+                                                       list[#list+1] = new
+                                               else
+                                                       list[#list+1] = net
+                                               end
+                                       end
+                                       if #list > 0 then
+                                               _uci_real:set("wireless", s['.name'], "network",
+                                                             table.concat(list, " "))
+                                       end
+                               end)
+
+                       _uci_real:delete("network", old)
+               end
+       end
+       return r or false
+end
+
+function get_interface(self, i)
+       if _interfaces[i] or _wifi_iface(i) then
+               return interface(i)
+       else
+               local ifc
+               local num = { }
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               if s.device then
+                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
+                                       if s['.name'] == i then
+                                               ifc = interface(
+                                                       "%s.network%d" %{s.device, num[s.device] })
+                                               return false
+                                       end
+                               end
+                       end)
+               return ifc
+       end
+end
+
+function get_interfaces(self)
+       local iface
+       local ifaces = { }
+       local seen = { }
+       local nfs = { }
+       local baseof = { }
+
+       -- find normal interfaces
+       _uci_real:foreach("network", "interface",
+               function(s)
+                       for iface in utl.imatch(s.ifname) do
+                               if not _iface_ignore(iface) and not _wifi_iface(iface) then
+                                       seen[iface] = true
+                                       nfs[iface] = interface(iface)
+                               end
+                       end
+               end)
+
+       for iface in utl.kspairs(_interfaces) do
+               if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
+                       nfs[iface] = interface(iface)
+               end
+       end
+
+       -- find vlan interfaces
+       _uci_real:foreach("network", "switch_vlan",
+               function(s)
+                       if not s.device then
+                               return
+                       end
+
+                       local base = baseof[s.device]
+                       if not base then
+                               if not s.device:match("^eth%d") then
+                                       local l
+                                       for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
+                                               if not base then
+                                                       base = l:match("^%w+: (%w+)")
+                                               end
+                                       end
+                                       if not base or not base:match("^eth%d") then
+                                               base = "eth0"
+                                       end
+                               else
+                                       base = s.device
+                               end
+                               baseof[s.device] = base
+                       end
+
+                       local vid = tonumber(s.vid or s.vlan)
+                       if vid ~= nil and vid >= 0 and vid <= 4095 then
+                               local iface = "%s.%d" %{ base, vid }
+                               if not seen[iface] then
+                                       seen[iface] = true
+                                       nfs[iface] = interface(iface)
+                               end
+                       end
+               end)
+
+       for iface in utl.kspairs(nfs) do
+               ifaces[#ifaces+1] = nfs[iface]
+       end
+
+       -- find wifi interfaces
+       local num = { }
+       local wfs = { }
+       _uci_real:foreach("wireless", "wifi-iface",
+               function(s)
+                       if s.device then
+                               num[s.device] = num[s.device] and num[s.device] + 1 or 1
+                               local i = "%s.network%d" %{ s.device, num[s.device] }
+                               wfs[i] = interface(i)
+                       end
+               end)
+
+       for iface in utl.kspairs(wfs) do
+               ifaces[#ifaces+1] = wfs[iface]
+       end
+
+       return ifaces
+end
+
+function ignore_interface(self, x)
+       return _iface_ignore(x)
+end
+
+function get_wifidev(self, dev)
+       if _uci_real:get("wireless", dev) == "wifi-device" then
+               return wifidev(dev)
+       end
+end
+
+function get_wifidevs(self)
+       local devs = { }
+       local wfd  = { }
+
+       _uci_real:foreach("wireless", "wifi-device",
+               function(s) wfd[#wfd+1] = s['.name'] end)
+
+       local dev
+       for _, dev in utl.vspairs(wfd) do
+               devs[#devs+1] = wifidev(dev)
+       end
+
+       return devs
+end
+
+function get_wifinet(self, net)
+       local wnet = _wifi_lookup(net)
+       if wnet then
+               return wifinet(wnet)
+       end
+end
+
+function add_wifinet(self, net, options)
+       if type(options) == "table" and options.device and
+               _uci_real:get("wireless", options.device) == "wifi-device"
+       then
+               local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
+               return wifinet(wnet)
+       end
+end
+
+function del_wifinet(self, net)
+       local wnet = _wifi_lookup(net)
+       if wnet then
+               _uci_real:delete("wireless", wnet)
+               return true
+       end
+       return false
+end
+
+function get_status_by_route(self, addr, mask)
+       local _, object
+       for _, object in ipairs(_ubus:objects()) do
+               local net = object:match("^network%.interface%.(.+)")
+               if net then
+                       local s = _ubus:call(object, "status", {})
+                       if s and s.route then
+                               local rt
+                               for _, rt in ipairs(s.route) do
+                                       if not rt.table and rt.target == addr and rt.mask == mask then
+                                               return net, s
+                                       end
+                               end
+                       end
+               end
+       end
+end
+
+function get_status_by_address(self, addr)
+       local _, object
+       for _, object in ipairs(_ubus:objects()) do
+               local net = object:match("^network%.interface%.(.+)")
+               if net then
+                       local s = _ubus:call(object, "status", {})
+                       if s and s['ipv4-address'] then
+                               local a
+                               for _, a in ipairs(s['ipv4-address']) do
+                                       if a.address == addr then
+                                               return net, s
+                                       end
+                               end
+                       end
+                       if s and s['ipv6-address'] then
+                               local a
+                               for _, a in ipairs(s['ipv6-address']) do
+                                       if a.address == addr then
+                                               return net, s
+                                       end
+                               end
+                       end
+               end
+       end
+end
+
+function get_wannet(self)
+       local net = self:get_status_by_route("0.0.0.0", 0)
+       return net and network(net)
+end
+
+function get_wandev(self)
+       local _, stat = self:get_status_by_route("0.0.0.0", 0)
+       return stat and interface(stat.l3_device or stat.device)
+end
+
+function get_wan6net(self)
+       local net = self:get_status_by_route("::", 0)
+       return net and network(net)
+end
+
+function get_wan6dev(self)
+       local _, stat = self:get_status_by_route("::", 0)
+       return stat and interface(stat.l3_device or stat.device)
+end
+
+
+function network(name, proto)
+       if name then
+               local p = proto or _uci_real:get("network", name, "proto")
+               local c = p and _protocols[p] or protocol
+               return c(name)
+       end
+end
+
+function protocol.__init__(self, name)
+       self.sid = name
+end
+
+function protocol._get(self, opt)
+       local v = _uci_real:get("network", self.sid, opt)
+       if type(v) == "table" then
+               return table.concat(v, " ")
+       end
+       return v or ""
+end
+
+function protocol._ubus(self, field)
+       if not _ubusnetcache[self.sid] then
+               _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
+                                                    "status", { })
+       end
+       if _ubusnetcache[self.sid] and field then
+               return _ubusnetcache[self.sid][field]
+       end
+       return _ubusnetcache[self.sid]
+end
+
+function protocol.get(self, opt)
+       return _get("network", self.sid, opt)
+end
+
+function protocol.set(self, opt, val)
+       return _set("network", self.sid, opt, val)
+end
+
+function protocol.ifname(self)
+       local ifname
+       if self:is_floating() then
+               ifname = self:_ubus("l3_device")
+       else
+               ifname = self:_ubus("device")
+       end
+       if not ifname then
+               local num = { }
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               if s.device then
+                                       num[s.device] = num[s.device]
+                                               and num[s.device] + 1 or 1
+
+                                       local net
+                                       for net in utl.imatch(s.network) do
+                                               if net == self.sid then
+                                                       ifname = "%s.network%d" %{ s.device, num[s.device] }
+                                                       return false
+                                               end
+                                       end
+                               end
+                       end)
+       end
+       return ifname
+end
+
+function protocol.proto(self)
+       return "none"
+end
+
+function protocol.get_i18n(self)
+       local p = self:proto()
+       if p == "none" then
+               return lng.translate("Unmanaged")
+       elseif p == "static" then
+               return lng.translate("Static address")
+       elseif p == "dhcp" then
+               return lng.translate("DHCP client")
+       else
+               return lng.translate("Unknown")
+       end
+end
+
+function protocol.type(self)
+       return self:_get("type")
+end
+
+function protocol.name(self)
+       return self.sid
+end
+
+function protocol.uptime(self)
+       return self:_ubus("uptime") or 0
+end
+
+function protocol.expires(self)
+       local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
+       local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
+       if a and l then
+               l = l - (nxo.sysinfo().uptime - a)
+               return l > 0 and l or 0
+       end
+       return -1
+end
+
+function protocol.metric(self)
+       return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
+end
+
+function protocol.ipaddr(self)
+       local addrs = self:_ubus("ipv4-address")
+       return addrs and #addrs > 0 and addrs[1].address
+end
+
+function protocol.netmask(self)
+       local addrs = self:_ubus("ipv4-address")
+       return addrs and #addrs > 0 and
+               ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
+end
+
+function protocol.gwaddr(self)
+       local _, route
+       for _, route in ipairs(self:_ubus("route") or { }) do
+               if route.target == "0.0.0.0" and route.mask == 0 then
+                       return route.nexthop
+               end
+       end
+end
+
+function protocol.dnsaddrs(self)
+       local dns = { }
+       local _, addr
+       for _, addr in ipairs(self:_ubus("dns-server") or { }) do
+               if not addr:match(":") then
+                       dns[#dns+1] = addr
+               end
+       end
+       return dns
+end
+
+function protocol.ip6addr(self)
+       local addrs = self:_ubus("ipv6-address")
+       if addrs and #addrs > 0 then
+               return "%s/%d" %{ addrs[1].address, addrs[1].mask }
+       else
+               addrs = self:_ubus("ipv6-prefix-assignment")
+               if addrs and #addrs > 0 then
+                       return "%s/%d" %{ addrs[1].address, addrs[1].mask }
+               end
+       end
+end
+
+function protocol.gw6addr(self)
+       local _, route
+       for _, route in ipairs(self:_ubus("route") or { }) do
+               if route.target == "::" and route.mask == 0 then
+                       return ipc.IPv6(route.nexthop):string()
+               end
+       end
+end
+
+function protocol.dns6addrs(self)
+       local dns = { }
+       local _, addr
+       for _, addr in ipairs(self:_ubus("dns-server") or { }) do
+               if addr:match(":") then
+                       dns[#dns+1] = addr
+               end
+       end
+       return dns
+end
+
+function protocol.is_bridge(self)
+       return (not self:is_virtual() and self:type() == "bridge")
+end
+
+function protocol.opkg_package(self)
+       return nil
+end
+
+function protocol.is_installed(self)
+       return true
+end
+
+function protocol.is_virtual(self)
+       return false
+end
+
+function protocol.is_floating(self)
+       return false
+end
+
+function protocol.is_empty(self)
+       if self:is_floating() then
+               return false
+       else
+               local rv = true
+
+               if (self:_get("ifname") or ""):match("%S+") then
+                       rv = false
+               end
+
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               local n
+                               for n in utl.imatch(s.network) do
+                                       if n == self.sid then
+                                               rv = false
+                                               return false
+                                       end
+                               end
+                       end)
+
+               return rv
+       end
+end
+
+function protocol.add_interface(self, ifname)
+       ifname = _M:ifnameof(ifname)
+       if ifname and not self:is_floating() then
+               -- if its a wifi interface, change its network option
+               local wif = _wifi_lookup(ifname)
+               if wif then
+                       _append("wireless", wif, "network", self.sid)
+
+               -- add iface to our iface list
+               else
+                       _append("network", self.sid, "ifname", ifname)
+               end
+       end
+end
+
+function protocol.del_interface(self, ifname)
+       ifname = _M:ifnameof(ifname)
+       if ifname and not self:is_floating() then
+               -- if its a wireless interface, clear its network option
+               local wif = _wifi_lookup(ifname)
+               if wif then _filter("wireless", wif, "network", self.sid) end
+
+               -- remove the interface
+               _filter("network", self.sid, "ifname", ifname)
+       end
+end
+
+function protocol.get_interface(self)
+       if self:is_virtual() then
+               _tunnel[self:proto() .. "-" .. self.sid] = true
+               return interface(self:proto() .. "-" .. self.sid, self)
+       elseif self:is_bridge() then
+               _bridge["br-" .. self.sid] = true
+               return interface("br-" .. self.sid, self)
+       else
+               local ifn = nil
+               local num = { }
+               for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
+                       ifn = ifn:match("^[^:/]+")
+                       return ifn and interface(ifn, self)
+               end
+               ifn = nil
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               if s.device then
+                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
+
+                                       local net
+                                       for net in utl.imatch(s.network) do
+                                               if net == self.sid then
+                                                       ifn = "%s.network%d" %{ s.device, num[s.device] }
+                                                       return false
+                                               end
+                                       end
+                               end
+                       end)
+               return ifn and interface(ifn, self)
+       end
+end
+
+function protocol.get_interfaces(self)
+       if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
+               local ifaces = { }
+
+               local ifn
+               local nfs = { }
+               for ifn in utl.imatch(self:get("ifname")) do
+                       ifn = ifn:match("^[^:/]+")
+                       nfs[ifn] = interface(ifn, self)
+               end
+
+               for ifn in utl.kspairs(nfs) do
+                       ifaces[#ifaces+1] = nfs[ifn]
+               end
+
+               local num = { }
+               local wfs = { }
+               _uci_real:foreach("wireless", "wifi-iface",
+                       function(s)
+                               if s.device then
+                                       num[s.device] = num[s.device] and num[s.device] + 1 or 1
+
+                                       local net
+                                       for net in utl.imatch(s.network) do
+                                               if net == self.sid then
+                                                       ifn = "%s.network%d" %{ s.device, num[s.device] }
+                                                       wfs[ifn] = interface(ifn, self)
+                                               end
+                                       end
+                               end
+                       end)
+
+               for ifn in utl.kspairs(wfs) do
+                       ifaces[#ifaces+1] = wfs[ifn]
+               end
+
+               return ifaces
+       end
+end
+
+function protocol.contains_interface(self, ifname)
+       ifname = _M:ifnameof(ifname)
+       if not ifname then
+               return false
+       elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
+               return true
+       elseif self:is_bridge() and "br-" .. self.sid == ifname then
+               return true
+       else
+               local ifn
+               for ifn in utl.imatch(self:get("ifname")) do
+                       ifn = ifn:match("[^:]+")
+                       if ifn == ifname then
+                               return true
+                       end
+               end
+
+               local wif = _wifi_lookup(ifname)
+               if wif then
+                       local n
+                       for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
+                               if n == self.sid then
+                                       return true
+                               end
+                       end
+               end
+       end
+
+       return false
+end
+
+function protocol.adminlink(self)
+       return dsp.build_url("admin", "network", "network", self.sid)
+end
+
+
+interface = utl.class()
+
+function interface.__init__(self, ifname, network)
+       local wif = _wifi_lookup(ifname)
+       if wif then
+               self.wif    = wifinet(wif)
+               self.ifname = _wifi_state("section", wif, "ifname")
+       end
+
+       self.ifname  = self.ifname or ifname
+       self.dev     = _interfaces[self.ifname]
+       self.network = network
+end
+
+function interface._ubus(self, field)
+       if not _ubusdevcache[self.ifname] then
+               _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
+                                                       { name = self.ifname })
+       end
+       if _ubusdevcache[self.ifname] and field then
+               return _ubusdevcache[self.ifname][field]
+       end
+       return _ubusdevcache[self.ifname]
+end
+
+function interface.name(self)
+       return self.wif and self.wif:ifname() or self.ifname
+end
+
+function interface.mac(self)
+       return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
+end
+
+function interface.ipaddrs(self)
+       return self.dev and self.dev.ipaddrs or { }
+end
+
+function interface.ip6addrs(self)
+       return self.dev and self.dev.ip6addrs or { }
+end
+
+function interface.type(self)
+       if self.wif or _wifi_iface(self.ifname) then
+               return "wifi"
+       elseif _bridge[self.ifname] then
+               return "bridge"
+       elseif _tunnel[self.ifname] then
+               return "tunnel"
+       elseif self.ifname:match("%.") then
+               return "vlan"
+       elseif _switch[self.ifname] then
+               return "switch"
+       else
+               return "ethernet"
+       end
+end
+
+function interface.shortname(self)
+       if self.wif then
+               return "%s %q" %{
+                       self.wif:active_mode(),
+                       self.wif:active_ssid() or self.wif:active_bssid()
+               }
+       else
+               return self.ifname
+       end
+end
+
+function interface.get_i18n(self)
+       if self.wif then
+               return "%s: %s %q" %{
+                       lng.translate("Wireless Network"),
+                       self.wif:active_mode(),
+                       self.wif:active_ssid() or self.wif:active_bssid()
+               }
+       else
+               return "%s: %q" %{ self:get_type_i18n(), self:name() }
+       end
+end
+
+function interface.get_type_i18n(self)
+       local x = self:type()
+       if x == "wifi" then
+               return lng.translate("Wireless Adapter")
+       elseif x == "bridge" then
+               return lng.translate("Bridge")
+       elseif x == "switch" then
+               return lng.translate("Ethernet Switch")
+       elseif x == "vlan" then
+               return lng.translate("VLAN Interface")
+       elseif x == "tunnel" then
+               return lng.translate("Tunnel Interface")
+       else
+               return lng.translate("Ethernet Adapter")
+       end
+end
+
+function interface.adminlink(self)
+       if self.wif then
+               return self.wif:adminlink()
+       end
+end
+
+function interface.ports(self)
+       local members = self:_ubus("bridge-members")
+       if members then
+               local _, iface
+               local ifaces = { }
+               for _, iface in ipairs(members) do
+                       ifaces[#ifaces+1] = interface(iface)
+               end
+       end
+end
+
+function interface.bridge_id(self)
+       if self.br then
+               return self.br.id
+       else
+               return nil
+       end
+end
+
+function interface.bridge_stp(self)
+       if self.br then
+               return self.br.stp
+       else
+               return false
+       end
+end
+
+function interface.is_up(self)
+       return self:_ubus("up") or false
+end
+
+function interface.is_bridge(self)
+       return (self:type() == "bridge")
+end
+
+function interface.is_bridgeport(self)
+       return self.dev and self.dev.bridge and true or false
+end
+
+function interface.tx_bytes(self)
+       local stat = self:_ubus("statistics")
+       return stat and stat.tx_bytes or 0
+end
+
+function interface.rx_bytes(self)
+       local stat = self:_ubus("statistics")
+       return stat and stat.rx_bytes or 0
+end
+
+function interface.tx_packets(self)
+       local stat = self:_ubus("statistics")
+       return stat and stat.tx_packets or 0
+end
+
+function interface.rx_packets(self)
+       local stat = self:_ubus("statistics")
+       return stat and stat.rx_packets or 0
+end
+
+function interface.get_network(self)
+       return self:get_networks()[1]
+end
+
+function interface.get_networks(self)
+       if not self.networks then
+               local nets = { }
+               local _, net
+               for _, net in ipairs(_M:get_networks()) do
+                       if net:contains_interface(self.ifname) or
+                          net:ifname() == self.ifname
+                       then
+                               nets[#nets+1] = net
+                       end
+               end
+               table.sort(nets, function(a, b) return a.sid < b.sid end)
+               self.networks = nets
+               return nets
+       else
+               return self.networks
+       end
+end
+
+function interface.get_wifinet(self)
+       return self.wif
+end
+
+
+wifidev = utl.class()
+
+function wifidev.__init__(self, dev)
+       self.sid    = dev
+       self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
+end
+
+function wifidev.get(self, opt)
+       return _get("wireless", self.sid, opt)
+end
+
+function wifidev.set(self, opt, val)
+       return _set("wireless", self.sid, opt, val)
+end
+
+function wifidev.name(self)
+       return self.sid
+end
+
+function wifidev.hwmodes(self)
+       local l = self.iwinfo.hwmodelist
+       if l and next(l) then
+               return l
+       else
+               return { b = true, g = true }
+       end
+end
+
+function wifidev.get_i18n(self)
+       local t = "Generic"
+       if self.iwinfo.type == "wl" then
+               t = "Broadcom"
+       elseif self.iwinfo.type == "madwifi" then
+               t = "Atheros"
+       end
+
+       local m = ""
+       local l = self:hwmodes()
+       if l.a then m = m .. "a" end
+       if l.b then m = m .. "b" end
+       if l.g then m = m .. "g" end
+       if l.n then m = m .. "n" end
+
+       return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
+end
+
+function wifidev.is_up(self)
+       if _ubuswificache[self.sid] then
+               return (_ubuswificache[self.sid].up == true)
+       end
+
+       local up = false
+       _uci_state:foreach("wireless", "wifi-iface",
+               function(s)
+                       if s.device == self.sid then
+                               if s.up == "1" then
+                                       up = true
+                                       return false
+                               end
+                       end
+               end)
+
+       return up
+end
+
+function wifidev.get_wifinet(self, net)
+       if _uci_real:get("wireless", net) == "wifi-iface" then
+               return wifinet(net)
+       else
+               local wnet = _wifi_lookup(net)
+               if wnet then
+                       return wifinet(wnet)
+               end
+       end
+end
+
+function wifidev.get_wifinets(self)
+       local nets = { }
+
+       _uci_real:foreach("wireless", "wifi-iface",
+               function(s)
+                       if s.device == self.sid then
+                               nets[#nets+1] = wifinet(s['.name'])
+                       end
+               end)
+
+       return nets
+end
+
+function wifidev.add_wifinet(self, options)
+       options = options or { }
+       options.device = self.sid
+
+       local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
+       if wnet then
+               return wifinet(wnet, options)
+       end
+end
+
+function wifidev.del_wifinet(self, net)
+       if utl.instanceof(net, wifinet) then
+               net = net.sid
+       elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
+               net = _wifi_lookup(net)
+       end
+
+       if net and _uci_real:get("wireless", net, "device") == self.sid then
+               _uci_real:delete("wireless", net)
+               return true
+       end
+
+       return false
+end
+
+
+wifinet = utl.class()
+
+function wifinet.__init__(self, net, data)
+       self.sid = net
+
+       local num = { }
+       local netid
+       _uci_real:foreach("wireless", "wifi-iface",
+               function(s)
+                       if s.device then
+                               num[s.device] = num[s.device] and num[s.device] + 1 or 1
+                               if s['.name'] == self.sid then
+                                       netid = "%s.network%d" %{ s.device, num[s.device] }
+                                       return false
+                               end
+                       end
+               end)
+
+       local dev = _wifi_state("section", self.sid, "ifname") or netid
+
+       self.netid  = netid
+       self.wdev   = dev
+       self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
+       self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
+               _uci_real:get_all("wireless", self.sid) or { }
+end
+
+function wifinet.get(self, opt)
+       return _get("wireless", self.sid, opt)
+end
+
+function wifinet.set(self, opt, val)
+       return _set("wireless", self.sid, opt, val)
+end
+
+function wifinet.mode(self)
+       return _uci_state:get("wireless", self.sid, "mode") or "ap"
+end
+
+function wifinet.ssid(self)
+       return _uci_state:get("wireless", self.sid, "ssid")
+end
+
+function wifinet.bssid(self)
+       return _uci_state:get("wireless", self.sid, "bssid")
+end
+
+function wifinet.network(self)
+       return _uci_state:get("wifinet", self.sid, "network")
+end
+
+function wifinet.id(self)
+       return self.netid
+end
+
+function wifinet.name(self)
+       return self.sid
+end
+
+function wifinet.ifname(self)
+       local ifname = self.iwinfo.ifname
+       if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
+               ifname = self.wdev
+       end
+       return ifname
+end
+
+function wifinet.get_device(self)
+       if self.iwdata.device then
+               return wifidev(self.iwdata.device)
+       end
+end
+
+function wifinet.is_up(self)
+       local ifc = self:get_interface()
+       return (ifc and ifc:is_up() or false)
+end
+
+function wifinet.active_mode(self)
+       local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
+
+       if     m == "ap"      then m = "Master"
+       elseif m == "sta"     then m = "Client"
+       elseif m == "adhoc"   then m = "Ad-Hoc"
+       elseif m == "mesh"    then m = "Mesh"
+       elseif m == "monitor" then m = "Monitor"
+       end
+
+       return m
+end
+
+function wifinet.active_mode_i18n(self)
+       return lng.translate(self:active_mode())
+end
+
+function wifinet.active_ssid(self)
+       return _stror(self.iwinfo.ssid, self.iwdata.ssid)
+end
+
+function wifinet.active_bssid(self)
+       return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
+end
+
+function wifinet.active_encryption(self)
+       local enc = self.iwinfo and self.iwinfo.encryption
+       return enc and enc.description or "-"
+end
+
+function wifinet.assoclist(self)
+       return self.iwinfo.assoclist or { }
+end
+
+function wifinet.frequency(self)
+       local freq = self.iwinfo.frequency
+       if freq and freq > 0 then
+               return "%.03f" % (freq / 1000)
+       end
+end
+
+function wifinet.bitrate(self)
+       local rate = self.iwinfo.bitrate
+       if rate and rate > 0 then
+               return (rate / 1000)
+       end
+end
+
+function wifinet.channel(self)
+       return self.iwinfo.channel or
+               tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
+end
+
+function wifinet.signal(self)
+       return self.iwinfo.signal or 0
+end
+
+function wifinet.noise(self)
+       return self.iwinfo.noise or 0
+end
+
+function wifinet.country(self)
+       return self.iwinfo.country or "00"
+end
+
+function wifinet.txpower(self)
+       local pwr = (self.iwinfo.txpower or 0)
+       return pwr + self:txpower_offset()
+end
+
+function wifinet.txpower_offset(self)
+       return self.iwinfo.txpower_offset or 0
+end
+
+function wifinet.signal_level(self, s, n)
+       if self:active_bssid() ~= "00:00:00:00:00:00" then
+               local signal = s or self:signal()
+               local noise  = n or self:noise()
+
+               if signal < 0 and noise < 0 then
+                       local snr = -1 * (noise - signal)
+                       return math.floor(snr / 5)
+               else
+                       return 0
+               end
+       else
+               return -1
+       end
+end
+
+function wifinet.signal_percent(self)
+       local qc = self.iwinfo.quality or 0
+       local qm = self.iwinfo.quality_max or 0
+
+       if qc > 0 and qm > 0 then
+               return math.floor((100 / qm) * qc)
+       else
+               return 0
+       end
+end
+
+function wifinet.shortname(self)
+       return "%s %q" %{
+               lng.translate(self:active_mode()),
+               self:active_ssid() or self:active_bssid()
+       }
+end
+
+function wifinet.get_i18n(self)
+       return "%s: %s %q (%s)" %{
+               lng.translate("Wireless Network"),
+               lng.translate(self:active_mode()),
+               self:active_ssid() or self:active_bssid(),
+               self:ifname()
+       }
+end
+
+function wifinet.adminlink(self)
+       return dsp.build_url("admin", "network", "wireless", self.netid)
+end
+
+function wifinet.get_network(self)
+       return self:get_networks()[1]
+end
+
+function wifinet.get_networks(self)
+       local nets = { }
+       local net
+       for net in utl.imatch(tostring(self.iwdata.network)) do
+               if _uci_real:get("network", net) == "interface" then
+                       nets[#nets+1] = network(net)
+               end
+       end
+       table.sort(nets, function(a, b) return a.sid < b.sid end)
+       return nets
+end
+
+function wifinet.get_interface(self)
+       return interface(self:ifname())
+end
+
+
+-- setup base protocols
+_M:register_protocol("static")
+_M:register_protocol("dhcp")
+_M:register_protocol("none")
+
+-- load protocol extensions
+local exts = nfs.dir(utl.libpath() .. "/model/network")
+if exts then
+       local ext
+       for ext in exts do
+               if ext:match("%.lua$") then
+                       require("luci.model.network." .. ext:gsub("%.lua$", ""))
+               end
+       end
+end
diff --git a/modules/base/luasrc/model/uci.lua b/modules/base/luasrc/model/uci.lua
new file mode 100644 (file)
index 0000000..a394563
--- /dev/null
@@ -0,0 +1,404 @@
+--[[
+LuCI - UCI model
+
+Description:
+Generalized UCI model
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+local os    = require "os"
+local uci   = require "uci"
+local util  = require "luci.util"
+local table = require "table"
+
+
+local setmetatable, rawget, rawset = setmetatable, rawget, rawset
+local require, getmetatable = require, getmetatable
+local error, pairs, ipairs = error, pairs, ipairs
+local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
+
+--- LuCI UCI model library.
+-- The typical workflow for UCI is:  Get a cursor instance from the
+-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
+-- save the changes to the staging area via Cursor.save and finally
+-- Cursor.commit the data to the actual config files.
+-- LuCI then needs to Cursor.apply the changes so deamons etc. are
+-- reloaded.
+-- @cstyle     instance
+module "luci.model.uci"
+
+--- Create a new UCI-Cursor.
+-- @class function
+-- @name cursor
+-- @return     UCI-Cursor
+cursor = uci.cursor
+
+APIVERSION = uci.APIVERSION
+
+--- Create a new Cursor initialized to the state directory.
+-- @return UCI cursor
+function cursor_state()
+       return cursor(nil, "/var/state")
+end
+
+
+inst = cursor()
+inst_state = cursor_state()
+
+local Cursor = getmetatable(inst)
+
+--- Applies UCI configuration changes
+-- @param configlist           List of UCI configurations
+-- @param command                      Don't apply only return the command
+function Cursor.apply(self, configlist, command)
+       configlist = self:_affected(configlist)
+       if command then
+               return { "/sbin/luci-reload", unpack(configlist) }
+       else
+               return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
+                       % table.concat(configlist, " "))
+       end
+end
+
+
+--- Delete all sections of a given type that match certain criteria.
+-- @param config               UCI config
+-- @param type                 UCI section type
+-- @param comparator   Function that will be called for each section and
+-- returns a boolean whether to delete the current section (optional)
+function Cursor.delete_all(self, config, stype, comparator)
+       local del = {}
+
+       if type(comparator) == "table" then
+               local tbl = comparator
+               comparator = function(section)
+                       for k, v in pairs(tbl) do
+                               if section[k] ~= v then
+                                       return false
+                               end
+                       end
+                       return true
+               end
+       end
+
+       local function helper (section)
+
+               if not comparator or comparator(section) then
+                       del[#del+1] = section[".name"]
+               end
+       end
+
+       self:foreach(config, stype, helper)
+
+       for i, j in ipairs(del) do
+               self:delete(config, j)
+       end
+end
+
+--- Create a new section and initialize it with data.
+-- @param config       UCI config
+-- @param type         UCI section type
+-- @param name         UCI section name (optional)
+-- @param values       Table of key - value pairs to initialize the section with
+-- @return                     Name of created section
+function Cursor.section(self, config, type, name, values)
+       local stat = true
+       if name then
+               stat = self:set(config, name, type)
+       else
+               name = self:add(config, type)
+               stat = name and true
+       end
+
+       if stat and values then
+               stat = self:tset(config, name, values)
+       end
+
+       return stat and name
+end
+
+--- Updated the data of a section using data from a table.
+-- @param config       UCI config
+-- @param section      UCI section name (optional)
+-- @param values       Table of key - value pairs to update the section with
+function Cursor.tset(self, config, section, values)
+       local stat = true
+       for k, v in pairs(values) do
+               if k:sub(1, 1) ~= "." then
+                       stat = stat and self:set(config, section, k, v)
+               end
+       end
+       return stat
+end
+
+--- Get a boolean option and return it's value as true or false.
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option
+-- @return                     Boolean
+function Cursor.get_bool(self, ...)
+       local val = self:get(...)
+       return ( val == "1" or val == "true" or val == "yes" or val == "on" )
+end
+
+--- Get an option or list and return values as table.
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option
+-- @return                     UCI value
+function Cursor.get_list(self, config, section, option)
+       if config and section and option then
+               local val = self:get(config, section, option)
+               return ( type(val) == "table" and val or { val } )
+       end
+       return nil
+end
+
+--- Get the given option from the first section with the given type.
+-- @param config       UCI config
+-- @param type         UCI section type
+-- @param option       UCI option (optional)
+-- @param default      Default value (optional)
+-- @return                     UCI value
+function Cursor.get_first(self, conf, stype, opt, def)
+       local rv = def
+
+       self:foreach(conf, stype,
+               function(s)
+                       local val = not opt and s['.name'] or s[opt]
+
+                       if type(def) == "number" then
+                               val = tonumber(val)
+                       elseif type(def) == "boolean" then
+                               val = (val == "1" or val == "true" or
+                                      val == "yes" or val == "on")
+                       end
+
+                       if val ~= nil then
+                               rv = val
+                               return false
+                       end
+               end)
+
+       return rv
+end
+
+--- Set given values as list.
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option
+-- @param value                UCI value
+-- @return                     Boolean whether operation succeeded
+function Cursor.set_list(self, config, section, option, value)
+       if config and section and option then
+               return self:set(
+                       config, section, option,
+                       ( type(value) == "table" and value or { value } )
+               )
+       end
+       return false
+end
+
+-- Return a list of initscripts affected by configuration changes.
+function Cursor._affected(self, configlist)
+       configlist = type(configlist) == "table" and configlist or {configlist}
+
+       local c = cursor()
+       c:load("ucitrack")
+
+       -- Resolve dependencies
+       local reloadlist = {}
+
+       local function _resolve_deps(name)
+               local reload = {name}
+               local deps = {}
+
+               c:foreach("ucitrack", name,
+                       function(section)
+                               if section.affects then
+                                       for i, aff in ipairs(section.affects) do
+                                               deps[#deps+1] = aff
+                                       end
+                               end
+                       end)
+
+               for i, dep in ipairs(deps) do
+                       for j, add in ipairs(_resolve_deps(dep)) do
+                               reload[#reload+1] = add
+                       end
+               end
+
+               return reload
+       end
+
+       -- Collect initscripts
+       for j, config in ipairs(configlist) do
+               for i, e in ipairs(_resolve_deps(config)) do
+                       if not util.contains(reloadlist, e) then
+                               reloadlist[#reloadlist+1] = e
+                       end
+               end
+       end
+
+       return reloadlist
+end
+
+--- Create a sub-state of this cursor. The sub-state is tied to the parent
+-- curser, means it the parent unloads or loads configs, the sub state will
+-- do so as well.
+-- @return                     UCI state cursor tied to the parent cursor
+function Cursor.substate(self)
+       Cursor._substates = Cursor._substates or { }
+       Cursor._substates[self] = Cursor._substates[self] or cursor_state()
+       return Cursor._substates[self]
+end
+
+local _load = Cursor.load
+function Cursor.load(self, ...)
+       if Cursor._substates and Cursor._substates[self] then
+               _load(Cursor._substates[self], ...)
+       end
+       return _load(self, ...)
+end
+
+local _unload = Cursor.unload
+function Cursor.unload(self, ...)
+       if Cursor._substates and Cursor._substates[self] then
+               _unload(Cursor._substates[self], ...)
+       end
+       return _unload(self, ...)
+end
+
+
+--- Add an anonymous section.
+-- @class function
+-- @name Cursor.add
+-- @param config       UCI config
+-- @param type         UCI section type
+-- @return                     Name of created section
+
+--- Get a table of saved but uncommitted changes.
+-- @class function
+-- @name Cursor.changes
+-- @param config       UCI config
+-- @return                     Table of changes
+-- @see Cursor.save
+
+--- Commit saved changes.
+-- @class function
+-- @name Cursor.commit
+-- @param config       UCI config
+-- @return                     Boolean whether operation succeeded
+-- @see Cursor.revert
+-- @see Cursor.save
+
+--- Deletes a section or an option.
+-- @class function
+-- @name Cursor.delete
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option (optional)
+-- @return                     Boolean whether operation succeeded
+
+--- Call a function for every section of a certain type.
+-- @class function
+-- @name Cursor.foreach
+-- @param config       UCI config
+-- @param type         UCI section type
+-- @param callback     Function to be called
+-- @return                     Boolean whether operation succeeded
+
+--- Get a section type or an option
+-- @class function
+-- @name Cursor.get
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option (optional)
+-- @return                     UCI value
+
+--- Get all sections of a config or all values of a section.
+-- @class function
+-- @name Cursor.get_all
+-- @param config       UCI config
+-- @param section      UCI section name (optional)
+-- @return                     Table of UCI sections or table of UCI values
+
+--- Manually load a config.
+-- @class function
+-- @name Cursor.load
+-- @param config       UCI config
+-- @return                     Boolean whether operation succeeded
+-- @see Cursor.save
+-- @see Cursor.unload
+
+--- Revert saved but uncommitted changes.
+-- @class function
+-- @name Cursor.revert
+-- @param config       UCI config
+-- @return                     Boolean whether operation succeeded
+-- @see Cursor.commit
+-- @see Cursor.save
+
+--- Saves changes made to a config to make them committable.
+-- @class function
+-- @name Cursor.save
+-- @param config       UCI config
+-- @return                     Boolean whether operation succeeded
+-- @see Cursor.load
+-- @see Cursor.unload
+
+--- Set a value or create a named section.
+-- @class function
+-- @name Cursor.set
+-- @param config       UCI config
+-- @param section      UCI section name
+-- @param option       UCI option or UCI section type
+-- @param value                UCI value or nil if you want to create a section
+-- @return                     Boolean whether operation succeeded
+
+--- Get the configuration directory.
+-- @class function
+-- @name Cursor.get_confdir
+-- @return                     Configuration directory
+
+--- Get the directory for uncomitted changes.
+-- @class function
+-- @name Cursor.get_savedir
+-- @return                     Save directory
+
+--- Set the configuration directory.
+-- @class function
+-- @name Cursor.set_confdir
+-- @param directory    UCI configuration directory
+-- @return                     Boolean whether operation succeeded
+
+--- Set the directory for uncommited changes.
+-- @class function
+-- @name Cursor.set_savedir
+-- @param directory    UCI changes directory
+-- @return                     Boolean whether operation succeeded
+
+--- Discard changes made to a config.
+-- @class function
+-- @name Cursor.unload
+-- @param config       UCI config
+-- @return                     Boolean whether operation succeeded
+-- @see Cursor.load
+-- @see Cursor.save
diff --git a/modules/base/luasrc/sgi/cgi.lua b/modules/base/luasrc/sgi/cgi.lua
new file mode 100644 (file)
index 0000000..04ae9aa
--- /dev/null
@@ -0,0 +1,95 @@
+--[[
+LuCI - SGI-Module for CGI
+
+Description:
+Server Gateway Interface for CGI
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at 
+
+       http://www.apache.org/licenses/LICENSE-2.0 
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+exectime = os.clock()
+module("luci.sgi.cgi", package.seeall)
+local ltn12 = require("luci.ltn12")
+require("nixio.util")
+require("luci.http")
+require("luci.sys")
+require("luci.dispatcher")
+
+-- Limited source to avoid endless blocking
+local function limitsource(handle, limit)
+       limit = limit or 0
+       local BLOCKSIZE = ltn12.BLOCKSIZE
+
+       return function()
+               if limit < 1 then
+                       handle:close()
+                       return nil
+               else
+                       local read = (limit > BLOCKSIZE) and BLOCKSIZE or limit
+                       limit = limit - read
+
+                       local chunk = handle:read(read)
+                       if not chunk then handle:close() end
+                       return chunk
+               end
+       end
+end
+
+function run()
+       local r = luci.http.Request(
+               luci.sys.getenv(),
+               limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
+               ltn12.sink.file(io.stderr)
+       )
+       
+       local x = coroutine.create(luci.dispatcher.httpdispatch)
+       local hcache = ""
+       local active = true
+       
+       while coroutine.status(x) ~= "dead" do
+               local res, id, data1, data2 = coroutine.resume(x, r)
+
+               if not res then
+                       print("Status: 500 Internal Server Error")
+                       print("Content-Type: text/plain\n")
+                       print(id)
+                       break;
+               end
+
+               if active then
+                       if id == 1 then
+                               io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
+                       elseif id == 2 then
+                               hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
+                       elseif id == 3 then
+                               io.write(hcache)
+                               io.write("\r\n")
+                       elseif id == 4 then
+                               io.write(tostring(data1 or ""))
+                       elseif id == 5 then
+                               io.flush()
+                               io.close()
+                               active = false
+                       elseif id == 6 then
+                               data1:copyz(nixio.stdout, data2)
+                               data1:close()
+                       end
+               end
+       end
+end
diff --git a/modules/base/luasrc/sgi/uhttpd.lua b/modules/base/luasrc/sgi/uhttpd.lua
new file mode 100644 (file)
index 0000000..db8780f
--- /dev/null
@@ -0,0 +1,105 @@
+--[[
+LuCI - Server Gateway Interface for the uHTTPd server
+
+Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at 
+
+       http://www.apache.org/licenses/LICENSE-2.0 
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+require "nixio.util"
+require "luci.http"
+require "luci.sys"
+require "luci.dispatcher"
+require "luci.ltn12"
+
+function handle_request(env)
+       exectime = os.clock()
+       local renv = {
+               CONTENT_LENGTH  = env.CONTENT_LENGTH,
+               CONTENT_TYPE    = env.CONTENT_TYPE,
+               REQUEST_METHOD  = env.REQUEST_METHOD,
+               REQUEST_URI     = env.REQUEST_URI,
+               PATH_INFO       = env.PATH_INFO,
+               SCRIPT_NAME     = env.SCRIPT_NAME:gsub("/+$", ""),
+               SCRIPT_FILENAME = env.SCRIPT_NAME,
+               SERVER_PROTOCOL = env.SERVER_PROTOCOL,
+               QUERY_STRING    = env.QUERY_STRING
+       }
+
+       local k, v
+       for k, v in pairs(env.headers) do
+               k = k:upper():gsub("%-", "_")
+               renv["HTTP_" .. k] = v
+       end
+
+       local len = tonumber(env.CONTENT_LENGTH) or 0
+       local function recv()
+               if len > 0 then
+                       local rlen, rbuf = uhttpd.recv(4096)
+                       if rlen >= 0 then
+                               len = len - rlen
+                               return rbuf
+                       end
+               end
+               return nil
+       end
+
+       local send = uhttpd.send
+
+       local req = luci.http.Request(
+               renv, recv, luci.ltn12.sink.file(io.stderr)
+       )
+       
+
+       local x = coroutine.create(luci.dispatcher.httpdispatch)
+       local hcache = { }
+       local active = true
+
+       while coroutine.status(x) ~= "dead" do
+               local res, id, data1, data2 = coroutine.resume(x, req)
+
+               if not res then
+                       send("Status: 500 Internal Server Error\r\n")
+                       send("Content-Type: text/plain\r\n\r\n")
+                       send(tostring(id))
+                       break
+               end
+
+               if active then
+                       if id == 1 then
+                               send("Status: ")
+                               send(tostring(data1))
+                               send(" ")
+                               send(tostring(data2))
+                               send("\r\n")
+                       elseif id == 2 then
+                               hcache[data1] = data2
+                       elseif id == 3 then
+                               for k, v in pairs(hcache) do
+                                       send(tostring(k))
+                                       send(": ")
+                                       send(tostring(v))
+                                       send("\r\n")
+                               end
+                               send("\r\n")
+                       elseif id == 4 then
+                               send(tostring(data1 or ""))
+                       elseif id == 5 then
+                               active = false
+                       elseif id == 6 then
+                               data1:copyz(nixio.stdout, data2)
+                       end
+               end
+       end
+end
diff --git a/modules/base/luasrc/store.lua b/modules/base/luasrc/store.lua
new file mode 100644 (file)
index 0000000..c33ef07
--- /dev/null
@@ -0,0 +1,16 @@
+--[[
+
+LuCI - Lua Development Framework
+(c) 2009 Steven Barth <steven@midlink.org>
+(c) 2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+local util = require "luci.util"
+module("luci.store", util.threadlocal)
\ No newline at end of file
diff --git a/modules/base/luasrc/sys.lua b/modules/base/luasrc/sys.lua
new file mode 100644 (file)
index 0000000..df6280d
--- /dev/null
@@ -0,0 +1,961 @@
+--[[
+LuCI - System library
+
+Description:
+Utilities for interaction with the Linux system
+
+FileId:
+$Id$
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+
+local io     = require "io"
+local os     = require "os"
+local table  = require "table"
+local nixio  = require "nixio"
+local fs     = require "nixio.fs"
+local uci    = require "luci.model.uci"
+
+local luci  = {}
+luci.util   = require "luci.util"
+luci.ip     = require "luci.ip"
+
+local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
+       tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
+
+
+--- LuCI Linux and POSIX system utilities.
+module "luci.sys"
+
+--- Execute a given shell command and return the error code
+-- @class              function
+-- @name               call
+-- @param              ...             Command to call
+-- @return             Error code of the command
+function call(...)
+       return os.execute(...) / 256
+end
+
+--- Execute a given shell command and capture its standard output
+-- @class              function
+-- @name               exec
+-- @param command      Command to call
+-- @return                     String containg the return the output of the command
+exec = luci.util.exec
+
+--- Retrieve information about currently mounted file systems.
+-- @return     Table containing mount information
+function mounts()
+       local data = {}
+       local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
+       local ps = luci.util.execi("df")
+
+       if not ps then
+               return
+       else
+               ps()
+       end
+
+       for line in ps do
+               local row = {}
+
+               local j = 1
+               for value in line:gmatch("[^%s]+") do
+                       row[k[j]] = value
+                       j = j + 1
+               end
+
+               if row[k[1]] then
+
+                       -- this is a rather ugly workaround to cope with wrapped lines in
+                       -- the df output:
+                       --
+                       --      /dev/scsi/host0/bus0/target0/lun0/part3
+                       --                   114382024  93566472  15005244  86% /mnt/usb
+                       --
+
+                       if not row[k[2]] then
+                               j = 2
+                               line = ps()
+                               for value in line:gmatch("[^%s]+") do
+                                       row[k[j]] = value
+                                       j = j + 1
+                               end
+                       end
+
+                       table.insert(data, row)
+               end
+       end
+
+       return data
+end
+
+--- Retrieve environment variables. If no variable is given then a table
+-- containing the whole environment is returned otherwise this function returns
+-- the corresponding string value for the given name or nil if no such variable
+-- exists.
+-- @class              function
+-- @name               getenv
+-- @param var  Name of the environment variable to retrieve (optional)
+-- @return             String containg the value of the specified variable
+-- @return             Table containing all variables if no variable name is given
+getenv = nixio.getenv
+
+--- Get or set the current hostname.
+-- @param              String containing a new hostname to set (optional)
+-- @return             String containing the system hostname
+function hostname(newname)
+       if type(newname) == "string" and #newname > 0 then
+               fs.writefile( "/proc/sys/kernel/hostname", newname )
+               return newname
+       else
+               return nixio.uname().nodename
+       end
+end
+
+--- Returns the contents of a documented referred by an URL.
+-- @param url   The URL to retrieve
+-- @param stream Return a stream instead of a buffer
+-- @param target Directly write to target file name
+-- @return             String containing the contents of given the URL
+function httpget(url, stream, target)
+       if not target then
+               local source = stream and io.popen or luci.util.exec
+               return source("wget -qO- '"..url:gsub("'", "").."'")
+       else
+               return os.execute("wget -qO '%s' '%s'" %
+                       {target:gsub("'", ""), url:gsub("'", "")})
+       end
+end
+
+--- Returns the system load average values.
+-- @return     String containing the average load value 1 minute ago
+-- @return     String containing the average load value 5 minutes ago
+-- @return     String containing the average load value 15 minutes ago
+function loadavg()
+       local info = nixio.sysinfo()
+       return info.loads[1], info.loads[2], info.loads[3]
+end
+
+--- Initiate a system reboot.
+-- @return     Return value of os.execute()
+function reboot()
+       return os.execute("reboot >/dev/null 2>&1")
+end
+
+--- Returns the system type, cpu name and installed physical memory.
+-- @return     String containing the system or platform identifier
+-- @return     String containing hardware model information
+-- @return     String containing the total memory amount in kB
+-- @return     String containing the memory used for caching in kB
+-- @return     String containing the memory used for buffering in kB
+-- @return     String containing the free memory amount in kB
+-- @return     String containing the cpu bogomips (number)
+function sysinfo()
+       local cpuinfo = fs.readfile("/proc/cpuinfo")
+       local meminfo = fs.readfile("/proc/meminfo")
+
+       local memtotal = tonumber(meminfo:match("MemTotal:%s*(%d+)"))
+       local memcached = tonumber(meminfo:match("\nCached:%s*(%d+)"))
+       local memfree = tonumber(meminfo:match("MemFree:%s*(%d+)"))
+       local membuffers = tonumber(meminfo:match("Buffers:%s*(%d+)"))
+       local bogomips = tonumber(cpuinfo:match("[Bb]ogo[Mm][Ii][Pp][Ss].-: ([^\n]+)")) or 0
+       local swaptotal = tonumber(meminfo:match("SwapTotal:%s*(%d+)"))
+       local swapcached = tonumber(meminfo:match("SwapCached:%s*(%d+)"))
+       local swapfree = tonumber(meminfo:match("SwapFree:%s*(%d+)"))
+
+       local system =
+               cpuinfo:match("system type\t+: ([^\n]+)") or
+               cpuinfo:match("Processor\t+: ([^\n]+)") or
+               cpuinfo:match("model name\t+: ([^\n]+)")
+
+       local model =
+               luci.util.pcdata(fs.readfile("/tmp/sysinfo/model")) or
+               cpuinfo:match("machine\t+: ([^\n]+)") or
+               cpuinfo:match("Hardware\t+: ([^\n]+)") or
+               luci.util.pcdata(fs.readfile("/proc/diag/model")) or
+               nixio.uname().machine or
+               system
+
+       return system, model, memtotal, memcached, membuffers, memfree, bogomips, swaptotal, swapcached, swapfree
+end
+
+--- Retrieves the output of the "logread" command.
+-- @return     String containing the current log buffer
+function syslog()
+       return luci.util.exec("logread")
+end
+
+--- Retrieves the output of the "dmesg" command.
+-- @return     String containing the current log buffer
+function dmesg()
+       return luci.util.exec("dmesg")
+end
+
+--- Generates a random id with specified length.
+-- @param bytes        Number of bytes for the unique id
+-- @return             String containing hex encoded id
+function uniqueid(bytes)
+       local rand = fs.readfile("/dev/urandom", bytes)
+       return rand and nixio.bin.hexlify(rand)
+end
+
+--- Returns the current system uptime stats.
+-- @return     String containing total uptime in seconds
+function uptime()
+       return nixio.sysinfo().uptime
+end
+
+
+--- LuCI system utilities / network related functions.
+-- @class      module
+-- @name       luci.sys.net
+net = {}
+
+--- Returns the current arp-table entries as two-dimensional table.
+-- @return     Table of table containing the current arp entries.
+--                     The following fields are defined for arp entry objects:
+--                     { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
+function net.arptable(callback)
+       local arp = (not callback) and {} or nil
+       local e, r, v
+       if fs.access("/proc/net/arp") then
+               for e in io.lines("/proc/net/arp") do
+                       local r = { }, v
+                       for v in e:gmatch("%S+") do
+                               r[#r+1] = v
+                       end
+
+                       if r[1] ~= "IP" then
+                               local x = {
+                                       ["IP address"] = r[1],
+                                       ["HW type"]    = r[2],
+                                       ["Flags"]      = r[3],
+                                       ["HW address"] = r[4],
+                                       ["Mask"]       = r[5],
+                                       ["Device"]     = r[6]
+                               }
+
+                               if callback then
+                                       callback(x)
+                               else
+                                       arp = arp or { }
+                                       arp[#arp+1] = x
+                               end
+                       end
+               end
+       end
+       return arp
+end
+
+local function _nethints(what, callback)
+       local _, k, e, mac, ip, name
+       local cur = uci.cursor()
+       local ifn = { }
+       local hosts = { }
+
+       local function _add(i, ...)
+               local k = select(i, ...)
+               if k then
+                       if not hosts[k] then hosts[k] = { } end
+                       hosts[k][1] = select(1, ...) or hosts[k][1]
+                       hosts[k][2] = select(2, ...) or hosts[k][2]
+                       hosts[k][3] = select(3, ...) or hosts[k][3]
+                       hosts[k][4] = select(4, ...) or hosts[k][4]
+               end
+       end
+
+       if fs.access("/proc/net/arp") then
+               for e in io.lines("/proc/net/arp") do
+                       ip, mac = e:match("^([%d%.]+)%s+%S+%s+%S+%s+([a-fA-F0-9:]+)%s+")
+                       if ip and mac then
+                               _add(what, mac:upper(), ip, nil, nil)
+                       end
+               end
+       end
+
+       if fs.access("/etc/ethers") then
+               for e in io.lines("/etc/ethers") do
+                       mac, ip = e:match("^([a-f0-9]%S+) (%S+)")
+                       if mac and ip then
+                               _add(what, mac:upper(), ip, nil, nil)
+                       end
+               end
+       end
+
+       if fs.access("/var/dhcp.leases") then
+               for e in io.lines("/var/dhcp.leases") do
+                       mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
+                       if mac and ip then
+                               _add(what, mac:upper(), ip, nil, name ~= "*" and name)
+                       end
+               end
+       end
+
+       cur:foreach("dhcp", "host",
+               function(s)
+                       for mac in luci.util.imatch(s.mac) do
+                               _add(what, mac:upper(), s.ip, nil, s.name)
+                       end
+               end)
+
+       for _, e in ipairs(nixio.getifaddrs()) do
+               if e.name ~= "lo" then
+                       ifn[e.name] = ifn[e.name] or { }
+                       if e.family == "packet" and e.addr and #e.addr == 17 then
+                               ifn[e.name][1] = e.addr:upper()
+                       elseif e.family == "inet" then
+                               ifn[e.name][2] = e.addr
+                       elseif e.family == "inet6" then
+                               ifn[e.name][3] = e.addr
+                       end
+               end
+       end
+
+       for _, e in pairs(ifn) do
+               if e[what] and (e[2] or e[3]) then
+                       _add(what, e[1], e[2], e[3], e[4])
+               end
+       end
+
+       for _, e in luci.util.kspairs(hosts) do
+               callback(e[1], e[2], e[3], e[4])
+       end
+end
+
+--- Returns a two-dimensional table of mac address hints.
+-- @return  Table of table containing known hosts from various sources.
+--          Each entry contains the values in the following order:
+--          [ "mac", "name" ]
+function net.mac_hints(callback)
+       if callback then
+               _nethints(1, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
+                       if name and name ~= mac then
+                               callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4)
+                       end
+               end)
+       else
+               local rv = { }
+               _nethints(1, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4
+                       if name and name ~= mac then
+                               rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 }
+                       end
+               end)
+               return rv
+       end
+end
+
+--- Returns a two-dimensional table of IPv4 address hints.
+-- @return  Table of table containing known hosts from various sources.
+--          Each entry contains the values in the following order:
+--          [ "ip", "name" ]
+function net.ipv4_hints(callback)
+       if callback then
+               _nethints(2, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
+                       if name and name ~= v4 then
+                               callback(v4, name)
+                       end
+               end)
+       else
+               local rv = { }
+               _nethints(2, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v4, nil, 100) or mac
+                       if name and name ~= v4 then
+                               rv[#rv+1] = { v4, name }
+                       end
+               end)
+               return rv
+       end
+end
+
+--- Returns a two-dimensional table of IPv6 address hints.
+-- @return  Table of table containing known hosts from various sources.
+--          Each entry contains the values in the following order:
+--          [ "ip", "name" ]
+function net.ipv6_hints(callback)
+       if callback then
+               _nethints(3, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
+                       if name and name ~= v6 then
+                               callback(v6, name)
+                       end
+               end)
+       else
+               local rv = { }
+               _nethints(3, function(mac, v4, v6, name)
+                       name = name or nixio.getnameinfo(v6, nil, 100) or mac
+                       if name and name ~= v6 then
+                               rv[#rv+1] = { v6, name }
+                       end
+               end)
+               return rv
+       end
+end
+
+--- Returns conntrack information
+-- @return     Table with the currently tracked IP connections
+function net.conntrack(callback)
+       local connt = {}
+       if fs.access("/proc/net/nf_conntrack", "r") then
+               for line in io.lines("/proc/net/nf_conntrack") do
+                       line = line:match "^(.-( [^ =]+=).-)%2"
+                       local entry, flags = _parse_mixed_record(line, " +")
+                       if flags[6] ~= "TIME_WAIT" then
+                               entry.layer3 = flags[1]
+                               entry.layer4 = flags[3]
+                               for i=1, #entry do
+                                       entry[i] = nil
+                               end
+
+                               if callback then
+                                       callback(entry)
+                               else
+                                       connt[#connt+1] = entry
+                               end
+                       end
+               end
+       elseif fs.access("/proc/net/ip_conntrack", "r") then
+               for line in io.lines("/proc/net/ip_conntrack") do
+                       line = line:match "^(.-( [^ =]+=).-)%2"
+                       local entry, flags = _parse_mixed_record(line, " +")
+                       if flags[4] ~= "TIME_WAIT" then
+                               entry.layer3 = "ipv4"
+                               entry.layer4 = flags[1]
+                               for i=1, #entry do
+                                       entry[i] = nil
+                               end
+
+                               if callback then
+                                       callback(entry)
+                               else
+                                       connt[#connt+1] = entry
+                               end
+                       end
+               end
+       else
+               return nil
+       end
+       return connt
+end
+
+--- Determine the current IPv4 default route. If multiple default routes exist,
+-- return the one with the lowest metric.
+-- @return     Table with the properties of the current default route.
+--                     The following fields are defined:
+--                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
+--                       "flags", "device" }
+function net.defaultroute()
+       local route
+
+       net.routes(function(rt)
+               if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
+                       route = rt
+               end
+       end)
+
+       return route
+end
+
+--- Determine the current IPv6 default route. If multiple default routes exist,
+-- return the one with the lowest metric.
+-- @return     Table with the properties of the current default route.
+--                     The following fields are defined:
+--                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
+--                       "flags", "device" }
+function net.defaultroute6()
+       local route
+
+       net.routes6(function(rt)
+               if rt.dest:prefix() == 0 and rt.device ~= "lo" and
+                  (not route or route.metric > rt.metric)
+               then
+                       route = rt
+               end
+       end)
+
+       if not route then
+               local global_unicast = luci.ip.IPv6("2000::/3")
+               net.routes6(function(rt)
+                       if rt.dest:equal(global_unicast) and
+                          (not route or route.metric > rt.metric)
+                       then
+                               route = rt
+                       end
+               end)
+       end
+
+       return route
+end
+
+--- Determine the names of available network interfaces.
+-- @return     Table containing all current interface names
+function net.devices()
+       local devs = {}
+       for k, v in ipairs(nixio.getifaddrs()) do
+               if v.family == "packet" then
+                       devs[#devs+1] = v.name
+               end
+       end
+       return devs
+end
+
+
+--- Return information about available network interfaces.
+-- @return     Table containing all current interface names and their information
+function net.deviceinfo()
+       local devs = {}
+       for k, v in ipairs(nixio.getifaddrs()) do
+               if v.family == "packet" then
+                       local d = v.data
+                       d[1] = d.rx_bytes
+                       d[2] = d.rx_packets
+                       d[3] = d.rx_errors
+                       d[4] = d.rx_dropped
+                       d[5] = 0
+                       d[6] = 0
+                       d[7] = 0
+                       d[8] = d.multicast
+                       d[9] = d.tx_bytes
+                       d[10] = d.tx_packets
+                       d[11] = d.tx_errors
+                       d[12] = d.tx_dropped
+                       d[13] = 0
+                       d[14] = d.collisions
+                       d[15] = 0
+                       d[16] = 0
+                       devs[v.name] = d
+               end
+       end
+       return devs
+end
+
+
+-- Determine the MAC address belonging to the given IP address.
+-- @param ip   IPv4 address
+-- @return             String containing the MAC address or nil if it cannot be found
+function net.ip4mac(ip)
+       local mac = nil
+       net.arptable(function(e)
+               if e["IP address"] == ip then
+                       mac = e["HW address"]
+               end
+       end)
+       return mac
+end
+
+--- Returns the current kernel routing table entries.
+-- @return     Table of tables with properties of the corresponding routes.
+--                     The following fields are defined for route entry tables:
+--                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
+--                       "flags", "device" }
+function net.routes(callback)
+       local routes = { }
+
+       for line in io.lines("/proc/net/route") do
+
+               local dev, dst_ip, gateway, flags, refcnt, usecnt, metric,
+                         dst_mask, mtu, win, irtt = line:match(
+                       "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" ..
+                       "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)"
+               )
+
+               if dev then
+                       gateway  = luci.ip.Hex( gateway,  32, luci.ip.FAMILY_INET4 )
+                       dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 )
+                       dst_ip   = luci.ip.Hex(
+                               dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4
+                       )
+
+                       local rt = {
+                               dest     = dst_ip,
+                               gateway  = gateway,
+                               metric   = tonumber(metric),
+                               refcount = tonumber(refcnt),
+                               usecount = tonumber(usecnt),
+                               mtu      = tonumber(mtu),
+                               window   = tonumber(window),
+                               irtt     = tonumber(irtt),
+                               flags    = tonumber(flags, 16),
+                               device   = dev
+                       }
+
+                       if callback then
+                               callback(rt)
+                       else
+                               routes[#routes+1] = rt
+                       end
+               end
+       end
+
+       return routes
+end
+
+--- Returns the current ipv6 kernel routing table entries.
+-- @return     Table of tables with properties of the corresponding routes.
+--                     The following fields are defined for route entry tables:
+--                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
+--                       "flags", "device" }
+function net.routes6(callback)
+       if fs.access("/proc/net/ipv6_route", "r") then
+               local routes = { }
+
+               for line in io.lines("/proc/net/ipv6_route") do
+
+                       local dst_ip, dst_prefix, src_ip, src_prefix, nexthop,
+                                 metric, refcnt, usecnt, flags, dev = line:match(
+                               "([a-f0-9]+) ([a-f0-9]+) " ..
+                               "([a-f0-9]+) ([a-f0-9]+) " ..
+                               "([a-f0-9]+) ([a-f0-9]+) " ..
+                               "([a-f0-9]+) ([a-f0-9]+) " ..
+                               "([a-f0-9]+) +([^%s]+)"
+                       )
+
+                       if dst_ip and dst_prefix and
+                          src_ip and src_prefix and
+                          nexthop and metric and
+                          refcnt and usecnt and
+                          flags and dev
+                       then
+                               src_ip = luci.ip.Hex(
+                                       src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false
+                               )
+
+                               dst_ip = luci.ip.Hex(
+                                       dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false
+                               )
+
+                               nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false )
+
+                               local rt = {
+                                       source   = src_ip,
+                                       dest     = dst_ip,
+                                       nexthop  = nexthop,
+                                       metric   = tonumber(metric, 16),
+                                       refcount = tonumber(refcnt, 16),
+                                       usecount = tonumber(usecnt, 16),
+                                       flags    = tonumber(flags, 16),
+                                       device   = dev,
+
+                                       -- lua number is too small for storing the metric
+                                       -- add a metric_raw field with the original content
+                                       metric_raw = metric
+                               }
+
+                               if callback then
+                                       callback(rt)
+                               else
+                                       routes[#routes+1] = rt
+                               end
+                       end
+               end
+
+               return routes
+       end
+end
+
+--- Tests whether the given host responds to ping probes.
+-- @param host String containing a hostname or IPv4 address
+-- @return             Number containing 0 on success and >= 1 on error
+function net.pingtest(host)
+       return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
+end
+
+
+--- LuCI system utilities / process related functions.
+-- @class      module
+-- @name       luci.sys.process
+process = {}
+
+--- Get the current process id.
+-- @class function
+-- @name  process.info
+-- @return     Number containing the current pid
+function process.info(key)
+       local s = {uid = nixio.getuid(), gid = nixio.getgid()}
+       return not key and s or s[key]
+end
+
+--- Retrieve information about currently running processes.
+-- @return     Table containing process information
+function process.list()
+       local data = {}
+       local k
+       local ps = luci.util.execi("/bin/busybox top -bn1")
+
+       if not ps then
+               return
+       end
+
+       for line in ps do
+               local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
+                       "^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
+               )
+
+               local idx = tonumber(pid)
+               if idx then
+                       data[idx] = {
+                               ['PID']     = pid,
+                               ['PPID']    = ppid,
+                               ['USER']    = user,
+                               ['STAT']    = stat,
+                               ['VSZ']     = vsz,
+                               ['%MEM']    = mem,
+                               ['%CPU']    = cpu,
+                               ['COMMAND'] = cmd
+                       }
+               end
+       end
+
+       return data
+end
+
+--- Set the gid of a process identified by given pid.
+-- @param gid  Number containing the Unix group id
+-- @return             Boolean indicating successful operation
+-- @return             String containing the error message if failed
+-- @return             Number containing the error code if failed
+function process.setgroup(gid)
+       return nixio.setgid(gid)
+end
+
+--- Set the uid of a process identified by given pid.
+-- @param uid  Number containing the Unix user id
+-- @return             Boolean indicating successful operation
+-- @return             String containing the error message if failed
+-- @return             Number containing the error code if failed
+function process.setuser(uid)
+       return nixio.setuid(uid)
+end
+
+--- Send a signal to a process identified by given pid.
+-- @class function
+-- @name  process.signal
+-- @param pid  Number containing the process id
+-- @param sig  Signal to send (default: 15 [SIGTERM])
+-- @return             Boolean indicating successful operation
+-- @return             Number containing the error code if failed
+process.signal = nixio.kill
+
+
+--- LuCI system utilities / user related functions.
+-- @class      module
+-- @name       luci.sys.user
+user = {}
+
+--- Retrieve user informations for given uid.
+-- @class              function
+-- @name               getuser
+-- @param uid  Number containing the Unix user id
+-- @return             Table containing the following fields:
+--                             { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
+user.getuser = nixio.getpw
+
+--- Retrieve the current user password hash.
+-- @param username     String containing the username to retrieve the password for
+-- @return                     String containing the hash or nil if no password is set.
+-- @return                     Password database entry
+function user.getpasswd(username)
+       local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
+       local pwh = pwe and (pwe.pwdp or pwe.passwd)
+       if not pwh or #pwh < 1 or pwh == "!" or pwh == "x" then
+               return nil, pwe
+       else
+               return pwh, pwe
+       end
+end
+
+--- Test whether given string matches the password of a given system user.
+-- @param username     String containing the Unix user name
+-- @param pass         String containing the password to compare
+-- @return                     Boolean indicating wheather the passwords are equal
+function user.checkpasswd(username, pass)
+       local pwh, pwe = user.getpasswd(username)
+       if pwe then
+               return (pwh == nil or nixio.crypt(pass, pwh) == pwh)
+       end
+       return false
+end
+
+--- Change the password of given user.
+-- @param username     String containing the Unix user name
+-- @param password     String containing the password to compare
+-- @return                     Number containing 0 on success and >= 1 on error
+function user.setpasswd(username, password)
+       if password then
+               password = password:gsub("'", [['"'"']])
+       end
+
+       if username then
+               username = username:gsub("'", [['"'"']])
+       end
+
+       return os.execute(
+               "(echo '" .. password .. "'; sleep 1; echo '" .. password .. "') | " ..
+               "passwd '" .. username .. "' >/dev/null 2>&1"
+       )
+end
+
+
+--- LuCI system utilities / wifi related functions.
+-- @class      module
+-- @name       luci.sys.wifi
+wifi = {}
+
+--- Get wireless information for given interface.
+-- @param ifname        String containing the interface name
+-- @return              A wrapped iwinfo object instance
+function wifi.getiwinfo(ifname)
+       local stat, iwinfo = pcall(require, "iwinfo")
+
+       if ifname then
+               local c = 0
+               local u = uci.cursor_state()
+               local d, n = ifname:match("^(%w+)%.network(%d+)")
+               if d and n then
+                       ifname = d
+                       n = tonumber(n)
+                       u:foreach("wireless", "wifi-iface",
+                               function(s)
+                                       if s.device == d then
+                                               c = c + 1
+                                               if c == n then
+                                                       ifname = s.ifname or s.device
+                                                       return false
+                                               end
+                                       end
+                               end)
+               elseif u:get("wireless", ifname) == "wifi-device" then
+                       u:foreach("wireless", "wifi-iface",
+                               function(s)
+                                       if s.device == ifname and s.ifname then
+                                               ifname = s.ifname
+                                               return false
+                                       end
+                               end)
+               end
+
+               local t = stat and iwinfo.type(ifname)
+               local x = t and iwinfo[t] or { }
+               return setmetatable({}, {
+                       __index = function(t, k)
+                               if k == "ifname" then
+                                       return ifname
+                               elseif x[k] then
+                                       return x[k](ifname)
+                               end
+                       end
+               })
+       end
+end
+
+
+--- LuCI system utilities / init related functions.
+-- @class      module
+-- @name       luci.sys.init
+init = {}
+init.dir = "/etc/init.d/"
+
+--- Get the names of all installed init scripts
+-- @return     Table containing the names of all inistalled init scripts
+function init.names()
+       local names = { }
+       for name in fs.glob(init.dir.."*") do
+               names[#names+1] = fs.basename(name)
+       end
+       return names
+end
+
+--- Get the index of he given init script
+-- @param name Name of the init script
+-- @return             Numeric index value
+function init.index(name)
+       if fs.access(init.dir..name) then
+               return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
+                       %{ init.dir, name })
+       end
+end
+
+local function init_action(action, name)
+       if fs.access(init.dir..name) then
+               return call("env -i %s%s %s >/dev/null" %{ init.dir, name, action })
+       end
+end
+
+--- Test whether the given init script is enabled
+-- @param name Name of the init script
+-- @return             Boolean indicating whether init is enabled
+function init.enabled(name)
+       return (init_action("enabled", name) == 0)
+end
+
+--- Enable the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.enable(name)
+       return (init_action("enable", name) == 1)
+end
+
+--- Disable the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.disable(name)
+       return (init_action("disable", name) == 0)
+end
+
+--- Start the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.start(name)
+       return (init_action("start", name) == 0)
+end
+
+--- Stop the given init script
+-- @param name Name of the init script
+-- @return             Boolean indicating success
+function init.stop(name)
+       return (init_action("stop", name) == 0)
+end
+
+
+-- Internal functions
+
+function _parse_mixed_record(cnt, delimiter)
+       delimiter = delimiter or "  "
+       local data = {}
+       local flags = {}
+
+       for i, l in pairs(luci.util.split(luci.util.trim(cnt), "\n")) do
+               for j, f in pairs(luci.util.split(luci.util.trim(l), delimiter, nil, true)) do
+                       local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
+
+                       if k then
+                               if x == "" then
+                                       table.insert(flags, k)
+                               else
+                                       data[k] = v
+                               end
+                       end
+               end
+       end
+
+       return data, flags
+end
diff --git a/modules/base/luasrc/sys/iptparser.lua b/modules/base/luasrc/sys/iptparser.lua
new file mode 100644 (file)
index 0000000..d823633
--- /dev/null
@@ -0,0 +1,373 @@
+--[[
+
+Iptables parser and query library
+(c) 2008-2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+(c) 2008-2009 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+]]--
+
+local luci  = {}
+luci.util   = require "luci.util"
+luci.sys    = require "luci.sys"
+luci.ip     = require "luci.ip"
+
+local tonumber, ipairs, table = tonumber, ipairs, table
+
+--- LuCI iptables parser and query library
+-- @cstyle     instance
+module("luci.sys.iptparser")
+
+--- Create a new iptables parser object.
+-- @class      function
+-- @name       IptParser
+-- @param      family  Number specifying the address family. 4 for IPv4, 6 for IPv6
+-- @return     IptParser instance
+IptParser = luci.util.class()
+
+function IptParser.__init__( self, family )
+       self._family = (tonumber(family) == 6) and 6 or 4
+       self._rules  = { }
+       self._chains = { }
+
+       if self._family == 4 then
+               self._nulladdr = "0.0.0.0/0"
+               self._tables   = { "filter", "nat", "mangle", "raw" }
+               self._command  = "iptables -t %s --line-numbers -nxvL"
+       else
+               self._nulladdr = "::/0"
+               self._tables   = { "filter", "mangle", "raw" }
+               self._command  = "ip6tables -t %s --line-numbers -nxvL"
+       end
+
+       self:_parse_rules()
+end
+
+--- Find all firewall rules that match the given criteria. Expects a table with
+-- search criteria as only argument. If args is nil or an empty table then all
+-- rules will be returned.
+--
+-- The following keys in the args table are recognized:
+-- <ul>
+--  <li> table          - Match rules that are located within the given table
+--  <li> chain          - Match rules that are located within the given chain
+--  <li> target                 - Match rules with the given target
+--  <li> protocol       - Match rules that match the given protocol, rules with
+--                                             protocol "all" are always matched
+--  <li> source                 - Match rules with the given source, rules with source
+--                                             "0.0.0.0/0" (::/0) are always matched
+--  <li> destination - Match rules with the given destination, rules with
+--                                             destination "0.0.0.0/0" (::/0) are always matched
+--  <li> inputif        - Match rules with the given input interface, rules
+--                                             with input      interface "*" (=all) are always matched
+--  <li> outputif       - Match rules with the given output interface, rules
+--                                             with output     interface "*" (=all) are always matched
+--  <li> flags          - Match rules that match the given flags, current
+--                                             supported values are "-f" (--fragment)
+--                                             and "!f" (! --fragment)
+--  <li> options        - Match rules containing all given options
+-- </ul>
+-- The return value is a list of tables representing the matched rules.
+-- Each rule table contains the following fields:
+-- <ul>
+--  <li> index          - The index number of the rule
+--  <li> table          - The table where the rule is located, can be one
+--                                             of "filter", "nat" or "mangle"
+--  <li> chain          - The chain where the rule is located, e.g. "INPUT"
+--                                             or "postrouting_wan"
+--  <li> target                 - The rule target, e.g. "REJECT" or "DROP"
+--  <li> protocol              The matching protocols, e.g. "all" or "tcp"
+--  <li> flags          - Special rule options ("--", "-f" or "!f")
+--  <li> inputif        - Input interface of the rule, e.g. "eth0.0"
+--                                             or "*" for all interfaces
+--  <li> outputif       - Output interface of the rule,e.g. "eth0.0"
+--                                             or "*" for all interfaces
+--  <li> source                 - The source ip range, e.g. "0.0.0.0/0" (::/0)
+--  <li> destination - The destination ip range, e.g. "0.0.0.0/0" (::/0)
+--  <li> options        - A list of specific options of the rule,
+--                                             e.g. { "reject-with", "tcp-reset" }
+--  <li> packets        - The number of packets matched by the rule
+--  <li> bytes          - The number of total bytes matched by the rule
+-- </ul>
+-- Example:
+-- <pre>
+-- ip = luci.sys.iptparser.IptParser()
+-- result = ip.find( {
+--     target="REJECT",
+--     protocol="tcp",
+--     options={ "reject-with", "tcp-reset" }
+-- } )
+-- </pre>
+-- This will match all rules with target "-j REJECT",
+-- protocol "-p tcp" (or "-p all")
+-- and the option "--reject-with tcp-reset".
+-- @params args                Table containing the search arguments (optional)
+-- @return                     Table of matching rule tables
+function IptParser.find( self, args )
+
+       local args = args or { }
+       local rv   = { }
+
+       args.source      = args.source      and self:_parse_addr(args.source)
+       args.destination = args.destination and self:_parse_addr(args.destination)
+
+       for i, rule in ipairs(self._rules) do
+               local match = true
+
+               -- match table
+               if not ( not args.table or args.table:lower() == rule.table ) then
+                       match = false
+               end
+
+               -- match chain
+               if not ( match == true and (
+                       not args.chain or args.chain == rule.chain
+               ) ) then
+                       match = false
+               end
+
+               -- match target
+               if not ( match == true and (
+                       not args.target or args.target == rule.target
+               ) ) then
+                       match = false
+               end
+
+               -- match protocol
+               if not ( match == true and (
+                       not args.protocol or rule.protocol == "all" or
+                       args.protocol:lower() == rule.protocol
+               ) ) then
+                       match = false
+               end
+
+               -- match source
+               if not ( match == true and (
+                       not args.source or rule.source == self._nulladdr or
+                       self:_parse_addr(rule.source):contains(args.source)
+               ) ) then
+                       match = false
+               end
+
+               -- match destination
+               if not ( match == true and (
+                       not args.destination or rule.destination == self._nulladdr or
+                       self:_parse_addr(rule.destination):contains(args.destination)
+               ) ) then
+                       match = false
+               end
+
+               -- match input interface
+               if not ( match == true and (
+                       not args.inputif or rule.inputif == "*" or
+                       args.inputif == rule.inputif
+               ) ) then
+                       match = false
+               end
+
+               -- match output interface
+               if not ( match == true and (
+                       not args.outputif or rule.outputif == "*" or
+                       args.outputif == rule.outputif
+               ) ) then
+                       match = false
+               end
+
+               -- match flags (the "opt" column)
+               if not ( match == true and (
+                       not args.flags or rule.flags == args.flags
+               ) ) then
+                       match = false
+               end
+
+               -- match specific options
+               if not ( match == true and (
+                       not args.options or
+                       self:_match_options( rule.options, args.options )
+               ) ) then
+                       match = false
+               end
+
+               -- insert match
+               if match == true then
+                       rv[#rv+1] = rule
+               end
+       end
+
+       return rv
+end
+
+
+--- Rebuild the internal lookup table, for example when rules have changed
+-- through external commands.
+-- @return     nothing
+function IptParser.resync( self )
+       self._rules = { }
+       self._chain = nil
+       self:_parse_rules()
+end
+
+
+--- Find the names of all tables.
+-- @return             Table of table names.
+function IptParser.tables( self )
+       return self._tables
+end
+
+
+--- Find the names of all chains within the given table name.
+-- @param table        String containing the table name
+-- @return             Table of chain names in the order they occur.
+function IptParser.chains( self, table )
+       local lookup = { }
+       local chains = { }
+       for _, r in ipairs(self:find({table=table})) do
+               if not lookup[r.chain] then
+                       lookup[r.chain]   = true
+                       chains[#chains+1] = r.chain
+               end
+       end
+       return chains
+end
+
+
+--- Return the given firewall chain within the given table name.
+-- @param table        String containing the table name
+-- @param chain        String containing the chain name
+-- @return             Table containing the fields "policy", "packets", "bytes"
+--                             and "rules". The "rules" field is a table of rule tables.
+function IptParser.chain( self, table, chain )
+       return self._chains[table:lower()] and self._chains[table:lower()][chain]
+end
+
+
+--- Test whether the given target points to a custom chain.
+-- @param target       String containing the target action
+-- @return                     Boolean indicating whether target is a custom chain.
+function IptParser.is_custom_target( self, target )
+       for _, r in ipairs(self._rules) do
+               if r.chain == target then
+                       return true
+               end
+       end
+       return false
+end
+
+
+-- [internal] Parse address according to family.
+function IptParser._parse_addr( self, addr )
+       if self._family == 4 then
+               return luci.ip.IPv4(addr)
+       else
+               return luci.ip.IPv6(addr)
+       end
+end
+
+-- [internal] Parse iptables output from all tables.
+function IptParser._parse_rules( self )
+
+       for i, tbl in ipairs(self._tables) do
+
+               self._chains[tbl] = { }
+
+               for i, rule in ipairs(luci.util.execl(self._command % tbl)) do
+
+                       if rule:find( "^Chain " ) == 1 then
+
+                               local crefs
+                               local cname, cpol, cpkt, cbytes = rule:match(
+                                       "^Chain ([^%s]*) %(policy (%w+) " ..
+                                       "(%d+) packets, (%d+) bytes%)"
+                               )
+
+                               if not cname then
+                                       cname, crefs = rule:match(
+                                               "^Chain ([^%s]*) %((%d+) references%)"
+                                       )
+                               end
+
+                               self._chain = cname
+                               self._chains[tbl][cname] = {
+                                       policy     = cpol,
+                                       packets    = tonumber(cpkt or 0),
+                                       bytes      = tonumber(cbytes or 0),
+                                       references = tonumber(crefs or 0),
+                                       rules      = { }
+                               }
+
+                       else
+                               if rule:find("%d") == 1 then
+
+                                       local rule_parts   = luci.util.split( rule, "%s+", nil, true )
+                                       local rule_details = { }
+
+                                       -- cope with rules that have no target assigned
+                                       if rule:match("^%d+%s+%d+%s+%d+%s%s") then
+                                               table.insert(rule_parts, 4, nil)
+                                       end
+
+                                       -- ip6tables opt column is usually zero-width
+                                       if self._family == 6 then
+                                               table.insert(rule_parts, 6, "--")
+                                       end
+
+                                       rule_details["table"]       = tbl
+                                       rule_details["chain"]       = self._chain
+                                       rule_details["index"]       = tonumber(rule_parts[1])
+                                       rule_details["packets"]     = tonumber(rule_parts[2])
+                                       rule_details["bytes"]       = tonumber(rule_parts[3])
+                                       rule_details["target"]      = rule_parts[4]
+                                       rule_details["protocol"]    = rule_parts[5]
+                                       rule_details["flags"]       = rule_parts[6]
+                                       rule_details["inputif"]     = rule_parts[7]
+                                       rule_details["outputif"]    = rule_parts[8]
+                                       rule_details["source"]      = rule_parts[9]
+                                       rule_details["destination"] = rule_parts[10]
+                                       rule_details["options"]     = { }
+
+                                       for i = 11, #rule_parts  do
+                                               if #rule_parts[i] > 0 then
+                                                       rule_details["options"][i-10] = rule_parts[i]
+                                               end
+                                       end
+
+                                       self._rules[#self._rules+1] = rule_details
+
+                                       self._chains[tbl][self._chain].rules[
+                                               #self._chains[tbl][self._chain].rules + 1
+                                       ] = rule_details
+                               end
+                       end
+               end
+       end
+
+       self._chain = nil
+end
+
+
+-- [internal] Return true if optlist1 contains all elements of optlist 2.
+--            Return false in all other cases.
+function IptParser._match_options( self, o1, o2 )
+
+       -- construct a hashtable of first options list to speed up lookups
+       local oh = { }
+       for i, opt in ipairs( o1 ) do oh[opt] = true end
+
+       -- iterate over second options list
+       -- each string in o2 must be also present in o1
+       -- if o2 contains a string which is not found in o1 then return false
+       for i, opt in ipairs( o2 ) do
+               if not oh[opt] then
+                       return false
+               end
+       end
+
+       return true
+end
diff --git a/modules/base/luasrc/sys/zoneinfo.lua b/modules/base/luasrc/sys/zoneinfo.lua
new file mode 100644 (file)
index 0000000..f5a12bf
--- /dev/null
@@ -0,0 +1,28 @@
+--[[
+LuCI - Autogenerated Zoneinfo Module
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+local setmetatable, require, rawget, rawset = setmetatable, require, rawget, rawset
+
+module "luci.sys.zoneinfo"
+
+setmetatable(_M, {
+       __index = function(t, k)
+               if k == "TZ" and not rawget(t, k) then
+                       local m = require "luci.sys.zoneinfo.tzdata"
+                       rawset(t, k, rawget(m, k))
+               elseif k == "OFFSET" and not rawget(t, k) then
+                       local m = require "luci.sys.zoneinfo.tzoffset"
+                       rawset(t, k, rawget(m, k))
+               end
+
+               return rawget(t, k)
+       end
+})
diff --git a/modules/base/luasrc/sys/zoneinfo/tzdata.lua b/modules/base/luasrc/sys/zoneinfo/tzdata.lua
new file mode 100644 (file)
index 0000000..1a99f6a
--- /dev/null
@@ -0,0 +1,420 @@
+--[[
+LuCI - Autogenerated Zoneinfo Module
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+module "luci.sys.zoneinfo.tzdata"
+
+TZ = {
+       { 'Africa/Abidjan', 'GMT0' },
+       { 'Africa/Accra', 'GMT0' },
+       { 'Africa/Addis Ababa', 'EAT-3' },
+       { 'Africa/Algiers', 'CET-1' },
+       { 'Africa/Asmara', 'EAT-3' },
+       { 'Africa/Bamako', 'GMT0' },
+       { 'Africa/Bangui', 'WAT-1' },
+       { 'Africa/Banjul', 'GMT0' },
+       { 'Africa/Bissau', 'GMT0' },
+       { 'Africa/Blantyre', 'CAT-2' },
+       { 'Africa/Brazzaville', 'WAT-1' },
+       { 'Africa/Bujumbura', 'CAT-2' },
+       { 'Africa/Casablanca', 'WET0' },
+       { 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Africa/Conakry', 'GMT0' },
+       { 'Africa/Dakar', 'GMT0' },
+       { 'Africa/Dar es Salaam', 'EAT-3' },
+       { 'Africa/Djibouti', 'EAT-3' },
+       { 'Africa/Douala', 'WAT-1' },
+       { 'Africa/El Aaiun', 'WET0' },
+       { 'Africa/Freetown', 'GMT0' },
+       { 'Africa/Gaborone', 'CAT-2' },
+       { 'Africa/Harare', 'CAT-2' },
+       { 'Africa/Johannesburg', 'SAST-2' },
+       { 'Africa/Juba', 'EAT-3' },
+       { 'Africa/Kampala', 'EAT-3' },
+       { 'Africa/Khartoum', 'EAT-3' },
+       { 'Africa/Kigali', 'CAT-2' },
+       { 'Africa/Kinshasa', 'WAT-1' },
+       { 'Africa/Lagos', 'WAT-1' },
+       { 'Africa/Libreville', 'WAT-1' },
+       { 'Africa/Lome', 'GMT0' },
+       { 'Africa/Luanda', 'WAT-1' },
+       { 'Africa/Lubumbashi', 'CAT-2' },
+       { 'Africa/Lusaka', 'CAT-2' },
+       { 'Africa/Malabo', 'WAT-1' },
+       { 'Africa/Maputo', 'CAT-2' },
+       { 'Africa/Maseru', 'SAST-2' },
+       { 'Africa/Mbabane', 'SAST-2' },
+       { 'Africa/Mogadishu', 'EAT-3' },
+       { 'Africa/Monrovia', 'GMT0' },
+       { 'Africa/Nairobi', 'EAT-3' },
+       { 'Africa/Ndjamena', 'WAT-1' },
+       { 'Africa/Niamey', 'WAT-1' },
+       { 'Africa/Nouakchott', 'GMT0' },
+       { 'Africa/Ouagadougou', 'GMT0' },
+       { 'Africa/Porto-Novo', 'WAT-1' },
+       { 'Africa/Sao Tome', 'GMT0' },
+       { 'Africa/Tripoli', 'EET-2' },
+       { 'Africa/Tunis', 'CET-1' },
+       { 'Africa/Windhoek', 'WAT-1WAST,M9.1.0,M4.1.0' },
+       { 'America/Adak', 'HAST10HADT,M3.2.0,M11.1.0' },
+       { 'America/Anchorage', 'AKST9AKDT,M3.2.0,M11.1.0' },
+       { 'America/Anguilla', 'AST4' },
+       { 'America/Antigua', 'AST4' },
+       { 'America/Araguaina', 'BRT3' },
+       { 'America/Argentina/Buenos Aires', 'ART3' },
+       { 'America/Argentina/Catamarca', 'ART3' },
+       { 'America/Argentina/Cordoba', 'ART3' },
+       { 'America/Argentina/Jujuy', 'ART3' },
+       { 'America/Argentina/La Rioja', 'ART3' },
+       { 'America/Argentina/Mendoza', 'ART3' },
+       { 'America/Argentina/Rio Gallegos', 'ART3' },
+       { 'America/Argentina/Salta', 'ART3' },
+       { 'America/Argentina/San Juan', 'ART3' },
+       { 'America/Argentina/Tucuman', 'ART3' },
+       { 'America/Argentina/Ushuaia', 'ART3' },
+       { 'America/Aruba', 'AST4' },
+       { 'America/Asuncion', 'PYT4PYST,M10.1.0/0,M4.2.0/0' },
+       { 'America/Atikokan', 'EST5' },
+       { 'America/Bahia', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
+       { 'America/Bahia Banderas', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Barbados', 'AST4' },
+       { 'America/Belem', 'BRT3' },
+       { 'America/Belize', 'CST6' },
+       { 'America/Blanc-Sablon', 'AST4' },
+       { 'America/Boa Vista', 'AMT4' },
+       { 'America/Bogota', 'COT5' },
+       { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
+       { 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Caracas', 'VET4:30' },
+       { 'America/Cayenne', 'GFT3' },
+       { 'America/Cayman', 'EST5' },
+       { 'America/Chicago', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Chihuahua', 'MST7MDT,M4.1.0,M10.5.0' },
+       { 'America/Costa Rica', 'CST6' },
+       { 'America/Cuiaba', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
+       { 'America/Curacao', 'AST4' },
+       { 'America/Danmarkshavn', 'GMT0' },
+       { 'America/Dawson', 'PST8PDT,M3.2.0,M11.1.0' },
+       { 'America/Dawson Creek', 'MST7' },
+       { 'America/Denver', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Detroit', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Dominica', 'AST4' },
+       { 'America/Edmonton', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Eirunepe', 'AMT4' },
+       { 'America/El Salvador', 'CST6' },
+       { 'America/Fortaleza', 'BRT3' },
+       { 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'America/Goose Bay', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'America/Grand Turk', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Grenada', 'AST4' },
+       { 'America/Guadeloupe', 'AST4' },
+       { 'America/Guatemala', 'CST6' },
+       { 'America/Guayaquil', 'ECT5' },
+       { 'America/Guyana', 'GYT4' },
+       { 'America/Halifax', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'America/Havana', 'CST5CDT,M3.2.0/0,M10.5.0/1' },
+       { 'America/Hermosillo', 'MST7' },
+       { 'America/Indiana/Indianapolis', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Knox', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Marengo', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Petersburg', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Tell City', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Vevay', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Vincennes', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Indiana/Winamac', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Inuvik', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Iqaluit', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Jamaica', 'EST5' },
+       { 'America/Juneau', 'AKST9AKDT,M3.2.0,M11.1.0' },
+       { 'America/Kentucky/Louisville', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Kentucky/Monticello', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Kralendijk', 'AST4' },
+       { 'America/La Paz', 'BOT4' },
+       { 'America/Lima', 'PET5' },
+       { 'America/Los Angeles', 'PST8PDT,M3.2.0,M11.1.0' },
+       { 'America/Lower Princes', 'AST4' },
+       { 'America/Maceio', 'BRT3' },
+       { 'America/Managua', 'CST6' },
+       { 'America/Manaus', 'AMT4' },
+       { 'America/Marigot', 'AST4' },
+       { 'America/Martinique', 'AST4' },
+       { 'America/Matamoros', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Mazatlan', 'MST7MDT,M4.1.0,M10.5.0' },
+       { 'America/Menominee', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Merida', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Metlakatla', 'MeST8' },
+       { 'America/Mexico City', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Miquelon', 'PMST3PMDT,M3.2.0,M11.1.0' },
+       { 'America/Moncton', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'America/Monterrey', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Montevideo', 'UYT3UYST,M10.1.0,M3.2.0' },
+       { 'America/Montreal', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Montserrat', 'AST4' },
+       { 'America/Nassau', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/New York', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Nipigon', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Nome', 'AKST9AKDT,M3.2.0,M11.1.0' },
+       { 'America/Noronha', 'FNT2' },
+       { 'America/North Dakota/Beulah', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/North Dakota/Center', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/North Dakota/New Salem', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Ojinaga', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Panama', 'EST5' },
+       { 'America/Pangnirtung', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Paramaribo', 'SRT3' },
+       { 'America/Phoenix', 'MST7' },
+       { 'America/Port of Spain', 'AST4' },
+       { 'America/Port-au-Prince', 'EST5' },
+       { 'America/Porto Velho', 'AMT4' },
+       { 'America/Puerto Rico', 'AST4' },
+       { 'America/Rainy River', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Rankin Inlet', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Recife', 'BRT3' },
+       { 'America/Regina', 'CST6' },
+       { 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Rio Branco', 'AMT4' },
+       { 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' },
+       { 'America/Santarem', 'BRT3' },
+       { 'America/Santo Domingo', 'AST4' },
+       { 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
+       { 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' },
+       { 'America/Shiprock', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'America/Sitka', 'AKST9AKDT,M3.2.0,M11.1.0' },
+       { 'America/St Barthelemy', 'AST4' },
+       { 'America/St Johns', 'NST3:30NDT,M3.2.0,M11.1.0' },
+       { 'America/St Kitts', 'AST4' },
+       { 'America/St Lucia', 'AST4' },
+       { 'America/St Thomas', 'AST4' },
+       { 'America/St Vincent', 'AST4' },
+       { 'America/Swift Current', 'CST6' },
+       { 'America/Tegucigalpa', 'CST6' },
+       { 'America/Thule', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'America/Thunder Bay', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Tijuana', 'PST8PDT,M3.2.0,M11.1.0' },
+       { 'America/Toronto', 'EST5EDT,M3.2.0,M11.1.0' },
+       { 'America/Tortola', 'AST4' },
+       { 'America/Vancouver', 'PST8PDT,M3.2.0,M11.1.0' },
+       { 'America/Whitehorse', 'PST8PDT,M3.2.0,M11.1.0' },
+       { 'America/Winnipeg', 'CST6CDT,M3.2.0,M11.1.0' },
+       { 'America/Yakutat', 'AKST9AKDT,M3.2.0,M11.1.0' },
+       { 'America/Yellowknife', 'MST7MDT,M3.2.0,M11.1.0' },
+       { 'Antarctica/Casey', 'WST-8' },
+       { 'Antarctica/Davis', 'DAVT-7' },
+       { 'Antarctica/DumontDUrville', 'DDUT-10' },
+       { 'Antarctica/Macquarie', 'MIST-11' },
+       { 'Antarctica/Mawson', 'MAWT-5' },
+       { 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
+       { 'Antarctica/Rothera', 'ROTT3' },
+       { 'Antarctica/South Pole', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
+       { 'Antarctica/Syowa', 'SYOT-3' },
+       { 'Antarctica/Vostok', 'VOST-6' },
+       { 'Arctic/Longyearbyen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Asia/Aden', 'AST-3' },
+       { 'Asia/Almaty', 'ALMT-6' },
+       { 'Asia/Anadyr', 'ANAT-12' },
+       { 'Asia/Aqtau', 'AQTT-5' },
+       { 'Asia/Aqtobe', 'AQTT-5' },
+       { 'Asia/Ashgabat', 'TMT-5' },
+       { 'Asia/Baghdad', 'AST-3' },
+       { 'Asia/Bahrain', 'AST-3' },
+       { 'Asia/Baku', 'AZT-4AZST,M3.5.0/4,M10.5.0/5' },
+       { 'Asia/Bangkok', 'ICT-7' },
+       { 'Asia/Beirut', 'EET-2EEST,M3.5.0/0,M10.5.0/0' },
+       { 'Asia/Bishkek', 'KGT-6' },
+       { 'Asia/Brunei', 'BNT-8' },
+       { 'Asia/Choibalsan', 'CHOT-8' },
+       { 'Asia/Chongqing', 'CST-8' },
+       { 'Asia/Colombo', 'IST-5:30' },
+       { 'Asia/Damascus', 'EET-2EEST,M4.1.5/0,M10.5.5/0' },
+       { 'Asia/Dhaka', 'BDT-6' },
+       { 'Asia/Dili', 'TLT-9' },
+       { 'Asia/Dubai', 'GST-4' },
+       { 'Asia/Dushanbe', 'TJT-5' },
+       { 'Asia/Gaza', 'EET-2' },
+       { 'Asia/Harbin', 'CST-8' },
+       { 'Asia/Hebron', 'EET-2' },
+       { 'Asia/Ho Chi Minh', 'ICT-7' },
+       { 'Asia/Hong Kong', 'HKT-8' },
+       { 'Asia/Hovd', 'HOVT-7' },
+       { 'Asia/Irkutsk', 'IRKT-9' },
+       { 'Asia/Jakarta', 'WIT-7' },
+       { 'Asia/Jayapura', 'EIT-9' },
+       { 'Asia/Kabul', 'AFT-4:30' },
+       { 'Asia/Kamchatka', 'PETT-12' },
+       { 'Asia/Karachi', 'PKT-5' },
+       { 'Asia/Kashgar', 'CST-8' },
+       { 'Asia/Kathmandu', 'NPT-5:45' },
+       { 'Asia/Kolkata', 'IST-5:30' },
+       { 'Asia/Krasnoyarsk', 'KRAT-8' },
+       { 'Asia/Kuala Lumpur', 'MYT-8' },
+       { 'Asia/Kuching', 'MYT-8' },
+       { 'Asia/Kuwait', 'AST-3' },
+       { 'Asia/Macau', 'CST-8' },
+       { 'Asia/Magadan', 'MAGT-12' },
+       { 'Asia/Makassar', 'CIT-8' },
+       { 'Asia/Manila', 'PHT-8' },
+       { 'Asia/Muscat', 'GST-4' },
+       { 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Asia/Novokuznetsk', 'NOVT-7' },
+       { 'Asia/Novosibirsk', 'NOVT-7' },
+       { 'Asia/Omsk', 'OMST-7' },
+       { 'Asia/Oral', 'ORAT-5' },
+       { 'Asia/Phnom Penh', 'ICT-7' },
+       { 'Asia/Pontianak', 'WIT-7' },
+       { 'Asia/Pyongyang', 'KST-9' },
+       { 'Asia/Qatar', 'AST-3' },
+       { 'Asia/Qyzylorda', 'QYZT-6' },
+       { 'Asia/Rangoon', 'MMT-6:30' },
+       { 'Asia/Riyadh', 'AST-3' },
+       { 'Asia/Sakhalin', 'SAKT-11' },
+       { 'Asia/Samarkand', 'UZT-5' },
+       { 'Asia/Seoul', 'KST-9' },
+       { 'Asia/Shanghai', 'CST-8' },
+       { 'Asia/Singapore', 'SGT-8' },
+       { 'Asia/Taipei', 'CST-8' },
+       { 'Asia/Tashkent', 'UZT-5' },
+       { 'Asia/Tbilisi', 'GET-4' },
+       { 'Asia/Thimphu', 'BTT-6' },
+       { 'Asia/Tokyo', 'JST-9' },
+       { 'Asia/Ulaanbaatar', 'ULAT-8' },
+       { 'Asia/Urumqi', 'CST-8' },
+       { 'Asia/Vientiane', 'ICT-7' },
+       { 'Asia/Vladivostok', 'VLAT-11' },
+       { 'Asia/Yakutsk', 'YAKT-10' },
+       { 'Asia/Yekaterinburg', 'YEKT-6' },
+       { 'Asia/Yerevan', 'AMT-4AMST,M3.5.0,M10.5.0/3' },
+       { 'Atlantic/Azores', 'AZOT1AZOST,M3.5.0/0,M10.5.0/1' },
+       { 'Atlantic/Bermuda', 'AST4ADT,M3.2.0,M11.1.0' },
+       { 'Atlantic/Canary', 'WET0WEST,M3.5.0/1,M10.5.0' },
+       { 'Atlantic/Cape Verde', 'CVT1' },
+       { 'Atlantic/Faroe', 'WET0WEST,M3.5.0/1,M10.5.0' },
+       { 'Atlantic/Madeira', 'WET0WEST,M3.5.0/1,M10.5.0' },
+       { 'Atlantic/Reykjavik', 'GMT0' },
+       { 'Atlantic/South Georgia', 'GST2' },
+       { 'Atlantic/St Helena', 'GMT0' },
+       { 'Atlantic/Stanley', 'FKT4FKST,M9.1.0,M4.3.0' },
+       { 'Australia/Adelaide', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
+       { 'Australia/Brisbane', 'EST-10' },
+       { 'Australia/Broken Hill', 'CST-9:30CST,M10.1.0,M4.1.0/3' },
+       { 'Australia/Currie', 'EST-10EST,M10.1.0,M4.1.0/3' },
+       { 'Australia/Darwin', 'CST-9:30' },
+       { 'Australia/Eucla', 'CWST-8:45' },
+       { 'Australia/Hobart', 'EST-10EST,M10.1.0,M4.1.0/3' },
+       { 'Australia/Lindeman', 'EST-10' },
+       { 'Australia/Lord Howe', 'LHST-10:30LHST-11,M10.1.0,M4.1.0' },
+       { 'Australia/Melbourne', 'EST-10EST,M10.1.0,M4.1.0/3' },
+       { 'Australia/Perth', 'WST-8' },
+       { 'Australia/Sydney', 'EST-10EST,M10.1.0,M4.1.0/3' },
+       { 'Europe/Amsterdam', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Andorra', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Athens', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Belgrade', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Berlin', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Bratislava', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Brussels', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Bucharest', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Budapest', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Chisinau', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Isle of Man', 'GMT0BST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Istanbul', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Jersey', 'GMT0BST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Kaliningrad', 'FET-3' },
+       { 'Europe/Kiev', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Lisbon', 'WET0WEST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Ljubljana', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/London', 'GMT0BST,M3.5.0/1,M10.5.0' },
+       { 'Europe/Luxembourg', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Madrid', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Malta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Mariehamn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Minsk', 'FET-3' },
+       { 'Europe/Monaco', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Moscow', 'MSK-4' },
+       { 'Europe/Oslo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Paris', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Podgorica', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Prague', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Riga', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Rome', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Samara', 'SAMT-4' },
+       { 'Europe/San Marino', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Sarajevo', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Simferopol', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Skopje', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Sofia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Stockholm', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Tallinn', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Tirane', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Uzhgorod', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Vaduz', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Volgograd', 'VOLT-4' },
+       { 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
+       { 'Europe/Zurich', 'CET-1CEST,M3.5.0,M10.5.0/3' },
+       { 'Indian/Antananarivo', 'EAT-3' },
+       { 'Indian/Chagos', 'IOT-6' },
+       { 'Indian/Christmas', 'CXT-7' },
+       { 'Indian/Cocos', 'CCT-6:30' },
+       { 'Indian/Comoro', 'EAT-3' },
+       { 'Indian/Kerguelen', 'TFT-5' },
+       { 'Indian/Mahe', 'SCT-4' },
+       { 'Indian/Maldives', 'MVT-5' },
+       { 'Indian/Mauritius', 'MUT-4' },
+       { 'Indian/Mayotte', 'EAT-3' },
+       { 'Indian/Reunion', 'RET-4' },
+       { 'Pacific/Apia', 'WST-13' },
+       { 'Pacific/Auckland', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
+       { 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' },
+       { 'Pacific/Chuuk', 'CHUT-10' },
+       { 'Pacific/Efate', 'VUT-11' },
+       { 'Pacific/Enderbury', 'PHOT-13' },
+       { 'Pacific/Fakaofo', 'TKT10' },
+       { 'Pacific/Fiji', 'FJT-12' },
+       { 'Pacific/Funafuti', 'TVT-12' },
+       { 'Pacific/Galapagos', 'GALT6' },
+       { 'Pacific/Gambier', 'GAMT9' },
+       { 'Pacific/Guadalcanal', 'SBT-11' },
+       { 'Pacific/Guam', 'ChST-10' },
+       { 'Pacific/Honolulu', 'HST10' },
+       { 'Pacific/Johnston', 'HST10' },
+       { 'Pacific/Kiritimati', 'LINT-14' },
+       { 'Pacific/Kosrae', 'KOST-11' },
+       { 'Pacific/Kwajalein', 'MHT-12' },
+       { 'Pacific/Majuro', 'MHT-12' },
+       { 'Pacific/Marquesas', 'MART9:30' },
+       { 'Pacific/Midway', 'SST11' },
+       { 'Pacific/Nauru', 'NRT-12' },
+       { 'Pacific/Niue', 'NUT11' },
+       { 'Pacific/Norfolk', 'NFT-11:30' },
+       { 'Pacific/Noumea', 'NCT-11' },
+       { 'Pacific/Pago Pago', 'SST11' },
+       { 'Pacific/Palau', 'PWT-9' },
+       { 'Pacific/Pitcairn', 'PST8' },
+       { 'Pacific/Pohnpei', 'PONT-11' },
+       { 'Pacific/Port Moresby', 'PGT-10' },
+       { 'Pacific/Rarotonga', 'CKT10' },
+       { 'Pacific/Saipan', 'ChST-10' },
+       { 'Pacific/Tahiti', 'TAHT10' },
+       { 'Pacific/Tarawa', 'GILT-12' },
+       { 'Pacific/Tongatapu', 'TOT-13' },
+       { 'Pacific/Wake', 'WAKT-12' },
+       { 'Pacific/Wallis', 'WFT-12' },
+}
diff --git a/modules/base/luasrc/sys/zoneinfo/tzoffset.lua b/modules/base/luasrc/sys/zoneinfo/tzoffset.lua
new file mode 100644 (file)
index 0000000..bbe75d5
--- /dev/null
@@ -0,0 +1,162 @@
+--[[
+LuCI - Autogenerated Zoneinfo Module
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+module "luci.sys.zoneinfo.tzoffset"
+
+OFFSET = {
+       gmt   =      0, -- GMT
+       eat   =  10800, -- EAT
+       cet   =   3600, -- CET
+       wat   =   3600, -- WAT
+       cat   =   7200, -- CAT
+       wet   =      0, -- WET
+       sast  =   7200, -- SAST
+       eet   =   7200, -- EET
+       hast  = -36000, -- HAST
+       hadt  = -32400, -- HADT
+       akst  = -32400, -- AKST
+       akdt  = -28800, -- AKDT
+       ast   = -14400, -- AST
+       brt   = -10800, -- BRT
+       art   = -10800, -- ART
+       pyt   = -14400, -- PYT
+       pyst  = -10800, -- PYST
+       est   = -18000, -- EST
+       cst   = -21600, -- CST
+       cdt   = -18000, -- CDT
+       amt   = -14400, -- AMT
+       cot   = -18000, -- COT
+       mst   = -25200, -- MST
+       mdt   = -21600, -- MDT
+       vet   = -16200, -- VET
+       gft   = -10800, -- GFT
+       pst   = -28800, -- PST
+       pdt   = -25200, -- PDT
+       ect   = -18000, -- ECT
+       gyt   = -14400, -- GYT
+       bot   = -14400, -- BOT
+       pet   = -18000, -- PET
+       pmst  = -10800, -- PMST
+       pmdt  =  -7200, -- PMDT
+       uyt   = -10800, -- UYT
+       uyst  =  -7200, -- UYST
+       fnt   =  -7200, -- FNT
+       srt   = -10800, -- SRT
+       egt   =  -3600, -- EGT
+       egst  =      0, -- EGST
+       nst   = -12600, -- NST
+       ndt   =  -9000, -- NDT
+       wst   =  28800, -- WST
+       davt  =  25200, -- DAVT
+       ddut  =  36000, -- DDUT
+       mist  =  39600, -- MIST
+       mawt  =  18000, -- MAWT
+       nzst  =  43200, -- NZST
+       nzdt  =  46800, -- NZDT
+       rott  = -10800, -- ROTT
+       syot  =  10800, -- SYOT
+       vost  =  21600, -- VOST
+       almt  =  21600, -- ALMT
+       anat  =  43200, -- ANAT
+       aqtt  =  18000, -- AQTT
+       tmt   =  18000, -- TMT
+       azt   =  14400, -- AZT
+       azst  =  18000, -- AZST
+       ict   =  25200, -- ICT
+       kgt   =  21600, -- KGT
+       bnt   =  28800, -- BNT
+       chot  =  28800, -- CHOT
+       ist   =  19800, -- IST
+       bdt   =  21600, -- BDT
+       tlt   =  32400, -- TLT
+       gst   =  14400, -- GST
+       tjt   =  18000, -- TJT
+       hkt   =  28800, -- HKT
+       hovt  =  25200, -- HOVT
+       irkt  =  32400, -- IRKT
+       wit   =  25200, -- WIT
+       eit   =  32400, -- EIT
+       aft   =  16200, -- AFT
+       pett  =  43200, -- PETT
+       pkt   =  18000, -- PKT
+       npt   =  20700, -- NPT
+       krat  =  28800, -- KRAT
+       myt   =  28800, -- MYT
+       magt  =  43200, -- MAGT
+       cit   =  28800, -- CIT
+       pht   =  28800, -- PHT
+       novt  =  25200, -- NOVT
+       omst  =  25200, -- OMST
+       orat  =  18000, -- ORAT
+       kst   =  32400, -- KST
+       qyzt  =  21600, -- QYZT
+       mmt   =  23400, -- MMT
+       sakt  =  39600, -- SAKT
+       uzt   =  18000, -- UZT
+       sgt   =  28800, -- SGT
+       get   =  14400, -- GET
+       btt   =  21600, -- BTT
+       jst   =  32400, -- JST
+       ulat  =  28800, -- ULAT
+       vlat  =  39600, -- VLAT
+       yakt  =  36000, -- YAKT
+       yekt  =  21600, -- YEKT
+       azot  =  -3600, -- AZOT
+       azost =      0, -- AZOST
+       cvt   =  -3600, -- CVT
+       fkt   = -14400, -- FKT
+       fkst  = -10800, -- FKST
+       cwst  =  31500, -- CWST
+       lhst  =  37800, -- LHST
+       lhst  =  39600, -- LHST
+       fet   =  10800, -- FET
+       msk   =  14400, -- MSK
+       samt  =  14400, -- SAMT
+       volt  =  14400, -- VOLT
+       iot   =  21600, -- IOT
+       cxt   =  25200, -- CXT
+       cct   =  23400, -- CCT
+       tft   =  18000, -- TFT
+       sct   =  14400, -- SCT
+       mvt   =  18000, -- MVT
+       mut   =  14400, -- MUT
+       ret   =  14400, -- RET
+       chast =  45900, -- CHAST
+       chadt =  49500, -- CHADT
+       chut  =  36000, -- CHUT
+       vut   =  39600, -- VUT
+       phot  =  46800, -- PHOT
+       tkt   = -36000, -- TKT
+       fjt   =  43200, -- FJT
+       tvt   =  43200, -- TVT
+       galt  = -21600, -- GALT
+       gamt  = -32400, -- GAMT
+       sbt   =  39600, -- SBT
+       hst   = -36000, -- HST
+       lint  =  50400, -- LINT
+       kost  =  39600, -- KOST
+       mht   =  43200, -- MHT
+       mart  = -34200, -- MART
+       sst   = -39600, -- SST
+       nrt   =  43200, -- NRT
+       nut   = -39600, -- NUT
+       nft   =  41400, -- NFT
+       nct   =  39600, -- NCT
+       pwt   =  32400, -- PWT
+       pont  =  39600, -- PONT
+       pgt   =  36000, -- PGT
+       ckt   = -36000, -- CKT
+       taht  = -36000, -- TAHT
+       gilt  =  43200, -- GILT
+       tot   =  46800, -- TOT
+       wakt  =  43200, -- WAKT
+       wft   =  43200, -- WFT
+}
diff --git a/modules/base/luasrc/tools/proto.lua b/modules/base/luasrc/tools/proto.lua
new file mode 100644 (file)
index 0000000..4df0269
--- /dev/null
@@ -0,0 +1,46 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+module("luci.tools.proto", package.seeall)
+
+function opt_macaddr(s, ifc, ...)
+       local v = luci.cbi.Value
+       local o = s:taboption("advanced", v, "macaddr", ...)
+
+       o.placeholder = ifc and ifc:mac()
+       o.datatype    = "macaddr"
+
+       function o.cfgvalue(self, section)
+               local w = ifc and ifc:get_wifinet()
+               if w then
+                       return w:get("macaddr")
+               else
+                       return v.cfgvalue(self, section)
+               end
+       end
+
+       function o.write(self, section, value)
+               local w = ifc and ifc:get_wifinet()
+               if w then
+                       w:set("macaddr", value)
+               elseif value then
+                       v.write(self, section, value)
+               else
+                       v.remove(self, section)
+               end
+       end
+
+       function o.remove(self, section)
+               self:write(section, nil)
+       end
+end
diff --git a/modules/base/luasrc/tools/status.lua b/modules/base/luasrc/tools/status.lua
new file mode 100644 (file)
index 0000000..27bc925
--- /dev/null
@@ -0,0 +1,216 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+]]--
+
+module("luci.tools.status", package.seeall)
+
+local uci = require "luci.model.uci".cursor()
+
+local function dhcp_leases_common(family)
+       local rv = { }
+       local nfs = require "nixio.fs"
+       local leasefile = "/var/dhcp.leases"
+
+       uci:foreach("dhcp", "dnsmasq",
+               function(s)
+                       if s.leasefile and nfs.access(s.leasefile) then
+                               leasefile = s.leasefile
+                               return false
+                       end
+               end)
+
+       local fd = io.open(leasefile, "r")
+       if fd then
+               while true do
+                       local ln = fd:read("*l")
+                       if not ln then
+                               break
+                       else
+                               local ts, mac, ip, name, duid = ln:match("^(%d+) (%S+) (%S+) (%S+) (%S+)")
+                               if ts and mac and ip and name and duid then
+                                       if family == 4 and not ip:match(":") then
+                                               rv[#rv+1] = {
+                                                       expires  = os.difftime(tonumber(ts) or 0, os.time()),
+                                                       macaddr  = mac,
+                                                       ipaddr   = ip,
+                                                       hostname = (name ~= "*") and name
+                                               }
+                                       elseif family == 6 and ip:match(":") then
+                                               rv[#rv+1] = {
+                                                       expires  = os.difftime(tonumber(ts) or 0, os.time()),
+                                                       ip6addr  = ip,
+                                                       duid     = (duid ~= "*") and duid,
+                                                       hostname = (name ~= "*") and name
+                                               }
+                                       end
+                               end
+                       end
+               end
+               fd:close()
+       end
+
+       local fd = io.open("/tmp/hosts/odhcpd", "r")
+       if fd then
+               while true do
+                       local ln = fd:read("*l")
+                       if not ln then
+                               break
+                       else
+                               local iface, duid, iaid, name, ts, id, length, ip = ln:match("^# (%S+) (%S+) (%S+) (%S+) (%d+) (%S+) (%S+) (.*)")
+                               if ip and iaid ~= "ipv4" and family == 6 then
+                                       rv[#rv+1] = {
+                                               expires  = os.difftime(tonumber(ts) or 0, os.time()),
+                                               duid     = duid,
+                                               ip6addr  = ip,
+                                               hostname = (name ~= "-") and name
+                                       }
+                               elseif ip and iaid == "ipv4" and family == 4 then
+                                       rv[#rv+1] = {
+                                               expires  = os.difftime(tonumber(ts) or 0, os.time()),
+                                               macaddr  = duid,
+                                               ipaddr   = ip,
+                                               hostname = (name ~= "-") and name
+                                       }
+                               end
+                       end
+               end
+               fd:close()
+       end
+
+       return rv
+end
+
+function dhcp_leases()
+       return dhcp_leases_common(4)
+end
+
+function dhcp6_leases()
+       return dhcp_leases_common(6)
+end
+
+function wifi_networks()
+       local rv = { }
+       local ntm = require "luci.model.network".init()
+
+       local dev
+       for _, dev in ipairs(ntm:get_wifidevs()) do
+               local rd = {
+                       up       = dev:is_up(),
+                       device   = dev:name(),
+                       name     = dev:get_i18n(),
+                       networks = { }
+               }
+
+               local net
+               for _, net in ipairs(dev:get_wifinets()) do
+                       rd.networks[#rd.networks+1] = {
+                               name       = net:shortname(),
+                               link       = net:adminlink(),
+                               up         = net:is_up(),
+                               mode       = net:active_mode(),
+                               ssid       = net:active_ssid(),
+                               bssid      = net:active_bssid(),
+                               encryption = net:active_encryption(),
+                               frequency  = net:frequency(),
+                               channel    = net:channel(),
+                               signal     = net:signal(),
+                               quality    = net:signal_percent(),
+                               noise      = net:noise(),
+                               bitrate    = net:bitrate(),
+                               ifname     = net:ifname(),
+                               assoclist  = net:assoclist(),
+                               country    = net:country(),
+                               txpower    = net:txpower(),
+                               txpoweroff = net:txpower_offset()
+                       }
+               end
+
+               rv[#rv+1] = rd
+       end
+
+       return rv
+end
+
+function wifi_network(id)
+       local ntm = require "luci.model.network".init()
+       local net = ntm:get_wifinet(id)
+       if net then
+               local dev = net:get_device()
+               if dev then
+                       return {
+                               id         = id,
+                               name       = net:shortname(),
+                               link       = net:adminlink(),
+                               up         = net:is_up(),
+                               mode       = net:active_mode(),
+                               ssid       = net:active_ssid(),
+                               bssid      = net:active_bssid(),
+                               encryption = net:active_encryption(),
+                               frequency  = net:frequency(),
+                               channel    = net:channel(),
+                               signal     = net:signal(),
+                               quality    = net:signal_percent(),
+                               noise      = net:noise(),
+                               bitrate    = net:bitrate(),
+                               ifname     = net:ifname(),
+                               assoclist  = net:assoclist(),
+                               country    = net:country(),
+                               txpower    = net:txpower(),
+                               txpoweroff = net:txpower_offset(),
+                               device     = {
+                                       up     = dev:is_up(),
+                                       device = dev:name(),
+                                       name   = dev:get_i18n()
+                               }
+                       }
+               end
+       end
+       return { }
+end
+
+function switch_status(devs)
+       local dev
+       local switches = { }
+       for dev in devs:gmatch("[^%s,]+") do
+               local ports = { }
+               local swc = io.popen("swconfig dev %q show" % dev, "r")
+               if swc then
+                       local l
+                       repeat
+                               l = swc:read("*l")
+                               if l then
+                                       local port, up = l:match("port:(%d+) link:(%w+)")
+                                       if port then
+                                               local speed  = l:match(" speed:(%d+)")
+                                               local duplex = l:match(" (%w+)-duplex")
+                                               local txflow = l:match(" (txflow)")
+                                               local rxflow = l:match(" (rxflow)")
+                                               local auto   = l:match(" (auto)")
+
+                                               ports[#ports+1] = {
+                                                       port   = tonumber(port) or 0,
+                                                       speed  = tonumber(speed) or 0,
+                                                       link   = (up == "up"),
+                                                       duplex = (duplex == "full"),
+                                                       rxflow = (not not rxflow),
+                                                       txflow = (not not txflow),
+                                                       auto   = (not not auto)
+                                               }
+                                       end
+                               end
+                       until not l
+                       swc:close()
+               end
+               switches[dev] = ports
+       end
+       return switches
+end
diff --git a/modules/base/luasrc/tools/webadmin.lua b/modules/base/luasrc/tools/webadmin.lua
new file mode 100644 (file)
index 0000000..0e09be9
--- /dev/null
@@ -0,0 +1,173 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+]]--
+
+module("luci.tools.webadmin", package.seeall)
+local uci = require("luci.model.uci")
+require("luci.sys")
+require("luci.ip")
+
+function byte_format(byte)
+       local suff = {"B", "KB", "MB", "GB", "TB"}
+       for i=1, 5 do
+               if byte > 1024 and i < 5 then
+                       byte = byte / 1024
+               else
+                       return string.format("%.2f %s", byte, suff[i]) 
+               end 
+       end
+end
+
+function date_format(secs)
+       local suff = {"min", "h", "d"}
+       local mins = 0
+       local hour = 0
+       local days = 0
+       
+       secs = math.floor(secs)
+       if secs > 60 then
+               mins = math.floor(secs / 60)
+               secs = secs % 60
+       end
+       
+       if mins > 60 then
+               hour = math.floor(mins / 60)
+               mins = mins % 60
+       end
+       
+       if hour > 24 then
+               days = math.floor(hour / 24)
+               hour = hour % 24
+       end
+       
+       if days > 0 then
+               return string.format("%.0fd %02.0fh %02.0fmin %02.0fs", days, hour, mins, secs)
+       else
+               return string.format("%02.0fh %02.0fmin %02.0fs", hour, mins, secs)
+       end
+end
+
+function network_get_addresses(net)
+       local state = uci.cursor_state()
+       state:load("network")
+       local addr = {}
+       local ipv4 = state:get("network", net, "ipaddr")
+       local mav4 = state:get("network", net, "netmask")
+       local ipv6 = state:get("network", net, "ip6addr")
+       
+       if ipv4 and #ipv4 > 0 then
+               if mav4 and #mav4 == 0 then mav4 = nil end
+
+               ipv4 = luci.ip.IPv4(ipv4, mav4)
+               
+               if ipv4 then 
+                       table.insert(addr, ipv4:string())
+               end
+       end
+
+       if ipv6 then
+               table.insert(addr, ipv6)
+       end
+       
+       state:foreach("network", "alias",
+               function (section)
+                       if section.interface == net then
+                               if section.ipaddr and section.netmask then
+                                       local ipv4 = luci.ip.IPv4(section.ipaddr, section.netmask)
+                                       
+                                       if ipv4 then
+                                               table.insert(addr, ipv4:string())
+                                       end
+                               end
+                               
+                               if section.ip6addr then
+                                       table.insert(addr, section.ip6addr)
+                               end
+                       end
+               end
+       )
+       
+       return addr
+end
+
+function cbi_add_networks(field)
+       uci.cursor():foreach("network", "interface",
+               function (section)
+                       if section[".name"] ~= "loopback" then
+                               field:value(section[".name"])
+                       end
+               end
+       )
+       field.titleref = luci.dispatcher.build_url("admin", "network", "network")
+end
+
+function cbi_add_knownips(field)
+       for i, dataset in ipairs(luci.sys.net.arptable()) do
+               field:value(dataset["IP address"])
+       end
+end
+
+function network_get_zones(net)
+       local state = uci.cursor_state()
+       if not state:load("firewall") then
+               return nil
+       end
+       
+       local zones = {}
+       
+       state:foreach("firewall", "zone", 
+               function (section)
+                       local znet = section.network or section.name
+                       if luci.util.contains(luci.util.split(znet, " "), net) then
+                               table.insert(zones, section.name)
+                       end
+               end
+       )
+       
+       return zones
+end
+
+function firewall_find_zone(name)
+       local find
+       
+       luci.model.uci.cursor():foreach("firewall", "zone", 
+               function (section)
+                       if section.name == name then
+                               find = section[".name"]
+                       end
+               end
+       )
+       
+       return find
+end
+
+function iface_get_network(iface)
+       local state = uci.cursor_state()
+       state:load("network")
+       local net
+       
+       state:foreach("network", "interface",
+               function (section)
+                       local ifname = state:get(
+                               "network", section[".name"], "ifname"
+                       )
+                       
+                       if iface == ifname then
+                               net = section[".name"]
+                       end
+               end
+       )
+       
+       return net
+end
diff --git a/modules/base/luasrc/util.lua b/modules/base/luasrc/util.lua
new file mode 100644 (file)
index 0000000..da761e2
--- /dev/null
@@ -0,0 +1,791 @@
+--[[
+LuCI - Utility library
+
+Description:
+Several common useful Lua functions
+
+License:
+Copyright 2008 Steven Barth <steven@midlink.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local io = require "io"
+local math = require "math"
+local table = require "table"
+local debug = require "debug"
+local ldebug = require "luci.debug"
+local string = require "string"
+local coroutine = require "coroutine"
+local tparser = require "luci.template.parser"
+
+local getmetatable, setmetatable = getmetatable, setmetatable
+local rawget, rawset, unpack = rawget, rawset, unpack
+local tostring, type, assert = tostring, type, assert
+local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
+local require, pcall, xpcall = require, pcall, xpcall
+local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
+
+--- LuCI utility functions.
+module "luci.util"
+
+--
+-- Pythonic string formatting extension
+--
+getmetatable("").__mod = function(a, b)
+       if not b then
+               return a
+       elseif type(b) == "table" then
+               for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
+               return a:format(unpack(b))
+       else
+               if type(b) == "userdata" then b = tostring(b) end
+               return a:format(b)
+       end
+end
+
+
+--
+-- Class helper routines
+--
+
+-- Instantiates a class
+local function _instantiate(class, ...)
+       local inst = setmetatable({}, {__index = class})
+
+       if inst.__init__ then
+               inst:__init__(...)
+       end
+
+       return inst
+end
+
+--- Create a Class object (Python-style object model).
+-- The class object can be instantiated by calling itself.
+-- Any class functions or shared parameters can be attached to this object.
+-- Attaching a table to the class object makes this table shared between
+-- all instances of this class. For object parameters use the __init__ function.
+-- Classes can inherit member functions and values from a base class.
+-- Class can be instantiated by calling them. All parameters will be passed
+-- to the __init__ function of this class - if such a function exists.
+-- The __init__ function must be used to set any object parameters that are not shared
+-- with other objects of this class. Any return values will be ignored.
+-- @param base The base class to inherit from (optional)
+-- @return             A class object
+-- @see                        instanceof
+-- @see                        clone
+function class(base)
+       return setmetatable({}, {
+               __call  = _instantiate,
+               __index = base
+       })
+end
+
+--- Test whether the given object is an instance of the given class.
+-- @param object       Object instance
+-- @param class                Class object to test against
+-- @return                     Boolean indicating whether the object is an instance
+-- @see                                class
+-- @see                                clone
+function instanceof(object, class)
+       local meta = getmetatable(object)
+       while meta and meta.__index do
+               if meta.__index == class then
+                       return true
+               end
+               meta = getmetatable(meta.__index)
+       end
+       return false
+end
+
+
+--
+-- Scope manipulation routines
+--
+
+local tl_meta = {
+       __mode = "k",
+
+       __index = function(self, key)
+               local t = rawget(self, coxpt[coroutine.running()]
+                or coroutine.running() or 0)
+               return t and t[key]
+       end,
+
+       __newindex = function(self, key, value)
+               local c = coxpt[coroutine.running()] or coroutine.running() or 0
+               if not rawget(self, c) then
+                       rawset(self, c, { [key] = value })
+               else
+                       rawget(self, c)[key] = value
+               end
+       end
+}
+
+--- Create a new or get an already existing thread local store associated with
+-- the current active coroutine. A thread local store is private a table object
+-- whose values can't be accessed from outside of the running coroutine.
+-- @return     Table value representing the corresponding thread local store
+function threadlocal(tbl)
+       return setmetatable(tbl or {}, tl_meta)
+end
+
+
+--
+-- Debugging routines
+--
+
+--- Write given object to stderr.
+-- @param obj  Value to write to stderr
+-- @return             Boolean indicating whether the write operation was successful
+function perror(obj)
+       return io.stderr:write(tostring(obj) .. "\n")
+end
+
+--- Recursively dumps a table to stdout, useful for testing and debugging.
+-- @param t    Table value to dump
+-- @param maxdepth     Maximum depth
+-- @return     Always nil
+function dumptable(t, maxdepth, i, seen)
+       i = i or 0
+       seen = seen or setmetatable({}, {__mode="k"})
+
+       for k,v in pairs(t) do
+               perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
+               if type(v) == "table" and (not maxdepth or i < maxdepth) then
+                       if not seen[v] then
+                               seen[v] = true
+                               dumptable(v, maxdepth, i+1, seen)
+                       else
+                               perror(string.rep("\t", i) .. "*** RECURSION ***")
+                       end
+               end
+       end
+end
+
+
+--
+-- String and data manipulation routines
+--
+
+--- Create valid XML PCDATA from given string.
+-- @param value        String value containing the data to escape
+-- @return             String value containing the escaped data
+function pcdata(value)
+       return value and tparser.pcdata(tostring(value))
+end
+
+--- Strip HTML tags from given string.
+-- @param value        String containing the HTML text
+-- @return     String with HTML tags stripped of
+function striptags(value)
+       return value and tparser.striptags(tostring(value))
+end
+
+--- Splits given string on a defined separator sequence and return a table
+-- containing the resulting substrings. The optional max parameter specifies
+-- the number of bytes to process, regardless of the actual length of the given
+-- string. The optional last parameter, regex, specifies whether the separator
+-- sequence is interpreted as regular expression.
+-- @param str          String value containing the data to split up
+-- @param pat          String with separator pattern (optional, defaults to "\n")
+-- @param max          Maximum times to split (optional)
+-- @param regex        Boolean indicating whether to interpret the separator
+--                                     pattern as regular expression (optional, default is false)
+-- @return                     Table containing the resulting substrings
+function split(str, pat, max, regex)
+       pat = pat or "\n"
+       max = max or #str
+
+       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)
+               max = max - 1
+               if s and max < 0 then
+                       t[#t+1] = str:sub(c)
+               else
+                       t[#t+1] = str:sub(c, s and s - 1)
+               end
+               c = e and e + 1 or #str + 1
+       until not s or max < 0
+
+       return t
+end
+
+--- Remove leading and trailing whitespace from given string value.
+-- @param str  String value containing whitespace padded data
+-- @return             String value with leading and trailing space removed
+function trim(str)
+       return (str:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+--- Count the occurences of given substring in given string.
+-- @param str          String to search in
+-- @param pattern      String containing pattern to find
+-- @return                     Number of found occurences
+function cmatch(str, pat)
+       local count = 0
+       for _ in str:gmatch(pat) do count = count + 1 end
+       return count
+end
+
+--- Return a matching iterator for the given value. The iterator will return
+-- one token per invocation, the tokens are separated by whitespace. If the
+-- input value is a table, it is transformed into a string first. A nil value
+-- will result in a valid interator which aborts with the first invocation.
+-- @param val          The value to scan (table, string or nil)
+-- @return                     Iterator which returns one token per call
+function imatch(v)
+       if type(v) == "table" then
+               local k = nil
+               return function()
+                       k = next(v, k)
+                       return v[k]
+               end
+
+       elseif type(v) == "number" or type(v) == "boolean" then
+               local x = true
+               return function()
+                       if x then
+                               x = false
+                               return tostring(v)
+                       end
+               end
+
+       elseif type(v) == "userdata" or type(v) == "string" then
+               return tostring(v):gmatch("%S+")
+       end
+
+       return function() end
+end
+
+--- Parse certain units from the given string and return the canonical integer
+-- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
+-- Recognized units are:
+--     o "y"   - one year   (60*60*24*366)
+--  o "m"      - one month  (60*60*24*31)
+--  o "w"      - one week   (60*60*24*7)
+--  o "d"      - one day    (60*60*24)
+--  o "h"      - one hour       (60*60)
+--  o "min"    - one minute (60)
+--  o "kb"  - one kilobyte (1024)
+--  o "mb"     - one megabyte (1024*1024)
+--  o "gb"     - one gigabyte (1024*1024*1024)
+--  o "kib" - one si kilobyte (1000)
+--  o "mib"    - one si megabyte (1000*1000)
+--  o "gib"    - one si gigabyte (1000*1000*1000)
+-- @param ustr String containing a numerical value with trailing unit
+-- @return             Number containing the canonical value
+function parse_units(ustr)
+
+       local val = 0
+
+       -- unit map
+       local map = {
+               -- date stuff
+               y   = 60 * 60 * 24 * 366,
+               m   = 60 * 60 * 24 * 31,
+               w   = 60 * 60 * 24 * 7,
+               d   = 60 * 60 * 24,
+               h   = 60 * 60,
+               min = 60,
+
+               -- storage sizes
+               kb  = 1024,
+               mb  = 1024 * 1024,
+               gb  = 1024 * 1024 * 1024,
+
+               -- storage sizes (si)
+               kib = 1000,
+               mib = 1000 * 1000,
+               gib = 1000 * 1000 * 1000
+       }
+
+       -- parse input string
+       for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
+
+               local num = spec:gsub("[^0-9%.]+$","")
+               local spn = spec:gsub("^[0-9%.]+", "")
+
+               if map[spn] or map[spn:sub(1,1)] then
+                       val = val + num * ( map[spn] or map[spn:sub(1,1)] )
+               else
+                       val = val + num
+               end
+       end
+
+
+       return val
+end
+
+-- also register functions above in the central string class for convenience
+string.pcdata      = pcdata
+string.striptags   = striptags
+string.split       = split
+string.trim        = trim
+string.cmatch      = cmatch
+string.parse_units = parse_units
+
+
+--- Appends numerically indexed tables or single objects to a given table.
+-- @param src  Target table
+-- @param ...  Objects to insert
+-- @return             Target table
+function append(src, ...)
+       for i, a in ipairs({...}) do
+               if type(a) == "table" then
+                       for j, v in ipairs(a) do
+                               src[#src+1] = v
+                       end
+               else
+                       src[#src+1] = a
+               end
+       end
+       return src
+end
+
+--- Combines two or more numerically indexed tables and single objects into one table.
+-- @param tbl1 Table value to combine
+-- @param tbl2 Table value to combine
+-- @param ...  More tables to combine
+-- @return             Table value containing all values of given tables
+function combine(...)
+       return append({}, ...)
+end
+
+--- Checks whether the given table contains the given value.
+-- @param table        Table value
+-- @param value        Value to search within the given table
+-- @return             Boolean indicating whether the given value occurs within table
+function contains(table, value)
+       for k, v in pairs(table) do
+               if value == v then
+                       return k
+               end
+       end
+       return false
+end
+
+--- Update values in given table with the values from the second given table.
+-- Both table are - in fact - merged together.
+-- @param t                    Table which should be updated
+-- @param updates      Table containing the values to update
+-- @return                     Always nil
+function update(t, updates)
+       for k, v in pairs(updates) do
+               t[k] = v
+       end
+end
+
+--- Retrieve all keys of given associative table.
+-- @param t    Table to extract keys from
+-- @return     Sorted table containing the keys
+function keys(t)
+       local keys = { }
+       if t then
+               for k, _ in kspairs(t) do
+                       keys[#keys+1] = k
+               end
+       end
+       return keys
+end
+
+--- Clones the given object and return it's copy.
+-- @param object       Table value to clone
+-- @param deep         Boolean indicating whether to do recursive cloning
+-- @return                     Cloned table value
+function clone(object, deep)
+       local copy = {}
+
+       for k, v in pairs(object) do
+               if deep and type(v) == "table" then
+                       v = clone(v, deep)
+               end
+               copy[k] = v
+       end
+
+       return setmetatable(copy, getmetatable(object))
+end
+
+
+--- Create a dynamic table which automatically creates subtables.
+-- @return     Dynamic Table
+function dtable()
+        return setmetatable({}, { __index =
+                function(tbl, key)
+                        return rawget(tbl, key)
+                         or rawget(rawset(tbl, key, dtable()), key)
+                end
+        })
+end
+
+
+-- Serialize the contents of a table value.
+function _serialize_table(t, seen)
+       assert(not seen[t], "Recursion detected.")
+       seen[t] = true
+
+       local data  = ""
+       local idata = ""
+       local ilen  = 0
+
+       for k, v in pairs(t) do
+               if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
+                       k = serialize_data(k, seen)
+                       v = serialize_data(v, seen)
+                       data = data .. ( #data > 0 and ", " or "" ) ..
+                               '[' .. k .. '] = ' .. v
+               elseif k > ilen then
+                       ilen = k
+               end
+       end
+
+       for i = 1, ilen do
+               local v = serialize_data(t[i], seen)
+               idata = idata .. ( #idata > 0 and ", " or "" ) .. v
+       end
+
+       return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
+end
+
+--- Recursively serialize given data to lua code, suitable for restoring
+-- with loadstring().
+-- @param val  Value containing the data to serialize
+-- @return             String value containing the serialized code
+-- @see                        restore_data
+-- @see                        get_bytecode
+function serialize_data(val, seen)
+       seen = seen or setmetatable({}, {__mode="k"})
+
+       if val == nil then
+               return "nil"
+       elseif type(val) == "number" then
+               return val
+       elseif type(val) == "string" then
+               return "%q" % val
+       elseif type(val) == "boolean" then
+               return val and "true" or "false"
+       elseif type(val) == "function" then
+               return "loadstring(%q)" % get_bytecode(val)
+       elseif type(val) == "table" then
+               return "{ " .. _serialize_table(val, seen) .. " }"
+       else
+               return '"[unhandled data type:' .. type(val) .. ']"'
+       end
+end
+
+--- Restore data previously serialized with serialize_data().
+-- @param str  String containing the data to restore
+-- @return             Value containing the restored data structure
+-- @see                        serialize_data
+-- @see                        get_bytecode
+function restore_data(str)
+       return loadstring("return " .. str)()
+end
+
+
+--
+-- Byte code manipulation routines
+--
+
+--- Return the current runtime bytecode of the given data. The byte code
+-- will be stripped before it is returned.
+-- @param val  Value to return as bytecode
+-- @return             String value containing the bytecode of the given data
+function get_bytecode(val)
+       local code
+
+       if type(val) == "function" then
+               code = string.dump(val)
+       else
+               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
+       end
+
+       return code -- and strip_bytecode(code)
+end
+
+--- Strips unnescessary lua bytecode from given string. Information like line
+-- numbers and debugging numbers will be discarded. Original version by
+-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+-- @param code String value containing the original lua byte code
+-- @return             String value containing the stripped lua byte code
+function strip_bytecode(code)
+       local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
+       local subint
+       if endian == 1 then
+               subint = function(code, i, l)
+                       local val = 0
+                       for n = l, 1, -1 do
+                               val = val * 256 + code:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       else
+               subint = function(code, i, l)
+                       local val = 0
+                       for n = 1, l, 1 do
+                               val = val * 256 + code:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       end
+
+       local function strip_function(code)
+               local count, offset = subint(code, 1, size)
+               local stripped = { string.rep("\0", size) }
+               local dirty = offset + count
+               offset = offset + count + int * 2 + 4
+               offset = offset + int + subint(code, offset, int) * ins
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       local t
+                       t, offset = subint(code, offset, 1)
+                       if t == 1 then
+                               offset = offset + 1
+                       elseif t == 4 then
+                               offset = offset + size + subint(code, offset, size)
+                       elseif t == 3 then
+                               offset = offset + num
+                       elseif t == 254 or t == 9 then
+                               offset = offset + lnum
+                       end
+               end
+               count, offset = subint(code, offset, int)
+               stripped[#stripped+1] = code:sub(dirty, offset - 1)
+               for n = 1, count do
+                       local proto, off = strip_function(code:sub(offset, -1))
+                       stripped[#stripped+1] = proto
+                       offset = offset + off - 1
+               end
+               offset = offset + subint(code, offset, int) * int + int
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(code, offset, size) + size + int * 2
+               end
+               count, offset = subint(code, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(code, offset, size) + size
+               end
+               stripped[#stripped+1] = string.rep("\0", int * 3)
+               return table.concat(stripped), offset
+       end
+
+       return code:sub(1,12) .. strip_function(code:sub(13,-1))
+end
+
+
+--
+-- Sorting iterator functions
+--
+
+function _sortiter( t, f )
+       local keys = { }
+
+       local k, v
+       for k, v in pairs(t) do
+               keys[#keys+1] = k
+       end
+
+       local _pos = 0
+
+       table.sort( keys, f )
+
+       return function()
+               _pos = _pos + 1
+               if _pos <= #keys then
+                       return keys[_pos], t[keys[_pos]], _pos
+               end
+       end
+end
+
+--- Return a key, value iterator which returns the values sorted according to
+-- the provided callback function.
+-- @param t    The table to iterate
+-- @param f A callback function to decide the order of elements
+-- @return     Function value containing the corresponding iterator
+function spairs(t,f)
+       return _sortiter( t, f )
+end
+
+--- Return a key, value iterator for the given table.
+-- The table pairs are sorted by key.
+-- @param t    The table to iterate
+-- @return     Function value containing the corresponding iterator
+function kspairs(t)
+       return _sortiter( t )
+end
+
+--- Return a key, value iterator for the given table.
+-- The table pairs are sorted by value.
+-- @param t    The table to iterate
+-- @return     Function value containing the corresponding iterator
+function vspairs(t)
+       return _sortiter( t, function (a,b) return t[a] < t[b] end )
+end
+
+
+--
+-- System utility functions
+--
+
+--- Test whether the current system is operating in big endian mode.
+-- @return     Boolean value indicating whether system is big endian
+function bigendian()
+       return string.byte(string.dump(function() end), 7) == 0
+end
+
+--- Execute given commandline and gather stdout.
+-- @param command      String containing command to execute
+-- @return                     String containing the command's stdout
+function exec(command)
+       local pp   = io.popen(command)
+       local data = pp:read("*a")
+       pp:close()
+
+       return data
+end
+
+--- Return a line-buffered iterator over the output of given command.
+-- @param command      String containing the command to execute
+-- @return                     Iterator
+function execi(command)
+       local pp = io.popen(command)
+
+       return pp and function()
+               local line = pp:read()
+
+               if not line then
+                       pp:close()
+               end
+
+               return line
+       end
+end
+
+-- Deprecated
+function execl(command)
+       local pp   = io.popen(command)
+       local line = ""
+       local data = {}
+
+       while true do
+               line = pp:read()
+               if (line == nil) then break end
+               data[#data+1] = line
+       end
+       pp:close()
+
+       return data
+end
+
+--- Returns the absolute path to LuCI base directory.
+-- @return             String containing the directory path
+function libpath()
+       return require "nixio.fs".dirname(ldebug.__file__)
+end
+
+
+--
+-- Coroutine safe xpcall and pcall versions modified for Luci
+-- original version:
+-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
+--
+-- Copyright Â© 2005 Kepler Project.
+-- 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.
+
+local performResume, handleReturnValue
+local oldpcall, oldxpcall = pcall, xpcall
+coxpt = {}
+setmetatable(coxpt, {__mode = "kv"})
+
+-- Identity function for copcall
+local function copcall_id(trace, ...)
+  return ...
+end
+
+--- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
+-- @param f            Lua function to be called protected
+-- @param err  Custom error handler
+-- @param ...  Parameters passed to the function
+-- @return             A boolean whether the function call succeeded and the return
+--                             values of either the function or the error handler
+function coxpcall(f, err, ...)
+       local res, co = oldpcall(coroutine.create, f)
+       if not res then
+               local params = {...}
+               local newf = function() return f(unpack(params)) end
+               co = coroutine.create(newf)
+       end
+       local c = coroutine.running()
+       coxpt[co] = coxpt[c] or c or 0
+
+       return performResume(err, co, ...)
+end
+
+--- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
+-- @param f            Lua function to be called protected
+-- @param ...  Parameters passed to the function
+-- @return             A boolean whether the function call succeeded and the returns
+--                             values of the function or the error object
+function copcall(f, ...)
+       return coxpcall(f, copcall_id, ...)
+end
+
+-- Handle return value of protected call
+function handleReturnValue(err, co, status, ...)
+       if not status then
+               return false, err(debug.traceback(co, (...)), ...)
+       end
+
+       if coroutine.status(co) ~= 'suspended' then
+               return true, ...
+       end
+
+       return performResume(err, co, coroutine.yield(...))
+end
+
+-- Resume execution of protected function call
+function performResume(err, co, ...)
+       return handleReturnValue(err, co, coroutine.resume(co, ...))
+end
diff --git a/modules/base/luasrc/version.lua b/modules/base/luasrc/version.lua
new file mode 100644 (file)
index 0000000..9e5cb71
--- /dev/null
@@ -0,0 +1,12 @@
+--[[
+LuCI - Lua Configuration Interface
+Version definition - do not edit this file
+]]--
+
+module "luci.version"
+
+distname    = "Host System"
+distversion = "SDK"
+
+luciname    = "LuCI"
+luciversion = "SVN"
diff --git a/modules/base/luasrc/view/error404.htm b/modules/base/luasrc/view/error404.htm
new file mode 100644 (file)
index 0000000..813604d
--- /dev/null
@@ -0,0 +1,19 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+-%>
+<%+header%>
+<h2><a id="content" name="content">404 <%:Not Found%></a></h2>
+<p><%:Sorry, the object you requested was not found.%></p>
+<tt><%:Unable to dispatch%>: <%=luci.http.request.env.PATH_INFO%></tt>
+<%+footer%>
diff --git a/modules/base/luasrc/view/error500.htm b/modules/base/luasrc/view/error500.htm
new file mode 100644 (file)
index 0000000..14ba041
--- /dev/null
@@ -0,0 +1,19 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+-%>
+<%+header%>
+<h2><a id="content" name="content">500 <%:Internal Server Error%></a></h2>
+<p><%:Sorry, the server encountered an unexpected error.%></p>
+<pre class="error500"><%=message%></pre>
+<%+footer%>
diff --git a/modules/base/luasrc/view/footer.htm b/modules/base/luasrc/view/footer.htm
new file mode 100644 (file)
index 0000000..6c6d214
--- /dev/null
@@ -0,0 +1,15 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+-%>
+<% include("themes/" .. theme .. "/footer") %>
\ No newline at end of file
diff --git a/modules/base/luasrc/view/header.htm b/modules/base/luasrc/view/header.htm
new file mode 100644 (file)
index 0000000..77018b1
--- /dev/null
@@ -0,0 +1,21 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+-%>
+
+<%
+       if not luci.dispatcher.context.template_header_sent then
+               include("themes/" .. theme .. "/header")
+               luci.dispatcher.context.template_header_sent = true
+       end
+%>
diff --git a/modules/base/luasrc/view/indexer.htm b/modules/base/luasrc/view/indexer.htm
new file mode 100644 (file)
index 0000000..c628289
--- /dev/null
@@ -0,0 +1,15 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+$Id$
+
+-%>
+<% include("themes/" .. theme .. "/indexer") %>
\ No newline at end of file
diff --git a/modules/base/luasrc/view/sysauth.htm b/modules/base/luasrc/view/sysauth.htm
new file mode 100644 (file)
index 0000000..7c39f0d
--- /dev/null
@@ -0,0 +1,80 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008-2012 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+-%>
+
+<%+header%>
+
+<form method="post" action="<%=pcdata(luci.http.getenv("REQUEST_URI"))%>">
+       <div class="cbi-map">
+               <h2><a id="content" name="content"><%:Authorization Required%></a></h2>
+               <div class="cbi-map-descr">
+                       <%:Please enter your username and password.%>
+                       <%- if fuser then %>
+                       <div class="error"><%:Invalid username and/or password! Please try again.%></div>
+                       <br />
+                       <% end -%>
+               </div>
+               <fieldset class="cbi-section"><fieldset class="cbi-section-node">
+                       <div class="cbi-value">
+                               <label class="cbi-value-title"><%:Username%></label>
+                               <div class="cbi-value-field">
+                                       <input class="cbi-input-user" type="text" name="username" value="<%=duser%>" />
+                               </div>
+                       </div>
+                       <div class="cbi-value cbi-value-last">
+                               <label class="cbi-value-title"><%:Password%></label>
+                               <div class="cbi-value-field">
+                                       <input id="focus_password" class="cbi-input-password" type="password" name="password" />
+                               </div>
+                       </div>
+               </fieldset></fieldset>
+       </div>
+
+       <div>
+               <input type="submit" value="<%:Login%>" class="cbi-button cbi-button-apply" />
+               <input type="reset" value="<%:Reset%>" class="cbi-button cbi-button-reset" />
+       </div>
+</form>
+<script type="text/javascript">//<![CDATA[
+       var input = document.getElementById('focus_password');
+       if (input)
+               input.focus();
+//]]></script>
+
+<%
+local uci  = require "luci.model.uci".cursor()
+local fs  = require "nixio.fs"
+local https_key = uci:get("uhttpd", "main", "key")
+local https_port = uci:get("uhttpd", "main", "listen_https")
+if type(https_port) == "table" then
+       https_port = https_port[1]
+end
+
+if https_port and fs.access(https_key) then
+       https_port = https_port:match("(%d+)$")
+%>
+
+<script type="text/javascript">//<![CDATA[
+       if (document.location.protocol != 'https:') {
+               var url = 'https://' + window.location.hostname + ':' + '<%=https_port%>' + window.location.pathname;
+               var img=new Image;
+               img.onload=function(){window.location = url};
+               img.src='https://' + window.location.hostname + ':' + '<%=https_port%>' + '<%=resource%>/cbi/up.gif?' + Math.random();;
+               setTimeout(function(){
+                       img.src=''
+               }, 5000);
+       }
+//]]></script>
+
+<% end %>
+
+<%+footer%>
diff --git a/modules/base/root/etc/config/ucitrack b/modules/base/root/etc/config/ucitrack
new file mode 100644 (file)
index 0000000..04467f4
--- /dev/null
@@ -0,0 +1,53 @@
+config network
+       option init network
+       list affects dhcp
+       list affects radvd
+
+config wireless
+       list affects network
+
+config firewall
+       option init firewall
+       list affects luci-splash
+       list affects qos
+       list affects miniupnpd
+
+config olsr
+       option init olsrd
+
+config dhcp
+       option init dnsmasq
+
+config dropbear
+       option init dropbear
+
+config httpd
+       option init httpd
+
+config fstab
+       option init fstab
+
+config qos
+       option init qos
+
+config system
+       option init led
+       list affects luci_statistics
+
+config luci_splash
+       option init luci_splash
+
+config upnpd
+       option init miniupnpd
+
+config ntpclient
+       option init ntpclient
+
+config samba
+       option init samba
+
+config tinyproxy
+       option init tinyproxy
+
+config 6relayd
+       option init 6relayd
diff --git a/modules/base/root/root/etc/config/luci b/modules/base/root/root/etc/config/luci
new file mode 100644 (file)
index 0000000..c503a8f
--- /dev/null
@@ -0,0 +1,24 @@
+config core main
+       option lang auto
+       option mediaurlbase /luci-static/openwrt.org
+       option resourcebase /luci-static/resources
+       
+config extern flash_keep
+       option uci              "/etc/config/"
+       option dropbear "/etc/dropbear/"
+       option openvpn  "/etc/openvpn/"
+       option passwd   "/etc/passwd"
+       option opkg             "/etc/opkg.conf"
+       option firewall "/etc/firewall.user"
+       option uploads  "/lib/uci/upload/"
+       
+config internal languages
+       
+config internal sauth
+       option sessionpath "/tmp/luci-sessions"
+       option sessiontime 3600
+       
+config internal ccache
+       option enable 1
+               
+config internal themes
diff --git a/modules/base/root/root/lib/uci/upload/.gitignore b/modules/base/root/root/lib/uci/upload/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/modules/base/root/sbin/luci-reload b/modules/base/root/sbin/luci-reload
new file mode 100755 (executable)
index 0000000..cc41da2
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+. /lib/functions.sh
+
+apply_config() {
+       config_get init "$1" init
+       config_get exec "$1" exec
+       config_get test "$1" test
+
+       echo "$2" > "/var/run/luci-reload-status"
+
+       [ -n "$init" ] && reload_init "$2" "$init" "$test"
+       [ -n "$exec" ] && reload_exec "$2" "$exec" "$test"
+}
+
+reload_exec() {
+       local service="$1"
+       local ok="$3"
+       set -- $2
+       local cmd="$1"; shift
+       
+       [ -x "$cmd" ] && {
+               echo "Reloading $service... "
+               ( $cmd "$@" ) 2>/dev/null 1>&2
+               [ -n "$ok" -a "$?" != "$ok" ] && echo '!!! Failed to reload' $service '!!!'
+       }
+}
+
+reload_init() {
+       [ -x /etc/init.d/$2 ] && /etc/init.d/$2 enabled && {
+               echo "Reloading $1... "
+               /etc/init.d/$2 reload >/dev/null 2>&1
+               [ -n "$3" -a "$?" != "$3" ] && echo '!!! Failed to reload' $1 '!!!'
+       }
+}
+
+lock "/var/run/luci-reload"
+
+config_load ucitrack
+
+for i in $*; do
+       config_foreach apply_config $i $i
+done
+
+rm -f "/var/run/luci-reload-status"
+lock -u "/var/run/luci-reload"
diff --git a/modules/base/root/www/index.html b/modules/base/root/www/index.html
new file mode 100644 (file)
index 0000000..0a7238b
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
+</head>
+<body style="background-color: black">
+<a style="color: white; text-decoration: none" href="/cgi-bin/luci">LuCI - Lua Configuration Interface</a>
+</body>
+</html>
diff --git a/modules/base/src/po2lmo.c b/modules/base/src/po2lmo.c
new file mode 100644 (file)
index 0000000..0da792b
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * lmo - Lua Machine Objects - PO to LMO conversion tool
+ *
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "template_lmo.h"
+
+static void die(const char *msg)
+{
+       fprintf(stderr, "Error: %s\n", msg);
+       exit(1);
+}
+
+static void usage(const char *name)
+{
+       fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
+       exit(1);
+}
+
+static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       if( fwrite(ptr, size, nmemb, stream) == 0 )
+               die("Failed to write stdout");
+}
+
+static int extract_string(const char *src, char *dest, int len)
+{
+       int pos = 0;
+       int esc = 0;
+       int off = -1;
+
+       for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
+       {
+               if( (off == -1) && (src[pos] == '"') )
+               {
+                       off = pos + 1;
+               }
+               else if( off >= 0 )
+               {
+                       if( esc == 1 )
+                       {
+                               switch (src[pos])
+                               {
+                               case '"':
+                               case '\\':
+                                       off++;
+                                       break;
+                               }
+                               dest[pos-off] = src[pos];
+                               esc = 0;
+                       }
+                       else if( src[pos] == '\\' )
+                       {
+                               dest[pos-off] = src[pos];
+                               esc = 1;
+                       }
+                       else if( src[pos] != '"' )
+                       {
+                               dest[pos-off] = src[pos];
+                       }
+                       else
+                       {
+                               dest[pos-off] = '\0';
+                               break;
+                       }
+               }
+       }
+
+       return (off > -1) ? strlen(dest) : -1;
+}
+
+static int cmp_index(const void *a, const void *b)
+{
+       uint32_t x = ((const lmo_entry_t *)a)->key_id;
+       uint32_t y = ((const lmo_entry_t *)b)->key_id;
+
+       if (x < y)
+               return -1;
+       else if (x > y)
+               return 1;
+
+       return 0;
+}
+
+static void print_uint32(uint32_t x, FILE *out)
+{
+       uint32_t y = htonl(x);
+       print(&y, sizeof(uint32_t), 1, out);
+}
+
+static void print_index(void *array, int n, FILE *out)
+{
+       lmo_entry_t *e;
+
+       qsort(array, n, sizeof(*e), cmp_index);
+
+       for (e = array; n > 0; n--, e++)
+       {
+               print_uint32(e->key_id, out);
+               print_uint32(e->val_id, out);
+               print_uint32(e->offset, out);
+               print_uint32(e->length, out);
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       char line[4096];
+       char key[4096];
+       char val[4096];
+       char tmp[4096];
+       int state  = 0;
+       int offset = 0;
+       int length = 0;
+       int n_entries = 0;
+       void *array = NULL;
+       lmo_entry_t *entry = NULL;
+       uint32_t key_id, val_id;
+
+       FILE *in;
+       FILE *out;
+
+       if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
+               usage(argv[0]);
+
+       memset(line, 0, sizeof(key));
+       memset(key, 0, sizeof(val));
+       memset(val, 0, sizeof(val));
+
+       while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
+       {
+               if( state == 0 && strstr(line, "msgid \"") == line )
+               {
+                       switch(extract_string(line, key, sizeof(key)))
+                       {
+                               case -1:
+                                       die("Syntax error in msgid");
+                               case 0:
+                                       state = 1;
+                                       break;
+                               default:
+                                       state = 2;
+                       }
+               }
+               else if( state == 1 || state == 2 )
+               {
+                       if( strstr(line, "msgstr \"") == line || state == 2 )
+                       {
+                               switch(extract_string(line, val, sizeof(val)))
+                               {
+                                       case -1:
+                                               state = 4;
+                                               break;
+                                       default:
+                                               state = 3;
+                               }
+                       }
+                       else
+                       {
+                               switch(extract_string(line, tmp, sizeof(tmp)))
+                               {
+                                       case -1:
+                                               state = 2;
+                                               break;
+                                       default:
+                                               strcat(key, tmp);
+                               }
+                       }
+               }
+               else if( state == 3 )
+               {
+                       switch(extract_string(line, tmp, sizeof(tmp)))
+                       {
+                               case -1:
+                                       state = 4;
+                                       break;
+                               default:
+                                       strcat(val, tmp);
+                       }
+               }
+
+               if( state == 4 )
+               {
+                       if( strlen(key) > 0 && strlen(val) > 0 )
+                       {
+                               key_id = sfh_hash(key, strlen(key));
+                               val_id = sfh_hash(val, strlen(val));
+
+                               if( key_id != val_id )
+                               {
+                                       n_entries++;
+                                       array = realloc(array, n_entries * sizeof(lmo_entry_t));
+                                       entry = (lmo_entry_t *)array + n_entries - 1;
+
+                                       if (!array)
+                                               die("Out of memory");
+
+                                       entry->key_id = key_id;
+                                       entry->val_id = val_id;
+                                       entry->offset = offset;
+                                       entry->length = strlen(val);
+
+                                       length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
+
+                                       print(val, length, 1, out);
+                                       offset += length;
+                               }
+                       }
+
+                       state = 0;
+                       memset(key, 0, sizeof(key));
+                       memset(val, 0, sizeof(val));
+               }
+
+               memset(line, 0, sizeof(line));
+       }
+
+       print_index(array, n_entries, out);
+
+       if( offset > 0 )
+       {
+               print_uint32(offset, out);
+               fsync(fileno(out));
+               fclose(out);
+       }
+       else
+       {
+               fclose(out);
+               unlink(argv[2]);
+       }
+
+       fclose(in);
+       return(0);
+}
diff --git a/modules/base/src/template_lmo.c b/modules/base/src/template_lmo.c
new file mode 100644 (file)
index 0000000..27205a7
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * lmo - Lua Machine Objects - Base functions
+ *
+ *   Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "template_lmo.h"
+
+/*
+ * Hash function from http://www.azillionmonkeys.com/qed/hash.html
+ * Copyright (C) 2004-2008 by Paul Hsieh
+ */
+
+uint32_t sfh_hash(const char *data, int len)
+{
+       uint32_t hash = len, tmp;
+       int rem;
+
+       if (len <= 0 || data == NULL) return 0;
+
+       rem = len & 3;
+       len >>= 2;
+
+       /* Main loop */
+       for (;len > 0; len--) {
+               hash  += sfh_get16(data);
+               tmp    = (sfh_get16(data+2) << 11) ^ hash;
+               hash   = (hash << 16) ^ tmp;
+               data  += 2*sizeof(uint16_t);
+               hash  += hash >> 11;
+       }
+
+       /* Handle end cases */
+       switch (rem) {
+               case 3: hash += sfh_get16(data);
+                       hash ^= hash << 16;
+                       hash ^= data[sizeof(uint16_t)] << 18;
+                       hash += hash >> 11;
+                       break;
+               case 2: hash += sfh_get16(data);
+                       hash ^= hash << 11;
+                       hash += hash >> 17;
+                       break;
+               case 1: hash += *data;
+                       hash ^= hash << 10;
+                       hash += hash >> 1;
+       }
+
+       /* Force "avalanching" of final 127 bits */
+       hash ^= hash << 3;
+       hash += hash >> 5;
+       hash ^= hash << 4;
+       hash += hash >> 17;
+       hash ^= hash << 25;
+       hash += hash >> 6;
+
+       return hash;
+}
+
+uint32_t lmo_canon_hash(const char *str, int len)
+{
+       char res[4096];
+       char *ptr, prev;
+       int off;
+
+       if (!str || len >= sizeof(res))
+               return 0;
+
+       for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
+       {
+               if (isspace(*str))
+               {
+                       if (!isspace(prev))
+                               *ptr++ = ' ';
+               }
+               else
+               {
+                       *ptr++ = *str;
+               }
+       }
+
+       if ((ptr > res) && isspace(*(ptr-1)))
+               ptr--;
+
+       return sfh_hash(res, ptr - res);
+}
+
+lmo_archive_t * lmo_open(const char *file)
+{
+       int in = -1;
+       uint32_t idx_offset = 0;
+       struct stat s;
+
+       lmo_archive_t *ar = NULL;
+
+       if (stat(file, &s) == -1)
+               goto err;
+
+       if ((in = open(file, O_RDONLY)) == -1)
+               goto err;
+
+       if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
+       {
+               memset(ar, 0, sizeof(*ar));
+
+               ar->fd     = in;
+               ar->size = s.st_size;
+
+               fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
+
+               if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
+                       goto err;
+
+               idx_offset = ntohl(*((const uint32_t *)
+                                    (ar->mmap + ar->size - sizeof(uint32_t))));
+
+               if (idx_offset >= ar->size)
+                       goto err;
+
+               ar->index  = (lmo_entry_t *)(ar->mmap + idx_offset);
+               ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
+               ar->end    = ar->mmap + ar->size;
+
+               return ar;
+       }
+
+err:
+       if (in > -1)
+               close(in);
+
+       if (ar != NULL)
+       {
+               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+                       munmap(ar->mmap, ar->size);
+
+               free(ar);
+       }
+
+       return NULL;
+}
+
+void lmo_close(lmo_archive_t *ar)
+{
+       if (ar != NULL)
+       {
+               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+                       munmap(ar->mmap, ar->size);
+
+               close(ar->fd);
+               free(ar);
+
+               ar = NULL;
+       }
+}
+
+
+lmo_catalog_t *_lmo_catalogs = NULL;
+lmo_catalog_t *_lmo_active_catalog = NULL;
+
+int lmo_load_catalog(const char *lang, const char *dir)
+{
+       DIR *dh = NULL;
+       char pattern[16];
+       char path[PATH_MAX];
+       struct dirent *de = NULL;
+
+       lmo_archive_t *ar = NULL;
+       lmo_catalog_t *cat = NULL;
+
+       if (!lmo_change_catalog(lang))
+               return 0;
+
+       if (!dir || !(dh = opendir(dir)))
+               goto err;
+
+       if (!(cat = malloc(sizeof(*cat))))
+               goto err;
+
+       memset(cat, 0, sizeof(*cat));
+
+       snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
+       snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
+
+       while ((de = readdir(dh)) != NULL)
+       {
+               if (!fnmatch(pattern, de->d_name, 0))
+               {
+                       snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
+                       ar = lmo_open(path);
+
+                       if (ar)
+                       {
+                               ar->next = cat->archives;
+                               cat->archives = ar;
+                       }
+               }
+       }
+
+       closedir(dh);
+
+       cat->next = _lmo_catalogs;
+       _lmo_catalogs = cat;
+
+       if (!_lmo_active_catalog)
+               _lmo_active_catalog = cat;
+
+       return 0;
+
+err:
+       if (dh) closedir(dh);
+       if (cat) free(cat);
+
+       return -1;
+}
+
+int lmo_change_catalog(const char *lang)
+{
+       lmo_catalog_t *cat;
+
+       for (cat = _lmo_catalogs; cat; cat = cat->next)
+       {
+               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+               {
+                       _lmo_active_catalog = cat;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
+{
+       unsigned int m, l, r;
+       uint32_t k;
+
+       l = 0;
+       r = ar->length - 1;
+
+       while (1)
+       {
+               m = l + ((r - l) / 2);
+
+               if (r < l)
+                       break;
+
+               k = ntohl(ar->index[m].key_id);
+
+               if (k == hash)
+                       return &ar->index[m];
+
+               if (k > hash)
+               {
+                       if (!m)
+                               break;
+
+                       r = m - 1;
+               }
+               else
+               {
+                       l = m + 1;
+               }
+       }
+
+       return NULL;
+}
+
+int lmo_translate(const char *key, int keylen, char **out, int *outlen)
+{
+       uint32_t hash;
+       lmo_entry_t *e;
+       lmo_archive_t *ar;
+
+       if (!key || !_lmo_active_catalog)
+               return -2;
+
+       hash = lmo_canon_hash(key, keylen);
+
+       for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
+       {
+               if ((e = lmo_find_entry(ar, hash)) != NULL)
+               {
+                       *out = ar->mmap + ntohl(e->offset);
+                       *outlen = ntohl(e->length);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+void lmo_close_catalog(const char *lang)
+{
+       lmo_archive_t *ar, *next;
+       lmo_catalog_t *cat, *prev;
+
+       for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
+       {
+               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+               {
+                       if (prev)
+                               prev->next = cat->next;
+                       else
+                               _lmo_catalogs = cat->next;
+
+                       for (ar = cat->archives; ar; ar = next)
+                       {
+                               next = ar->next;
+                               lmo_close(ar);
+                       }
+
+                       free(cat);
+                       break;
+               }
+       }
+}
diff --git a/modules/base/src/template_lmo.h b/modules/base/src/template_lmo.h
new file mode 100644 (file)
index 0000000..57f59aa
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * lmo - Lua Machine Objects - General header
+ *
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _TEMPLATE_LMO_H_
+#define _TEMPLATE_LMO_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+
+#if (defined(__GNUC__) && defined(__i386__))
+#define sfh_get16(d) (*((const uint16_t *) (d)))
+#else
+#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+                                          +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+
+struct lmo_entry {
+       uint32_t key_id;
+       uint32_t val_id;
+       uint32_t offset;
+       uint32_t length;
+} __attribute__((packed));
+
+typedef struct lmo_entry lmo_entry_t;
+
+
+struct lmo_archive {
+       int         fd;
+       int             length;
+       uint32_t    size;
+       lmo_entry_t *index;
+       char        *mmap;
+       char            *end;
+       struct lmo_archive *next;
+};
+
+typedef struct lmo_archive lmo_archive_t;
+
+
+struct lmo_catalog {
+       char lang[6];
+       struct lmo_archive *archives;
+       struct lmo_catalog *next;
+};
+
+typedef struct lmo_catalog lmo_catalog_t;
+
+
+uint32_t sfh_hash(const char *data, int len);
+uint32_t lmo_canon_hash(const char *data, int len);
+
+lmo_archive_t * lmo_open(const char *file);
+void lmo_close(lmo_archive_t *ar);
+
+
+extern lmo_catalog_t *_lmo_catalogs;
+extern lmo_catalog_t *_lmo_active_catalog;
+
+int lmo_load_catalog(const char *lang, const char *dir);
+int lmo_change_catalog(const char *lang);
+int lmo_translate(const char *key, int keylen, char **out, int *outlen);
+void lmo_close_catalog(const char *lang);
+
+#endif
diff --git a/modules/base/src/template_lualib.c b/modules/base/src/template_lualib.c
new file mode 100644 (file)
index 0000000..0d43641
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * LuCI Template - Lua binding
+ *
+ *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "template_lualib.h"
+
+int template_L_parse(lua_State *L)
+{
+       const char *file = luaL_checkstring(L, 1);
+       struct template_parser *parser = template_open(file);
+       int lua_status, rv;
+
+       if (!parser)
+       {
+               lua_pushnil(L);
+               lua_pushinteger(L, errno);
+               lua_pushstring(L, strerror(errno));
+               return 3;
+       }
+
+       lua_status = lua_load(L, template_reader, parser, file);
+
+       if (lua_status == 0)
+               rv = 1;
+       else
+               rv = template_error(L, parser);
+
+       template_close(parser);
+
+       return rv;
+}
+
+int template_L_utf8(lua_State *L)
+{
+       size_t len = 0;
+       const char *str = luaL_checklstring(L, 1, &len);
+       char *res = utf8(str, len);
+
+       if (res != NULL)
+       {
+               lua_pushstring(L, res);
+               free(res);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+int template_L_pcdata(lua_State *L)
+{
+       size_t len = 0;
+       const char *str = luaL_checklstring(L, 1, &len);
+       char *res = pcdata(str, len);
+
+       if (res != NULL)
+       {
+               lua_pushstring(L, res);
+               free(res);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+int template_L_striptags(lua_State *L)
+{
+       size_t len = 0;
+       const char *str = luaL_checklstring(L, 1, &len);
+       char *res = striptags(str, len);
+
+       if (res != NULL)
+       {
+               lua_pushstring(L, res);
+               free(res);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static int template_L_load_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       const char *dir  = luaL_optstring(L, 2, NULL);
+       lua_pushboolean(L, !lmo_load_catalog(lang, dir));
+       return 1;
+}
+
+static int template_L_close_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       lmo_close_catalog(lang);
+       return 0;
+}
+
+static int template_L_change_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       lua_pushboolean(L, !lmo_change_catalog(lang));
+       return 1;
+}
+
+static int template_L_translate(lua_State *L) {
+       size_t len;
+       char *tr;
+       int trlen;
+       const char *key = luaL_checklstring(L, 1, &len);
+
+       switch (lmo_translate(key, len, &tr, &trlen))
+       {
+               case 0:
+                       lua_pushlstring(L, tr, trlen);
+                       return 1;
+
+               case -1:
+                       return 0;
+       }
+
+       lua_pushnil(L);
+       lua_pushstring(L, "no catalog loaded");
+       return 2;
+}
+
+static int template_L_hash(lua_State *L) {
+       size_t len;
+       const char *key = luaL_checklstring(L, 1, &len);
+       lua_pushinteger(L, sfh_hash(key, len));
+       return 1;
+}
+
+
+/* module table */
+static const luaL_reg R[] = {
+       { "parse",                              template_L_parse },
+       { "utf8",                               template_L_utf8 },
+       { "pcdata",                             template_L_pcdata },
+       { "striptags",                  template_L_striptags },
+       { "load_catalog",               template_L_load_catalog },
+       { "close_catalog",              template_L_close_catalog },
+       { "change_catalog",             template_L_change_catalog },
+       { "translate",                  template_L_translate },
+       { "hash",                               template_L_hash },
+       { NULL,                                 NULL }
+};
+
+LUALIB_API int luaopen_luci_template_parser(lua_State *L) {
+       luaL_register(L, TEMPLATE_LUALIB_META, R);
+       return 1;
+}
diff --git a/modules/base/src/template_lualib.h b/modules/base/src/template_lualib.h
new file mode 100644 (file)
index 0000000..1b659be
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * LuCI Template - Lua library header
+ *
+ *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _TEMPLATE_LUALIB_H_
+#define _TEMPLATE_LUALIB_H_
+
+#include "template_parser.h"
+#include "template_utils.h"
+#include "template_lmo.h"
+
+#define TEMPLATE_LUALIB_META  "template.parser"
+
+LUALIB_API int luaopen_luci_template_parser(lua_State *L);
+
+#endif
diff --git a/modules/base/src/template_parser.c b/modules/base/src/template_parser.c
new file mode 100644 (file)
index 0000000..6054451
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * LuCI Template - Parser implementation
+ *
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "template_parser.h"
+#include "template_utils.h"
+#include "template_lmo.h"
+
+
+/* leading and trailing code for different types */
+const char *gen_code[9][2] = {
+       { NULL,                                 NULL                    },
+       { "write(\"",                   "\")"                   },
+       { NULL,                                 NULL                    },
+       { "write(tostring(",    " or \"\"))"    },
+       { "include(\"",                 "\")"                   },
+       { "write(\"",                   "\")"                   },
+       { "write(\"",                   "\")"                   },
+       { NULL,                                 " "                             },
+       { NULL,                                 NULL                    },
+};
+
+/* Simple strstr() like function that takes len arguments for both haystack and needle. */
+static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
+{
+       int match = 0;
+       int i, j;
+
+       for( i = 0; i < hslen; i++ )
+       {
+               if( haystack[i] == needle[0] )
+               {
+                       match = ((ndlen == 1) || ((i + ndlen) <= hslen));
+
+                       for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ )
+                       {
+                               if( haystack[i+j] != needle[j] )
+                               {
+                                       match = 0;
+                                       break;
+                               }
+                       }
+
+                       if( match )
+                               return &haystack[i];
+               }
+       }
+
+       return NULL;
+}
+
+struct template_parser * template_open(const char *file)
+{
+       struct stat s;
+       static struct template_parser *parser;
+
+       if (!(parser = malloc(sizeof(*parser))))
+               goto err;
+
+       memset(parser, 0, sizeof(*parser));
+       parser->fd = -1;
+       parser->file = file;
+
+       if (stat(file, &s))
+               goto err;
+
+       if ((parser->fd = open(file, O_RDONLY)) < 0)
+               goto err;
+
+       parser->size = s.st_size;
+       parser->mmap = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
+                                               parser->fd, 0);
+
+       if (parser->mmap != MAP_FAILED)
+       {
+               parser->off = parser->mmap;
+               parser->cur_chunk.type = T_TYPE_INIT;
+               parser->cur_chunk.s    = parser->mmap;
+               parser->cur_chunk.e    = parser->mmap;
+
+               return parser;
+       }
+
+err:
+       template_close(parser);
+       return NULL;
+}
+
+void template_close(struct template_parser *parser)
+{
+       if (!parser)
+               return;
+
+       if (parser->gc != NULL)
+               free(parser->gc);
+
+       if ((parser->mmap != NULL) && (parser->mmap != MAP_FAILED))
+               munmap(parser->mmap, parser->size);
+
+       if (parser->fd >= 0)
+               close(parser->fd);
+
+       free(parser);
+}
+
+void template_text(struct template_parser *parser, const char *e)
+{
+       const char *s = parser->off;
+
+       if (s < (parser->mmap + parser->size))
+       {
+               if (parser->strip_after)
+               {
+                       while ((s <= e) && isspace(*s))
+                               s++;
+               }
+
+               parser->cur_chunk.type = T_TYPE_TEXT;
+       }
+       else
+       {
+               parser->cur_chunk.type = T_TYPE_EOF;
+       }
+
+       parser->cur_chunk.line = parser->line;
+       parser->cur_chunk.s = s;
+       parser->cur_chunk.e = e;
+}
+
+void template_code(struct template_parser *parser, const char *e)
+{
+       const char *s = parser->off;
+
+       parser->strip_before = 0;
+       parser->strip_after = 0;
+
+       if (*s == '-')
+       {
+               parser->strip_before = 1;
+               for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
+       }
+
+       if (*(e-1) == '-')
+       {
+               parser->strip_after = 1;
+               for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
+       }
+
+       switch (*s)
+       {
+               /* comment */
+               case '#':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_COMMENT;
+                       break;
+
+               /* include */
+               case '+':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_INCLUDE;
+                       break;
+
+               /* translate */
+               case ':':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_I18N;
+                       break;
+
+               /* translate raw */
+               case '_':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_I18N_RAW;
+                       break;
+
+               /* expr */
+               case '=':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_EXPR;
+                       break;
+
+               /* code */
+               default:
+                       parser->cur_chunk.type = T_TYPE_CODE;
+                       break;
+       }
+
+       parser->cur_chunk.line = parser->line;
+       parser->cur_chunk.s = s;
+       parser->cur_chunk.e = e;
+}
+
+static const char *
+template_format_chunk(struct template_parser *parser, size_t *sz)
+{
+       const char *s, *p;
+       const char *head, *tail;
+       struct template_chunk *c = &parser->prv_chunk;
+       struct template_buffer *buf;
+
+       *sz = 0;
+       s = parser->gc = NULL;
+
+       if (parser->strip_before && c->type == T_TYPE_TEXT)
+       {
+               while ((c->e > c->s) && isspace(*(c->e - 1)))
+                       c->e--;
+       }
+
+       /* empty chunk */
+       if (c->s == c->e)
+       {
+               if (c->type == T_TYPE_EOF)
+               {
+                       *sz = 0;
+                       s = NULL;
+               }
+               else
+               {
+                       *sz = 1;
+                       s = " ";
+               }
+       }
+
+       /* format chunk */
+       else if ((buf = buf_init(c->e - c->s)) != NULL)
+       {
+               if ((head = gen_code[c->type][0]) != NULL)
+                       buf_append(buf, head, strlen(head));
+
+               switch (c->type)
+               {
+                       case T_TYPE_TEXT:
+                               luastr_escape(buf, c->s, c->e - c->s, 0);
+                               break;
+
+                       case T_TYPE_EXPR:
+                               buf_append(buf, c->s, c->e - c->s);
+                               for (p = c->s; p < c->e; p++)
+                                       parser->line += (*p == '\n');
+                               break;
+
+                       case T_TYPE_INCLUDE:
+                               luastr_escape(buf, c->s, c->e - c->s, 0);
+                               break;
+
+                       case T_TYPE_I18N:
+                               luastr_translate(buf, c->s, c->e - c->s, 1);
+                               break;
+
+                       case T_TYPE_I18N_RAW:
+                               luastr_translate(buf, c->s, c->e - c->s, 0);
+                               break;
+
+                       case T_TYPE_CODE:
+                               buf_append(buf, c->s, c->e - c->s);
+                               for (p = c->s; p < c->e; p++)
+                                       parser->line += (*p == '\n');
+                               break;
+               }
+
+               if ((tail = gen_code[c->type][1]) != NULL)
+                       buf_append(buf, tail, strlen(tail));
+
+               *sz = buf_length(buf);
+               s = parser->gc = buf_destroy(buf);
+
+               if (!*sz)
+               {
+                       *sz = 1;
+                       s = " ";
+               }
+       }
+
+       return s;
+}
+
+const char *template_reader(lua_State *L, void *ud, size_t *sz)
+{
+       struct template_parser *parser = ud;
+       int rem = parser->size - (parser->off - parser->mmap);
+       char *tag;
+
+       parser->prv_chunk = parser->cur_chunk;
+
+       /* free previous string */
+       if (parser->gc)
+       {
+               free(parser->gc);
+               parser->gc = NULL;
+       }
+
+       /* before tag */
+       if (!parser->in_expr)
+       {
+               if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL)
+               {
+                       template_text(parser, tag);
+                       parser->off = tag + 2;
+                       parser->in_expr = 1;
+               }
+               else
+               {
+                       template_text(parser, parser->mmap + parser->size);
+                       parser->off = parser->mmap + parser->size;
+               }
+       }
+
+       /* inside tag */
+       else
+       {
+               if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL)
+               {
+                       template_code(parser, tag);
+                       parser->off = tag + 2;
+                       parser->in_expr = 0;
+               }
+               else
+               {
+                       /* unexpected EOF */
+                       template_code(parser, parser->mmap + parser->size);
+
+                       *sz = 1;
+                       return "\033";
+               }
+       }
+
+       return template_format_chunk(parser, sz);
+}
+
+int template_error(lua_State *L, struct template_parser *parser)
+{
+       const char *err = luaL_checkstring(L, -1);
+       const char *off = parser->prv_chunk.s;
+       const char *ptr;
+       char msg[1024];
+       int line = 0;
+       int chunkline = 0;
+
+       if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL)
+       {
+               chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
+
+               while (*ptr)
+               {
+                       if (*ptr++ == ' ')
+                       {
+                               err = ptr;
+                               break;
+                       }
+               }
+       }
+
+       if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL)
+       {
+               off = parser->mmap + parser->size;
+               err = "'%>' expected before end of file";
+               chunkline = 0;
+       }
+
+       for (ptr = parser->mmap; ptr < off; ptr++)
+               if (*ptr == '\n')
+                       line++;
+
+       snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
+                        parser->file, line + chunkline, err ? err : "(unknown error)");
+
+       lua_pushnil(L);
+       lua_pushinteger(L, line + chunkline);
+       lua_pushstring(L, msg);
+
+       return 3;
+}
diff --git a/modules/base/src/template_parser.h b/modules/base/src/template_parser.h
new file mode 100644 (file)
index 0000000..d1c6062
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * LuCI Template - Parser header
+ *
+ *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _TEMPLATE_PARSER_H_
+#define _TEMPLATE_PARSER_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+
+/* code types */
+#define T_TYPE_INIT                    0
+#define T_TYPE_TEXT                    1
+#define T_TYPE_COMMENT         2
+#define T_TYPE_EXPR                    3
+#define T_TYPE_INCLUDE                 4
+#define T_TYPE_I18N                    5
+#define T_TYPE_I18N_RAW                6
+#define T_TYPE_CODE                    7
+#define T_TYPE_EOF                     8
+
+
+struct template_chunk {
+       const char *s;
+       const char *e;
+       int type;
+       int line;
+};
+
+/* parser state */
+struct template_parser {
+       int fd;
+       uint32_t size;
+       char *mmap;
+       char *off;
+       char *gc;
+       int line;
+       int in_expr;
+       int strip_before;
+       int strip_after;
+       struct template_chunk prv_chunk;
+       struct template_chunk cur_chunk;
+       const char *file;
+};
+
+struct template_parser * template_open(const char *file);
+void template_close(struct template_parser *parser);
+
+const char *template_reader(lua_State *L, void *ud, size_t *sz);
+int template_error(lua_State *L, struct template_parser *parser);
+
+#endif
diff --git a/modules/base/src/template_utils.c b/modules/base/src/template_utils.c
new file mode 100644 (file)
index 0000000..80542bd
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * LuCI Template - Utility functions
+ *
+ *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "template_utils.h"
+#include "template_lmo.h"
+
+/* initialize a buffer object */
+struct template_buffer * buf_init(int size)
+{
+       struct template_buffer *buf;
+
+       if (size <= 0)
+               size = 1024;
+
+       buf = (struct template_buffer *)malloc(sizeof(struct template_buffer));
+
+       if (buf != NULL)
+       {
+               buf->fill = 0;
+               buf->size = size;
+               buf->data = malloc(buf->size);
+
+               if (buf->data != NULL)
+               {
+                       buf->dptr = buf->data;
+                       buf->data[0] = 0;
+
+                       return buf;
+               }
+
+               free(buf);
+       }
+
+       return NULL;
+}
+
+/* grow buffer */
+int buf_grow(struct template_buffer *buf, int size)
+{
+       unsigned int off = (buf->dptr - buf->data);
+       char *data;
+
+       if (size <= 0)
+               size = 1024;
+
+       data = realloc(buf->data, buf->size + size);
+
+       if (data != NULL)
+       {
+               buf->data  = data;
+               buf->dptr  = data + off;
+               buf->size += size;
+
+               return buf->size;
+       }
+
+       return 0;
+}
+
+/* put one char into buffer object */
+int buf_putchar(struct template_buffer *buf, char c)
+{
+       if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) )
+               return 0;
+
+       *(buf->dptr++) = c;
+       *(buf->dptr) = 0;
+
+       buf->fill++;
+       return 1;
+}
+
+/* append data to buffer */
+int buf_append(struct template_buffer *buf, const char *s, int len)
+{
+       if ((buf->fill + len + 1) >= buf->size)
+       {
+               if (!buf_grow(buf, len + 1))
+                       return 0;
+       }
+
+       memcpy(buf->dptr, s, len);
+       buf->fill += len;
+       buf->dptr += len;
+
+       *(buf->dptr) = 0;
+
+       return len;
+}
+
+/* read buffer length */
+int buf_length(struct template_buffer *buf)
+{
+       return buf->fill;
+}
+
+/* destroy buffer object and return pointer to data */
+char * buf_destroy(struct template_buffer *buf)
+{
+       char *data = buf->data;
+
+       free(buf);
+       return data;
+}
+
+
+/* calculate the number of expected continuation chars */
+static inline int mb_num_chars(unsigned char c)
+{
+       if ((c & 0xE0) == 0xC0)
+               return 2;
+       else if ((c & 0xF0) == 0xE0)
+               return 3;
+       else if ((c & 0xF8) == 0xF0)
+               return 4;
+       else if ((c & 0xFC) == 0xF8)
+               return 5;
+       else if ((c & 0xFE) == 0xFC)
+               return 6;
+
+       return 1;
+}
+
+/* test whether the given byte is a valid continuation char */
+static inline int mb_is_cont(unsigned char c)
+{
+       return ((c >= 0x80) && (c <= 0xBF));
+}
+
+/* test whether the byte sequence at the given pointer with the given
+ * length is the shortest possible representation of the code point */
+static inline int mb_is_shortest(unsigned char *s, int n)
+{
+       switch (n)
+       {
+               case 2:
+                       /* 1100000x (10xxxxxx) */
+                       return !(((*s >> 1) == 0x60) &&
+                                        ((*(s+1) >> 6) == 0x02));
+
+               case 3:
+                       /* 11100000 100xxxxx (10xxxxxx) */
+                       return !((*s == 0xE0) &&
+                                        ((*(s+1) >> 5) == 0x04) &&
+                                        ((*(s+2) >> 6) == 0x02));
+
+               case 4:
+                       /* 11110000 1000xxxx (10xxxxxx 10xxxxxx) */
+                       return !((*s == 0xF0) &&
+                                        ((*(s+1) >> 4) == 0x08) &&
+                                        ((*(s+2) >> 6) == 0x02) &&
+                                        ((*(s+3) >> 6) == 0x02));
+
+               case 5:
+                       /* 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) */
+                       return !((*s == 0xF8) &&
+                                        ((*(s+1) >> 3) == 0x10) &&
+                                        ((*(s+2) >> 6) == 0x02) &&
+                                        ((*(s+3) >> 6) == 0x02) &&
+                                        ((*(s+4) >> 6) == 0x02));
+
+               case 6:
+                       /* 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) */
+                       return !((*s == 0xF8) &&
+                                        ((*(s+1) >> 2) == 0x20) &&
+                                        ((*(s+2) >> 6) == 0x02) &&
+                                        ((*(s+3) >> 6) == 0x02) &&
+                                        ((*(s+4) >> 6) == 0x02) &&
+                                        ((*(s+5) >> 6) == 0x02));
+       }
+
+       return 1;
+}
+
+/* test whether the byte sequence at the given pointer with the given
+ * length is an UTF-16 surrogate */
+static inline int mb_is_surrogate(unsigned char *s, int n)
+{
+       return ((n == 3) && (*s == 0xED) && (*(s+1) >= 0xA0) && (*(s+1) <= 0xBF));
+}
+
+/* test whether the byte sequence at the given pointer with the given
+ * length is an illegal UTF-8 code point */
+static inline int mb_is_illegal(unsigned char *s, int n)
+{
+       return ((n == 3) && (*s == 0xEF) && (*(s+1) == 0xBF) &&
+                       (*(s+2) >= 0xBE) && (*(s+2) <= 0xBF));
+}
+
+
+/* scan given source string, validate UTF-8 sequence and store result
+ * in given buffer object */
+static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
+{
+       unsigned char *ptr = *s;
+       unsigned int o = 0, v, n;
+
+       /* ascii byte without null */
+       if ((*(ptr+0) >= 0x01) && (*(ptr+0) <= 0x7F))
+       {
+               if (!buf_putchar(buf, *ptr++))
+                       return 0;
+
+               o = 1;
+       }
+
+       /* multi byte sequence */
+       else if ((n = mb_num_chars(*ptr)) > 1)
+       {
+               /* count valid chars */
+               for (v = 1; (v <= n) && ((o+v) < l) && mb_is_cont(*(ptr+v)); v++);
+
+               switch (n)
+               {
+                       case 6:
+                       case 5:
+                               /* five and six byte sequences are always invalid */
+                               if (!buf_putchar(buf, '?'))
+                                       return 0;
+
+                               break;
+
+                       default:
+                               /* if the number of valid continuation bytes matches the
+                                * expected number and if the sequence is legal, copy
+                                * the bytes to the destination buffer */
+                               if ((v == n) && mb_is_shortest(ptr, n) &&
+                                       !mb_is_surrogate(ptr, n) && !mb_is_illegal(ptr, n))
+                               {
+                                       /* copy sequence */
+                                       if (!buf_append(buf, (char *)ptr, n))
+                                               return 0;
+                               }
+
+                               /* the found sequence is illegal, skip it */
+                               else
+                               {
+                                       /* invalid sequence */
+                                       if (!buf_putchar(buf, '?'))
+                                               return 0;
+                               }
+
+                               break;
+               }
+
+               /* advance beyound the last found valid continuation char */
+               o = v;
+               ptr += v;
+       }
+
+       /* invalid byte (0x00) */
+       else
+       {
+               if (!buf_putchar(buf, '?')) /* or 0xEF, 0xBF, 0xBD */
+                       return 0;
+
+               o = 1;
+               ptr++;
+       }
+
+       *s = ptr;
+       return o;
+}
+
+/* sanitize given string and replace all invalid UTF-8 sequences with "?" */
+char * utf8(const char *s, unsigned int l)
+{
+       struct template_buffer *buf = buf_init(l);
+       unsigned char *ptr = (unsigned char *)s;
+       unsigned int v, o;
+
+       if (!buf)
+               return NULL;
+
+       for (o = 0; o < l; o++)
+       {
+               /* ascii char */
+               if ((*ptr >= 0x01) && (*ptr <= 0x7F))
+               {
+                       if (!buf_putchar(buf, (char)*ptr++))
+                               break;
+               }
+
+               /* invalid byte or multi byte sequence */
+               else
+               {
+                       if (!(v = _validate_utf8(&ptr, l - o, buf)))
+                               break;
+
+                       o += (v - 1);
+               }
+       }
+
+       return buf_destroy(buf);
+}
+
+/* Sanitize given string and strip all invalid XML bytes
+ * Validate UTF-8 sequences
+ * Escape XML control chars */
+char * pcdata(const char *s, unsigned int l)
+{
+       struct template_buffer *buf = buf_init(l);
+       unsigned char *ptr = (unsigned char *)s;
+       unsigned int o, v;
+       char esq[8];
+       int esl;
+
+       if (!buf)
+               return NULL;
+
+       for (o = 0; o < l; o++)
+       {
+               /* Invalid XML bytes */
+               if (((*ptr >= 0x00) && (*ptr <= 0x08)) ||
+                   ((*ptr >= 0x0B) && (*ptr <= 0x0C)) ||
+                   ((*ptr >= 0x0E) && (*ptr <= 0x1F)) ||
+                   (*ptr == 0x7F))
+               {
+                       ptr++;
+               }
+
+               /* Escapes */
+               else if ((*ptr == 0x26) ||
+                        (*ptr == 0x27) ||
+                        (*ptr == 0x22) ||
+                        (*ptr == 0x3C) ||
+                        (*ptr == 0x3E))
+               {
+                       esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
+
+                       if (!buf_append(buf, esq, esl))
+                               break;
+
+                       ptr++;
+               }
+
+               /* ascii char */
+               else if (*ptr <= 0x7F)
+               {
+                       buf_putchar(buf, (char)*ptr++);
+               }
+
+               /* multi byte sequence */
+               else
+               {
+                       if (!(v = _validate_utf8(&ptr, l - o, buf)))
+                               break;
+
+                       o += (v - 1);
+               }
+       }
+
+       return buf_destroy(buf);
+}
+
+char * striptags(const char *s, unsigned int l)
+{
+       struct template_buffer *buf = buf_init(l);
+       unsigned char *ptr = (unsigned char *)s;
+       unsigned char *end = ptr + l;
+       unsigned char *tag;
+       unsigned char prev;
+       char esq[8];
+       int esl;
+
+       for (prev = ' '; ptr < end; ptr++)
+       {
+               if ((*ptr == '<') && ((ptr + 2) < end) &&
+                       ((*(ptr + 1) == '/') || isalpha(*(ptr + 1))))
+               {
+                       for (tag = ptr; tag < end; tag++)
+                       {
+                               if (*tag == '>')
+                               {
+                                       if (!isspace(prev))
+                                               buf_putchar(buf, ' ');
+
+                                       ptr = tag;
+                                       prev = ' ';
+                                       break;
+                               }
+                       }
+               }
+               else if (isspace(*ptr))
+               {
+                       if (!isspace(prev))
+                               buf_putchar(buf, *ptr);
+
+                       prev = *ptr;
+               }
+               else
+               {
+                       switch(*ptr)
+                       {
+                               case '"':
+                               case '\'':
+                               case '<':
+                               case '>':
+                               case '&':
+                                       esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
+                                       buf_append(buf, esq, esl);
+                                       break;
+
+                               default:
+                                       buf_putchar(buf, *ptr);
+                                       break;
+                       }
+
+                       prev = *ptr;
+               }
+       }
+
+       return buf_destroy(buf);
+}
+
+void luastr_escape(struct template_buffer *out, const char *s, unsigned int l,
+                                  int escape_xml)
+{
+       int esl;
+       char esq[8];
+       char *ptr;
+
+       for (ptr = (char *)s; ptr < (s + l); ptr++)
+       {
+               switch (*ptr)
+               {
+               case '\\':
+                       buf_append(out, "\\\\", 2);
+                       break;
+
+               case '"':
+                       if (escape_xml)
+                               buf_append(out, "&#34;", 5);
+                       else
+                               buf_append(out, "\\\"", 2);
+                       break;
+
+               case '\n':
+                       buf_append(out, "\\n", 2);
+                       break;
+
+               case '\'':
+               case '&':
+               case '<':
+               case '>':
+                       if (escape_xml)
+                       {
+                               esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
+                               buf_append(out, esq, esl);
+                               break;
+                       }
+
+               default:
+                       buf_putchar(out, *ptr);
+               }
+       }
+}
+
+void luastr_translate(struct template_buffer *out, const char *s, unsigned int l,
+                                         int escape_xml)
+{
+       char *tr;
+       int trlen;
+
+       switch (lmo_translate(s, l, &tr, &trlen))
+       {
+               case 0:
+                       luastr_escape(out, tr, trlen, escape_xml);
+                       break;
+
+               case -1:
+                       luastr_escape(out, s, l, escape_xml);
+                       break;
+
+               default:
+                       /* no catalog loaded */
+                       break;
+       }
+}
diff --git a/modules/base/src/template_utils.h b/modules/base/src/template_utils.h
new file mode 100644 (file)
index 0000000..c54af75
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * LuCI Template - Utility header
+ *
+ *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _TEMPLATE_UTILS_H_
+#define _TEMPLATE_UTILS_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+/* buffer object */
+struct template_buffer {
+       char *data;
+       char *dptr;
+       unsigned int size;
+       unsigned int fill;
+};
+
+struct template_buffer * buf_init(int size);
+int buf_grow(struct template_buffer *buf, int size);
+int buf_putchar(struct template_buffer *buf, char c);
+int buf_append(struct template_buffer *buf, const char *s, int len);
+int buf_length(struct template_buffer *buf);
+char * buf_destroy(struct template_buffer *buf);
+
+char * utf8(const char *s, unsigned int l);
+char * pcdata(const char *s, unsigned int l);
+char * striptags(const char *s, unsigned int l);
+
+void luastr_escape(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
+void luastr_translate(struct template_buffer *out, const char *s, unsigned int l, int escape_xml);
+
+#endif
diff --git a/modules/base/standalone.mk b/modules/base/standalone.mk
new file mode 100644 (file)
index 0000000..66a0e5a
--- /dev/null
@@ -0,0 +1,56 @@
+LUAC = luac
+LUAC_OPTIONS = -s
+LUA_TARGET ?= source
+
+LUA_MODULEDIR = /usr/local/share/lua/5.1
+LUA_LIBRARYDIR = /usr/local/lib/lua/5.1
+
+OS ?= $(shell uname)
+
+LUA_SHLIBS = $(shell pkg-config --silence-errors --libs lua5.1 || pkg-config --silence-errors --libs lua-5.1 || pkg-config --silence-errors --libs lua)
+LUA_LIBS = $(if $(LUA_SHLIBS),$(LUA_SHLIBS),$(firstword $(wildcard /usr/lib/liblua.a /usr/local/lib/liblua.a /opt/local/lib/liblua.a)))
+LUA_CFLAGS = $(shell pkg-config --silence-errors --cflags lua5.1 || pkg-config --silence-errors --cflags lua-5.1 || pkg-config --silence-errors --cflags lua)
+
+CC = gcc
+AR = ar
+RANLIB = ranlib
+CFLAGS = -O2
+FPIC = -fPIC
+EXTRA_CFLAGS = --std=gnu99
+WFLAGS = -Wall -Werror -pedantic
+CPPFLAGS =
+COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(WFLAGS)
+ifeq ($(OS),Darwin)
+  SHLIB_FLAGS = -bundle -undefined dynamic_lookup
+else
+  SHLIB_FLAGS = -shared
+endif
+LINK = $(CC) $(LDFLAGS)
+
+.PHONY: all build compile luacompile luasource clean luaclean
+
+all: build
+
+build: luabuild gccbuild
+
+luabuild: lua$(LUA_TARGET)
+
+gccbuild: compile
+compile:
+
+clean: luaclean
+
+luasource:
+       mkdir -p dist$(LUA_MODULEDIR)
+       cp -pR root/* dist 2>/dev/null || true
+       cp -pR lua/* dist$(LUA_MODULEDIR) 2>/dev/null || true
+       for i in $$(find dist -name .svn); do rm -rf $$i || true; done
+
+luastrip: luasource
+       for i in $$(find dist -type f -name '*.lua'); do perl -e 'undef $$/; open( F, "< $$ARGV[0]" ) || die $$!; $$src = <F>; close F; $$src =~ s/--\[\[.*?\]\](--)?//gs; $$src =~ s/^\s*--.*?\n//gm; open( F, "> $$ARGV[0]" ) || die $$!; print F $$src; close F' $$i; done
+
+luacompile: luasource
+       for i in $$(find dist -name *.lua -not -name debug.lua); do $(LUAC) $(LUAC_OPTIONS) -o $$i $$i; done
+
+luaclean:
+       rm -rf dist
diff --git a/protocols/core/Makefile b/protocols/core/Makefile
deleted file mode 100644 (file)
index f7fac77..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
diff --git a/protocols/core/luasrc/model/cbi/admin_network/proto_dhcp.lua b/protocols/core/luasrc/model/cbi/admin_network/proto_dhcp.lua
deleted file mode 100644 (file)
index fe3fec6..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2011-2012 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-]]--
-
-local map, section, net = ...
-local ifc = net:get_interface()
-
-local hostname, accept_ra, send_rs
-local bcast, defaultroute, peerdns, dns, metric, clientid, vendorclass
-
-
-hostname = section:taboption("general", Value, "hostname",
-       translate("Hostname to send when requesting DHCP"))
-
-hostname.placeholder = luci.sys.hostname()
-hostname.datatype    = "hostname"
-
-
-bcast = section:taboption("advanced", Flag, "broadcast",
-       translate("Use broadcast flag"),
-       translate("Required for certain ISPs, e.g. Charter with DOCSIS 3"))
-
-bcast.default = bcast.disabled
-
-
-defaultroute = section:taboption("advanced", Flag, "defaultroute",
-       translate("Use default gateway"),
-       translate("If unchecked, no default route is configured"))
-
-defaultroute.default = defaultroute.enabled
-
-
-peerdns = section:taboption("advanced", Flag, "peerdns",
-       translate("Use DNS servers advertised by peer"),
-       translate("If unchecked, the advertised DNS server addresses are ignored"))
-
-peerdns.default = peerdns.enabled
-
-
-dns = section:taboption("advanced", DynamicList, "dns",
-       translate("Use custom DNS servers"))
-
-dns:depends("peerdns", "")
-dns.datatype = "ipaddr"
-dns.cast     = "string"
-
-
-metric = section:taboption("advanced", Value, "metric",
-       translate("Use gateway metric"))
-
-metric.placeholder = "0"
-metric.datatype    = "uinteger"
-
-
-clientid = section:taboption("advanced", Value, "clientid",
-       translate("Client ID to send when requesting DHCP"))
-
-
-vendorclass = section:taboption("advanced", Value, "vendorid",
-       translate("Vendor Class to send when requesting DHCP"))
-
-
-luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
-
-
-mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
-mtu.placeholder = "1500"
-mtu.datatype    = "max(9200)"
diff --git a/protocols/core/luasrc/model/cbi/admin_network/proto_none.lua b/protocols/core/luasrc/model/cbi/admin_network/proto_none.lua
deleted file mode 100644 (file)
index 0e34b67..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-]]--
-
-local map, section, net = ...
diff --git a/protocols/core/luasrc/model/cbi/admin_network/proto_static.lua b/protocols/core/luasrc/model/cbi/admin_network/proto_static.lua
deleted file mode 100644 (file)
index 338c0b7..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-]]--
-
-local map, section, net = ...
-local ifc = net:get_interface()
-
-local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
-local mtu, metric
-
-
-ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
-ipaddr.datatype = "ip4addr"
-
-
-netmask = section:taboption("general", Value, "netmask",
-       translate("IPv4 netmask"))
-
-netmask.datatype = "ip4addr"
-netmask:value("255.255.255.0")
-netmask:value("255.255.0.0")
-netmask:value("255.0.0.0")
-
-
-gateway = section:taboption("general", Value, "gateway", translate("IPv4 gateway"))
-gateway.datatype = "ip4addr"
-
-
-broadcast = section:taboption("general", Value, "broadcast", translate("IPv4 broadcast"))
-broadcast.datatype = "ip4addr"
-
-
-dns = section:taboption("general", DynamicList, "dns",
-       translate("Use custom DNS servers"))
-
-dns.datatype = "ipaddr"
-dns.cast     = "string"
-
-
-if luci.model.network:has_ipv6() then
-
-       local ip6assign = section:taboption("general", Value, "ip6assign", translate("IPv6 assignment length"),
-               translate("Assign a part of given length of every public IPv6-prefix to this interface"))
-       ip6assign:value("", translate("disabled"))
-       ip6assign:value("64")
-       ip6assign.datatype = "max(64)"
-
-       local ip6hint = section:taboption("general", Value, "ip6hint", translate("IPv6 assignment hint"),
-               translate("Assign prefix parts using this hexadecimal subprefix ID for this interface."))
-       for i=33,64 do ip6hint:depends("ip6assign", i) end
-
-       ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
-       ip6addr.datatype = "ip6addr"
-       ip6addr:depends("ip6assign", "")
-
-
-       ip6gw = section:taboption("general", Value, "ip6gw", translate("IPv6 gateway"))
-       ip6gw.datatype = "ip6addr"
-       ip6gw:depends("ip6assign", "")
-
-
-       local ip6prefix = s:taboption("general", Value, "ip6prefix", translate("IPv6 routed prefix"),
-               translate("Public prefix routed to this device for distribution to clients."))
-       ip6prefix.datatype = "ip6addr"
-       ip6prefix:depends("ip6assign", "")
-
-end
-
-
-luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
-
-
-mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
-mtu.placeholder = "1500"
-mtu.datatype    = "max(9200)"
-
-
-metric = section:taboption("advanced", Value, "metric",
-       translate("Use gateway metric"))
-
-metric.placeholder = "0"
-metric.datatype    = "uinteger"
diff --git a/protocols/core/luasrc/tools/proto.lua b/protocols/core/luasrc/tools/proto.lua
deleted file mode 100644 (file)
index 4df0269..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
---[[
-LuCI - Lua Configuration Interface
-
-Copyright 2012 Jo-Philipp Wich <xm@subsignal.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
-
-module("luci.tools.proto", package.seeall)
-
-function opt_macaddr(s, ifc, ...)
-       local v = luci.cbi.Value
-       local o = s:taboption("advanced", v, "macaddr", ...)
-
-       o.placeholder = ifc and ifc:mac()
-       o.datatype    = "macaddr"
-
-       function o.cfgvalue(self, section)
-               local w = ifc and ifc:get_wifinet()
-               if w then
-                       return w:get("macaddr")
-               else
-                       return v.cfgvalue(self, section)
-               end
-       end
-
-       function o.write(self, section, value)
-               local w = ifc and ifc:get_wifinet()
-               if w then
-                       w:set("macaddr", value)
-               elseif value then
-                       v.write(self, section, value)
-               else
-                       v.remove(self, section)
-               end
-       end
-
-       function o.remove(self, section)
-               self:write(section, nil)
-       end
-end
diff --git a/themes/base/Makefile b/themes/base/Makefile
deleted file mode 100644 (file)
index 81a96f6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
\ No newline at end of file
diff --git a/themes/base/htdocs/luci-static/resources/icons/bridge.png b/themes/base/htdocs/luci-static/resources/icons/bridge.png
deleted file mode 100644 (file)
index 4c163bf..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/bridge.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/bridge_disabled.png b/themes/base/htdocs/luci-static/resources/icons/bridge_disabled.png
deleted file mode 100644 (file)
index 0f367c5..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/bridge_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/encryption.png b/themes/base/htdocs/luci-static/resources/icons/encryption.png
deleted file mode 100644 (file)
index 41d2ba9..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/encryption.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/encryption_disabled.png b/themes/base/htdocs/luci-static/resources/icons/encryption_disabled.png
deleted file mode 100644 (file)
index f2e05a4..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/encryption_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/ethernet.png b/themes/base/htdocs/luci-static/resources/icons/ethernet.png
deleted file mode 100644 (file)
index a025381..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/ethernet.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/ethernet_disabled.png b/themes/base/htdocs/luci-static/resources/icons/ethernet_disabled.png
deleted file mode 100644 (file)
index 2bb02e4..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/ethernet_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/loading.gif b/themes/base/htdocs/luci-static/resources/icons/loading.gif
deleted file mode 100644 (file)
index 5bb90fd..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/loading.gif and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/port_down.png b/themes/base/htdocs/luci-static/resources/icons/port_down.png
deleted file mode 100644 (file)
index 25ea172..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/port_down.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/port_up.png b/themes/base/htdocs/luci-static/resources/icons/port_up.png
deleted file mode 100644 (file)
index e063037..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/port_up.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-0-25.png b/themes/base/htdocs/luci-static/resources/icons/signal-0-25.png
deleted file mode 100644 (file)
index 2e5dff4..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-0-25.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-0.png b/themes/base/htdocs/luci-static/resources/icons/signal-0.png
deleted file mode 100644 (file)
index 114583a..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-0.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-25-50.png b/themes/base/htdocs/luci-static/resources/icons/signal-25-50.png
deleted file mode 100644 (file)
index ee8cc4f..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-25-50.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-50-75.png b/themes/base/htdocs/luci-static/resources/icons/signal-50-75.png
deleted file mode 100644 (file)
index 26bcbf7..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-50-75.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-75-100.png b/themes/base/htdocs/luci-static/resources/icons/signal-75-100.png
deleted file mode 100644 (file)
index 5cffaa1..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-75-100.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/signal-none.png b/themes/base/htdocs/luci-static/resources/icons/signal-none.png
deleted file mode 100644 (file)
index b77585c..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/signal-none.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/switch.png b/themes/base/htdocs/luci-static/resources/icons/switch.png
deleted file mode 100644 (file)
index 5c99ba5..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/switch.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/switch_disabled.png b/themes/base/htdocs/luci-static/resources/icons/switch_disabled.png
deleted file mode 100644 (file)
index b8c84c8..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/switch_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/tunnel.png b/themes/base/htdocs/luci-static/resources/icons/tunnel.png
deleted file mode 100644 (file)
index c5a09dd..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/tunnel.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/tunnel_disabled.png b/themes/base/htdocs/luci-static/resources/icons/tunnel_disabled.png
deleted file mode 100644 (file)
index ad9856c..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/tunnel_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/vlan.png b/themes/base/htdocs/luci-static/resources/icons/vlan.png
deleted file mode 100644 (file)
index 5c99ba5..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/vlan.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/vlan_disabled.png b/themes/base/htdocs/luci-static/resources/icons/vlan_disabled.png
deleted file mode 100644 (file)
index b8c84c8..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/vlan_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/wifi.png b/themes/base/htdocs/luci-static/resources/icons/wifi.png
deleted file mode 100644 (file)
index 528ce2b..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/wifi.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/wifi_big.png b/themes/base/htdocs/luci-static/resources/icons/wifi_big.png
deleted file mode 100644 (file)
index d73a5e7..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/wifi_big.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png b/themes/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png
deleted file mode 100644 (file)
index af93b37..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/wifi_big_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/icons/wifi_disabled.png b/themes/base/htdocs/luci-static/resources/icons/wifi_disabled.png
deleted file mode 100644 (file)
index 338a34f..0000000
Binary files a/themes/base/htdocs/luci-static/resources/icons/wifi_disabled.png and /dev/null differ
diff --git a/themes/base/htdocs/luci-static/resources/xhr.js b/themes/base/htdocs/luci-static/resources/xhr.js
deleted file mode 100644 (file)
index 701c12a..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * xhr.js - XMLHttpRequest helper class
- * (c) 2008-2010 Jo-Philipp Wich
- */
-
-XHR = function()
-{
-       this.reinit = function()
-       {
-               if (window.XMLHttpRequest) {
-                       this._xmlHttp = new XMLHttpRequest();
-               }
-               else if (window.ActiveXObject) {
-                       this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
-               }
-               else {
-                       alert("xhr.js: XMLHttpRequest is not supported by this browser!");
-               }
-       }
-
-       this.busy = function() {
-               if (!this._xmlHttp)
-                       return false;
-
-               switch (this._xmlHttp.readyState)
-               {
-                       case 1:
-                       case 2:
-                       case 3:
-                               return true;
-
-                       default:
-                               return false;
-               }
-       }
-
-       this.abort = function() {
-               if (this.busy())
-                       this._xmlHttp.abort();
-       }
-
-       this.get = function(url,data,callback)
-       {
-               this.reinit();
-
-               var xhr  = this._xmlHttp;
-               var code = this._encode(data);
-
-               url = location.protocol + '//' + location.host + url;
-
-               if (code)
-                       if (url.substr(url.length-1,1) == '&')
-                               url += code;
-                       else
-                               url += '?' + code;
-
-               xhr.open('GET', url, true);
-
-               xhr.onreadystatechange = function()
-               {
-                       if (xhr.readyState == 4) {
-                               var json = null;
-                               if (xhr.getResponseHeader("Content-Type") == "application/json") {
-                                       try {
-                                               json = eval('(' + xhr.responseText + ')');
-                                       }
-                                       catch(e) {
-                                               json = null;
-                                       }
-                               }
-
-                               callback(xhr, json);
-                       }
-               }
-
-               xhr.send(null);
-       }
-
-       this.post = function(url,data,callback)
-       {
-               this.reinit();
-
-               var xhr  = this._xmlHttp;
-               var code = this._encode(data);
-
-               xhr.onreadystatechange = function()
-               {
-                       if (xhr.readyState == 4)
-                               callback(xhr);
-               }
-
-               xhr.open('POST', url, true);
-               xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
-               xhr.setRequestHeader('Content-length', code.length);
-               xhr.setRequestHeader('Connection', 'close');
-               xhr.send(code);
-       }
-
-       this.cancel = function()
-       {
-               this._xmlHttp.onreadystatechange = function(){};
-               this._xmlHttp.abort();
-       }
-
-       this.send_form = function(form,callback,extra_values)
-       {
-               var code = '';
-
-               for (var i = 0; i < form.elements.length; i++)
-               {
-                       var e = form.elements[i];
-
-                       if (e.options)
-                       {
-                               code += (code ? '&' : '') +
-                                       form.elements[i].name + '=' + encodeURIComponent(
-                                               e.options[e.selectedIndex].value
-                                       );
-                       }
-                       else if (e.length)
-                       {
-                               for (var j = 0; j < e.length; j++)
-                                       if (e[j].name) {
-                                               code += (code ? '&' : '') +
-                                                       e[j].name + '=' + encodeURIComponent(e[j].value);
-                                       }
-                       }
-                       else
-                       {
-                               code += (code ? '&' : '') +
-                                       e.name + '=' + encodeURIComponent(e.value);
-                       }
-               }
-
-               if (typeof extra_values == 'object')
-                       for (var key in extra_values)
-                               code += (code ? '&' : '') +
-                                       key + '=' + encodeURIComponent(extra_values[key]);
-
-               return(
-                       (form.method == 'get')
-                               ? this.get(form.getAttribute('action'), code, callback)
-                               : this.post(form.getAttribute('action'), code, callback)
-               );
-       }
-
-       this._encode = function(obj)
-       {
-               obj = obj ? obj : { };
-               obj['_'] = Math.random();
-
-               if (typeof obj == 'object')
-               {
-                       var code = '';
-                       var self = this;
-
-                       for (var k in obj)
-                               code += (code ? '&' : '') +
-                                       k + '=' + encodeURIComponent(obj[k]);
-
-                       return code;
-               }
-
-               return obj;
-       }
-}
-
-XHR.get = function(url, data, callback)
-{
-       (new XHR()).get(url, data, callback);
-}
-
-XHR.poll = function(interval, url, data, callback)
-{
-       if (isNaN(interval) || interval < 1)
-               interval = 5;
-
-       if (!XHR._q)
-       {
-               XHR._t = 0;
-               XHR._q = [ ];
-               XHR._r = function() {
-                       for (var i = 0, e = XHR._q[0]; i < XHR._q.length; e = XHR._q[++i])
-                       {
-                               if (!(XHR._t % e.interval) && !e.xhr.busy())
-                                       e.xhr.get(e.url, e.data, e.callback);
-                       }
-
-                       XHR._t++;
-               };
-       }
-
-       XHR._q.push({
-               interval: interval,
-               callback: callback,
-               url:      url,
-               data:     data,
-               xhr:      new XHR()
-       });
-
-       XHR.run();
-}
-
-XHR.halt = function()
-{
-       if (XHR._i)
-       {
-               /* show & set poll indicator */
-               try {
-                       document.getElementById('xhr_poll_status').style.display = '';
-                       document.getElementById('xhr_poll_status_on').style.display = 'none';
-                       document.getElementById('xhr_poll_status_off').style.display = '';
-               } catch(e) { }
-
-               window.clearInterval(XHR._i);
-               XHR._i = null;
-       }
-}
-
-XHR.run = function()
-{
-       if (XHR._r && !XHR._i)
-       {
-               /* show & set poll indicator */
-               try {
-                       document.getElementById('xhr_poll_status').style.display = '';
-                       document.getElementById('xhr_poll_status_on').style.display = '';
-                       document.getElementById('xhr_poll_status_off').style.display = 'none';
-               } catch(e) { }
-
-               /* kick first round manually to prevent one second lag when setting up
-                * the poll interval */
-               XHR._r();
-               XHR._i = window.setInterval(XHR._r, 1000);
-       }
-}
-
-XHR.running = function()
-{
-       return !!(XHR._r && XHR._i);
-}