Use master package instead of some customs
|
@ -1,53 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-base
|
||||
|
||||
LUCI_TYPE:=mod
|
||||
LUCI_BASENAME:=base
|
||||
|
||||
LUCI_TITLE:=LuCI core libraries
|
||||
LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +liblucihttp-lua
|
||||
|
||||
|
||||
PKG_SOURCE:=v1.0.0.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet/archive/
|
||||
PKG_HASH:=48162e63e77d009f5848f18a5cabffbdfc867d0e5e73c6d407f6af5d6880151b
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/luasrcdiet-1.0.0
|
||||
|
||||
include $(INCLUDE_DIR)/host-build.mk
|
||||
|
||||
define Package/luci-base/conffiles
|
||||
/etc/luci-uploads
|
||||
/etc/config/luci
|
||||
/etc/config/ucitrack
|
||||
endef
|
||||
|
||||
include ../luci/luci.mk
|
||||
|
||||
define Host/Configure
|
||||
endef
|
||||
|
||||
define Host/Compile
|
||||
$(MAKE) -C src/ clean po2lmo jsmin
|
||||
endef
|
||||
|
||||
define Host/Install
|
||||
$(INSTALL_DIR) $(1)/bin
|
||||
$(INSTALL_DIR) $(1)/lib/lua/5.1
|
||||
$(INSTALL_BIN) src/po2lmo $(1)/bin/po2lmo
|
||||
$(INSTALL_BIN) src/jsmin $(1)/bin/jsmin
|
||||
$(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/luasrcdiet $(1)/bin/luasrcdiet
|
||||
$(CP) $(HOST_BUILD_DIR)/luasrcdiet $(1)/lib/lua/5.1/
|
||||
endef
|
||||
|
||||
$(eval $(call HostBuild))
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/lua
|
||||
require "luci.cacheloader"
|
||||
require "luci.sgi.cgi"
|
||||
luci.dispatcher.indexcache = "/tmp/luci-indexcache"
|
||||
luci.sgi.cgi.run()
|
|
@ -1,834 +0,0 @@
|
|||
/*
|
||||
LuCI - Lua Configuration Interface
|
||||
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
|
||||
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_strings = { path: {}, label: {} };
|
||||
|
||||
function s8(bytes, off) {
|
||||
var n = bytes[off];
|
||||
return (n > 0x7F) ? (n - 256) >>> 0 : n;
|
||||
}
|
||||
|
||||
function u16(bytes, off) {
|
||||
return ((bytes[off + 1] << 8) + bytes[off]) >>> 0;
|
||||
}
|
||||
|
||||
function sfh(s) {
|
||||
if (s === null || s.length === 0)
|
||||
return null;
|
||||
|
||||
var bytes = [];
|
||||
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
var ch = s.charCodeAt(i);
|
||||
|
||||
if (ch <= 0x7F)
|
||||
bytes.push(ch);
|
||||
else if (ch <= 0x7FF)
|
||||
bytes.push(((ch >>> 6) & 0x1F) | 0xC0,
|
||||
( ch & 0x3F) | 0x80);
|
||||
else if (ch <= 0xFFFF)
|
||||
bytes.push(((ch >>> 12) & 0x0F) | 0xE0,
|
||||
((ch >>> 6) & 0x3F) | 0x80,
|
||||
( ch & 0x3F) | 0x80);
|
||||
else if (code <= 0x10FFFF)
|
||||
bytes.push(((ch >>> 18) & 0x07) | 0xF0,
|
||||
((ch >>> 12) & 0x3F) | 0x80,
|
||||
((ch >> 6) & 0x3F) | 0x80,
|
||||
( ch & 0x3F) | 0x80);
|
||||
}
|
||||
|
||||
if (!bytes.length)
|
||||
return null;
|
||||
|
||||
var hash = (bytes.length >>> 0),
|
||||
len = (bytes.length >>> 2),
|
||||
off = 0, tmp;
|
||||
|
||||
while (len--) {
|
||||
hash += u16(bytes, off);
|
||||
tmp = ((u16(bytes, off + 2) << 11) ^ hash) >>> 0;
|
||||
hash = ((hash << 16) ^ tmp) >>> 0;
|
||||
hash += hash >>> 11;
|
||||
off += 4;
|
||||
}
|
||||
|
||||
switch ((bytes.length & 3) >>> 0) {
|
||||
case 3:
|
||||
hash += u16(bytes, off);
|
||||
hash = (hash ^ (hash << 16)) >>> 0;
|
||||
hash = (hash ^ (s8(bytes, off + 2) << 18)) >>> 0;
|
||||
hash += hash >>> 11;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
hash += u16(bytes, off);
|
||||
hash = (hash ^ (hash << 11)) >>> 0;
|
||||
hash += hash >>> 17;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
hash += s8(bytes, off);
|
||||
hash = (hash ^ (hash << 10)) >>> 0;
|
||||
hash += hash >>> 1;
|
||||
break;
|
||||
}
|
||||
|
||||
hash = (hash ^ (hash << 3)) >>> 0;
|
||||
hash += hash >>> 5;
|
||||
hash = (hash ^ (hash << 4)) >>> 0;
|
||||
hash += hash >>> 17;
|
||||
hash = (hash ^ (hash << 25)) >>> 0;
|
||||
hash += hash >>> 6;
|
||||
|
||||
return (0x100000000 + hash).toString(16).substr(1);
|
||||
}
|
||||
|
||||
function _(s) {
|
||||
return (window.TR && TR[sfh(s)]) || s;
|
||||
}
|
||||
|
||||
|
||||
function cbi_d_add(field, dep, index) {
|
||||
var obj = (typeof(field) === 'string') ? document.getElementById(field) : field;
|
||||
if (obj) {
|
||||
var entry
|
||||
for (var i=0; i<cbi_d.length; i++) {
|
||||
if (cbi_d[i].id == obj.id) {
|
||||
entry = cbi_d[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!entry) {
|
||||
entry = {
|
||||
"node": obj,
|
||||
"id": obj.id,
|
||||
"parent": obj.parentNode.id,
|
||||
"deps": [],
|
||||
"index": index
|
||||
};
|
||||
cbi_d.unshift(entry);
|
||||
}
|
||||
entry.deps.push(dep)
|
||||
}
|
||||
}
|
||||
|
||||
function cbi_d_checkvalue(target, ref) {
|
||||
var value = null,
|
||||
query = 'input[id="'+target+'"], input[name="'+target+'"], ' +
|
||||
'select[id="'+target+'"], select[name="'+target+'"]';
|
||||
|
||||
document.querySelectorAll(query).forEach(function(i) {
|
||||
if (value === null && ((i.type !== 'radio' && i.type !== 'checkbox') || i.checked === true))
|
||||
value = i.value;
|
||||
});
|
||||
|
||||
return (((value !== null) ? 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 ^ reverse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
function cbi_d_update() {
|
||||
var state = false;
|
||||
for (var i=0; i<cbi_d.length; i++) {
|
||||
var entry = cbi_d[i];
|
||||
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;
|
||||
}
|
||||
else if (parent && (!node || !node.parentNode) && cbi_d_check(entry.deps)) {
|
||||
var next = undefined;
|
||||
|
||||
for (next = parent.firstChild; next; next = next.nextSibling) {
|
||||
if (next.getAttribute && parseInt(next.getAttribute('data-index'), 10) > entry.index)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
parent.appendChild(entry.node);
|
||||
else
|
||||
parent.insertBefore(entry.node, next);
|
||||
|
||||
state = true;
|
||||
}
|
||||
|
||||
// hide optionals widget if no choices remaining
|
||||
if (parent && parent.parentNode && parent.getAttribute('data-optionals'))
|
||||
parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : '';
|
||||
}
|
||||
|
||||
if (entry && entry.parent)
|
||||
cbi_tag_last(parent);
|
||||
|
||||
if (state)
|
||||
cbi_d_update();
|
||||
else if (parent)
|
||||
parent.dispatchEvent(new CustomEvent('dependency-update', { bubbles: true }));
|
||||
}
|
||||
|
||||
function cbi_init() {
|
||||
var nodes;
|
||||
|
||||
document.querySelectorAll('.cbi-dropdown').forEach(function(node) {
|
||||
cbi_dropdown_init(node);
|
||||
node.addEventListener('cbi-dropdown-change', cbi_d_update);
|
||||
});
|
||||
|
||||
nodes = document.querySelectorAll('[data-strings]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var str = JSON.parse(node.getAttribute('data-strings'));
|
||||
for (var key in str) {
|
||||
for (var key2 in str[key]) {
|
||||
var dst = cbi_strings[key] || (cbi_strings[key] = { });
|
||||
dst[key2] = str[key][key2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes = document.querySelectorAll('[data-depends]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var index = parseInt(node.getAttribute('data-index'), 10);
|
||||
var depends = JSON.parse(node.getAttribute('data-depends'));
|
||||
if (!isNaN(index) && depends.length > 0) {
|
||||
for (var alt = 0; alt < depends.length; alt++)
|
||||
cbi_d_add(node, depends[alt], index);
|
||||
}
|
||||
}
|
||||
|
||||
nodes = document.querySelectorAll('[data-update]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var events = node.getAttribute('data-update').split(' ');
|
||||
for (var j = 0, event; (event = events[j]) !== undefined; j++)
|
||||
node.addEventListener(event, cbi_d_update);
|
||||
}
|
||||
|
||||
nodes = document.querySelectorAll('[data-choices]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var choices = JSON.parse(node.getAttribute('data-choices')),
|
||||
options = {};
|
||||
|
||||
for (var j = 0; j < choices[0].length; j++)
|
||||
options[choices[0][j]] = choices[1][j];
|
||||
|
||||
var def = (node.getAttribute('data-optional') === 'true')
|
||||
? node.placeholder || '' : null;
|
||||
|
||||
var cb = new L.ui.Combobox(node.value, options, {
|
||||
name: node.getAttribute('name'),
|
||||
sort: choices[0],
|
||||
select_placeholder: def || _('-- Please choose --'),
|
||||
custom_placeholder: node.getAttribute('data-manual') || _('-- custom --')
|
||||
});
|
||||
|
||||
var n = cb.render();
|
||||
n.addEventListener('cbi-dropdown-change', cbi_d_update);
|
||||
node.parentNode.replaceChild(n, node);
|
||||
}
|
||||
|
||||
nodes = document.querySelectorAll('[data-dynlist]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
var choices = JSON.parse(node.getAttribute('data-dynlist')),
|
||||
values = JSON.parse(node.getAttribute('data-values') || '[]'),
|
||||
options = null;
|
||||
|
||||
if (choices[0] && choices[0].length) {
|
||||
options = {};
|
||||
|
||||
for (var j = 0; j < choices[0].length; j++)
|
||||
options[choices[0][j]] = choices[1][j];
|
||||
}
|
||||
|
||||
var dl = new L.ui.DynamicList(values, options, {
|
||||
name: node.getAttribute('data-prefix'),
|
||||
sort: choices[0],
|
||||
datatype: choices[2],
|
||||
optional: choices[3],
|
||||
placeholder: node.getAttribute('data-placeholder')
|
||||
});
|
||||
|
||||
var n = dl.render();
|
||||
n.addEventListener('cbi-dynlist-change', cbi_d_update);
|
||||
node.parentNode.replaceChild(n, node);
|
||||
}
|
||||
|
||||
nodes = document.querySelectorAll('[data-type]');
|
||||
|
||||
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
|
||||
cbi_validate_field(node, node.getAttribute('data-optional') === 'true',
|
||||
node.getAttribute('data-type'));
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-browser]').forEach(cbi_browser_init);
|
||||
|
||||
document.querySelectorAll('.cbi-tooltip:not(:empty)').forEach(function(s) {
|
||||
s.parentNode.classList.add('cbi-tooltip-container');
|
||||
});
|
||||
|
||||
document.querySelectorAll('.cbi-section-remove > input[name^="cbi.rts"]').forEach(function(i) {
|
||||
var handler = function(ev) {
|
||||
var bits = this.name.split(/\./),
|
||||
section = document.getElementById('cbi-' + bits[2] + '-' + bits[3]);
|
||||
|
||||
section.style.opacity = (ev.type === 'mouseover') ? 0.5 : '';
|
||||
};
|
||||
|
||||
i.addEventListener('mouseover', handler);
|
||||
i.addEventListener('mouseout', handler);
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-ui-widget]').forEach(function(node) {
|
||||
var args = JSON.parse(node.getAttribute('data-ui-widget') || '[]'),
|
||||
widget = new (Function.prototype.bind.apply(L.ui[args[0]], args)),
|
||||
markup = widget.render();
|
||||
|
||||
markup.addEventListener('widget-change', cbi_d_update);
|
||||
node.parentNode.replaceChild(markup, node);
|
||||
});
|
||||
|
||||
cbi_d_update();
|
||||
}
|
||||
|
||||
function cbi_filebrowser(id, defpath) {
|
||||
var field = L.dom.elem(id) ? id : document.getElementById(id);
|
||||
var browser = window.open(
|
||||
cbi_strings.path.browser + (field.value || defpath || '') + '?field=' + field.id,
|
||||
"luci_filebrowser", "width=300,height=400,left=100,top=200,scrollbars=yes"
|
||||
);
|
||||
|
||||
browser.focus();
|
||||
}
|
||||
|
||||
function cbi_browser_init(field)
|
||||
{
|
||||
field.parentNode.insertBefore(
|
||||
E('img', {
|
||||
'src': L.resource('cbi/folder.gif'),
|
||||
'class': 'cbi-image-button',
|
||||
'click': function(ev) {
|
||||
cbi_filebrowser(field, field.getAttribute('data-browser'));
|
||||
ev.preventDefault();
|
||||
}
|
||||
}), field.nextSibling);
|
||||
}
|
||||
|
||||
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_field(cbid, optional, type)
|
||||
{
|
||||
var field = isElem(cbid) ? cbid : document.getElementById(cbid);
|
||||
var validatorFn;
|
||||
|
||||
try {
|
||||
var cbiValidator = L.validation.create(field, type, optional);
|
||||
validatorFn = cbiValidator.validate.bind(cbiValidator);
|
||||
}
|
||||
catch(e) {
|
||||
validatorFn = null;
|
||||
};
|
||||
|
||||
if (validatorFn !== null) {
|
||||
var form = findParent(field, 'form');
|
||||
|
||||
if (!form.cbi_validators)
|
||||
form.cbi_validators = [ ];
|
||||
|
||||
form.cbi_validators.push(validatorFn);
|
||||
|
||||
field.addEventListener("blur", validatorFn);
|
||||
field.addEventListener("keyup", validatorFn);
|
||||
field.addEventListener("cbi-dropdown-change", validatorFn);
|
||||
|
||||
if (matchesElem(field, 'select')) {
|
||||
field.addEventListener("change", validatorFn);
|
||||
field.addEventListener("click", validatorFn);
|
||||
}
|
||||
|
||||
validatorFn();
|
||||
}
|
||||
}
|
||||
|
||||
function cbi_row_swap(elem, up, store)
|
||||
{
|
||||
var tr = findParent(elem.parentNode, '.cbi-section-table-row');
|
||||
|
||||
if (!tr)
|
||||
return false;
|
||||
|
||||
tr.classList.remove('flash');
|
||||
|
||||
if (up) {
|
||||
var prev = tr.previousElementSibling;
|
||||
|
||||
if (prev && prev.classList.contains('cbi-section-table-row'))
|
||||
tr.parentNode.insertBefore(tr, prev);
|
||||
else
|
||||
return;
|
||||
}
|
||||
else {
|
||||
var next = tr.nextElementSibling ? tr.nextElementSibling.nextElementSibling : null;
|
||||
|
||||
if (next && next.classList.contains('cbi-section-table-row'))
|
||||
tr.parentNode.insertBefore(tr, next);
|
||||
else if (!next)
|
||||
tr.parentNode.appendChild(tr);
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = [ ];
|
||||
|
||||
for (var i = 0, n = 0; i < tr.parentNode.childNodes.length; i++) {
|
||||
var node = tr.parentNode.childNodes[i];
|
||||
if (node.classList && node.classList.contains('cbi-section-table-row')) {
|
||||
node.classList.remove('cbi-rowstyle-1');
|
||||
node.classList.remove('cbi-rowstyle-2');
|
||||
node.classList.add((n++ % 2) ? 'cbi-rowstyle-2' : 'cbi-rowstyle-1');
|
||||
|
||||
if (/-([^\-]+)$/.test(node.id))
|
||||
ids.push(RegExp.$1);
|
||||
}
|
||||
}
|
||||
|
||||
var input = document.getElementById(store);
|
||||
if (input)
|
||||
input.value = ids.join(' ');
|
||||
|
||||
window.scrollTo(0, tr.offsetTop);
|
||||
void tr.offsetWidth;
|
||||
tr.classList.add('flash');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function cbi_tag_last(container)
|
||||
{
|
||||
var last;
|
||||
|
||||
for (var i = 0; i < container.childNodes.length; i++) {
|
||||
var c = container.childNodes[i];
|
||||
if (matchesElem(c, 'div')) {
|
||||
c.classList.remove('cbi-value-last');
|
||||
last = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (last)
|
||||
last.classList.add('cbi-value-last');
|
||||
}
|
||||
|
||||
function cbi_submit(elem, name, value, action)
|
||||
{
|
||||
var form = elem.form || findParent(elem, 'form');
|
||||
|
||||
if (!form)
|
||||
return false;
|
||||
|
||||
if (action)
|
||||
form.action = action;
|
||||
|
||||
if (name) {
|
||||
var hidden = form.querySelector('input[type="hidden"][name="%s"]'.format(name)) ||
|
||||
E('input', { type: 'hidden', name: name });
|
||||
|
||||
hidden.value = value || '1';
|
||||
form.appendChild(hidden);
|
||||
}
|
||||
|
||||
form.submit();
|
||||
return true;
|
||||
}
|
||||
|
||||
String.prototype.format = function()
|
||||
{
|
||||
if (!RegExp)
|
||||
return;
|
||||
|
||||
var html_esc = [/&/g, '&', /"/g, '"', /'/g, ''', /</g, '<', />/g, '>'];
|
||||
var quot_esc = [/"/g, '"', /'/g, '''];
|
||||
|
||||
function esc(s, r) {
|
||||
if (typeof(s) !== 'string' && !(s instanceof String))
|
||||
return '';
|
||||
|
||||
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;
|
||||
else
|
||||
pad = ' ';
|
||||
|
||||
var justifyRight = true;
|
||||
if (pJustify && pJustify === "-")
|
||||
justifyRight = false;
|
||||
|
||||
var minLength = -1;
|
||||
if (pMinLength)
|
||||
minLength = +pMinLength;
|
||||
|
||||
var precision = -1;
|
||||
if (pPrecision && pType == 'f')
|
||||
precision = +pPrecision.substring(1);
|
||||
|
||||
var subst = param;
|
||||
|
||||
switch(pType) {
|
||||
case 'b':
|
||||
subst = (~~param || 0).toString(2);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
subst = String.fromCharCode(+param || 0);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
subst = (~~param || 0);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
subst = ~~Math.abs(+param || 0);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
subst = (precision > -1)
|
||||
? ((+param || 0.0)).toFixed(precision)
|
||||
: (+param || 0.0);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
subst = (~~param || 0).toString(8);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
subst = param;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
subst = ('' + (~~param || 0).toString(16)).toLowerCase();
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
subst = ('' + (~~param || 0).toString(16)).toUpperCase();
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
subst = esc(param, html_esc);
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
subst = esc(param, quot_esc);
|
||||
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 ? +pMinLength : 1000;
|
||||
var pr = pPrecision ? ~~(10 * +('0' + pPrecision)) : 2;
|
||||
|
||||
var i = 0;
|
||||
var val = (+param || 0);
|
||||
var units = [ ' ', ' K', ' M', ' G', ' T', ' P', ' E' ];
|
||||
|
||||
for (i = 0; (i < units.length) && (val > mf); i++)
|
||||
val /= mf;
|
||||
|
||||
subst = (i ? val.toFixed(pr) : val) + units[i];
|
||||
pMinLength = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pMinLength) {
|
||||
subst = subst.toString();
|
||||
for (var i = subst.length; i < pMinLength; i++)
|
||||
if (pJustify == '-')
|
||||
subst = subst + ' ';
|
||||
else
|
||||
subst = pad + subst;
|
||||
}
|
||||
|
||||
out += leftpart + subst;
|
||||
str = str.substr(m.length);
|
||||
}
|
||||
|
||||
return out + str;
|
||||
}
|
||||
|
||||
String.prototype.nobr = function()
|
||||
{
|
||||
return this.replace(/[\s\n]+/g, ' ');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||
NodeList.prototype.forEach = function (callback, thisArg) {
|
||||
thisArg = thisArg || window;
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
callback.call(thisArg, this[i], i, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!window.requestAnimationFrame) {
|
||||
window.requestAnimationFrame = function(f) {
|
||||
window.setTimeout(function() {
|
||||
f(new Date().getTime())
|
||||
}, 1000/30);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function isElem(e) { return L.dom.elem(e) }
|
||||
function toElem(s) { return L.dom.parse(s) }
|
||||
function matchesElem(node, selector) { return L.dom.matches(node, selector) }
|
||||
function findParent(node, selector) { return L.dom.parent(node, selector) }
|
||||
function E() { return L.dom.create.apply(L.dom, arguments) }
|
||||
|
||||
if (typeof(window.CustomEvent) !== 'function') {
|
||||
function CustomEvent(event, params) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||
var evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
window.CustomEvent = CustomEvent;
|
||||
}
|
||||
|
||||
function cbi_dropdown_init(sb) {
|
||||
var dl = new L.ui.Dropdown(sb, null, { name: sb.getAttribute('name') });
|
||||
return dl.bind(sb);
|
||||
}
|
||||
|
||||
function cbi_update_table(table, data, placeholder) {
|
||||
var target = isElem(table) ? table : document.querySelector(table);
|
||||
|
||||
if (!isElem(target))
|
||||
return;
|
||||
|
||||
target.querySelectorAll('.tr.table-titles, .cbi-section-table-titles').forEach(function(thead) {
|
||||
var titles = [];
|
||||
|
||||
thead.querySelectorAll('.th').forEach(function(th) {
|
||||
titles.push(th);
|
||||
});
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
var n = 0, rows = target.querySelectorAll('.tr');
|
||||
|
||||
data.forEach(function(row) {
|
||||
var trow = E('div', { 'class': 'tr' });
|
||||
|
||||
for (var i = 0; i < titles.length; i++) {
|
||||
var text = (titles[i].innerText || '').trim();
|
||||
var td = trow.appendChild(E('div', {
|
||||
'class': titles[i].className,
|
||||
'data-title': (text !== '') ? text : null
|
||||
}, row[i] || ''));
|
||||
|
||||
td.classList.remove('th');
|
||||
td.classList.add('td');
|
||||
}
|
||||
|
||||
trow.classList.add('cbi-rowstyle-%d'.format((n++ % 2) ? 2 : 1));
|
||||
|
||||
if (rows[n])
|
||||
target.replaceChild(trow, rows[n]);
|
||||
else
|
||||
target.appendChild(trow);
|
||||
});
|
||||
|
||||
while (rows[++n])
|
||||
target.removeChild(rows[n]);
|
||||
|
||||
if (placeholder && target.firstElementChild === target.lastElementChild) {
|
||||
var trow = target.appendChild(E('div', { 'class': 'tr placeholder' }));
|
||||
var td = trow.appendChild(E('div', { 'class': titles[0].className }, placeholder));
|
||||
|
||||
td.classList.remove('th');
|
||||
td.classList.add('td');
|
||||
}
|
||||
}
|
||||
else {
|
||||
thead.parentNode.style.display = 'none';
|
||||
|
||||
thead.parentNode.querySelectorAll('.tr, .cbi-section-table-row').forEach(function(trow) {
|
||||
if (trow !== thead) {
|
||||
var n = 0;
|
||||
trow.querySelectorAll('.th, .td').forEach(function(td) {
|
||||
if (n < titles.length) {
|
||||
var text = (titles[n++].innerText || '').trim();
|
||||
if (text !== '')
|
||||
td.setAttribute('data-title', text);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
thead.parentNode.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showModal(title, children)
|
||||
{
|
||||
return L.showModal(title, children);
|
||||
}
|
||||
|
||||
function hideModal()
|
||||
{
|
||||
return L.hideModal();
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('validation-failure', function(ev) {
|
||||
if (ev.target === document.activeElement)
|
||||
L.showTooltip(ev);
|
||||
});
|
||||
|
||||
document.addEventListener('validation-success', function(ev) {
|
||||
if (ev.target === document.activeElement)
|
||||
L.hideTooltip(ev);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.table').forEach(cbi_update_table);
|
||||
});
|
Before Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 268 B |
Before Width: | Height: | Size: 135 B |
Before Width: | Height: | Size: 131 B |
Before Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 267 B |
Before Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 698 B |
Before Width: | Height: | Size: 266 B |
Before Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 279 B |
Before Width: | Height: | Size: 248 B |
Before Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 258 B |
Before Width: | Height: | Size: 263 B |
Before Width: | Height: | Size: 130 B |
Before Width: | Height: | Size: 246 B |
|
@ -1,568 +0,0 @@
|
|||
'use strict';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require tools.prng as random';
|
||||
|
||||
|
||||
function initFirewallState() {
|
||||
return uci.load('firewall');
|
||||
}
|
||||
|
||||
function parseEnum(s, values) {
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
s = String(s).toUpperCase();
|
||||
|
||||
if (s == '')
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
if (values[i].toUpperCase().indexOf(s) == 0)
|
||||
return values[i];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function parsePolicy(s, defaultValue) {
|
||||
return parseEnum(s, ['DROP', 'REJECT', 'ACCEPT']) || (arguments.length < 2 ? null : defaultValue);
|
||||
}
|
||||
|
||||
|
||||
var Firewall, AbstractFirewallItem, Defaults, Zone, Forwarding, Redirect, Rule;
|
||||
|
||||
function lookupZone(name) {
|
||||
var z = uci.get('firewall', name);
|
||||
|
||||
if (z != null && z['.type'] == 'zone')
|
||||
return new Zone(z['.name']);
|
||||
|
||||
var sections = uci.sections('firewall', 'zone');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].name != name)
|
||||
continue;
|
||||
|
||||
return new Zone(sections[i]['.name']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getColorForName(forName) {
|
||||
if (forName == null)
|
||||
return '#eeeeee';
|
||||
else if (forName == 'lan')
|
||||
return '#90f090';
|
||||
else if (forName == 'wan')
|
||||
return '#f09090';
|
||||
|
||||
random.seed(parseInt(sfh(forName), 16));
|
||||
|
||||
var r = random.get(128),
|
||||
g = random.get(128),
|
||||
min = 0,
|
||||
max = 128;
|
||||
|
||||
if ((r + g) < 128)
|
||||
min = 128 - r - g;
|
||||
else
|
||||
max = 255 - r - g;
|
||||
|
||||
var b = min + Math.floor(random.get() * (max - min));
|
||||
|
||||
return '#%02x%02x%02x'.format(0xff - r, 0xff - g, 0xff - b);
|
||||
}
|
||||
|
||||
|
||||
Firewall = L.Class.extend({
|
||||
getDefaults: function() {
|
||||
return initFirewallState().then(function() {
|
||||
return new Defaults();
|
||||
});
|
||||
},
|
||||
|
||||
newZone: function() {
|
||||
return initFirewallState().then(L.bind(function() {
|
||||
var name = 'newzone',
|
||||
count = 1;
|
||||
|
||||
while (this.getZone(name) != null)
|
||||
name = 'newzone%d'.format(++count);
|
||||
|
||||
return this.addZone(name);
|
||||
}, this));
|
||||
},
|
||||
|
||||
addZone: function(name) {
|
||||
return initFirewallState().then(L.bind(function() {
|
||||
if (name == null || !/^[a-zA-Z0-9_]+$/.test(name))
|
||||
return null;
|
||||
|
||||
if (lookupZone(name) != null)
|
||||
return null;
|
||||
|
||||
var d = new Defaults(),
|
||||
z = uci.add('firewall', 'zone');
|
||||
|
||||
uci.set('firewall', z, 'name', name);
|
||||
uci.set('firewall', z, 'network', ' ');
|
||||
uci.set('firewall', z, 'input', d.getInput() || 'DROP');
|
||||
uci.set('firewall', z, 'output', d.getOutput() || 'DROP');
|
||||
uci.set('firewall', z, 'forward', d.getForward() || 'DROP');
|
||||
|
||||
return new Zone(z);
|
||||
}, this));
|
||||
},
|
||||
|
||||
getZone: function(name) {
|
||||
return initFirewallState().then(function() {
|
||||
return lookupZone(name);
|
||||
});
|
||||
},
|
||||
|
||||
getZones: function() {
|
||||
return initFirewallState().then(function() {
|
||||
var sections = uci.sections('firewall', 'zone'),
|
||||
zones = [];
|
||||
|
||||
for (var i = 0; i < sections.length; i++)
|
||||
zones.push(new Zone(sections[i]['.name']));
|
||||
|
||||
zones.sort(function(a, b) { return a.getName() > b.getName() });
|
||||
|
||||
return zones;
|
||||
});
|
||||
},
|
||||
|
||||
getZoneByNetwork: function(network) {
|
||||
return initFirewallState().then(function() {
|
||||
var sections = uci.sections('firewall', 'zone');
|
||||
|
||||
for (var i = 0; i < sections.length; i++)
|
||||
if (L.toArray(sections[i].network || sections[i].name).indexOf(network) != -1)
|
||||
return new Zone(sections[i]['.name']);
|
||||
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
deleteZone: function(name) {
|
||||
return initFirewallState().then(function() {
|
||||
var section = uci.get('firewall', name),
|
||||
found = false;
|
||||
|
||||
if (section != null && section['.type'] == 'zone') {
|
||||
found = true;
|
||||
name = zone.name;
|
||||
uci.remove('firewall', zone['.name']);
|
||||
}
|
||||
else if (name != null) {
|
||||
var sections = uci.sections('firewall', 'zone');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].name != name)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
uci.remove('firewall', sections[i]['.name']);
|
||||
}
|
||||
}
|
||||
|
||||
if (found == true) {
|
||||
sections = uci.sections('firewall');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i]['.type'] != 'rule' &&
|
||||
sections[i]['.type'] != 'redirect' &&
|
||||
sections[i]['.type'] != 'forwarding')
|
||||
continue;
|
||||
|
||||
if (sections[i].src == name || sections[i].dest == name)
|
||||
uci.remove('firewall', sections[i]['.name']);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
});
|
||||
},
|
||||
|
||||
renameZone: function(oldName, newName) {
|
||||
return initFirewallState().then(L.bind(function() {
|
||||
if (oldName == null || newName == null || !/^[a-zA-Z0-9_]+$/.test(newName))
|
||||
return false;
|
||||
|
||||
if (lookupZone(newName) != null)
|
||||
return false;
|
||||
|
||||
var sections = uci.sections('firewall', 'zone'),
|
||||
found = false;
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].name != oldName)
|
||||
continue;
|
||||
|
||||
if (L.toArray(sections[i].network).length == 0)
|
||||
uci.set('firewall', sections[i]['.name'], 'network', oldName);
|
||||
|
||||
uci.set('firewall', sections[i]['.name'], 'name', newName);
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found == true) {
|
||||
sections = uci.sections('firewall');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i]['.type'] != 'rule' &&
|
||||
sections[i]['.type'] != 'redirect' &&
|
||||
sections[i]['.type'] != 'forwarding')
|
||||
continue;
|
||||
|
||||
if (sections[i].src == oldName)
|
||||
uci.set('firewall', sections[i]['.name'], 'src', newName);
|
||||
|
||||
if (sections[i].dest == oldName)
|
||||
uci.set('firewall', sections[i]['.name'], 'dest', newName);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}, this));
|
||||
},
|
||||
|
||||
deleteNetwork: function(network) {
|
||||
return this.getZones().then(L.bind(function(zones) {
|
||||
var rv = false;
|
||||
|
||||
for (var i = 0; i < zones.length; i++)
|
||||
if (zones[i].deleteNetwork(network))
|
||||
rv = true;
|
||||
|
||||
return rv;
|
||||
}, this));
|
||||
},
|
||||
|
||||
getColorForName: getColorForName
|
||||
});
|
||||
|
||||
|
||||
AbstractFirewallItem = L.Class.extend({
|
||||
get: function(option) {
|
||||
return uci.get('firewall', this.sid, option);
|
||||
},
|
||||
|
||||
set: function(option, value) {
|
||||
return uci.set('firewall', this.sid, option, value);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Defaults = AbstractFirewallItem.extend({
|
||||
__init__: function() {
|
||||
var sections = uci.sections('firewall', 'defaults');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
this.sid = sections[i]['.name'];
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.sid == null)
|
||||
this.sid = uci.add('firewall', 'defaults');
|
||||
},
|
||||
|
||||
isSynFlood: function() {
|
||||
return (this.get('syn_flood') == '1');
|
||||
},
|
||||
|
||||
isDropInvalid: function() {
|
||||
return (this.get('drop_invalid') == '1');
|
||||
},
|
||||
|
||||
getInput: function() {
|
||||
return parsePolicy(this.get('input'), 'DROP');
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
return parsePolicy(this.get('output'), 'DROP');
|
||||
},
|
||||
|
||||
getForward: function() {
|
||||
return parsePolicy(this.get('forward'), 'DROP');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Zone = AbstractFirewallItem.extend({
|
||||
__init__: function(name) {
|
||||
var section = uci.get('firewall', name);
|
||||
|
||||
if (section != null && section['.type'] == 'zone') {
|
||||
this.sid = name;
|
||||
this.data = section;
|
||||
}
|
||||
else if (name != null) {
|
||||
var sections = uci.get('firewall', 'zone');
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].name != name)
|
||||
continue;
|
||||
|
||||
this.sid = sections[i]['.name'];
|
||||
this.data = sections[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isMasquerade: function() {
|
||||
return (this.get('masq') == '1');
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return this.get('name');
|
||||
},
|
||||
|
||||
getNetwork: function() {
|
||||
return this.get('network');
|
||||
},
|
||||
|
||||
getInput: function() {
|
||||
return parsePolicy(this.get('input'), (new Defaults()).getInput());
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
return parsePolicy(this.get('output'), (new Defaults()).getOutput());
|
||||
},
|
||||
|
||||
getForward: function() {
|
||||
return parsePolicy(this.get('forward'), (new Defaults()).getForward());
|
||||
},
|
||||
|
||||
addNetwork: function(network) {
|
||||
var section = uci.get('network', network);
|
||||
|
||||
if (section == null || section['.type'] != 'interface')
|
||||
return false;
|
||||
|
||||
var newNetworks = this.getNetworks();
|
||||
|
||||
if (newNetworks.filter(function(net) { return net == network }).length)
|
||||
return false;
|
||||
|
||||
newNetworks.push(network);
|
||||
this.set('network', newNetworks.join(' '));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
deleteNetwork: function(network) {
|
||||
var oldNetworks = this.getNetworks(),
|
||||
newNetworks = oldNetworks.filter(function(net) { return net != network });
|
||||
|
||||
if (newNetworks.length > 0)
|
||||
this.set('network', newNetworks.join(' '));
|
||||
else
|
||||
this.set('network', ' ');
|
||||
|
||||
return (newNetworks.length < oldNetworks.length);
|
||||
},
|
||||
|
||||
getNetworks: function() {
|
||||
return L.toArray(this.get('network') || this.get('name'));
|
||||
},
|
||||
|
||||
clearNetworks: function() {
|
||||
this.set('network', ' ');
|
||||
},
|
||||
|
||||
getDevices: function() {
|
||||
return L.toArray(this.get('device'));
|
||||
},
|
||||
|
||||
getSubnets: function() {
|
||||
return L.toArray(this.get('subnet'));
|
||||
},
|
||||
|
||||
getForwardingsBy: function(what) {
|
||||
var sections = uci.sections('firewall', 'forwarding'),
|
||||
forwards = [];
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].src == null || sections[i].dest == null)
|
||||
continue;
|
||||
|
||||
if (sections[i][what] != this.getName())
|
||||
continue;
|
||||
|
||||
forwards.push(new Forwarding(sections[i]['.name']));
|
||||
}
|
||||
|
||||
return forwards;
|
||||
},
|
||||
|
||||
addForwardingTo: function(dest) {
|
||||
var forwards = this.getForwardingsBy('src'),
|
||||
zone = lookupZone(dest);
|
||||
|
||||
if (zone == null || zone.getName() == this.getName())
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < forwards.length; i++)
|
||||
if (forwards[i].getDestination() == zone.getName())
|
||||
return null;
|
||||
|
||||
var sid = uci.add('firewall', 'forwarding');
|
||||
|
||||
uci.set('firewall', sid, 'src', this.getName());
|
||||
uci.set('firewall', sid, 'dest', zone.getName());
|
||||
|
||||
return new Forwarding(sid);
|
||||
},
|
||||
|
||||
addForwardingFrom: function(src) {
|
||||
var forwards = this.getForwardingsBy('dest'),
|
||||
zone = lookupZone(src);
|
||||
|
||||
if (zone == null || zone.getName() == this.getName())
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < forwards.length; i++)
|
||||
if (forwards[i].getSource() == zone.getName())
|
||||
return null;
|
||||
|
||||
var sid = uci.add('firewall', 'forwarding');
|
||||
|
||||
uci.set('firewall', sid, 'src', zone.getName());
|
||||
uci.set('firewall', sid, 'dest', this.getName());
|
||||
|
||||
return new Forwarding(sid);
|
||||
},
|
||||
|
||||
deleteForwardingsBy: function(what) {
|
||||
var sections = uci.sections('firewall', 'forwarding'),
|
||||
found = false;
|
||||
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
if (sections[i].src == null || sections[i].dest == null)
|
||||
continue;
|
||||
|
||||
if (sections[i][what] != this.getName())
|
||||
continue;
|
||||
|
||||
uci.remove('firewall', sections[i]['.name']);
|
||||
found = true;
|
||||
}
|
||||
|
||||
return found;
|
||||
},
|
||||
|
||||
deleteForwarding: function(forwarding) {
|
||||
if (!(forwarding instanceof Forwarding))
|
||||
return false;
|
||||
|
||||
var section = uci.get('firewall', forwarding.sid);
|
||||
|
||||
if (!section || section['.type'] != 'forwarding')
|
||||
return false;
|
||||
|
||||
uci.remove('firewall', section['.name']);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
addRedirect: function(options) {
|
||||
var sid = uci.add('firewall', 'redirect');
|
||||
|
||||
if (options != null && typeof(options) == 'object')
|
||||
for (var key in options)
|
||||
if (options.hasOwnProperty(key))
|
||||
uci.set('firewall', sid, key, options[key]);
|
||||
|
||||
uci.set('firewall', sid, 'src', this.getName());
|
||||
|
||||
return new Redirect(sid);
|
||||
},
|
||||
|
||||
addRule: function(options) {
|
||||
var sid = uci.add('firewall', 'rule');
|
||||
|
||||
if (options != null && typeof(options) == 'object')
|
||||
for (var key in options)
|
||||
if (options.hasOwnProperty(key))
|
||||
uci.set('firewall', sid, key, options[key]);
|
||||
|
||||
uci.set('firewall', sid, 'src', this.getName());
|
||||
|
||||
return new Redirect(sid);
|
||||
},
|
||||
|
||||
getColor: function(forName) {
|
||||
var name = (arguments.length > 0 ? forName : this.getName());
|
||||
|
||||
return getColorForName(name);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Forwarding = AbstractFirewallItem.extend({
|
||||
__init__: function(sid) {
|
||||
this.sid = sid;
|
||||
},
|
||||
|
||||
getSource: function() {
|
||||
return this.get('src');
|
||||
},
|
||||
|
||||
getDestination: function() {
|
||||
return this.get('dest');
|
||||
},
|
||||
|
||||
getSourceZone: function() {
|
||||
return lookupZone(this.getSource());
|
||||
},
|
||||
|
||||
getDestinationZone: function() {
|
||||
return lookupZone(this.getDestination());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Rule = AbstractFirewallItem.extend({
|
||||
getSource: function() {
|
||||
return this.get('src');
|
||||
},
|
||||
|
||||
getDestination: function() {
|
||||
return this.get('dest');
|
||||
},
|
||||
|
||||
getSourceZone: function() {
|
||||
return lookupZone(this.getSource());
|
||||
},
|
||||
|
||||
getDestinationZone: function() {
|
||||
return lookupZone(this.getDestination());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Redirect = AbstractFirewallItem.extend({
|
||||
getSource: function() {
|
||||
return this.get('src');
|
||||
},
|
||||
|
||||
getDestination: function() {
|
||||
return this.get('dest');
|
||||
},
|
||||
|
||||
getSourceZone: function() {
|
||||
return lookupZone(this.getSource());
|
||||
},
|
||||
|
||||
getDestinationZone: function() {
|
||||
return lookupZone(this.getDestination());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return Firewall;
|
Before Width: | Height: | Size: 706 B |
Before Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 681 B |
Before Width: | Height: | Size: 405 B |
Before Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 399 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 769 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 465 B |
Before Width: | Height: | Size: 467 B |
Before Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 343 B |
Before Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 767 B |
Before Width: | Height: | Size: 494 B |
|
@ -1,5 +0,0 @@
|
|||
/* Licensed under the BSD license. Copyright 2014 - Bram Stein. All rights reserved.
|
||||
* https://github.com/bramstein/promis */
|
||||
(function(){'use strict';var f,g=[];function l(a){g.push(a);1==g.length&&f()}function m(){for(;g.length;)g[0](),g.shift()}f=function(){setTimeout(m)};function n(a){this.a=p;this.b=void 0;this.f=[];var b=this;try{a(function(a){q(b,a)},function(a){r(b,a)})}catch(c){r(b,c)}}var p=2;function t(a){return new n(function(b,c){c(a)})}function u(a){return new n(function(b){b(a)})}function q(a,b){if(a.a==p){if(b==a)throw new TypeError;var c=!1;try{var d=b&&b.then;if(null!=b&&"object"==typeof b&&"function"==typeof d){d.call(b,function(b){c||q(a,b);c=!0},function(b){c||r(a,b);c=!0});return}}catch(e){c||r(a,e);return}a.a=0;a.b=b;v(a)}}
|
||||
function r(a,b){if(a.a==p){if(b==a)throw new TypeError;a.a=1;a.b=b;v(a)}}function v(a){l(function(){if(a.a!=p)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0==a.a?"function"==typeof c?e(c.call(void 0,a.b)):e(a.b):1==a.a&&("function"==typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}n.prototype.g=function(a){return this.c(void 0,a)};n.prototype.c=function(a,b){var c=this;return new n(function(d,e){c.f.push([a,b,d,e]);v(c)})};
|
||||
function w(a){return new n(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e==a.length&&b(h)}}var e=0,h=[];0==a.length&&b(h);for(var k=0;k<a.length;k+=1)u(a[k]).c(d(k),c)})}function x(a){return new n(function(b,c){for(var d=0;d<a.length;d+=1)u(a[d]).c(b,c)})};window.Promise||(window.Promise=n,window.Promise.resolve=u,window.Promise.reject=t,window.Promise.race=x,window.Promise.all=w,window.Promise.prototype.then=n.prototype.c,window.Promise.prototype["catch"]=n.prototype.g,window.Promise.prototype.finally=function(a){return this.c(a,a)});}());
|
|
@ -1,160 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var rpcRequestID = 1,
|
||||
rpcSessionID = L.env.sessionid || '00000000000000000000000000000000',
|
||||
rpcBaseURL = L.url('admin/ubus');
|
||||
|
||||
return L.Class.extend({
|
||||
call: function(req, cb) {
|
||||
var q = '';
|
||||
|
||||
if (Array.isArray(req)) {
|
||||
if (req.length == 0)
|
||||
return Promise.resolve([]);
|
||||
|
||||
for (var i = 0; i < req.length; i++)
|
||||
q += '%s%s.%s'.format(
|
||||
q ? ';' : '/',
|
||||
req[i].params[1],
|
||||
req[i].params[2]
|
||||
);
|
||||
}
|
||||
else {
|
||||
q += '/%s.%s'.format(req.params[1], req.params[2]);
|
||||
}
|
||||
|
||||
return L.Request.post(rpcBaseURL + q, req, {
|
||||
timeout: (L.env.rpctimeout || 5) * 1000,
|
||||
credentials: true
|
||||
}).then(cb);
|
||||
},
|
||||
|
||||
handleListReply: function(req, msg) {
|
||||
var list = msg.result;
|
||||
|
||||
/* verify message frame */
|
||||
if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !Array.isArray(list))
|
||||
list = [ ];
|
||||
|
||||
req.resolve(list);
|
||||
},
|
||||
|
||||
handleCallReply: function(req, res) {
|
||||
var type = Object.prototype.toString,
|
||||
msg = null;
|
||||
|
||||
if (!res.ok)
|
||||
L.error('RPCError', 'RPC call failed with HTTP error %d: %s',
|
||||
res.status, res.statusText || '?');
|
||||
|
||||
msg = res.json();
|
||||
|
||||
/* fetch response attribute and verify returned type */
|
||||
var ret = undefined;
|
||||
|
||||
/* verify message frame */
|
||||
if (typeof(msg) == 'object' && msg.jsonrpc == '2.0') {
|
||||
if (typeof(msg.error) == 'object' && msg.error.code && msg.error.message)
|
||||
req.reject(new Error('RPC call failed with error %d: %s'
|
||||
.format(msg.error.code, msg.error.message || '?')));
|
||||
else if (Array.isArray(msg.result) && msg.result[0] == 0)
|
||||
ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0];
|
||||
}
|
||||
else {
|
||||
req.reject(new Error('Invalid message frame received'));
|
||||
}
|
||||
|
||||
if (req.expect) {
|
||||
for (var key in req.expect) {
|
||||
if (ret != null && key != '')
|
||||
ret = ret[key];
|
||||
|
||||
if (ret == null || type.call(ret) != type.call(req.expect[key]))
|
||||
ret = req.expect[key];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* apply filter */
|
||||
if (typeof(req.filter) == 'function') {
|
||||
req.priv[0] = ret;
|
||||
req.priv[1] = req.params;
|
||||
ret = req.filter.apply(this, req.priv);
|
||||
}
|
||||
|
||||
req.resolve(ret);
|
||||
},
|
||||
|
||||
list: function() {
|
||||
var msg = {
|
||||
jsonrpc: '2.0',
|
||||
id: rpcRequestID++,
|
||||
method: 'list',
|
||||
params: arguments.length ? this.varargs(arguments) : undefined
|
||||
};
|
||||
|
||||
return this.call(msg, this.handleListReply);
|
||||
},
|
||||
|
||||
declare: function(options) {
|
||||
return Function.prototype.bind.call(function(rpc, options) {
|
||||
var args = this.varargs(arguments, 2);
|
||||
return new Promise(function(resolveFn, rejectFn) {
|
||||
/* build parameter object */
|
||||
var p_off = 0;
|
||||
var params = { };
|
||||
if (Array.isArray(options.params))
|
||||
for (p_off = 0; p_off < options.params.length; p_off++)
|
||||
params[options.params[p_off]] = args[p_off];
|
||||
|
||||
/* all remaining arguments are private args */
|
||||
var priv = [ undefined, undefined ];
|
||||
for (; p_off < args.length; p_off++)
|
||||
priv.push(args[p_off]);
|
||||
|
||||
/* store request info */
|
||||
var req = {
|
||||
expect: options.expect,
|
||||
filter: options.filter,
|
||||
resolve: resolveFn,
|
||||
reject: rejectFn,
|
||||
params: params,
|
||||
priv: priv
|
||||
};
|
||||
|
||||
/* build message object */
|
||||
var msg = {
|
||||
jsonrpc: '2.0',
|
||||
id: rpcRequestID++,
|
||||
method: 'call',
|
||||
params: [
|
||||
rpcSessionID,
|
||||
options.object,
|
||||
options.method,
|
||||
params
|
||||
]
|
||||
};
|
||||
|
||||
/* call rpc */
|
||||
rpc.call(msg, rpc.handleCallReply.bind(rpc, req));
|
||||
});
|
||||
}, this, this, options);
|
||||
},
|
||||
|
||||
getSessionID: function() {
|
||||
return rpcSessionID;
|
||||
},
|
||||
|
||||
setSessionID: function(sid) {
|
||||
rpcSessionID = sid;
|
||||
},
|
||||
|
||||
getBaseURL: function() {
|
||||
return rpcBaseURL;
|
||||
},
|
||||
|
||||
setBaseURL: function(url) {
|
||||
rpcBaseURL = url;
|
||||
}
|
||||
});
|
|
@ -1,93 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var s = [0x0000, 0x0000, 0x0000, 0x0000];
|
||||
|
||||
function mul(a, b) {
|
||||
var r = [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000];
|
||||
|
||||
for (var j = 0; j < 4; j++) {
|
||||
var k = 0;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var t = a[i] * b[j] + r[i+j] + k;
|
||||
r[i+j] = t & 0xffff;
|
||||
k = t >>> 16;
|
||||
}
|
||||
r[j+4] = k;
|
||||
}
|
||||
|
||||
r.length = 4;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
function add(a, n) {
|
||||
var r = [0x0000, 0x0000, 0x0000, 0x0000],
|
||||
k = n;
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var t = a[i] + k;
|
||||
r[i] = t & 0xffff;
|
||||
k = t >>> 16;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
function shr(a, n) {
|
||||
var r = [a[0], a[1], a[2], a[3], 0x0000],
|
||||
i = 4,
|
||||
k = 0;
|
||||
|
||||
for (; n > 16; n -= 16, i--)
|
||||
for (var j = 0; j < 4; j++)
|
||||
r[j] = r[j+1];
|
||||
|
||||
for (; i > 0; i--) {
|
||||
var s = r[i-1];
|
||||
r[i-1] = (s >>> n) | k;
|
||||
k = ((s & ((1 << n) - 1)) << (16 - n));
|
||||
}
|
||||
|
||||
r.length = 4;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return L.Class.extend({
|
||||
seed: function(n) {
|
||||
n = (n - 1)|0;
|
||||
s[0] = n & 0xffff;
|
||||
s[1] = n >>> 16;
|
||||
s[2] = 0;
|
||||
s[3] = 0;
|
||||
},
|
||||
|
||||
int: function() {
|
||||
s = mul(s, [0x7f2d, 0x4c95, 0xf42d, 0x5851]);
|
||||
s = add(s, 1);
|
||||
|
||||
var r = shr(s, 33);
|
||||
return (r[1] << 16) | r[0];
|
||||
},
|
||||
|
||||
get: function() {
|
||||
var r = (this.int() % 0x7fffffff) / 0x7fffffff, l, u;
|
||||
|
||||
switch (arguments.length) {
|
||||
case 0:
|
||||
return r;
|
||||
|
||||
case 1:
|
||||
l = 1;
|
||||
u = arguments[0]|0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
l = arguments[0]|0;
|
||||
u = arguments[1]|0;
|
||||
break;
|
||||
}
|
||||
|
||||
return Math.floor(r * (u - l + 1)) + l;
|
||||
}
|
||||
});
|
|
@ -1,568 +0,0 @@
|
|||
'use strict';
|
||||
'require ui';
|
||||
'require form';
|
||||
'require network';
|
||||
'require firewall';
|
||||
|
||||
var CBIZoneSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.ZoneSelect',
|
||||
|
||||
load: function(section_id) {
|
||||
return Promise.all([ firewall.getZones(), network.getNetworks() ]).then(L.bind(function(zn) {
|
||||
this.zones = zn[0];
|
||||
this.networks = zn[1];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter: function(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
lookupZone: function(name) {
|
||||
return this.zones.filter(function(zone) { return zone.getName() == name })[0];
|
||||
},
|
||||
|
||||
lookupNetwork: function(name) {
|
||||
return this.networks.filter(function(network) { return network.getName() == name })[0];
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
isOutputOnly = false,
|
||||
choices = {};
|
||||
|
||||
if (this.option == 'dest') {
|
||||
for (var i = 0; i < this.section.children.length; i++) {
|
||||
var opt = this.section.children[i];
|
||||
if (opt.option == 'src') {
|
||||
var val = opt.cfgvalue(section_id) || opt.default;
|
||||
isOutputOnly = (val == null || val == '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.title = isOutputOnly ? _('Output zone') : _('Destination zone');
|
||||
}
|
||||
|
||||
if (this.allowlocal) {
|
||||
choices[''] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': 'background-color:' + firewall.getColorForName(null)
|
||||
}, [
|
||||
E('strong', _('Device')),
|
||||
(this.allowany || this.allowlocal)
|
||||
? ' (%s)'.format(this.option != 'dest' ? _('output') : _('input')) : ''
|
||||
]);
|
||||
}
|
||||
else if (!this.multiple && (this.rmempty || this.optional)) {
|
||||
choices[''] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': 'background-color:' + firewall.getColorForName(null)
|
||||
}, E('em', _('unspecified')));
|
||||
}
|
||||
|
||||
if (this.allowany) {
|
||||
choices['*'] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': 'background-color:' + firewall.getColorForName(null)
|
||||
}, [
|
||||
E('strong', _('Any zone')),
|
||||
(this.allowany && this.allowlocal && !isOutputOnly) ? ' (%s)'.format(_('forward')) : ''
|
||||
]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.zones.length; i++) {
|
||||
var zone = this.zones[i],
|
||||
name = zone.getName(),
|
||||
networks = zone.getNetworks(),
|
||||
ifaces = [];
|
||||
|
||||
if (!this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < networks.length; j++) {
|
||||
var network = this.lookupNetwork(networks[j]);
|
||||
|
||||
if (!network)
|
||||
continue;
|
||||
|
||||
var span = E('span', {
|
||||
'class': 'ifacebadge' + (network.getName() == this.network ? ' ifacebadge-active' : '')
|
||||
}, network.getName() + ': ');
|
||||
|
||||
var devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
|
||||
|
||||
for (var k = 0; k < devices.length; k++) {
|
||||
span.appendChild(E('img', {
|
||||
'title': devices[k].getI18n(),
|
||||
'src': L.resource('icons/%s%s.png'.format(devices[k].getType(), devices[k].isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!devices.length)
|
||||
span.appendChild(E('em', _('(empty)')));
|
||||
|
||||
ifaces.push(span);
|
||||
}
|
||||
|
||||
if (!ifaces.length)
|
||||
ifaces.push(E('em', _('(empty)')));
|
||||
|
||||
choices[name] = E('span', {
|
||||
'class': 'zonebadge',
|
||||
'style': 'background-color:' + zone.getColor()
|
||||
}, [ E('strong', name) ].concat(ifaces));
|
||||
}
|
||||
|
||||
var widget = new ui.Dropdown(values, choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<span class="zonebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">' +
|
||||
'<strong>{{value}}:</strong> <em>('+_('create')+')</em>' +
|
||||
'</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
var elem = widget.render();
|
||||
|
||||
if (this.option == 'src') {
|
||||
elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) {
|
||||
var opt = this.map.lookupOption('dest', section_id),
|
||||
val = ev.detail.instance.getValue();
|
||||
|
||||
if (opt == null)
|
||||
return;
|
||||
|
||||
var cbid = opt[0].cbid(section_id),
|
||||
label = document.querySelector('label[for="widget.%s"]'.format(cbid)),
|
||||
node = document.getElementById(cbid);
|
||||
|
||||
L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone'));
|
||||
|
||||
if (val == '') {
|
||||
if (L.dom.callClassMethod(node, 'getValue') == '')
|
||||
L.dom.callClassMethod(node, 'setValue', '*');
|
||||
|
||||
var emptyval = node.querySelector('[data-value=""]'),
|
||||
anyval = node.querySelector('[data-value="*"]');
|
||||
|
||||
L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone')));
|
||||
|
||||
if (emptyval != null)
|
||||
emptyval.parentNode.removeChild(emptyval);
|
||||
}
|
||||
else {
|
||||
var anyval = node.querySelector('[data-value="*"]'),
|
||||
emptyval = node.querySelector('[data-value=""]');
|
||||
|
||||
if (emptyval == null) {
|
||||
emptyval = anyval.cloneNode(true);
|
||||
emptyval.removeAttribute('display');
|
||||
emptyval.removeAttribute('selected');
|
||||
emptyval.setAttribute('data-value', '');
|
||||
}
|
||||
|
||||
L.dom.content(emptyval.querySelector('span'), [
|
||||
E('strong', _('Device')), ' (%s)'.format(_('input'))
|
||||
]);
|
||||
|
||||
L.dom.content(anyval.querySelector('span'), [
|
||||
E('strong', _('Any zone')), ' (%s)'.format(_('forward'))
|
||||
]);
|
||||
|
||||
anyval.parentNode.insertBefore(emptyval, anyval);
|
||||
}
|
||||
|
||||
}, this));
|
||||
}
|
||||
else if (isOutputOnly) {
|
||||
var emptyval = elem.querySelector('[data-value=""]');
|
||||
emptyval.parentNode.removeChild(emptyval);
|
||||
}
|
||||
|
||||
return elem;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIZoneForwards = form.DummyValue.extend({
|
||||
__name__: 'CBI.ZoneForwards',
|
||||
|
||||
load: function(section_id) {
|
||||
return Promise.all([
|
||||
firewall.getDefaults(),
|
||||
firewall.getZones(),
|
||||
network.getNetworks(),
|
||||
network.getDevices()
|
||||
]).then(L.bind(function(dznd) {
|
||||
this.defaults = dznd[0];
|
||||
this.zones = dznd[1];
|
||||
this.networks = dznd[2];
|
||||
this.devices = dznd[3];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
renderZone: function(zone) {
|
||||
var name = zone.getName(),
|
||||
networks = zone.getNetworks(),
|
||||
devices = zone.getDevices(),
|
||||
subnets = zone.getSubnets(),
|
||||
ifaces = [];
|
||||
|
||||
for (var j = 0; j < networks.length; j++) {
|
||||
var network = this.networks.filter(function(net) { return net.getName() == networks[j] })[0];
|
||||
|
||||
if (!network)
|
||||
continue;
|
||||
|
||||
var span = E('span', {
|
||||
'class': 'ifacebadge' + (network.getName() == this.network ? ' ifacebadge-active' : '')
|
||||
}, network.getName() + ': ');
|
||||
|
||||
var subdevs = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
|
||||
|
||||
for (var k = 0; k < subdevs.length && subdevs[k]; k++) {
|
||||
span.appendChild(E('img', {
|
||||
'title': subdevs[k].getI18n(),
|
||||
'src': L.resource('icons/%s%s.png'.format(subdevs[k].getType(), subdevs[k].isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!subdevs.length)
|
||||
span.appendChild(E('em', _('(empty)')));
|
||||
|
||||
ifaces.push(span);
|
||||
}
|
||||
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
var device = this.devices.filter(function(dev) { return dev.getName() == devices[i] })[0],
|
||||
title = device ? device.getI18n() : _('Absent Interface'),
|
||||
type = device ? device.getType() : 'ethernet',
|
||||
up = device ? device.isUp() : false;
|
||||
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, [
|
||||
E('img', {
|
||||
'title': title,
|
||||
'src': L.resource('icons/%s%s.png'.format(type, up ? '' : '_disabled'))
|
||||
}),
|
||||
device ? device.getName() : devices[i]
|
||||
]));
|
||||
}
|
||||
|
||||
if (subnets.length > 0)
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, [ '{ %s }'.format(subnets.join('; ')) ]));
|
||||
|
||||
if (!ifaces.length)
|
||||
ifaces.push(E('span', { 'class': 'ifacebadge' }, E('em', _('(empty)'))));
|
||||
|
||||
return E('label', {
|
||||
'class': 'zonebadge cbi-tooltip-container',
|
||||
'style': 'background-color:' + zone.getColor()
|
||||
}, [
|
||||
E('strong', name),
|
||||
E('div', { 'class': 'cbi-tooltip' }, ifaces)
|
||||
]);
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var value = (cfgvalue != null) ? cfgvalue : this.default,
|
||||
zone = this.zones.filter(function(z) { return z.getName() == value })[0];
|
||||
|
||||
if (!zone)
|
||||
return E([]);
|
||||
|
||||
var forwards = zone.getForwardingsBy('src'),
|
||||
dzones = [];
|
||||
|
||||
for (var i = 0; i < forwards.length; i++) {
|
||||
var dzone = forwards[i].getDestinationZone();
|
||||
|
||||
if (!dzone)
|
||||
continue;
|
||||
|
||||
dzones.push(this.renderZone(dzone));
|
||||
}
|
||||
|
||||
if (!dzones.length)
|
||||
dzones.push(E('label', { 'class': 'zonebadge zonebadge-empty' },
|
||||
E('strong', this.defaults.getForward())));
|
||||
|
||||
return E('div', { 'class': 'zone-forwards' }, [
|
||||
E('div', { 'class': 'zone-src' }, this.renderZone(zone)),
|
||||
E('span', '⇒'),
|
||||
E('div', { 'class': 'zone-dest' }, dzones)
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
var CBINetworkSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.NetworkSelect',
|
||||
|
||||
load: function(section_id) {
|
||||
return network.getNetworks().then(L.bind(function(networks) {
|
||||
this.networks = networks;
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter: function(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderIfaceBadge: function(network) {
|
||||
var span = E('span', { 'class': 'ifacebadge' }, network.getName() + ': '),
|
||||
devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
|
||||
|
||||
for (var j = 0; j < devices.length && devices[j]; j++) {
|
||||
span.appendChild(E('img', {
|
||||
'title': devices[j].getI18n(),
|
||||
'src': L.resource('icons/%s%s.png'.format(devices[j].getType(), devices[j].isUp() ? '' : '_disabled'))
|
||||
}));
|
||||
}
|
||||
|
||||
if (!devices.length) {
|
||||
span.appendChild(E('em', { 'class': 'hide-close' }, _('(no interfaces attached)')));
|
||||
span.appendChild(E('em', { 'class': 'hide-open' }, '-'));
|
||||
}
|
||||
|
||||
return span;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
choices = {},
|
||||
checked = {};
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
checked[values[i]] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
for (var i = 0; i < this.networks.length; i++) {
|
||||
var network = this.networks[i],
|
||||
name = network.getName();
|
||||
|
||||
if (name == 'loopback' || name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (this.novirtual && network.isVirtual())
|
||||
continue;
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = this.renderIfaceBadge(network);
|
||||
}
|
||||
|
||||
var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: true,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">' +
|
||||
'{{value}}: <em>('+_('create')+')</em>' +
|
||||
'</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
},
|
||||
|
||||
textvalue: function(section_id) {
|
||||
var cfgvalue = this.cfgvalue(section_id),
|
||||
values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
rv = E([]);
|
||||
|
||||
for (var i = 0; i < (this.networks || []).length; i++) {
|
||||
var network = this.networks[i],
|
||||
name = network.getName();
|
||||
|
||||
if (values.indexOf(name) == -1)
|
||||
continue;
|
||||
|
||||
if (rv.length)
|
||||
L.dom.append(rv, ' ');
|
||||
|
||||
L.dom.append(rv, this.renderIfaceBadge(network));
|
||||
}
|
||||
|
||||
if (!rv.firstChild)
|
||||
rv.appendChild(E('em', _('unspecified')));
|
||||
|
||||
return rv;
|
||||
},
|
||||
});
|
||||
|
||||
var CBIDeviceSelect = form.ListValue.extend({
|
||||
__name__: 'CBI.DeviceSelect',
|
||||
|
||||
load: function(section_id) {
|
||||
return Promise.all([
|
||||
network.getDevices(),
|
||||
this.noaliases ? null : network.getNetworks()
|
||||
]).then(L.bind(function(data) {
|
||||
this.devices = data[0];
|
||||
this.networks = data[1];
|
||||
|
||||
return this.super('load', section_id);
|
||||
}, this));
|
||||
},
|
||||
|
||||
filter: function(section_id, value) {
|
||||
return true;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
|
||||
choices = {},
|
||||
checked = {},
|
||||
order = [];
|
||||
|
||||
for (var i = 0; i < values.length; i++)
|
||||
checked[values[i]] = true;
|
||||
|
||||
values = [];
|
||||
|
||||
if (!this.multiple && (this.rmempty || this.optional))
|
||||
choices[''] = E('em', _('unspecified'));
|
||||
|
||||
for (var i = 0; i < this.devices.length; i++) {
|
||||
var device = this.devices[i],
|
||||
name = device.getName(),
|
||||
type = device.getType();
|
||||
|
||||
if (name == 'lo' || name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (this.noaliases && type == 'alias')
|
||||
continue;
|
||||
|
||||
if (this.nobridges && type == 'bridge')
|
||||
continue;
|
||||
|
||||
if (this.noinactive && device.isUp() == false)
|
||||
continue;
|
||||
|
||||
var item = E([
|
||||
E('img', {
|
||||
'title': device.getI18n(),
|
||||
'src': L.resource('icons/%s%s.png'.format(type, device.isUp() ? '' : '_disabled'))
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ name ]),
|
||||
E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
|
||||
]);
|
||||
|
||||
var networks = device.getNetworks();
|
||||
|
||||
if (networks.length > 0)
|
||||
L.dom.append(item.lastChild, [ ' (', networks.join(', '), ')' ]);
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = item;
|
||||
order.push(name);
|
||||
}
|
||||
|
||||
if (this.networks != null) {
|
||||
for (var i = 0; i < this.networks.length; i++) {
|
||||
var net = this.networks[i],
|
||||
device = network.instantiateDevice('@%s'.format(net.getName()), net),
|
||||
name = device.getName();
|
||||
|
||||
if (name == '@loopback' || name == this.exclude || !this.filter(section_id, name))
|
||||
continue;
|
||||
|
||||
if (this.noinactive && net.isUp() == false)
|
||||
continue;
|
||||
|
||||
var item = E([
|
||||
E('img', {
|
||||
'title': device.getI18n(),
|
||||
'src': L.resource('icons/alias%s.png'.format(net.isUp() ? '' : '_disabled'))
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ name ]),
|
||||
E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
|
||||
]);
|
||||
|
||||
if (checked[name])
|
||||
values.push(name);
|
||||
|
||||
choices[name] = item;
|
||||
order.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.nocreate) {
|
||||
var keys = Object.keys(checked).sort();
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (choices.hasOwnProperty(keys[i]))
|
||||
continue;
|
||||
|
||||
choices[keys[i]] = E([
|
||||
E('img', {
|
||||
'title': _('Absent Interface'),
|
||||
'src': L.resource('icons/ethernet_disabled.png')
|
||||
}),
|
||||
E('span', { 'class': 'hide-open' }, [ keys[i] ]),
|
||||
E('span', { 'class': 'hide-close'}, [ '%s: "%h"'.format(_('Absent Interface'), keys[i]) ])
|
||||
]);
|
||||
|
||||
values.push(keys[i]);
|
||||
order.push(keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: order,
|
||||
multiple: this.multiple,
|
||||
optional: this.optional || this.rmempty,
|
||||
select_placeholder: E('em', _('unspecified')),
|
||||
display_items: this.display_size || this.size || 3,
|
||||
dropdown_items: this.dropdown_size || this.size || 5,
|
||||
validate: L.bind(this.validate, this, section_id),
|
||||
create: !this.nocreate,
|
||||
create_markup: '' +
|
||||
'<li data-value="{{value}}">' +
|
||||
'<img title="'+_('Custom Interface')+': "{{value}}"" src="'+L.resource('icons/ethernet_disabled.png')+'" />' +
|
||||
'<span class="hide-open">{{value}}</span>' +
|
||||
'<span class="hide-close">'+_('Custom Interface')+': "{{value}}"</span>' +
|
||||
'</li>'
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
return L.Class.extend({
|
||||
ZoneSelect: CBIZoneSelect,
|
||||
ZoneForwards: CBIZoneForwards,
|
||||
NetworkSelect: CBINetworkSelect,
|
||||
DeviceSelect: CBIDeviceSelect,
|
||||
});
|
|
@ -1,540 +0,0 @@
|
|||
'use strict';
|
||||
'require rpc';
|
||||
|
||||
return L.Class.extend({
|
||||
__init__: function() {
|
||||
this.state = {
|
||||
newidx: 0,
|
||||
values: { },
|
||||
creates: { },
|
||||
changes: { },
|
||||
deletes: { },
|
||||
reorder: { }
|
||||
};
|
||||
|
||||
this.loaded = {};
|
||||
},
|
||||
|
||||
callLoad: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'get',
|
||||
params: [ 'config' ],
|
||||
expect: { values: { } }
|
||||
}),
|
||||
|
||||
callOrder: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'order',
|
||||
params: [ 'config', 'sections' ]
|
||||
}),
|
||||
|
||||
callAdd: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'add',
|
||||
params: [ 'config', 'type', 'name', 'values' ],
|
||||
expect: { section: '' }
|
||||
}),
|
||||
|
||||
callSet: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'set',
|
||||
params: [ 'config', 'section', 'values' ]
|
||||
}),
|
||||
|
||||
callDelete: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'delete',
|
||||
params: [ 'config', 'section', 'options' ]
|
||||
}),
|
||||
|
||||
callApply: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'apply',
|
||||
params: [ 'timeout', 'rollback' ]
|
||||
}),
|
||||
|
||||
callConfirm: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'confirm'
|
||||
}),
|
||||
|
||||
createSID: function(conf) {
|
||||
var v = this.state.values,
|
||||
n = this.state.creates,
|
||||
sid;
|
||||
|
||||
do {
|
||||
sid = "new%06x".format(Math.random() * 0xFFFFFF);
|
||||
} while ((n[conf] && n[conf][sid]) || (v[conf] && v[conf][sid]));
|
||||
|
||||
return sid;
|
||||
},
|
||||
|
||||
resolveSID: function(conf, sid) {
|
||||
if (typeof(sid) != 'string')
|
||||
return sid;
|
||||
|
||||
var m = /^@([a-zA-Z0-9_-]+)\[(-?[0-9]+)\]$/.exec(sid);
|
||||
|
||||
if (m) {
|
||||
var type = m[1],
|
||||
pos = +m[2],
|
||||
sections = this.sections(conf, type),
|
||||
section = sections[pos >= 0 ? pos : sections.length + pos];
|
||||
|
||||
return section ? section['.name'] : null;
|
||||
}
|
||||
|
||||
return sid;
|
||||
},
|
||||
|
||||
reorderSections: function() {
|
||||
var v = this.state.values,
|
||||
n = this.state.creates,
|
||||
r = this.state.reorder,
|
||||
tasks = [];
|
||||
|
||||
if (Object.keys(r).length === 0)
|
||||
return Promise.resolve();
|
||||
|
||||
/*
|
||||
gather all created and existing sections, sort them according
|
||||
to their index value and issue an uci order call
|
||||
*/
|
||||
for (var c in r) {
|
||||
var o = [ ];
|
||||
|
||||
if (n[c])
|
||||
for (var s in n[c])
|
||||
o.push(n[c][s]);
|
||||
|
||||
for (var s in v[c])
|
||||
o.push(v[c][s]);
|
||||
|
||||
if (o.length > 0) {
|
||||
o.sort(function(a, b) {
|
||||
return (a['.index'] - b['.index']);
|
||||
});
|
||||
|
||||
var sids = [ ];
|
||||
|
||||
for (var i = 0; i < o.length; i++)
|
||||
sids.push(o[i]['.name']);
|
||||
|
||||
tasks.push(this.callOrder(c, sids));
|
||||
}
|
||||
}
|
||||
|
||||
this.state.reorder = { };
|
||||
return Promise.all(tasks);
|
||||
},
|
||||
|
||||
loadPackage: function(packageName) {
|
||||
if (this.loaded[packageName] == null)
|
||||
return (this.loaded[packageName] = this.callLoad(packageName));
|
||||
|
||||
return Promise.resolve(this.loaded[packageName]);
|
||||
},
|
||||
|
||||
load: function(packages) {
|
||||
var self = this,
|
||||
pkgs = [ ],
|
||||
tasks = [];
|
||||
|
||||
if (!Array.isArray(packages))
|
||||
packages = [ packages ];
|
||||
|
||||
for (var i = 0; i < packages.length; i++)
|
||||
if (!self.state.values[packages[i]]) {
|
||||
pkgs.push(packages[i]);
|
||||
tasks.push(self.loadPackage(packages[i]));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(function(responses) {
|
||||
for (var i = 0; i < responses.length; i++)
|
||||
self.state.values[pkgs[i]] = responses[i];
|
||||
|
||||
if (responses.length)
|
||||
document.dispatchEvent(new CustomEvent('uci-loaded'));
|
||||
|
||||
return pkgs;
|
||||
});
|
||||
},
|
||||
|
||||
unload: function(packages) {
|
||||
if (!Array.isArray(packages))
|
||||
packages = [ packages ];
|
||||
|
||||
for (var i = 0; i < packages.length; i++) {
|
||||
delete this.state.values[packages[i]];
|
||||
delete this.state.creates[packages[i]];
|
||||
delete this.state.changes[packages[i]];
|
||||
delete this.state.deletes[packages[i]];
|
||||
|
||||
delete this.loaded[packages[i]];
|
||||
}
|
||||
},
|
||||
|
||||
add: function(conf, type, name) {
|
||||
var n = this.state.creates,
|
||||
sid = name || this.createSID(conf);
|
||||
|
||||
if (!n[conf])
|
||||
n[conf] = { };
|
||||
|
||||
n[conf][sid] = {
|
||||
'.type': type,
|
||||
'.name': sid,
|
||||
'.create': name,
|
||||
'.anonymous': !name,
|
||||
'.index': 1000 + this.state.newidx++
|
||||
};
|
||||
|
||||
return sid;
|
||||
},
|
||||
|
||||
remove: function(conf, sid) {
|
||||
var n = this.state.creates,
|
||||
c = this.state.changes,
|
||||
d = this.state.deletes;
|
||||
|
||||
/* requested deletion of a just created section */
|
||||
if (n[conf] && n[conf][sid]) {
|
||||
delete n[conf][sid];
|
||||
}
|
||||
else {
|
||||
if (c[conf])
|
||||
delete c[conf][sid];
|
||||
|
||||
if (!d[conf])
|
||||
d[conf] = { };
|
||||
|
||||
d[conf][sid] = true;
|
||||
}
|
||||
},
|
||||
|
||||
sections: function(conf, type, cb) {
|
||||
var sa = [ ],
|
||||
v = this.state.values[conf],
|
||||
n = this.state.creates[conf],
|
||||
c = this.state.changes[conf],
|
||||
d = this.state.deletes[conf];
|
||||
|
||||
if (!v)
|
||||
return sa;
|
||||
|
||||
for (var s in v)
|
||||
if (!d || d[s] !== true)
|
||||
if (!type || v[s]['.type'] == type)
|
||||
sa.push(Object.assign({ }, v[s], c ? c[s] : undefined));
|
||||
|
||||
if (n)
|
||||
for (var s in n)
|
||||
if (!type || n[s]['.type'] == type)
|
||||
sa.push(Object.assign({ }, n[s]));
|
||||
|
||||
sa.sort(function(a, b) {
|
||||
return a['.index'] - b['.index'];
|
||||
});
|
||||
|
||||
for (var i = 0; i < sa.length; i++)
|
||||
sa[i]['.index'] = i;
|
||||
|
||||
if (typeof(cb) == 'function')
|
||||
for (var i = 0; i < sa.length; i++)
|
||||
cb.call(this, sa[i], sa[i]['.name']);
|
||||
|
||||
return sa;
|
||||
},
|
||||
|
||||
get: function(conf, sid, opt) {
|
||||
var v = this.state.values,
|
||||
n = this.state.creates,
|
||||
c = this.state.changes,
|
||||
d = this.state.deletes;
|
||||
|
||||
sid = this.resolveSID(conf, sid);
|
||||
|
||||
if (sid == null)
|
||||
return null;
|
||||
|
||||
/* requested option in a just created section */
|
||||
if (n[conf] && n[conf][sid]) {
|
||||
if (!n[conf])
|
||||
return undefined;
|
||||
|
||||
if (opt == null)
|
||||
return n[conf][sid];
|
||||
|
||||
return n[conf][sid][opt];
|
||||
}
|
||||
|
||||
/* requested an option value */
|
||||
if (opt != null) {
|
||||
/* check whether option was deleted */
|
||||
if (d[conf] && d[conf][sid]) {
|
||||
if (d[conf][sid] === true)
|
||||
return undefined;
|
||||
|
||||
for (var i = 0; i < d[conf][sid].length; i++)
|
||||
if (d[conf][sid][i] == opt)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* check whether option was changed */
|
||||
if (c[conf] && c[conf][sid] && c[conf][sid][opt] != null)
|
||||
return c[conf][sid][opt];
|
||||
|
||||
/* return base value */
|
||||
if (v[conf] && v[conf][sid])
|
||||
return v[conf][sid][opt];
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* requested an entire section */
|
||||
if (v[conf])
|
||||
return v[conf][sid];
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
set: function(conf, sid, opt, val) {
|
||||
var v = this.state.values,
|
||||
n = this.state.creates,
|
||||
c = this.state.changes,
|
||||
d = this.state.deletes;
|
||||
|
||||
sid = this.resolveSID(conf, sid);
|
||||
|
||||
if (sid == null || opt == null || opt.charAt(0) == '.')
|
||||
return;
|
||||
|
||||
if (n[conf] && n[conf][sid]) {
|
||||
if (val != null)
|
||||
n[conf][sid][opt] = val;
|
||||
else
|
||||
delete n[conf][sid][opt];
|
||||
}
|
||||
else if (val != null && val !== '') {
|
||||
/* do not set within deleted section */
|
||||
if (d[conf] && d[conf][sid] === true)
|
||||
return;
|
||||
|
||||
/* only set in existing sections */
|
||||
if (!v[conf] || !v[conf][sid])
|
||||
return;
|
||||
|
||||
if (!c[conf])
|
||||
c[conf] = {};
|
||||
|
||||
if (!c[conf][sid])
|
||||
c[conf][sid] = {};
|
||||
|
||||
/* undelete option */
|
||||
if (d[conf] && d[conf][sid])
|
||||
d[conf][sid] = d[conf][sid].filter(function(o) { return o !== opt });
|
||||
|
||||
c[conf][sid][opt] = val;
|
||||
}
|
||||
else {
|
||||
/* only delete in existing sections */
|
||||
if (!(v[conf] && v[conf][sid] && v[conf][sid].hasOwnProperty(opt)) &&
|
||||
!(c[conf] && c[conf][sid] && c[conf][sid].hasOwnProperty(opt)))
|
||||
return;
|
||||
|
||||
if (!d[conf])
|
||||
d[conf] = { };
|
||||
|
||||
if (!d[conf][sid])
|
||||
d[conf][sid] = [ ];
|
||||
|
||||
if (d[conf][sid] !== true)
|
||||
d[conf][sid].push(opt);
|
||||
}
|
||||
},
|
||||
|
||||
unset: function(conf, sid, opt) {
|
||||
return this.set(conf, sid, opt, null);
|
||||
},
|
||||
|
||||
get_first: function(conf, type, opt) {
|
||||
var sid = null;
|
||||
|
||||
this.sections(conf, type, function(s) {
|
||||
if (sid == null)
|
||||
sid = s['.name'];
|
||||
});
|
||||
|
||||
return this.get(conf, sid, opt);
|
||||
},
|
||||
|
||||
set_first: function(conf, type, opt, val) {
|
||||
var sid = null;
|
||||
|
||||
this.sections(conf, type, function(s) {
|
||||
if (sid == null)
|
||||
sid = s['.name'];
|
||||
});
|
||||
|
||||
return this.set(conf, sid, opt, val);
|
||||
},
|
||||
|
||||
unset_first: function(conf, type, opt) {
|
||||
return this.set_first(conf, type, opt, null);
|
||||
},
|
||||
|
||||
move: function(conf, sid1, sid2, after) {
|
||||
var sa = this.sections(conf),
|
||||
s1 = null, s2 = null;
|
||||
|
||||
sid1 = this.resolveSID(conf, sid1);
|
||||
sid2 = this.resolveSID(conf, sid2);
|
||||
|
||||
for (var i = 0; i < sa.length; i++) {
|
||||
if (sa[i]['.name'] != sid1)
|
||||
continue;
|
||||
|
||||
s1 = sa[i];
|
||||
sa.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (s1 == null)
|
||||
return false;
|
||||
|
||||
if (sid2 == null) {
|
||||
sa.push(s1);
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < sa.length; i++) {
|
||||
if (sa[i]['.name'] != sid2)
|
||||
continue;
|
||||
|
||||
s2 = sa[i];
|
||||
sa.splice(i + !!after, 0, s1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (s2 == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < sa.length; i++)
|
||||
this.get(conf, sa[i]['.name'])['.index'] = i;
|
||||
|
||||
this.state.reorder[conf] = true;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
save: function() {
|
||||
var v = this.state.values,
|
||||
n = this.state.creates,
|
||||
c = this.state.changes,
|
||||
d = this.state.deletes,
|
||||
r = this.state.reorder,
|
||||
self = this,
|
||||
snew = [ ],
|
||||
pkgs = { },
|
||||
tasks = [];
|
||||
|
||||
if (n)
|
||||
for (var conf in n) {
|
||||
for (var sid in n[conf]) {
|
||||
var r = {
|
||||
config: conf,
|
||||
values: { }
|
||||
};
|
||||
|
||||
for (var k in n[conf][sid]) {
|
||||
if (k == '.type')
|
||||
r.type = n[conf][sid][k];
|
||||
else if (k == '.create')
|
||||
r.name = n[conf][sid][k];
|
||||
else if (k.charAt(0) != '.')
|
||||
r.values[k] = n[conf][sid][k];
|
||||
}
|
||||
|
||||
snew.push(n[conf][sid]);
|
||||
tasks.push(self.callAdd(r.config, r.type, r.name, r.values));
|
||||
}
|
||||
|
||||
pkgs[conf] = true;
|
||||
}
|
||||
|
||||
if (c)
|
||||
for (var conf in c) {
|
||||
for (var sid in c[conf])
|
||||
tasks.push(self.callSet(conf, sid, c[conf][sid]));
|
||||
|
||||
pkgs[conf] = true;
|
||||
}
|
||||
|
||||
if (d)
|
||||
for (var conf in d) {
|
||||
for (var sid in d[conf]) {
|
||||
var o = d[conf][sid];
|
||||
tasks.push(self.callDelete(conf, sid, (o === true) ? null : o));
|
||||
}
|
||||
|
||||
pkgs[conf] = true;
|
||||
}
|
||||
|
||||
if (r)
|
||||
for (var conf in r)
|
||||
pkgs[conf] = true;
|
||||
|
||||
return Promise.all(tasks).then(function(responses) {
|
||||
/*
|
||||
array "snew" holds references to the created uci sections,
|
||||
use it to assign the returned names of the new sections
|
||||
*/
|
||||
for (var i = 0; i < snew.length; i++)
|
||||
snew[i]['.name'] = responses[i];
|
||||
|
||||
return self.reorderSections();
|
||||
}).then(function() {
|
||||
pkgs = Object.keys(pkgs);
|
||||
|
||||
self.unload(pkgs);
|
||||
|
||||
return self.load(pkgs);
|
||||
});
|
||||
},
|
||||
|
||||
apply: function(timeout) {
|
||||
var self = this,
|
||||
date = new Date();
|
||||
|
||||
if (typeof(timeout) != 'number' || timeout < 1)
|
||||
timeout = 10;
|
||||
|
||||
return self.callApply(timeout, true).then(function(rv) {
|
||||
if (rv != 0)
|
||||
return Promise.reject(rv);
|
||||
|
||||
var try_deadline = date.getTime() + 1000 * timeout;
|
||||
var try_confirm = function() {
|
||||
return self.callConfirm().then(function(rv) {
|
||||
if (rv != 0) {
|
||||
if (date.getTime() < try_deadline)
|
||||
window.setTimeout(try_confirm, 250);
|
||||
else
|
||||
return Promise.reject(rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
});
|
||||
};
|
||||
|
||||
window.setTimeout(try_confirm, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
changes: rpc.declare({
|
||||
object: 'uci',
|
||||
method: 'changes',
|
||||
expect: { changes: { } }
|
||||
})
|
||||
});
|
|
@ -1,568 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Validator = L.Class.extend({
|
||||
__name__: 'Validation',
|
||||
|
||||
__init__: function(field, type, optional, vfunc, validatorFactory) {
|
||||
this.field = field;
|
||||
this.optional = optional;
|
||||
this.vfunc = vfunc;
|
||||
this.vstack = validatorFactory.compile(type);
|
||||
this.factory = validatorFactory;
|
||||
},
|
||||
|
||||
assert: function(condition, message) {
|
||||
if (!condition) {
|
||||
this.field.classList.add('cbi-input-invalid');
|
||||
this.error = message;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.field.classList.remove('cbi-input-invalid');
|
||||
this.error = null;
|
||||
return true;
|
||||
},
|
||||
|
||||
apply: function(name, value, args) {
|
||||
var func;
|
||||
|
||||
if (typeof(name) === 'function')
|
||||
func = name;
|
||||
else if (typeof(this.factory.types[name]) === 'function')
|
||||
func = this.factory.types[name];
|
||||
else
|
||||
return false;
|
||||
|
||||
if (value != null)
|
||||
this.value = value;
|
||||
|
||||
return func.apply(this, args);
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
/* element is detached */
|
||||
if (!findParent(this.field, 'body') && !findParent(this.field, '[data-field]'))
|
||||
return true;
|
||||
|
||||
this.field.classList.remove('cbi-input-invalid');
|
||||
this.value = (this.field.value != null) ? this.field.value : '';
|
||||
this.error = null;
|
||||
|
||||
var valid;
|
||||
|
||||
if (this.value.length === 0)
|
||||
valid = this.assert(this.optional, _('non-empty value'));
|
||||
else
|
||||
valid = this.vstack[0].apply(this, this.vstack[1]);
|
||||
|
||||
if (valid !== true) {
|
||||
this.field.setAttribute('data-tooltip', _('Expecting: %s').format(this.error));
|
||||
this.field.setAttribute('data-tooltip-style', 'error');
|
||||
this.field.dispatchEvent(new CustomEvent('validation-failure', { bubbles: true }));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(this.vfunc) == 'function')
|
||||
valid = this.vfunc(this.value);
|
||||
|
||||
if (valid !== true) {
|
||||
this.assert(false, valid);
|
||||
this.field.setAttribute('data-tooltip', valid);
|
||||
this.field.setAttribute('data-tooltip-style', 'error');
|
||||
this.field.dispatchEvent(new CustomEvent('validation-failure', { bubbles: true }));
|
||||
return false;
|
||||
}
|
||||
|
||||
this.field.removeAttribute('data-tooltip');
|
||||
this.field.removeAttribute('data-tooltip-style');
|
||||
this.field.dispatchEvent(new CustomEvent('validation-success', { bubbles: true }));
|
||||
return true;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
var ValidatorFactory = L.Class.extend({
|
||||
__name__: 'ValidatorFactory',
|
||||
|
||||
create: function(field, type, optional, vfunc) {
|
||||
return new Validator(field, type, optional, vfunc, this);
|
||||
},
|
||||
|
||||
compile: function(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 this.types[label] == 'function') {
|
||||
stack.push(this.types[label]);
|
||||
stack.push(null);
|
||||
}
|
||||
else {
|
||||
L.raise('SyntaxError', 'Unhandled token "%s"', label);
|
||||
}
|
||||
}
|
||||
|
||||
pos = i+1;
|
||||
}
|
||||
|
||||
depth += (code.charCodeAt(i) == 40);
|
||||
break;
|
||||
|
||||
case 41:
|
||||
if (--depth <= 0) {
|
||||
if (typeof stack[stack.length-2] != 'function')
|
||||
L.raise('SyntaxError', 'Argument list follows non-function');
|
||||
|
||||
stack[stack.length-1] = this.compile(code.substring(pos, i));
|
||||
pos = i+1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stack;
|
||||
},
|
||||
|
||||
parseInteger: function(x) {
|
||||
return (/^-?\d+$/.test(x) ? +x : NaN);
|
||||
},
|
||||
|
||||
parseDecimal: function(x) {
|
||||
return (/^-?\d+(?:\.\d+)?$/.test(x) ? +x : NaN);
|
||||
},
|
||||
|
||||
parseIPv4: function(x) {
|
||||
if (!x.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))
|
||||
return null;
|
||||
|
||||
if (RegExp.$1 > 255 || RegExp.$2 > 255 || RegExp.$3 > 255 || RegExp.$4 > 255)
|
||||
return null;
|
||||
|
||||
return [ +RegExp.$1, +RegExp.$2, +RegExp.$3, +RegExp.$4 ];
|
||||
},
|
||||
|
||||
parseIPv6: function(x) {
|
||||
if (x.match(/^([a-fA-F0-9:]+):(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/)) {
|
||||
var v6 = RegExp.$1, v4 = this.parseIPv4(RegExp.$2);
|
||||
|
||||
if (!v4)
|
||||
return null;
|
||||
|
||||
x = v6 + ':' + (v4[0] * 256 + v4[1]).toString(16)
|
||||
+ ':' + (v4[2] * 256 + v4[3]).toString(16);
|
||||
}
|
||||
|
||||
if (!x.match(/^[a-fA-F0-9:]+$/))
|
||||
return null;
|
||||
|
||||
var prefix_suffix = x.split(/::/);
|
||||
|
||||
if (prefix_suffix.length > 2)
|
||||
return null;
|
||||
|
||||
var prefix = (prefix_suffix[0] || '0').split(/:/);
|
||||
var suffix = prefix_suffix.length > 1 ? (prefix_suffix[1] || '0').split(/:/) : [];
|
||||
|
||||
if (suffix.length ? (prefix.length + suffix.length > 7)
|
||||
: ((prefix_suffix.length < 2 && prefix.length < 8) || prefix.length > 8))
|
||||
return null;
|
||||
|
||||
var i, word;
|
||||
var words = [];
|
||||
|
||||
for (i = 0, word = parseInt(prefix[0], 16); i < prefix.length; word = parseInt(prefix[++i], 16))
|
||||
if (prefix[i].length <= 4 && !isNaN(word) && word <= 0xFFFF)
|
||||
words.push(word);
|
||||
else
|
||||
return null;
|
||||
|
||||
for (i = 0; i < (8 - prefix.length - suffix.length); i++)
|
||||
words.push(0);
|
||||
|
||||
for (i = 0, word = parseInt(suffix[0], 16); i < suffix.length; word = parseInt(suffix[++i], 16))
|
||||
if (suffix[i].length <= 4 && !isNaN(word) && word <= 0xFFFF)
|
||||
words.push(word);
|
||||
else
|
||||
return null;
|
||||
|
||||
return words;
|
||||
},
|
||||
|
||||
types: {
|
||||
integer: function() {
|
||||
return this.assert(this.factory.parseInteger(this.value) !== NaN, _('valid integer value'));
|
||||
},
|
||||
|
||||
uinteger: function() {
|
||||
return this.assert(this.factory.parseInteger(this.value) >= 0, _('positive integer value'));
|
||||
},
|
||||
|
||||
float: function() {
|
||||
return this.assert(this.factory.parseDecimal(this.value) !== NaN, _('valid decimal value'));
|
||||
},
|
||||
|
||||
ufloat: function() {
|
||||
return this.assert(this.factory.parseDecimal(this.value) >= 0, _('positive decimal value'));
|
||||
},
|
||||
|
||||
ipaddr: function(nomask) {
|
||||
return this.assert(this.apply('ip4addr', null, [nomask]) || this.apply('ip6addr', null, [nomask]),
|
||||
nomask ? _('valid IP address') : _('valid IP address or prefix'));
|
||||
},
|
||||
|
||||
ip4addr: function(nomask) {
|
||||
var re = nomask ? /^(\d+\.\d+\.\d+\.\d+)$/ : /^(\d+\.\d+\.\d+\.\d+)(?:\/(\d+\.\d+\.\d+\.\d+)|\/(\d{1,2}))?$/,
|
||||
m = this.value.match(re);
|
||||
|
||||
return this.assert(m && this.factory.parseIPv4(m[1]) && (m[2] ? this.factory.parseIPv4(m[2]) : (m[3] ? this.apply('ip4prefix', m[3]) : true)),
|
||||
nomask ? _('valid IPv4 address') : _('valid IPv4 address or network'));
|
||||
},
|
||||
|
||||
ip6addr: function(nomask) {
|
||||
var re = nomask ? /^([0-9a-fA-F:.]+)$/ : /^([0-9a-fA-F:.]+)(?:\/(\d{1,3}))?$/,
|
||||
m = this.value.match(re);
|
||||
|
||||
return this.assert(m && this.factory.parseIPv6(m[1]) && (m[2] ? this.apply('ip6prefix', m[2]) : true),
|
||||
nomask ? _('valid IPv6 address') : _('valid IPv6 address or prefix'));
|
||||
},
|
||||
|
||||
ip4prefix: function() {
|
||||
return this.assert(!isNaN(this.value) && this.value >= 0 && this.value <= 32,
|
||||
_('valid IPv4 prefix value (0-32)'));
|
||||
},
|
||||
|
||||
ip6prefix: function() {
|
||||
return this.assert(!isNaN(this.value) && this.value >= 0 && this.value <= 128,
|
||||
_('valid IPv6 prefix value (0-128)'));
|
||||
},
|
||||
|
||||
cidr: function() {
|
||||
return this.assert(this.apply('cidr4') || this.apply('cidr6'), _('valid IPv4 or IPv6 CIDR'));
|
||||
},
|
||||
|
||||
cidr4: function() {
|
||||
var m = this.value.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/);
|
||||
return this.assert(m && this.factory.parseIPv4(m[1]) && this.apply('ip4prefix', m[2]), _('valid IPv4 CIDR'));
|
||||
},
|
||||
|
||||
cidr6: function() {
|
||||
var m = this.value.match(/^([0-9a-fA-F:.]+)\/(\d{1,3})$/);
|
||||
return this.assert(m && this.factory.parseIPv6(m[1]) && this.apply('ip6prefix', m[2]), _('valid IPv6 CIDR'));
|
||||
},
|
||||
|
||||
ipnet4: function() {
|
||||
var m = this.value.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
|
||||
return this.assert(m && this.factory.parseIPv4(m[1]) && this.factory.parseIPv4(m[2]), _('IPv4 network in address/netmask notation'));
|
||||
},
|
||||
|
||||
ipnet6: function() {
|
||||
var m = this.value.match(/^([0-9a-fA-F:.]+)\/([0-9a-fA-F:.]+)$/);
|
||||
return this.assert(m && this.factory.parseIPv6(m[1]) && this.factory.parseIPv6(m[2]), _('IPv6 network in address/netmask notation'));
|
||||
},
|
||||
|
||||
ip6hostid: function() {
|
||||
if (this.value == "eui64" || this.value == "random")
|
||||
return true;
|
||||
|
||||
var v6 = this.factory.parseIPv6(this.value);
|
||||
return this.assert(!(!v6 || v6[0] || v6[1] || v6[2] || v6[3]), _('valid IPv6 host id'));
|
||||
},
|
||||
|
||||
ipmask: function() {
|
||||
return this.assert(this.apply('ipmask4') || this.apply('ipmask6'),
|
||||
_('valid network in address/netmask notation'));
|
||||
},
|
||||
|
||||
ipmask4: function() {
|
||||
return this.assert(this.apply('cidr4') || this.apply('ipnet4') || this.apply('ip4addr'),
|
||||
_('valid IPv4 network'));
|
||||
},
|
||||
|
||||
ipmask6: function() {
|
||||
return this.assert(this.apply('cidr6') || this.apply('ipnet6') || this.apply('ip6addr'),
|
||||
_('valid IPv6 network'));
|
||||
},
|
||||
|
||||
port: function() {
|
||||
var p = this.factory.parseInteger(this.value);
|
||||
return this.assert(p >= 0 && p <= 65535, _('valid port value'));
|
||||
},
|
||||
|
||||
portrange: function() {
|
||||
if (this.value.match(/^(\d+)-(\d+)$/)) {
|
||||
var p1 = +RegExp.$1;
|
||||
var p2 = +RegExp.$2;
|
||||
return this.assert(p1 <= p2 && p2 <= 65535,
|
||||
_('valid port or port range (port1-port2)'));
|
||||
}
|
||||
|
||||
return this.assert(this.apply('port'), _('valid port or port range (port1-port2)'));
|
||||
},
|
||||
|
||||
macaddr: function() {
|
||||
return this.assert(this.value.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null,
|
||||
_('valid MAC address'));
|
||||
},
|
||||
|
||||
host: function(ipv4only) {
|
||||
return this.assert(this.apply('hostname') || this.apply(ipv4only == 1 ? 'ip4addr' : 'ipaddr'),
|
||||
_('valid hostname or IP address'));
|
||||
},
|
||||
|
||||
hostname: function(strict) {
|
||||
if (this.value.length <= 253)
|
||||
return this.assert(
|
||||
(this.value.match(/^[a-zA-Z0-9_]+$/) != null ||
|
||||
(this.value.match(/^[a-zA-Z0-9_][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$/) &&
|
||||
this.value.match(/[^0-9.]/))) &&
|
||||
(!strict || !this.value.match(/^_/)),
|
||||
_('valid hostname'));
|
||||
|
||||
return this.assert(false, _('valid hostname'));
|
||||
},
|
||||
|
||||
network: function() {
|
||||
return this.assert(this.apply('uciname') || this.apply('host'),
|
||||
_('valid UCI identifier, hostname or IP address'));
|
||||
},
|
||||
|
||||
hostport: function(ipv4only) {
|
||||
var hp = this.value.split(/:/);
|
||||
return this.assert(hp.length == 2 && this.apply('host', hp[0], [ipv4only]) && this.apply('port', hp[1]),
|
||||
_('valid host:port'));
|
||||
},
|
||||
|
||||
ip4addrport: function() {
|
||||
var hp = this.value.split(/:/);
|
||||
return this.assert(hp.length == 2 && this.apply('ip4addr', hp[0], [true]) && this.apply('port', hp[1]),
|
||||
_('valid IPv4 address:port'));
|
||||
},
|
||||
|
||||
ipaddrport: function(bracket) {
|
||||
var m4 = this.value.match(/^([^\[\]:]+):(\d+)$/),
|
||||
m6 = this.value.match((bracket == 1) ? /^\[(.+)\]:(\d+)$/ : /^([^\[\]]+):(\d+)$/);
|
||||
|
||||
if (m4)
|
||||
return this.assert(this.apply('ip4addr', m4[1], [true]) && this.apply('port', m4[2]),
|
||||
_('valid address:port'));
|
||||
|
||||
return this.assert(m6 && this.apply('ip6addr', m6[1], [true]) && this.apply('port', m6[2]),
|
||||
_('valid address:port'));
|
||||
},
|
||||
|
||||
wpakey: function() {
|
||||
var v = this.value;
|
||||
|
||||
if (v.length == 64)
|
||||
return this.assert(v.match(/^[a-fA-F0-9]{64}$/), _('valid hexadecimal WPA key'));
|
||||
|
||||
return this.assert((v.length >= 8) && (v.length <= 63), _('key between 8 and 63 characters'));
|
||||
},
|
||||
|
||||
wepkey: function() {
|
||||
var v = this.value;
|
||||
|
||||
if (v.substr(0, 2) === 's:')
|
||||
v = v.substr(2);
|
||||
|
||||
if ((v.length == 10) || (v.length == 26))
|
||||
return this.assert(v.match(/^[a-fA-F0-9]{10,26}$/), _('valid hexadecimal WEP key'));
|
||||
|
||||
return this.assert((v.length === 5) || (v.length === 13), _('key with either 5 or 13 characters'));
|
||||
},
|
||||
|
||||
uciname: function() {
|
||||
return this.assert(this.value.match(/^[a-zA-Z0-9_]+$/), _('valid UCI identifier'));
|
||||
},
|
||||
|
||||
range: function(min, max) {
|
||||
var val = this.factory.parseDecimal(this.value);
|
||||
return this.assert(val >= +min && val <= +max, _('value between %f and %f').format(min, max));
|
||||
},
|
||||
|
||||
min: function(min) {
|
||||
return this.assert(this.factory.parseDecimal(this.value) >= +min, _('value greater or equal to %f').format(min));
|
||||
},
|
||||
|
||||
max: function(max) {
|
||||
return this.assert(this.factory.parseDecimal(this.value) <= +max, _('value smaller or equal to %f').format(max));
|
||||
},
|
||||
|
||||
rangelength: function(min, max) {
|
||||
var val = '' + this.value;
|
||||
return this.assert((val.length >= +min) && (val.length <= +max),
|
||||
_('value between %d and %d characters').format(min, max));
|
||||
},
|
||||
|
||||
minlength: function(min) {
|
||||
return this.assert((''+this.value).length >= +min,
|
||||
_('value with at least %d characters').format(min));
|
||||
},
|
||||
|
||||
maxlength: function(max) {
|
||||
return this.assert((''+this.value).length <= +max,
|
||||
_('value with at most %d characters').format(max));
|
||||
},
|
||||
|
||||
or: function() {
|
||||
var errors = [];
|
||||
|
||||
for (var i = 0; i < arguments.length; i += 2) {
|
||||
if (typeof arguments[i] != 'function') {
|
||||
if (arguments[i] == this.value)
|
||||
return this.assert(true);
|
||||
errors.push('"%s"'.format(arguments[i]));
|
||||
i--;
|
||||
}
|
||||
else if (arguments[i].apply(this, arguments[i+1])) {
|
||||
return this.assert(true);
|
||||
}
|
||||
else {
|
||||
errors.push(this.error);
|
||||
}
|
||||
}
|
||||
|
||||
var t = _('One of the following: %s');
|
||||
|
||||
return this.assert(false, t.format('\n - ' + errors.join('\n - ')));
|
||||
},
|
||||
|
||||
and: function() {
|
||||
for (var i = 0; i < arguments.length; i += 2) {
|
||||
if (typeof arguments[i] != 'function') {
|
||||
if (arguments[i] != this.value)
|
||||
return this.assert(false, '"%s"'.format(arguments[i]));
|
||||
i--;
|
||||
}
|
||||
else if (!arguments[i].apply(this, arguments[i+1])) {
|
||||
return this.assert(false, this.error);
|
||||
}
|
||||
}
|
||||
|
||||
return this.assert(true);
|
||||
},
|
||||
|
||||
neg: function() {
|
||||
this.value = this.value.replace(/^[ \t]*![ \t]*/, '');
|
||||
|
||||
if (arguments[0].apply(this, arguments[1]))
|
||||
return this.assert(true);
|
||||
|
||||
return this.assert(false, _('Potential negation of: %s').format(this.error));
|
||||
},
|
||||
|
||||
list: function(subvalidator, subargs) {
|
||||
this.field.setAttribute('data-is-list', 'true');
|
||||
|
||||
var tokens = this.value.match(/[^ \t]+/g);
|
||||
for (var i = 0; i < tokens.length; i++)
|
||||
if (!this.apply(subvalidator, tokens[i], subargs))
|
||||
return this.assert(false, this.error);
|
||||
|
||||
return this.assert(true);
|
||||
},
|
||||
|
||||
phonedigit: function() {
|
||||
return this.assert(this.value.match(/^[0-9\*#!\.]+$/),
|
||||
_('valid phone digit (0-9, "*", "#", "!" or ".")'));
|
||||
},
|
||||
|
||||
timehhmmss: function() {
|
||||
return this.assert(this.value.match(/^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$/),
|
||||
_('valid time (HH:MM:SS)'));
|
||||
},
|
||||
|
||||
dateyyyymmdd: function() {
|
||||
if (this.value.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/)) {
|
||||
var year = +RegExp.$1,
|
||||
month = +RegExp.$2,
|
||||
day = +RegExp.$3,
|
||||
days_in_month = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
|
||||
|
||||
var is_leap_year = function(year) {
|
||||
return ((!(year % 4) && (year % 100)) || !(year % 400));
|
||||
}
|
||||
|
||||
var get_days_in_month = function(month, year) {
|
||||
return (month === 2 && is_leap_year(year)) ? 29 : days_in_month[month - 1];
|
||||
}
|
||||
|
||||
/* Firewall rules in the past don't make sense */
|
||||
return this.assert(year >= 2015 && month && month <= 12 && day && day <= get_days_in_month(month, year),
|
||||
_('valid date (YYYY-MM-DD)'));
|
||||
|
||||
}
|
||||
|
||||
return this.assert(false, _('valid date (YYYY-MM-DD)'));
|
||||
},
|
||||
|
||||
unique: function(subvalidator, subargs) {
|
||||
var ctx = this,
|
||||
option = findParent(ctx.field, '[data-type][data-name]'),
|
||||
section = findParent(option, '.cbi-section'),
|
||||
query = '[data-type="%s"][data-name="%s"]'.format(option.getAttribute('data-type'), option.getAttribute('data-name')),
|
||||
unique = true;
|
||||
|
||||
section.querySelectorAll(query).forEach(function(sibling) {
|
||||
if (sibling === option)
|
||||
return;
|
||||
|
||||
var input = sibling.querySelector('[data-type]'),
|
||||
values = input ? (input.getAttribute('data-is-list') ? input.value.match(/[^ \t]+/g) : [ input.value ]) : null;
|
||||
|
||||
if (values !== null && values.indexOf(ctx.value) !== -1)
|
||||
unique = false;
|
||||
});
|
||||
|
||||
if (!unique)
|
||||
return this.assert(false, _('unique value'));
|
||||
|
||||
if (typeof(subvalidator) === 'function')
|
||||
return this.apply(subvalidator, null, subargs);
|
||||
|
||||
return this.assert(true);
|
||||
},
|
||||
|
||||
hexstring: function() {
|
||||
return this.assert(this.value.match(/^([a-f0-9][a-f0-9]|[A-F0-9][A-F0-9])+$/),
|
||||
_('hexadecimal encoded value'));
|
||||
},
|
||||
|
||||
string: function() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ValidatorFactory;
|
|
@ -1 +0,0 @@
|
|||
/* replaced by luci.js */
|
|
@ -1,12 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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
|
|
@ -1,470 +0,0 @@
|
|||
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Copyright 2017 Dan Luedtke <mail@danrl.com>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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 cidr(val)
|
||||
return cidr4(val) or cidr6(val)
|
||||
end
|
||||
|
||||
function cidr4(val)
|
||||
local ip, mask = val:match("^([^/]+)/([^/]+)$")
|
||||
|
||||
return ip4addr(ip) and ip4prefix(mask)
|
||||
end
|
||||
|
||||
function cidr6(val)
|
||||
local ip, mask = val:match("^([^/]+)/([^/]+)$")
|
||||
|
||||
return ip6addr(ip) and ip6prefix(mask)
|
||||
end
|
||||
|
||||
function ipnet4(val)
|
||||
local ip, mask = val:match("^([^/]+)/([^/]+)$")
|
||||
|
||||
return ip4addr(ip) and ip4addr(mask)
|
||||
end
|
||||
|
||||
function ipnet6(val)
|
||||
local ip, mask = val:match("^([^/]+)/([^/]+)$")
|
||||
|
||||
return ip6addr(ip) and ip6addr(mask)
|
||||
end
|
||||
|
||||
function ipmask(val)
|
||||
return ipmask4(val) or ipmask6(val)
|
||||
end
|
||||
|
||||
function ipmask4(val)
|
||||
return cidr4(val) or ipnet4(val) or ip4addr(val)
|
||||
end
|
||||
|
||||
function ipmask6(val)
|
||||
return cidr6(val) or ipnet6(val) or ip6addr(val)
|
||||
end
|
||||
|
||||
function ip6hostid(val)
|
||||
if val == "eui64" or val == "random" then
|
||||
return true
|
||||
else
|
||||
local addr = ip.IPv6(val)
|
||||
if addr and addr:prefix() == 128 and addr:lower("::1:0:0:0:0") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
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)
|
||||
return ip.checkmac(val) and true or false
|
||||
end
|
||||
|
||||
function hostname(val, strict)
|
||||
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 (not strict or not val:match("^_"))
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function host(val, ipv4only)
|
||||
return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
|
||||
end
|
||||
|
||||
function network(val)
|
||||
return uciname(val) or host(val)
|
||||
end
|
||||
|
||||
function hostport(val, ipv4only)
|
||||
local h, p = val:match("^([^:]+):([^:]+)$")
|
||||
return not not (h and p and host(h, ipv4only) and port(p))
|
||||
end
|
||||
|
||||
function ip4addrport(val, bracket)
|
||||
local h, p = val:match("^([^:]+):([^:]+)$")
|
||||
return (h and p and ip4addr(h) and port(p))
|
||||
end
|
||||
|
||||
function ip4addrport(val)
|
||||
local h, p = val:match("^([^:]+):([^:]+)$")
|
||||
return (h and p and ip4addr(h) and port(p))
|
||||
end
|
||||
|
||||
function ipaddrport(val, bracket)
|
||||
local h, p = val:match("^([^%[%]:]+):([^:]+)$")
|
||||
if (h and p and ip4addr(h) and port(p)) then
|
||||
return true
|
||||
elseif (bracket == 1) then
|
||||
h, p = val:match("^%[(.+)%]:([^:]+)$")
|
||||
if (h and p and ip6addr(h) and port(p)) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
h, p = val:match("^([^%[%]]+):([^:]+)$")
|
||||
return (h and p and ip6addr(h) and port(p))
|
||||
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 hexstring(val)
|
||||
if val then
|
||||
return (val:match("^[a-fA-F0-9]+$") ~= nil)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function hex(val, maxbytes)
|
||||
maxbytes = tonumber(maxbytes)
|
||||
if val and maxbytes ~= nil then
|
||||
return ((val:match("^0x[a-fA-F0-9]+$") ~= nil) and (#val <= 2 + maxbytes * 2))
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function base64(val)
|
||||
if val then
|
||||
return (val:match("^[a-zA-Z0-9/+]+=?=?$") ~= nil) and (math.fmod(#val, 4) == 0)
|
||||
end
|
||||
return false
|
||||
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
|
||||
|
||||
function timehhmmss(val)
|
||||
return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
|
||||
end
|
||||
|
||||
function dateyyyymmdd(val)
|
||||
if val ~= nil then
|
||||
yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
|
||||
if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
|
||||
return false;
|
||||
end
|
||||
year = tonumber(yearstr)
|
||||
month = tonumber(monthstr)
|
||||
day = tonumber(daystr)
|
||||
if (year == nil) or (month == nil) or (day == nil) then
|
||||
return false;
|
||||
end
|
||||
|
||||
local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||
|
||||
local function is_leap_year(year)
|
||||
return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
|
||||
end
|
||||
|
||||
function get_days_in_month(month, year)
|
||||
if (month == 2) and is_leap_year(year) then
|
||||
return 29
|
||||
else
|
||||
return days_in_month[month]
|
||||
end
|
||||
end
|
||||
if (year < 2015) then
|
||||
return false
|
||||
end
|
||||
if ((month == 0) or (month > 12)) then
|
||||
return false
|
||||
end
|
||||
if ((day == 0) or (day > get_days_in_month(month, year))) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function unique(val)
|
||||
return true
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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
|
|
@ -1,18 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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)
|
|
@ -1,308 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module("luci.controller.admin.index", package.seeall)
|
||||
|
||||
function index()
|
||||
function toplevel_page(page, preflookup, preftarget)
|
||||
if preflookup and preftarget then
|
||||
if lookup(preflookup) then
|
||||
page.target = preftarget
|
||||
end
|
||||
end
|
||||
|
||||
if not page.target then
|
||||
page.target = firstchild()
|
||||
end
|
||||
end
|
||||
|
||||
local uci = require("luci.model.uci").cursor()
|
||||
|
||||
local root = node()
|
||||
if not root.target then
|
||||
root.target = alias("admin")
|
||||
root.index = true
|
||||
end
|
||||
|
||||
local page = node("admin")
|
||||
|
||||
page.title = _("Administration")
|
||||
page.order = 10
|
||||
page.sysauth = "root"
|
||||
page.sysauth_authenticator = "htmlauth"
|
||||
page.ucidata = true
|
||||
page.index = true
|
||||
page.target = firstnode()
|
||||
|
||||
-- Empty menu tree to be populated by addons and modules
|
||||
|
||||
page = node("admin", "status")
|
||||
page.title = _("Status")
|
||||
page.order = 10
|
||||
page.index = true
|
||||
-- overview is from mod-admin-full
|
||||
toplevel_page(page, "admin/status/overview", alias("admin", "status", "overview"))
|
||||
|
||||
page = node("admin", "system")
|
||||
page.title = _("System")
|
||||
page.order = 20
|
||||
page.index = true
|
||||
-- system/system is from mod-admin-full
|
||||
toplevel_page(page, "admin/system/system", alias("admin", "system", "system"))
|
||||
|
||||
-- Only used if applications add items
|
||||
page = node("admin", "vpn")
|
||||
page.title = _("VPN")
|
||||
page.order = 30
|
||||
page.index = true
|
||||
toplevel_page(page, false, false)
|
||||
|
||||
-- Only used if applications add items
|
||||
page = node("admin", "services")
|
||||
page.title = _("Services")
|
||||
page.order = 40
|
||||
page.index = true
|
||||
toplevel_page(page, false, false)
|
||||
|
||||
-- Even for mod-admin-full network just uses first submenu item as landing
|
||||
page = node("admin", "network")
|
||||
page.title = _("Network")
|
||||
page.order = 50
|
||||
page.index = true
|
||||
toplevel_page(page, false, false)
|
||||
|
||||
if nixio.fs.access("/etc/config/dhcp") then
|
||||
page = entry({"admin", "dhcplease_status"}, call("lease_status"), nil)
|
||||
page.leaf = true
|
||||
end
|
||||
|
||||
local has_wifi = false
|
||||
|
||||
uci:foreach("wireless", "wifi-device",
|
||||
function(s)
|
||||
has_wifi = true
|
||||
return false
|
||||
end)
|
||||
|
||||
if has_wifi then
|
||||
page = entry({"admin", "wireless_assoclist"}, call("wifi_assoclist"), nil)
|
||||
page.leaf = true
|
||||
|
||||
page = entry({"admin", "wireless_deauth"}, post("wifi_deauth"), nil)
|
||||
page.leaf = true
|
||||
end
|
||||
|
||||
page = entry({"admin", "translations"}, call("action_translations"), nil)
|
||||
page.leaf = true
|
||||
|
||||
page = entry({"admin", "ubus"}, call("action_ubus"), nil)
|
||||
page.sysauth = false
|
||||
page.leaf = true
|
||||
|
||||
-- Logout is last
|
||||
entry({"admin", "logout"}, call("action_logout"), _("Logout"), 999)
|
||||
end
|
||||
|
||||
function action_logout()
|
||||
local dsp = require "luci.dispatcher"
|
||||
local utl = require "luci.util"
|
||||
local sid = dsp.context.authsession
|
||||
|
||||
if sid then
|
||||
utl.ubus("session", "destroy", { ubus_rpc_session = sid })
|
||||
|
||||
luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s" %{
|
||||
'', 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url()
|
||||
})
|
||||
end
|
||||
|
||||
luci.http.redirect(dsp.build_url())
|
||||
end
|
||||
|
||||
function action_translations(lang)
|
||||
local i18n = require "luci.i18n"
|
||||
local http = require "luci.http"
|
||||
local fs = require "nixio".fs
|
||||
|
||||
if lang and #lang > 0 then
|
||||
lang = i18n.setlanguage(lang)
|
||||
if lang then
|
||||
local s = fs.stat("%s/base.%s.lmo" %{ i18n.i18ndir, lang })
|
||||
if s then
|
||||
http.header("Cache-Control", "public, max-age=31536000")
|
||||
http.header("ETag", "%x-%x-%x" %{ s["ino"], s["size"], s["mtime"] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
http.prepare_content("application/javascript; charset=utf-8")
|
||||
http.write("window.TR=")
|
||||
http.write_json(i18n.dump())
|
||||
end
|
||||
|
||||
local function ubus_reply(id, data, code, errmsg)
|
||||
local reply = { jsonrpc = "2.0", id = id }
|
||||
if errmsg then
|
||||
reply.error = {
|
||||
code = code,
|
||||
message = errmsg
|
||||
}
|
||||
else
|
||||
reply.result = { code, data }
|
||||
end
|
||||
|
||||
return reply
|
||||
end
|
||||
|
||||
local ubus_types = {
|
||||
nil,
|
||||
"array",
|
||||
"object",
|
||||
"string",
|
||||
nil, -- INT64
|
||||
"number",
|
||||
nil, -- INT16,
|
||||
"boolean",
|
||||
"double"
|
||||
}
|
||||
|
||||
local function ubus_access(sid, obj, fun)
|
||||
local res, code = luci.util.ubus("session", "access", {
|
||||
ubus_rpc_session = sid,
|
||||
scope = "ubus",
|
||||
object = obj,
|
||||
["function"] = fun
|
||||
})
|
||||
|
||||
return (type(res) == "table" and res.access == true)
|
||||
end
|
||||
|
||||
local function ubus_request(req)
|
||||
if type(req) ~= "table" or type(req.method) ~= "string" or type(req.params) ~= "table" or
|
||||
#req.params < 2 or req.jsonrpc ~= "2.0" or req.id == nil then
|
||||
return ubus_reply(nil, nil, -32600, "Invalid request")
|
||||
|
||||
elseif req.method == "call" then
|
||||
local sid, obj, fun, arg =
|
||||
req.params[1], req.params[2], req.params[3], req.params[4] or {}
|
||||
if type(arg) ~= "table" or arg.ubus_rpc_session ~= nil then
|
||||
return ubus_reply(req.id, nil, -32602, "Invalid parameters")
|
||||
end
|
||||
|
||||
if sid == "00000000000000000000000000000000" and luci.dispatcher.context.authsession then
|
||||
sid = luci.dispatcher.context.authsession
|
||||
end
|
||||
|
||||
if not ubus_access(sid, obj, fun) then
|
||||
return ubus_reply(req.id, nil, -32002, "Access denied")
|
||||
end
|
||||
|
||||
arg.ubus_rpc_session = sid
|
||||
|
||||
local res, code = luci.util.ubus(obj, fun, arg)
|
||||
return ubus_reply(req.id, res, code or 0)
|
||||
|
||||
elseif req.method == "list" then
|
||||
if type(params) ~= "table" or #params == 0 then
|
||||
local objs = { luci.util.ubus() }
|
||||
return ubus_reply(req.id, objs, 0)
|
||||
else
|
||||
local n, rv = nil, {}
|
||||
for n = 1, #params do
|
||||
if type(params[n]) ~= "string" then
|
||||
return ubus_reply(req.id, nil, -32602, "Invalid parameters")
|
||||
end
|
||||
|
||||
local sig = luci.util.ubus(params[n])
|
||||
if sig and type(sig) == "table" then
|
||||
rv[params[n]] = {}
|
||||
|
||||
local m, p
|
||||
for m, p in pairs(sig) do
|
||||
if type(p) == "table" then
|
||||
rv[params[n]][m] = {}
|
||||
|
||||
local pn, pt
|
||||
for pn, pt in pairs(p) do
|
||||
rv[params[n]][m][pn] = ubus_types[pt] or "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return ubus_reply(req.id, rv, 0)
|
||||
end
|
||||
end
|
||||
|
||||
return ubus_reply(req.id, nil, -32601, "Method not found")
|
||||
end
|
||||
|
||||
function action_ubus()
|
||||
local parser = require "luci.jsonc".new()
|
||||
|
||||
luci.http.context.request:setfilehandler(function(_, s)
|
||||
if not s then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ok, err = parser:parse(s)
|
||||
return (not err or nil)
|
||||
end)
|
||||
|
||||
luci.http.context.request:content()
|
||||
|
||||
local json = parser:get()
|
||||
if json == nil or type(json) ~= "table" then
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(ubus_reply(nil, nil, -32700, "Parse error"))
|
||||
return
|
||||
end
|
||||
|
||||
local response
|
||||
if #json == 0 then
|
||||
response = ubus_request(json)
|
||||
else
|
||||
response = {}
|
||||
|
||||
local _, request
|
||||
for _, request in ipairs(json) do
|
||||
response[_] = ubus_request(request)
|
||||
end
|
||||
end
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(response)
|
||||
end
|
||||
|
||||
function lease_status()
|
||||
local s = require "luci.tools.status"
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write('[')
|
||||
luci.http.write_json(s.dhcp_leases())
|
||||
luci.http.write(',')
|
||||
luci.http.write_json(s.dhcp6_leases())
|
||||
luci.http.write(']')
|
||||
end
|
||||
|
||||
function wifi_assoclist()
|
||||
local s = require "luci.tools.status"
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(s.wifi_assoclist())
|
||||
end
|
||||
|
||||
function wifi_deauth()
|
||||
local iface = luci.http.formvalue("iface")
|
||||
local bssid = luci.http.formvalue("bssid")
|
||||
|
||||
if iface and bssid then
|
||||
luci.util.ubus("hostapd.%s" % iface, "del_client", {
|
||||
addr = bssid,
|
||||
deauth = true,
|
||||
reason = 5,
|
||||
ban_time = 60000
|
||||
})
|
||||
end
|
||||
luci.http.status(200, "OK")
|
||||
end
|
|
@ -1,96 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2010-2019 Jo-Philipp Wich <jo@mein.io>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module("luci.controller.admin.uci", package.seeall)
|
||||
|
||||
function index()
|
||||
local redir = luci.http.formvalue("redir", true)
|
||||
or table.concat(luci.dispatcher.context.request, "/")
|
||||
|
||||
entry({"admin", "uci"}, nil, _("Configuration"))
|
||||
entry({"admin", "uci", "revert"}, post("action_revert"), nil)
|
||||
|
||||
local node
|
||||
local authen = function(checkpass, allowed_users)
|
||||
return "root", luci.http.formvalue("sid")
|
||||
end
|
||||
|
||||
node = entry({"admin", "uci", "apply_rollback"}, post("action_apply_rollback"), nil)
|
||||
node.cors = true
|
||||
node.sysauth_authenticator = authen
|
||||
|
||||
node = entry({"admin", "uci", "apply_unchecked"}, post("action_apply_unchecked"), nil)
|
||||
node.cors = true
|
||||
node.sysauth_authenticator = authen
|
||||
|
||||
node = entry({"admin", "uci", "confirm"}, call("action_confirm"), nil)
|
||||
node.cors = true
|
||||
node.sysauth = false
|
||||
end
|
||||
|
||||
|
||||
local function ubus_state_to_http(errstr)
|
||||
local map = {
|
||||
["Invalid command"] = 400,
|
||||
["Invalid argument"] = 400,
|
||||
["Method not found"] = 404,
|
||||
["Entry not found"] = 404,
|
||||
["No data"] = 204,
|
||||
["Permission denied"] = 403,
|
||||
["Timeout"] = 504,
|
||||
["Not supported"] = 500,
|
||||
["Unknown error"] = 500,
|
||||
["Connection failed"] = 503
|
||||
}
|
||||
|
||||
local code = map[errstr] or 200
|
||||
local msg = errstr or "OK"
|
||||
|
||||
luci.http.status(code, msg)
|
||||
|
||||
if code ~= 204 then
|
||||
luci.http.prepare_content("text/plain")
|
||||
luci.http.write(msg)
|
||||
end
|
||||
end
|
||||
|
||||
function action_apply_rollback()
|
||||
local uci = require "luci.model.uci"
|
||||
local token, errstr = uci:apply(true)
|
||||
if token then
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ token = token })
|
||||
else
|
||||
ubus_state_to_http(errstr)
|
||||
end
|
||||
end
|
||||
|
||||
function action_apply_unchecked()
|
||||
local uci = require "luci.model.uci"
|
||||
local _, errstr = uci:apply(false)
|
||||
ubus_state_to_http(errstr)
|
||||
end
|
||||
|
||||
function action_confirm()
|
||||
local uci = require "luci.model.uci"
|
||||
local token = luci.http.formvalue("token")
|
||||
local _, errstr = uci:confirm(token)
|
||||
ubus_state_to_http(errstr)
|
||||
end
|
||||
|
||||
function action_revert()
|
||||
local uci = require "luci.model.uci"
|
||||
local changes = uci:changes()
|
||||
|
||||
-- Collect files to be reverted
|
||||
local _, errstr, r, tbl
|
||||
for r, tbl in pairs(changes) do
|
||||
_, errstr = uci:revert(r)
|
||||
if errstr then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
ubus_state_to_http(errstr or "OK")
|
||||
end
|
|
@ -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
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
---[[
|
||||
LuCI web dispatcher.
|
||||
]]
|
||||
module "luci.dispatcher"
|
||||
|
||||
---[[
|
||||
Build the URL relative to the server webroot from given virtual path.
|
||||
|
||||
@class function
|
||||
@name build_url
|
||||
@param ... Virtual path
|
||||
@return Relative URL
|
||||
]]
|
||||
|
||||
---[[
|
||||
Check whether a dispatch node shall be visible
|
||||
|
||||
@class function
|
||||
@name node_visible
|
||||
@param node Dispatch node
|
||||
@return Boolean indicating whether the node should be visible
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return a sorted table of visible children within a given node
|
||||
|
||||
@class function
|
||||
@name node_childs
|
||||
@param node Dispatch node
|
||||
@return Ordered table of child node names
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send a 404 error code and render the "error404" template if available.
|
||||
|
||||
@class function
|
||||
@name error404
|
||||
@param message Custom error message (optional)
|
||||
@return false
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send a 500 error code and render the "error500" template if available.
|
||||
|
||||
@class function
|
||||
@name error500
|
||||
@param message Custom error message (optional)#
|
||||
@return false
|
||||
]]
|
||||
|
||||
---[[
|
||||
Dispatch an HTTP request.
|
||||
|
||||
@class function
|
||||
@name httpdispatch
|
||||
@param request LuCI HTTP Request object
|
||||
]]
|
||||
|
||||
---[[
|
||||
Dispatches a LuCI virtual path.
|
||||
|
||||
@class function
|
||||
@name dispatch
|
||||
@param request Virtual path
|
||||
]]
|
||||
|
||||
---[[
|
||||
Generate the dispatching index using the native file-cache based strategy.
|
||||
|
||||
|
||||
@class function
|
||||
@name createindex
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create the dispatching tree from the index.
|
||||
|
||||
Build the index before if it does not exist yet.
|
||||
|
||||
@class function
|
||||
@name createtree
|
||||
]]
|
||||
|
||||
---[[
|
||||
Clone a node of the dispatching tree to another position.
|
||||
|
||||
@class function
|
||||
@name assign
|
||||
@param path Virtual path destination
|
||||
@param clone Virtual path source
|
||||
@param title Destination node title (optional)
|
||||
@param order Destination node order value (optional)
|
||||
@return Dispatching tree node
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a new dispatching node and define common parameters.
|
||||
|
||||
@class function
|
||||
@name entry
|
||||
@param path Virtual path
|
||||
@param target Target function to call when dispatched.
|
||||
@param title Destination node title
|
||||
@param order Destination node order value (optional)
|
||||
@return Dispatching tree node
|
||||
]]
|
||||
|
||||
---[[
|
||||
Fetch or create a dispatching node without setting the target module or
|
||||
enabling the node.
|
||||
|
||||
@class function
|
||||
@name get
|
||||
@param ... Virtual path
|
||||
@return Dispatching tree node
|
||||
]]
|
||||
|
||||
---[[
|
||||
Fetch or create a new dispatching node.
|
||||
|
||||
@class function
|
||||
@name node
|
||||
@param ... Virtual path
|
||||
@return Dispatching tree node
|
||||
]]
|
||||
|
||||
---[[
|
||||
Lookup node in dispatching tree.
|
||||
|
||||
@class function
|
||||
@name lookup
|
||||
@param ... Virtual path
|
||||
@return Node object, canonical url or nil if the path was not found.
|
||||
]]
|
||||
|
||||
---[[
|
||||
Alias the first (lowest order) page automatically
|
||||
|
||||
|
||||
@class function
|
||||
@name firstchild
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a redirect to another dispatching node.
|
||||
|
||||
@class function
|
||||
@name alias
|
||||
@param ... Virtual path destination
|
||||
]]
|
||||
|
||||
---[[
|
||||
Rewrite the first x path values of the request.
|
||||
|
||||
@class function
|
||||
@name rewrite
|
||||
@param n Number of path values to replace
|
||||
@param ... Virtual path to replace removed path values with
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a function-call dispatching target.
|
||||
|
||||
@class function
|
||||
@name call
|
||||
@param name Target function of local controller
|
||||
@param ... Additional parameters passed to the function
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a template render dispatching target.
|
||||
|
||||
@class function
|
||||
@name template
|
||||
@param name Template to be rendered
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a CBI model dispatching target.
|
||||
|
||||
@class function
|
||||
@name cbi
|
||||
@param model CBI model to be rendered
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a combined dispatching target for non argv and argv requests.
|
||||
|
||||
@class function
|
||||
@name arcombine
|
||||
@param trg1 Overview Target
|
||||
@param trg2 Detail Target
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a CBI form model dispatching target.
|
||||
|
||||
@class function
|
||||
@name form
|
||||
@param model CBI form model tpo be rendered
|
||||
]]
|
||||
|
||||
---[[
|
||||
Access the luci.i18n translate() api.
|
||||
|
||||
@class function
|
||||
@name translate
|
||||
@param text Text to translate
|
||||
]]
|
||||
|
||||
---[[
|
||||
No-op function used to mark translation entries for menu labels.
|
||||
|
||||
This function does not actually translate the given argument but
|
||||
is used by build/i18n-scan.pl to find translatable entries.
|
||||
|
||||
@class function
|
||||
@name _
|
||||
]]
|
||||
|
|
@ -1,554 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local util = require "luci.util"
|
||||
local coroutine = require "coroutine"
|
||||
local table = require "table"
|
||||
local lhttp = require "lucihttp"
|
||||
local nixio = require "nixio"
|
||||
local ltn12 = require "luci.ltn12"
|
||||
|
||||
local table, ipairs, pairs, type, tostring, tonumber, error =
|
||||
table, ipairs, pairs, type, tostring, tonumber, error
|
||||
|
||||
module "luci.http"
|
||||
|
||||
HTTP_MAX_CONTENT = 1024*100 -- 100 kB maximum content size
|
||||
|
||||
context = util.threadlocal()
|
||||
|
||||
Request = util.class()
|
||||
function Request.__init__(self, env, sourcein, sinkerr)
|
||||
self.input = sourcein
|
||||
self.error = sinkerr
|
||||
|
||||
|
||||
-- File handler nil by default to let .content() work
|
||||
self.filehandler = nil
|
||||
|
||||
-- HTTP-Message table
|
||||
self.message = {
|
||||
env = env,
|
||||
headers = {},
|
||||
params = 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)
|
||||
return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
|
||||
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
|
||||
|
||||
if not self.parsed_input then
|
||||
return
|
||||
end
|
||||
|
||||
-- If input has already been parsed then uploads are stored as unlinked
|
||||
-- temporary files pointed to by open file handles in the parameter
|
||||
-- value table. Loop all params, and invoke the file callback for any
|
||||
-- param with an open file handle.
|
||||
local name, value
|
||||
for name, value in pairs(self.message.params) do
|
||||
if type(value) == "table" then
|
||||
while value.fd do
|
||||
local data = value.fd:read(1024)
|
||||
local eof = (not data or data == "")
|
||||
|
||||
callback(value, data, eof)
|
||||
|
||||
if eof then
|
||||
value.fd:close()
|
||||
value.fd = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Request._parse_input(self)
|
||||
parse_message_body(
|
||||
self.input,
|
||||
self.message,
|
||||
self.filehandler
|
||||
)
|
||||
self.parsed_input = true
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function content()
|
||||
return context.request:content()
|
||||
end
|
||||
|
||||
function formvalue(name, noparse)
|
||||
return context.request:formvalue(name, noparse)
|
||||
end
|
||||
|
||||
function formvaluetable(prefix)
|
||||
return context.request:formvaluetable(prefix)
|
||||
end
|
||||
|
||||
function getcookie(name)
|
||||
return context.request:getcookie(name)
|
||||
end
|
||||
|
||||
-- or the environment table itself.
|
||||
function getenv(name)
|
||||
return context.request:getenv(name)
|
||||
end
|
||||
|
||||
function setfilehandler(callback)
|
||||
return context.request:setfilehandler(callback)
|
||||
end
|
||||
|
||||
function header(key, value)
|
||||
if not context.headers then
|
||||
context.headers = {}
|
||||
end
|
||||
context.headers[key:lower()] = value
|
||||
coroutine.yield(2, key, value)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function source()
|
||||
return context.request.input
|
||||
end
|
||||
|
||||
function status(code, message)
|
||||
code = code or 200
|
||||
message = message or "OK"
|
||||
context.status = code
|
||||
coroutine.yield(1, code, message)
|
||||
end
|
||||
|
||||
-- This function is as a valid LTN12 sink.
|
||||
-- If the content chunk is nil this function will automatically invoke 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
|
||||
if not context.headers["x-frame-options"] then
|
||||
header("X-Frame-Options", "SAMEORIGIN")
|
||||
end
|
||||
if not context.headers["x-xss-protection"] then
|
||||
header("X-XSS-Protection", "1; mode=block")
|
||||
end
|
||||
if not context.headers["x-content-type-options"] then
|
||||
header("X-Content-Type-Options", "nosniff")
|
||||
end
|
||||
|
||||
context.eoh = true
|
||||
coroutine.yield(3)
|
||||
end
|
||||
coroutine.yield(4, content)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function splice(fd, size)
|
||||
coroutine.yield(6, fd, size)
|
||||
end
|
||||
|
||||
function redirect(url)
|
||||
if url == "" then url = "/" end
|
||||
status(302, "Found")
|
||||
header("Location", url)
|
||||
close()
|
||||
end
|
||||
|
||||
function build_querystring(q)
|
||||
local s, n, k, v = {}, 1, nil, nil
|
||||
|
||||
for k, v in pairs(q) do
|
||||
s[n+0] = (n == 1) and "?" or "&"
|
||||
s[n+1] = util.urlencode(k)
|
||||
s[n+2] = "="
|
||||
s[n+3] = util.urlencode(v)
|
||||
n = n + 4
|
||||
end
|
||||
|
||||
return table.concat(s, "")
|
||||
end
|
||||
|
||||
urldecode = util.urldecode
|
||||
|
||||
urlencode = util.urlencode
|
||||
|
||||
function write_json(x)
|
||||
util.serialize_json(x, write)
|
||||
end
|
||||
|
||||
-- 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.
|
||||
function urldecode_params(url, tbl)
|
||||
local parser, name
|
||||
local params = tbl or { }
|
||||
|
||||
parser = lhttp.urlencoded_parser(function (what, buffer, length)
|
||||
if what == parser.TUPLE then
|
||||
name, value = nil, nil
|
||||
elseif what == parser.NAME then
|
||||
name = lhttp.urldecode(buffer)
|
||||
elseif what == parser.VALUE and name then
|
||||
params[name] = lhttp.urldecode(buffer) or ""
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
if parser then
|
||||
parser:parse((url or ""):match("[^?]*$"))
|
||||
parser:parse(nil)
|
||||
end
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
-- separated by "&". Tables are encoded as parameters with multiple values by
|
||||
-- repeating the parameter name with each value.
|
||||
function urlencode_params(tbl)
|
||||
local k, v
|
||||
local n, enc = 1, {}
|
||||
for k, v in pairs(tbl) do
|
||||
if type(v) == "table" then
|
||||
local i, v2
|
||||
for i, v2 in ipairs(v) do
|
||||
if enc[1] then
|
||||
enc[n] = "&"
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
enc[n+0] = lhttp.urlencode(k)
|
||||
enc[n+1] = "="
|
||||
enc[n+2] = lhttp.urlencode(v2)
|
||||
n = n + 3
|
||||
end
|
||||
else
|
||||
if enc[1] then
|
||||
enc[n] = "&"
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
enc[n+0] = lhttp.urlencode(k)
|
||||
enc[n+1] = "="
|
||||
enc[n+2] = lhttp.urlencode(v)
|
||||
n = n + 3
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(enc, "")
|
||||
end
|
||||
|
||||
-- Content-Type. Stores all extracted data associated with its parameter name
|
||||
-- in the params table within 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 fed 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 whether the current chunk is the last one (eof)
|
||||
function mimedecode_message_body(src, msg, file_cb)
|
||||
local parser, header, field
|
||||
local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
|
||||
|
||||
parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length)
|
||||
if what == parser.PART_INIT then
|
||||
field = { }
|
||||
|
||||
elseif what == parser.HEADER_NAME then
|
||||
header = buffer:lower()
|
||||
|
||||
elseif what == parser.HEADER_VALUE and header then
|
||||
if header:lower() == "content-disposition" and
|
||||
lhttp.header_attribute(buffer, nil) == "form-data"
|
||||
then
|
||||
field.name = lhttp.header_attribute(buffer, "name")
|
||||
field.file = lhttp.header_attribute(buffer, "filename")
|
||||
field[1] = field.file
|
||||
end
|
||||
|
||||
if field.headers then
|
||||
field.headers[header] = buffer
|
||||
else
|
||||
field.headers = { [header] = buffer }
|
||||
end
|
||||
|
||||
elseif what == parser.PART_BEGIN then
|
||||
return not field.file
|
||||
|
||||
elseif what == parser.PART_DATA and field.name and length > 0 then
|
||||
if field.file then
|
||||
if file_cb then
|
||||
file_cb(field, buffer, false)
|
||||
msg.params[field.name] = msg.params[field.name] or field
|
||||
else
|
||||
if not field.fd then
|
||||
field.fd = nixio.mkstemp(field.name)
|
||||
end
|
||||
|
||||
if field.fd then
|
||||
field.fd:write(buffer)
|
||||
msg.params[field.name] = msg.params[field.name] or field
|
||||
end
|
||||
end
|
||||
else
|
||||
field.value = buffer
|
||||
end
|
||||
|
||||
elseif what == parser.PART_END and field.name then
|
||||
if field.file and msg.params[field.name] then
|
||||
if file_cb then
|
||||
file_cb(field, "", true)
|
||||
elseif field.fd then
|
||||
field.fd:seek(0, "set")
|
||||
end
|
||||
else
|
||||
local val = msg.params[field.name]
|
||||
|
||||
if type(val) == "table" then
|
||||
val[#val+1] = field.value or ""
|
||||
elseif val ~= nil then
|
||||
msg.params[field.name] = { val, field.value or "" }
|
||||
else
|
||||
msg.params[field.name] = field.value or ""
|
||||
end
|
||||
end
|
||||
|
||||
field = nil
|
||||
|
||||
elseif what == parser.ERROR then
|
||||
err = buffer
|
||||
end
|
||||
|
||||
return true
|
||||
end, HTTP_MAX_CONTENT)
|
||||
|
||||
return ltn12.pump.all(src, function (chunk)
|
||||
len = len + (chunk and #chunk or 0)
|
||||
|
||||
if maxlen and len > maxlen + 2 then
|
||||
return nil, "Message body size exceeds Content-Length"
|
||||
end
|
||||
|
||||
if not parser or not parser:parse(chunk) then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
|
||||
-- Content-Type. Stores all extracted data associated with its parameter name
|
||||
-- in the params table within the given message object. Multiple parameter
|
||||
-- values are stored as tables, ordinary ones as strings.
|
||||
function urldecode_message_body(src, msg)
|
||||
local err, name, value, parser
|
||||
local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
|
||||
|
||||
parser = lhttp.urlencoded_parser(function (what, buffer, length)
|
||||
if what == parser.TUPLE then
|
||||
name, value = nil, nil
|
||||
elseif what == parser.NAME then
|
||||
name = lhttp.urldecode(buffer, lhttp.DECODE_PLUS)
|
||||
elseif what == parser.VALUE and name then
|
||||
local val = msg.params[name]
|
||||
|
||||
if type(val) == "table" then
|
||||
val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
|
||||
elseif val ~= nil then
|
||||
msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
|
||||
else
|
||||
msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
|
||||
end
|
||||
elseif what == parser.ERROR then
|
||||
err = buffer
|
||||
end
|
||||
|
||||
return true
|
||||
end, HTTP_MAX_CONTENT)
|
||||
|
||||
return ltn12.pump.all(src, function (chunk)
|
||||
len = len + (chunk and #chunk or 0)
|
||||
|
||||
if maxlen and len > maxlen + 2 then
|
||||
return nil, "Message body size exceeds Content-Length"
|
||||
elseif len > HTTP_MAX_CONTENT then
|
||||
return nil, "Message body size exceeds maximum allowed length"
|
||||
end
|
||||
|
||||
if not parser or not parser:parse(chunk) then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
|
||||
-- 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.
|
||||
function parse_message_body(src, msg, filecb)
|
||||
if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then
|
||||
local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
|
||||
|
||||
-- Is it multipart/mime ?
|
||||
if ctype == "multipart/form-data" then
|
||||
return mimedecode_message_body(src, msg, filecb)
|
||||
|
||||
-- Is it application/x-www-form-urlencoded ?
|
||||
elseif ctype == "application/x-www-form-urlencoded" then
|
||||
return urldecode_message_body(src, msg)
|
||||
|
||||
end
|
||||
|
||||
-- Unhandled encoding
|
||||
-- If a file callback is given then feed it chunk by chunk, else
|
||||
-- store whole buffer in message.content
|
||||
local sink
|
||||
|
||||
-- If we have a file callback then feed it
|
||||
if type(filecb) == "function" then
|
||||
local meta = {
|
||||
name = "raw",
|
||||
encoding = msg.env.CONTENT_TYPE
|
||||
}
|
||||
sink = function( chunk )
|
||||
if chunk then
|
||||
return filecb(meta, chunk, false)
|
||||
else
|
||||
return filecb(meta, nil, true)
|
||||
end
|
||||
end
|
||||
-- ... else append to .content
|
||||
else
|
||||
msg.content = ""
|
||||
msg.content_length = 0
|
||||
|
||||
sink = function( chunk )
|
||||
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 ok then -- eof
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
|
@ -1,260 +0,0 @@
|
|||
---[[
|
||||
LuCI Web Framework high-level HTTP functions.
|
||||
]]
|
||||
module "luci.http"
|
||||
|
||||
---[[
|
||||
Close the HTTP-Connection.
|
||||
|
||||
@class function
|
||||
@name close
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the request content if the request was of unknown type.
|
||||
|
||||
@class function
|
||||
@name content
|
||||
@return HTTP request body
|
||||
@return HTTP request body length
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get a certain HTTP input value or a table of all input values.
|
||||
|
||||
@class function
|
||||
@name formvalue
|
||||
@param name Name of the GET or POST variable to fetch
|
||||
@param noparse Don't parse POST data before getting the value
|
||||
@return HTTP input value or table of all input value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get a table of all HTTP input values with a certain prefix.
|
||||
|
||||
@class function
|
||||
@name formvaluetable
|
||||
@param prefix Prefix
|
||||
@return Table of all HTTP input values with given prefix
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the value of a certain HTTP-Cookie.
|
||||
|
||||
@class function
|
||||
@name getcookie
|
||||
@param name Cookie Name
|
||||
@return String containing cookie data
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the value of a certain HTTP environment variable
|
||||
or the environment table itself.
|
||||
|
||||
@class function
|
||||
@name getenv
|
||||
@param name Environment variable
|
||||
@return HTTP environment value or environment table
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set a handler function for incoming user file uploads.
|
||||
|
||||
@class function
|
||||
@name setfilehandler
|
||||
@param callback Handler function
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send a HTTP-Header.
|
||||
|
||||
@class function
|
||||
@name header
|
||||
@param key Header key
|
||||
@param value Header value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the mime type of following content data.
|
||||
|
||||
@class function
|
||||
@name prepare_content
|
||||
@param mime Mimetype of following content
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the RAW HTTP input source
|
||||
|
||||
@class function
|
||||
@name source
|
||||
@return HTTP LTN12 source
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the HTTP status code and status message.
|
||||
|
||||
@class function
|
||||
@name status
|
||||
@param code Status code
|
||||
@param message Status message
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send a chunk of content data to the client.
|
||||
|
||||
This function is as a valid LTN12 sink.
|
||||
If the content chunk is nil this function will automatically invoke close.
|
||||
|
||||
@class function
|
||||
@name write
|
||||
@param content Content chunk
|
||||
@param src_err Error object from source (optional)
|
||||
@see close
|
||||
]]
|
||||
|
||||
---[[
|
||||
Splice data from a filedescriptor to the client.
|
||||
|
||||
@class function
|
||||
@name splice
|
||||
@param fp File descriptor
|
||||
@param size Bytes to splice (optional)
|
||||
]]
|
||||
|
||||
---[[
|
||||
Redirects the client to a new URL and closes the connection.
|
||||
|
||||
@class function
|
||||
@name redirect
|
||||
@param url Target URL
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a querystring out of a table of key - value pairs.
|
||||
|
||||
@class function
|
||||
@name build_querystring
|
||||
@param table Query string source table
|
||||
@return Encoded HTTP query string
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the URL-decoded equivalent of a string.
|
||||
|
||||
@class function
|
||||
@name urldecode
|
||||
@param str URL-encoded string
|
||||
@param no_plus Don't decode + to " "
|
||||
@return URL-decoded string
|
||||
@see urlencode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the URL-encoded equivalent of a string.
|
||||
|
||||
@class function
|
||||
@name urlencode
|
||||
@param str Source string
|
||||
@return URL-encoded string
|
||||
@see urldecode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send the given data as JSON encoded string.
|
||||
|
||||
@class function
|
||||
@name write_json
|
||||
@param data Data to send
|
||||
]]
|
||||
|
||||
---[[
|
||||
Extract and split urlencoded data pairs, separated bei either "&" or ";"
|
||||
from given url or string. Returns a table with urldecoded values.
|
||||
|
||||
Simple parameters are stored as string values associated with the parameter
|
||||
name within the table. Parameters with multiple values are stored as array
|
||||
containing the corresponding values.
|
||||
|
||||
@class function
|
||||
@name urldecode_params
|
||||
@param url The url or string which contains x-www-urlencoded form data
|
||||
@param tbl Use the given table for storing values (optional)
|
||||
@return Table containing the urldecoded parameters
|
||||
@see urlencode_params
|
||||
]]
|
||||
|
||||
---[[
|
||||
Encode each key-value-pair in given table to x-www-urlencoded format,
|
||||
separated by "&".
|
||||
|
||||
Tables are encoded as parameters with multiple values by repeating the
|
||||
parameter name with each value.
|
||||
|
||||
@class function
|
||||
@name urlencode_params
|
||||
@param tbl Table with the values
|
||||
@return String containing encoded values
|
||||
@see urldecode_params
|
||||
]]
|
||||
|
||||
---[[
|
||||
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 within 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 fed 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 whether the current chunk is the last one (eof)
|
||||
|
||||
@class function
|
||||
@name mimedecode_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@param filecb File callback function (optional)
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
||||
|
||||
---[[
|
||||
Decode an urlencoded http message body with application/x-www-urlencoded
|
||||
Content-Type.
|
||||
|
||||
Stores all extracted data associated with its parameter name in the params
|
||||
table within the given message object. Multiple parameter values are stored
|
||||
as tables, ordinary ones as strings.
|
||||
|
||||
@class function
|
||||
@name urldecode_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
||||
|
||||
---[[
|
||||
Try to extract and decode a http message body from the given ltn12 source.
|
||||
This function will examine the Content-Type within the given message object
|
||||
to select the appropriate content decoder.
|
||||
|
||||
Currently the application/x-www-urlencoded and application/form-data
|
||||
mime types are supported. If the encountered content encoding can't be
|
||||
handled then the whole message body will be stored unaltered as "content"
|
||||
property within the given message object.
|
||||
|
||||
@class function
|
||||
@name parse_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@param filecb File data callback (optional, see mimedecode_message_body())
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
|
@ -1,55 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local tparser = require "luci.template.parser"
|
||||
local util = require "luci.util"
|
||||
local tostring = tostring
|
||||
|
||||
module "luci.i18n"
|
||||
|
||||
i18ndir = util.libpath() .. "/i18n/"
|
||||
context = util.threadlocal()
|
||||
default = "en"
|
||||
|
||||
|
||||
function setlanguage(lang)
|
||||
local code, subcode = lang:match("^([A-Za-z][A-Za-z])[%-_]([A-Za-z][A-Za-z])$")
|
||||
if not (code and subcode) then
|
||||
subcode = lang:match("^([A-Za-z][A-Za-z])$")
|
||||
if not subcode then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
context.parent = code and code:lower()
|
||||
context.lang = context.parent and context.parent.."-"..subcode:lower() or subcode:lower()
|
||||
|
||||
if tparser.load_catalog(context.lang, i18ndir) and
|
||||
tparser.change_catalog(context.lang)
|
||||
then
|
||||
return context.lang
|
||||
|
||||
elseif context.parent then
|
||||
if tparser.load_catalog(context.parent, i18ndir) and
|
||||
tparser.change_catalog(context.parent)
|
||||
then
|
||||
return context.parent
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function translate(key)
|
||||
return tparser.translate(key) or key
|
||||
end
|
||||
|
||||
function translatef(key, ...)
|
||||
return tostring(translate(key)):format(...)
|
||||
end
|
||||
|
||||
function dump()
|
||||
local rv = {}
|
||||
tparser.get_translations(function(k, v) rv[k] = v end)
|
||||
return rv
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
---[[
|
||||
LuCI translation library.
|
||||
]]
|
||||
module "luci.i18n"
|
||||
|
||||
---[[
|
||||
Set the context default translation language.
|
||||
|
||||
@class function
|
||||
@name setlanguage
|
||||
@param lang An IETF/BCP 47 language tag or ISO3166 country code, e.g. "en-US" or "de"
|
||||
@return The effective loaded language, e.g. "en" for "en-US" - or nil on failure
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the translated value for a specific translation key.
|
||||
|
||||
@class function
|
||||
@name translate
|
||||
@param key Default translation text
|
||||
@return Translated string
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the translated value for a specific translation key and use it as sprintf pattern.
|
||||
|
||||
@class function
|
||||
@name translatef
|
||||
@param key Default translation text
|
||||
@param ... Format parameters
|
||||
@return Translated and formatted string
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return all currently loaded translation strings as a key-value table. The key is the
|
||||
hexadecimal representation of the translation key while the value is the translated
|
||||
text content.
|
||||
|
||||
@class function
|
||||
@name dump
|
||||
@return Key-value translation string table.
|
||||
]]
|
|
@ -1,316 +0,0 @@
|
|||
--[[
|
||||
LuaSocket 2.0.2 license
|
||||
Copyright <EFBFBD> 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
|
||||
|
||||
-- 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
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- by passing it each chunk and updating a context between calls.
|
||||
function filter.cycle(low, ctx, extra)
|
||||
base.assert(low)
|
||||
return function(chunk)
|
||||
local ret
|
||||
ret, ctx = low(ctx, chunk, extra)
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
-- (thanks to Wim Couwenberg)
|
||||
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
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- create an empty source
|
||||
local function empty()
|
||||
return nil
|
||||
end
|
||||
|
||||
function source.empty()
|
||||
return empty
|
||||
end
|
||||
|
||||
function source.error(err)
|
||||
return function()
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
function source.file(handle, io_err)
|
||||
if handle then
|
||||
return function()
|
||||
local chunk = handle:read(BLOCKSIZE)
|
||||
if chunk and chunk:len() == 0 then chunk = nil end
|
||||
if not chunk then handle:close() end
|
||||
return chunk
|
||||
end
|
||||
else return source.error(io_err or "unable to open file") end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
-- Sources will be used one after the other, as if they were concatenated
|
||||
-- (thanks to Wim Couwenberg)
|
||||
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
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
function sink.null()
|
||||
return null
|
||||
end
|
||||
|
||||
function sink.error(err)
|
||||
return function()
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
-- Copyright 2011-2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache 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"))
|
||||
clientid.datatype = "hexstring"
|
||||
|
||||
|
||||
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)"
|
|
@ -1,4 +0,0 @@
|
|||
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local map, section, net = ...
|
|
@ -1,167 +0,0 @@
|
|||
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local map, section, net = ...
|
||||
local ifc = net:get_interface()
|
||||
|
||||
local netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
|
||||
local mtu, metric, usecidr, ipaddr_single, ipaddr_multi
|
||||
|
||||
|
||||
local function is_cidr(s)
|
||||
return (type(s) == "string" and luci.ip.IPv4(s) and s:find("/"))
|
||||
end
|
||||
|
||||
usecidr = section:taboption("general", Value, "ipaddr_usecidr")
|
||||
usecidr.forcewrite = true
|
||||
|
||||
usecidr.cfgvalue = function(self, section)
|
||||
local cfgvalue = self.map:get(section, "ipaddr")
|
||||
return (type(cfgvalue) == "table" or is_cidr(cfgvalue)) and "1" or "0"
|
||||
end
|
||||
|
||||
usecidr.render = function(self, section, scope)
|
||||
luci.template.Template(nil, [[
|
||||
<input type="hidden"<%= attr("id", cbid) .. attr("name", cbid) .. attr("value", value) %> />
|
||||
]]):render({
|
||||
cbid = self:cbid(section),
|
||||
value = self:cfgvalue(section)
|
||||
})
|
||||
end
|
||||
|
||||
usecidr.write = function(self, section)
|
||||
local cfgvalue = self.map:get(section, "ipaddr")
|
||||
local formvalue = (self:formvalue(section) == "1") and ipaddr_multi:formvalue(section) or ipaddr_single:formvalue(section)
|
||||
local equal = (cfgvalue == formvalue)
|
||||
|
||||
if not equal and type(cfgvalue) == "table" and type(formvalue) == "table" and #cfgvalue == #formvalue then
|
||||
equal = true
|
||||
|
||||
local _, v
|
||||
for _, v in ipairs(cfgvalue) do
|
||||
if v ~= formvalue[_] then
|
||||
equal = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not equal then
|
||||
self.map:set(section, "ipaddr", formvalue or "")
|
||||
end
|
||||
|
||||
return not equal
|
||||
end
|
||||
|
||||
|
||||
ipaddr_multi = section:taboption("general", DynamicList, "ipaddrs", translate("IPv4 address"))
|
||||
ipaddr_multi:depends("ipaddr_usecidr", "1")
|
||||
ipaddr_multi.datatype = "or(cidr4,ipnet4)"
|
||||
ipaddr_multi.placeholder = translate("Add IPv4 address…")
|
||||
|
||||
ipaddr_multi.alias = "ipaddr"
|
||||
ipaddr_multi.write = function() end
|
||||
ipaddr_multi.remove = function() end
|
||||
ipaddr_multi.cfgvalue = function(self, section)
|
||||
local addr = self.map:get(section, "ipaddr")
|
||||
local mask = self.map:get(section, "netmask")
|
||||
|
||||
if is_cidr(addr) then
|
||||
return { addr }
|
||||
elseif type(addr) == "string" and
|
||||
type(mask) == "string" and
|
||||
#addr > 0 and #mask > 0
|
||||
then
|
||||
return { "%s/%s" %{ addr, mask } }
|
||||
elseif type(addr) == "table" then
|
||||
return addr
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ipaddr_single = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
|
||||
ipaddr_single:depends("ipaddr_usecidr", "0")
|
||||
ipaddr_single.datatype = "ip4addr"
|
||||
ipaddr_single.template = "cbi/ipaddr"
|
||||
ipaddr_single.write = function() end
|
||||
ipaddr_single.remove = function() end
|
||||
|
||||
|
||||
netmask = section:taboption("general", Value, "netmask", translate("IPv4 netmask"))
|
||||
netmask:depends("ipaddr_usecidr", "0")
|
||||
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", DynamicList, "ip6addr", translate("IPv6 address"))
|
||||
ip6addr.datatype = "ip6addr"
|
||||
ip6addr.placeholder = translate("Add IPv6 address…")
|
||||
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", "")
|
||||
|
||||
local ip6ifaceid = s:taboption("general", Value, "ip6ifaceid", translate("IPv6 suffix"),
|
||||
translate("Optional. Allowed values: 'eui64', 'random', fixed value like '::1' " ..
|
||||
"or '::1:2'. When IPv6 prefix (like 'a:b:c:d::') is received from a " ..
|
||||
"delegating server, use the suffix (like '::1') to form the IPv6 address " ..
|
||||
"('a:b:c:d::1') for the interface."))
|
||||
ip6ifaceid.datatype = "ip6hostid"
|
||||
ip6ifaceid.placeholder = "::1"
|
||||
ip6ifaceid.rmempty = true
|
||||
|
||||
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"
|
|
@ -1,568 +0,0 @@
|
|||
-- Copyright 2009 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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)
|
||||
local z = zone(self:src())
|
||||
return z.sid and z
|
||||
end
|
||||
|
||||
function forwarding.dest_zone(self)
|
||||
local z = zone(self:dest())
|
||||
return z.sid and z
|
||||
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
|
|
@ -1,508 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local os = require "os"
|
||||
local util = require "luci.util"
|
||||
local table = require "table"
|
||||
|
||||
|
||||
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||||
local require, getmetatable, assert = require, getmetatable, assert
|
||||
local error, pairs, ipairs, select = error, pairs, ipairs, select
|
||||
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
|
||||
|
||||
-- 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 daemons etc. are
|
||||
-- reloaded.
|
||||
module "luci.model.uci"
|
||||
|
||||
local ERRSTR = {
|
||||
"Invalid command",
|
||||
"Invalid argument",
|
||||
"Method not found",
|
||||
"Entry not found",
|
||||
"No data",
|
||||
"Permission denied",
|
||||
"Timeout",
|
||||
"Not supported",
|
||||
"Unknown error",
|
||||
"Connection failed"
|
||||
}
|
||||
|
||||
local session_id = nil
|
||||
|
||||
local function call(cmd, args)
|
||||
if type(args) == "table" and session_id then
|
||||
args.ubus_rpc_session = session_id
|
||||
end
|
||||
return util.ubus("uci", cmd, args)
|
||||
end
|
||||
|
||||
|
||||
function cursor()
|
||||
return _M
|
||||
end
|
||||
|
||||
function cursor_state()
|
||||
return _M
|
||||
end
|
||||
|
||||
function substate(self)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function get_confdir(self)
|
||||
return "/etc/config"
|
||||
end
|
||||
|
||||
function get_savedir(self)
|
||||
return "/tmp/.uci"
|
||||
end
|
||||
|
||||
function get_session_id(self)
|
||||
return session_id
|
||||
end
|
||||
|
||||
function set_confdir(self, directory)
|
||||
return false
|
||||
end
|
||||
|
||||
function set_savedir(self, directory)
|
||||
return false
|
||||
end
|
||||
|
||||
function set_session_id(self, id)
|
||||
session_id = id
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function load(self, config)
|
||||
return true
|
||||
end
|
||||
|
||||
function save(self, config)
|
||||
return true
|
||||
end
|
||||
|
||||
function unload(self, config)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function changes(self, config)
|
||||
local rv, err = call("changes", { config = config })
|
||||
|
||||
if type(rv) == "table" and type(rv.changes) == "table" then
|
||||
return rv.changes
|
||||
elseif err then
|
||||
return nil, ERRSTR[err]
|
||||
else
|
||||
return { }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function revert(self, config)
|
||||
local _, err = call("revert", { config = config })
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
function commit(self, config)
|
||||
local _, err = call("commit", { config = config })
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
function apply(self, rollback)
|
||||
local _, err
|
||||
|
||||
if rollback then
|
||||
local sys = require "luci.sys"
|
||||
local conf = require "luci.config"
|
||||
local timeout = tonumber(conf and conf.apply and conf.apply.rollback or 30) or 0
|
||||
|
||||
_, err = call("apply", {
|
||||
timeout = (timeout > 30) and timeout or 30,
|
||||
rollback = true
|
||||
})
|
||||
|
||||
if not err then
|
||||
local now = os.time()
|
||||
local token = sys.uniqueid(16)
|
||||
|
||||
util.ubus("session", "set", {
|
||||
ubus_rpc_session = "00000000000000000000000000000000",
|
||||
values = {
|
||||
rollback = {
|
||||
token = token,
|
||||
session = session_id,
|
||||
timeout = now + timeout
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return token
|
||||
end
|
||||
else
|
||||
_, err = call("changes", {})
|
||||
|
||||
if not err then
|
||||
if type(_) == "table" and type(_.changes) == "table" then
|
||||
local k, v
|
||||
for k, v in pairs(_.changes) do
|
||||
_, err = call("commit", { config = k })
|
||||
if err then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not err then
|
||||
_, err = call("apply", { rollback = false })
|
||||
end
|
||||
end
|
||||
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
function confirm(self, token)
|
||||
local is_pending, time_remaining, rollback_sid, rollback_token = self:rollback_pending()
|
||||
|
||||
if is_pending then
|
||||
if token ~= rollback_token then
|
||||
return false, "Permission denied"
|
||||
end
|
||||
|
||||
local _, err = util.ubus("uci", "confirm", {
|
||||
ubus_rpc_session = rollback_sid
|
||||
})
|
||||
|
||||
if not err then
|
||||
util.ubus("session", "set", {
|
||||
ubus_rpc_session = "00000000000000000000000000000000",
|
||||
values = { rollback = {} }
|
||||
})
|
||||
end
|
||||
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
return false, "No data"
|
||||
end
|
||||
|
||||
function rollback(self)
|
||||
local is_pending, time_remaining, rollback_sid = self:rollback_pending()
|
||||
|
||||
if is_pending then
|
||||
local _, err = util.ubus("uci", "rollback", {
|
||||
ubus_rpc_session = rollback_sid
|
||||
})
|
||||
|
||||
if not err then
|
||||
util.ubus("session", "set", {
|
||||
ubus_rpc_session = "00000000000000000000000000000000",
|
||||
values = { rollback = {} }
|
||||
})
|
||||
end
|
||||
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
return false, "No data"
|
||||
end
|
||||
|
||||
function rollback_pending(self)
|
||||
local rv, err = util.ubus("session", "get", {
|
||||
ubus_rpc_session = "00000000000000000000000000000000",
|
||||
keys = { "rollback" }
|
||||
})
|
||||
|
||||
local now = os.time()
|
||||
|
||||
if type(rv) == "table" and
|
||||
type(rv.values) == "table" and
|
||||
type(rv.values.rollback) == "table" and
|
||||
type(rv.values.rollback.token) == "string" and
|
||||
type(rv.values.rollback.session) == "string" and
|
||||
type(rv.values.rollback.timeout) == "number" and
|
||||
rv.values.rollback.timeout > now
|
||||
then
|
||||
return true,
|
||||
rv.values.rollback.timeout - now,
|
||||
rv.values.rollback.session,
|
||||
rv.values.rollback.token
|
||||
end
|
||||
|
||||
return false, ERRSTR[err]
|
||||
end
|
||||
|
||||
|
||||
function foreach(self, config, stype, callback)
|
||||
if type(callback) == "function" then
|
||||
local rv, err = call("get", {
|
||||
config = config,
|
||||
type = stype
|
||||
})
|
||||
|
||||
if type(rv) == "table" and type(rv.values) == "table" then
|
||||
local sections = { }
|
||||
local res = false
|
||||
local index = 1
|
||||
|
||||
local _, section
|
||||
for _, section in pairs(rv.values) do
|
||||
section[".index"] = section[".index"] or index
|
||||
sections[index] = section
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
table.sort(sections, function(a, b)
|
||||
return a[".index"] < b[".index"]
|
||||
end)
|
||||
|
||||
for _, section in ipairs(sections) do
|
||||
local continue = callback(section)
|
||||
res = true
|
||||
if continue == false then
|
||||
break
|
||||
end
|
||||
end
|
||||
return res
|
||||
else
|
||||
return false, ERRSTR[err] or "No data"
|
||||
end
|
||||
else
|
||||
return false, "Invalid argument"
|
||||
end
|
||||
end
|
||||
|
||||
local function _get(self, operation, config, section, option)
|
||||
if section == nil then
|
||||
return nil
|
||||
elseif type(option) == "string" and option:byte(1) ~= 46 then
|
||||
local rv, err = call(operation, {
|
||||
config = config,
|
||||
section = section,
|
||||
option = option
|
||||
})
|
||||
|
||||
if type(rv) == "table" then
|
||||
return rv.value or nil
|
||||
elseif err then
|
||||
return false, ERRSTR[err]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
elseif option == nil then
|
||||
local values = self:get_all(config, section)
|
||||
if values then
|
||||
return values[".type"], values[".name"]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
return false, "Invalid argument"
|
||||
end
|
||||
end
|
||||
|
||||
function get(self, ...)
|
||||
return _get(self, "get", ...)
|
||||
end
|
||||
|
||||
function get_state(self, ...)
|
||||
return _get(self, "state", ...)
|
||||
end
|
||||
|
||||
function get_all(self, config, section)
|
||||
local rv, err = call("get", {
|
||||
config = config,
|
||||
section = section
|
||||
})
|
||||
|
||||
if type(rv) == "table" and type(rv.values) == "table" then
|
||||
return rv.values
|
||||
elseif err then
|
||||
return false, ERRSTR[err]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function get_bool(self, ...)
|
||||
local val = self:get(...)
|
||||
return (val == "1" or val == "true" or val == "yes" or val == "on")
|
||||
end
|
||||
|
||||
function get_first(self, config, stype, option, default)
|
||||
local rv = default
|
||||
|
||||
self:foreach(config, stype, function(s)
|
||||
local val = not option and s[".name"] or s[option]
|
||||
|
||||
if type(default) == "number" then
|
||||
val = tonumber(val)
|
||||
elseif type(default) == "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
|
||||
|
||||
function 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 { }
|
||||
end
|
||||
|
||||
|
||||
function section(self, config, stype, name, values)
|
||||
local rv, err = call("add", {
|
||||
config = config,
|
||||
type = stype,
|
||||
name = name,
|
||||
values = values
|
||||
})
|
||||
|
||||
if type(rv) == "table" then
|
||||
return rv.section
|
||||
elseif err then
|
||||
return false, ERRSTR[err]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function add(self, config, stype)
|
||||
return self:section(config, stype)
|
||||
end
|
||||
|
||||
function set(self, config, section, option, ...)
|
||||
if select('#', ...) == 0 then
|
||||
local sname, err = self:section(config, option, section)
|
||||
return (not not sname), err
|
||||
else
|
||||
local _, err = call("set", {
|
||||
config = config,
|
||||
section = section,
|
||||
values = { [option] = select(1, ...) }
|
||||
})
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
end
|
||||
|
||||
function set_list(self, config, section, option, value)
|
||||
if section == nil or option == nil then
|
||||
return false
|
||||
elseif value == nil or (type(value) == "table" and #value == 0) then
|
||||
return self:delete(config, section, option)
|
||||
elseif type(value) == "table" then
|
||||
return self:set(config, section, option, value)
|
||||
else
|
||||
return self:set(config, section, option, { value })
|
||||
end
|
||||
end
|
||||
|
||||
function tset(self, config, section, values)
|
||||
local _, err = call("set", {
|
||||
config = config,
|
||||
section = section,
|
||||
values = values
|
||||
})
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
function reorder(self, config, section, index)
|
||||
local sections
|
||||
|
||||
if type(section) == "string" and type(index) == "number" then
|
||||
local pos = 0
|
||||
|
||||
sections = { }
|
||||
|
||||
self:foreach(config, nil, function(s)
|
||||
if pos == index then
|
||||
pos = pos + 1
|
||||
end
|
||||
|
||||
if s[".name"] ~= section then
|
||||
pos = pos + 1
|
||||
sections[pos] = s[".name"]
|
||||
else
|
||||
sections[index + 1] = section
|
||||
end
|
||||
end)
|
||||
elseif type(section) == "table" then
|
||||
sections = section
|
||||
else
|
||||
return false, "Invalid argument"
|
||||
end
|
||||
|
||||
local _, err = call("order", {
|
||||
config = config,
|
||||
sections = sections
|
||||
})
|
||||
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
|
||||
function delete(self, config, section, option)
|
||||
local _, err = call("delete", {
|
||||
config = config,
|
||||
section = section,
|
||||
option = option
|
||||
})
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
||||
|
||||
function delete_all(self, config, stype, comparator)
|
||||
local _, err
|
||||
if type(comparator) == "table" then
|
||||
_, err = call("delete", {
|
||||
config = config,
|
||||
type = stype,
|
||||
match = comparator
|
||||
})
|
||||
elseif type(comparator) == "function" then
|
||||
local rv = call("get", {
|
||||
config = config,
|
||||
type = stype
|
||||
})
|
||||
|
||||
if type(rv) == "table" and type(rv.values) == "table" then
|
||||
local sname, section
|
||||
for sname, section in pairs(rv.values) do
|
||||
if comparator(section) then
|
||||
_, err = call("delete", {
|
||||
config = config,
|
||||
section = sname
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif comparator == nil then
|
||||
_, err = call("delete", {
|
||||
config = config,
|
||||
type = stype
|
||||
})
|
||||
else
|
||||
return false, "Invalid argument"
|
||||
end
|
||||
|
||||
return (err == nil), ERRSTR[err]
|
||||
end
|
|
@ -1,369 +0,0 @@
|
|||
---[[
|
||||
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 daemons etc. are
|
||||
reloaded.
|
||||
@cstyle instance
|
||||
]]
|
||||
module "luci.model.uci"
|
||||
|
||||
---[[
|
||||
Create a new UCI-Cursor.
|
||||
|
||||
@class function
|
||||
@name cursor
|
||||
@return UCI-Cursor
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a new Cursor initialized to the state directory.
|
||||
|
||||
@class function
|
||||
@name cursor_state
|
||||
@return UCI cursor
|
||||
]]
|
||||
|
||||
---[[
|
||||
Applies UCI configuration changes.
|
||||
|
||||
If the rollback parameter is set to true, the apply function will invoke the
|
||||
rollback mechanism which causes the configuration to be automatically reverted
|
||||
if no confirm() call occurs within a certain timeout.
|
||||
|
||||
The current default timeout is 30s and can be increased using the
|
||||
"luci.apply.timeout" uci configuration key.
|
||||
|
||||
@class function
|
||||
@name Cursor.apply
|
||||
@param rollback Enable rollback mechanism
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Confirms UCI apply process.
|
||||
|
||||
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||
this function confirms the process and cancels the pending rollback timer.
|
||||
|
||||
If no apply with rollback session is active, the function has no effect and
|
||||
returns with a "No data" error.
|
||||
|
||||
@class function
|
||||
@name Cursor.confirm
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Cancels UCI apply process.
|
||||
|
||||
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||
this function cancels the process and rolls back the configuration to the
|
||||
pre-apply state.
|
||||
|
||||
If no apply with rollback session is active, the function has no effect and
|
||||
returns with a "No data" error.
|
||||
|
||||
@class function
|
||||
@name Cursor.rollback
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Checks whether a pending rollback is scheduled.
|
||||
|
||||
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||
and has not been confirmed or rolled back yet, this function returns true
|
||||
and the remaining time until rollback in seconds. If no rollback is pending,
|
||||
the function returns false. On error, the function returns false and an
|
||||
additional string describing the error.
|
||||
|
||||
@class function
|
||||
@name Cursor.rollback_pending
|
||||
@return Boolean whether rollback is pending
|
||||
@return Remaining time in seconds
|
||||
]]
|
||||
|
||||
---[[
|
||||
Delete all sections of a given type that match certain criteria.
|
||||
|
||||
@class function
|
||||
@name Cursor.delete_all
|
||||
@param config UCI config
|
||||
@param type UCI section type
|
||||
@param comparator Function that will be called for each section and returns
|
||||
a boolean whether to delete the current section (optional)
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a new section and initialize it with data.
|
||||
|
||||
@class function
|
||||
@name Cursor.section
|
||||
@param config UCI config
|
||||
@param type UCI section type
|
||||
@param name UCI section name (optional)
|
||||
@param values Table of key - value pairs to initialize the section with
|
||||
@return Name of created section
|
||||
]]
|
||||
|
||||
---[[
|
||||
Updated the data of a section using data from a table.
|
||||
|
||||
@class function
|
||||
@name Cursor.tset
|
||||
@param config UCI config
|
||||
@param section UCI section name (optional)
|
||||
@param values Table of key - value pairs to update the section with
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get a boolean option and return it's value as true or false.
|
||||
|
||||
@class function
|
||||
@name Cursor.get_bool
|
||||
@param config UCI config
|
||||
@param section UCI section name
|
||||
@param option UCI option
|
||||
@return Boolean
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get an option or list and return values as table.
|
||||
|
||||
@class function
|
||||
@name Cursor.get_list
|
||||
@param config UCI config
|
||||
@param section UCI section name
|
||||
@param option UCI option
|
||||
@return table. If the option was not found, you will simply get an empty
|
||||
table.
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the given option from the first section with the given type.
|
||||
|
||||
@class function
|
||||
@name Cursor.get_first
|
||||
@param config UCI config
|
||||
@param type UCI section type
|
||||
@param option UCI option (optional)
|
||||
@param default Default value (optional)
|
||||
@return UCI value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set given values as list. Setting a list option to an empty list
|
||||
has the same effect as deleting the option.
|
||||
|
||||
@class function
|
||||
@name Cursor.set_list
|
||||
@param config UCI config
|
||||
@param section UCI section name
|
||||
@param option UCI option
|
||||
@param value Value or table. Non-table values will be set as single
|
||||
item UCI list.
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a sub-state of this cursor.
|
||||
|
||||
The sub-state is tied to the parent cursor, means it the parent unloads or
|
||||
loads configs, the sub state will do so as well.
|
||||
|
||||
@class function
|
||||
@name Cursor.substate
|
||||
@return UCI state cursor tied to the parent cursor
|
||||
]]
|
||||
|
||||
---[[
|
||||
Add an anonymous section.
|
||||
|
||||
@class function
|
||||
@name Cursor.add
|
||||
@param config UCI config
|
||||
@param type UCI section type
|
||||
@return Name of created section
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get a table of saved but uncommitted changes.
|
||||
|
||||
@class function
|
||||
@name Cursor.changes
|
||||
@param config UCI config
|
||||
@return Table of changes
|
||||
@see Cursor.save
|
||||
]]
|
||||
|
||||
---[[
|
||||
Commit saved changes.
|
||||
|
||||
@class function
|
||||
@name Cursor.commit
|
||||
@param config UCI config
|
||||
@return Boolean whether operation succeeded
|
||||
@see Cursor.revert
|
||||
@see Cursor.save
|
||||
]]
|
||||
|
||||
---[[
|
||||
Deletes a section or an option.
|
||||
|
||||
@class function
|
||||
@name Cursor.delete
|
||||
@param config UCI config
|
||||
@param section UCI section name
|
||||
@param option UCI option (optional)
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Call a function for every section of a certain type.
|
||||
|
||||
@class function
|
||||
@name Cursor.foreach
|
||||
@param config UCI config
|
||||
@param type UCI section type
|
||||
@param callback Function to be called
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get a section type or an option
|
||||
|
||||
@class function
|
||||
@name Cursor.get
|
||||
@param config UCI config
|
||||
@param section UCI section name
|
||||
@param option UCI option (optional)
|
||||
@return UCI value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get all sections of a config or all values of a section.
|
||||
|
||||
@class function
|
||||
@name Cursor.get_all
|
||||
@param config UCI config
|
||||
@param section UCI section name (optional)
|
||||
@return Table of UCI sections or table of UCI values
|
||||
]]
|
||||
|
||||
---[[
|
||||
Manually load a config.
|
||||
|
||||
@class function
|
||||
@name Cursor.load
|
||||
@param config UCI config
|
||||
@return Boolean whether operation succeeded
|
||||
@see Cursor.save
|
||||
@see Cursor.unload
|
||||
]]
|
||||
|
||||
---[[
|
||||
Revert saved but uncommitted changes.
|
||||
|
||||
@class function
|
||||
@name Cursor.revert
|
||||
@param config UCI config
|
||||
@return Boolean whether operation succeeded
|
||||
@see Cursor.commit
|
||||
@see Cursor.save
|
||||
]]
|
||||
|
||||
---[[
|
||||
Saves changes made to a config to make them committable.
|
||||
|
||||
@class function
|
||||
@name Cursor.save
|
||||
@param config UCI config
|
||||
@return Boolean whether operation succeeded
|
||||
@see Cursor.load
|
||||
@see Cursor.unload
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set a value or create a named section.
|
||||
|
||||
When invoked with three arguments `config`, `sectionname`, `sectiontype`,
|
||||
then a named section of the given type is created.
|
||||
|
||||
When invoked with four arguments `config`, `sectionname`, `optionname` and
|
||||
`optionvalue` then the value of the specified option is set to the given value.
|
||||
|
||||
@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 nothing 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
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the effective session ID.
|
||||
|
||||
@class function
|
||||
@name Cursor.get_session_id
|
||||
@return String containing the session ID
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the configuration directory.
|
||||
|
||||
@class function
|
||||
@name Cursor.set_confdir
|
||||
@param directory UCI configuration directory
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the directory for uncommitted changes.
|
||||
|
||||
@class function
|
||||
@name Cursor.set_savedir
|
||||
@param directory UCI changes directory
|
||||
@return Boolean whether operation succeeded
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the effective session ID.
|
||||
|
||||
@class function
|
||||
@name Cursor.set_session_id
|
||||
@param id String containing the session ID to set
|
||||
@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
|
||||
]]
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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
|
|
@ -1,89 +0,0 @@
|
|||
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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
|
|
@ -1,6 +0,0 @@
|
|||
-- Copyright 2009 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2009 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
local util = require "luci.util"
|
||||
module("luci.store", util.threadlocal)
|
|
@ -1,669 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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 ntm = require "luci.model.network"
|
||||
|
||||
local luci = {}
|
||||
luci.util = require "luci.util"
|
||||
luci.ip = require "luci.ip"
|
||||
|
||||
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack =
|
||||
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack
|
||||
|
||||
|
||||
module "luci.sys"
|
||||
|
||||
function call(...)
|
||||
return os.execute(...) / 256
|
||||
end
|
||||
|
||||
exec = luci.util.exec
|
||||
|
||||
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
|
||||
|
||||
function mtds()
|
||||
local data = {}
|
||||
|
||||
if fs.access("/proc/mtd") then
|
||||
for l in io.lines("/proc/mtd") do
|
||||
local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
|
||||
if s and n then
|
||||
local d = {}
|
||||
d.size = tonumber(s, 16)
|
||||
d.name = n
|
||||
table.insert(data, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
-- 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.
|
||||
getenv = nixio.getenv
|
||||
|
||||
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
|
||||
|
||||
function httpget(url, stream, target)
|
||||
if not target then
|
||||
local source = stream and io.popen or luci.util.exec
|
||||
return source("wget -qO- %s" % luci.util.shellquote(url))
|
||||
else
|
||||
return os.execute("wget -qO %s %s" %
|
||||
{luci.util.shellquote(target), luci.util.shellquote(url)})
|
||||
end
|
||||
end
|
||||
|
||||
function reboot()
|
||||
return os.execute("reboot >/dev/null 2>&1")
|
||||
end
|
||||
|
||||
function syslog()
|
||||
return luci.util.exec("logread")
|
||||
end
|
||||
|
||||
function dmesg()
|
||||
return luci.util.exec("dmesg")
|
||||
end
|
||||
|
||||
function uniqueid(bytes)
|
||||
local rand = fs.readfile("/dev/urandom", bytes)
|
||||
return rand and nixio.bin.hexlify(rand)
|
||||
end
|
||||
|
||||
function uptime()
|
||||
return nixio.sysinfo().uptime
|
||||
end
|
||||
|
||||
|
||||
net = {}
|
||||
|
||||
local function _nethints(what, callback)
|
||||
local _, k, e, mac, ip, name, duid, iaid
|
||||
local cur = uci.cursor()
|
||||
local ifn = { }
|
||||
local hosts = { }
|
||||
local lookup = { }
|
||||
|
||||
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
|
||||
|
||||
luci.ip.neighbors(nil, function(neigh)
|
||||
if neigh.mac and neigh.family == 4 then
|
||||
_add(what, neigh.mac:string(), neigh.dest:string(), nil, nil)
|
||||
elseif neigh.mac and neigh.family == 6 then
|
||||
_add(what, neigh.mac:string(), nil, neigh.dest:string(), nil)
|
||||
end
|
||||
end)
|
||||
|
||||
if fs.access("/etc/ethers") then
|
||||
for e in io.lines("/etc/ethers") do
|
||||
mac, name = e:match("^([a-fA-F0-9:-]+)%s+(%S+)")
|
||||
mac = luci.ip.checkmac(mac)
|
||||
if mac and name then
|
||||
if luci.ip.checkip4(name) then
|
||||
_add(what, mac, name, nil, nil)
|
||||
else
|
||||
_add(what, mac, nil, nil, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cur:foreach("dhcp", "dnsmasq",
|
||||
function(s)
|
||||
if s.leasefile and fs.access(s.leasefile) then
|
||||
for e in io.lines(s.leasefile) do
|
||||
mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)")
|
||||
mac = luci.ip.checkmac(mac)
|
||||
if mac and ip then
|
||||
_add(what, mac, ip, nil, name ~= "*" and name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
cur:foreach("dhcp", "odhcpd",
|
||||
function(s)
|
||||
if type(s.leasefile) == "string" and fs.access(s.leasefile) then
|
||||
for e in io.lines(s.leasefile) do
|
||||
duid, iaid, name, _, ip = e:match("^# %S+ (%S+) (%S+) (%S+) (-?%d+) %S+ %S+ ([0-9a-f:.]+)/[0-9]+")
|
||||
mac = net.duid_to_mac(duid)
|
||||
if mac then
|
||||
if ip and iaid == "ipv4" then
|
||||
_add(what, mac, ip, nil, name ~= "*" and name)
|
||||
elseif ip then
|
||||
_add(what, mac, nil, ip, name ~= "*" and name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
cur:foreach("dhcp", "host",
|
||||
function(s)
|
||||
for mac in luci.util.imatch(s.mac) do
|
||||
mac = luci.ip.checkmac(mac)
|
||||
if mac then
|
||||
_add(what, mac, s.ip, nil, s.name)
|
||||
end
|
||||
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 pairs(hosts) do
|
||||
lookup[#lookup+1] = (what > 1) and e[what] or (e[2] or e[3])
|
||||
end
|
||||
|
||||
if #lookup > 0 then
|
||||
lookup = luci.util.ubus("network.rrdns", "lookup", {
|
||||
addrs = lookup,
|
||||
timeout = 250,
|
||||
limit = 1000
|
||||
}) or { }
|
||||
end
|
||||
|
||||
for _, e in luci.util.kspairs(hosts) do
|
||||
callback(e[1], e[2], e[3], lookup[e[2]] or lookup[e[3]] or e[4])
|
||||
end
|
||||
end
|
||||
|
||||
-- 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 v4
|
||||
if name and name ~= mac then
|
||||
callback(mac, name or v4)
|
||||
end
|
||||
end)
|
||||
else
|
||||
local rv = { }
|
||||
_nethints(1, function(mac, v4, v6, name)
|
||||
name = name or v4
|
||||
if name and name ~= mac then
|
||||
rv[#rv+1] = { mac, name or v4 }
|
||||
end
|
||||
end)
|
||||
return rv
|
||||
end
|
||||
end
|
||||
|
||||
-- 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 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 mac
|
||||
if name and name ~= v4 then
|
||||
rv[#rv+1] = { v4, name }
|
||||
end
|
||||
end)
|
||||
return rv
|
||||
end
|
||||
end
|
||||
|
||||
-- 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 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 mac
|
||||
if name and name ~= v6 then
|
||||
rv[#rv+1] = { v6, name }
|
||||
end
|
||||
end)
|
||||
return rv
|
||||
end
|
||||
end
|
||||
|
||||
function net.host_hints(callback)
|
||||
if callback then
|
||||
_nethints(1, function(mac, v4, v6, name)
|
||||
if mac and mac ~= "00:00:00:00:00:00" and (v4 or v6 or name) then
|
||||
callback(mac, v4, v6, name)
|
||||
end
|
||||
end)
|
||||
else
|
||||
local rv = { }
|
||||
_nethints(1, function(mac, v4, v6, name)
|
||||
if mac and mac ~= "00:00:00:00:00:00" and (v4 or v6 or name) then
|
||||
local e = { }
|
||||
if v4 then e.ipv4 = v4 end
|
||||
if v6 then e.ipv6 = v6 end
|
||||
if name then e.name = name end
|
||||
rv[mac] = e
|
||||
end
|
||||
end)
|
||||
return rv
|
||||
end
|
||||
end
|
||||
|
||||
function net.conntrack(callback)
|
||||
local ok, nfct = pcall(io.lines, "/proc/net/nf_conntrack")
|
||||
if not ok or not nfct then
|
||||
return nil
|
||||
end
|
||||
|
||||
local line, connt = nil, (not callback) and { }
|
||||
for line in nfct do
|
||||
local fam, l3, l4, timeout, tuples =
|
||||
line:match("^(ipv[46]) +(%d+) +%S+ +(%d+) +(%d+) +(.+)$")
|
||||
|
||||
if fam and l3 and l4 and timeout and not tuples:match("^TIME_WAIT ") then
|
||||
l4 = nixio.getprotobynumber(l4)
|
||||
|
||||
local entry = {
|
||||
bytes = 0,
|
||||
packets = 0,
|
||||
layer3 = fam,
|
||||
layer4 = l4 and l4.name or "unknown",
|
||||
timeout = tonumber(timeout, 10)
|
||||
}
|
||||
|
||||
local key, val
|
||||
for key, val in tuples:gmatch("(%w+)=(%S+)") do
|
||||
if key == "bytes" or key == "packets" then
|
||||
entry[key] = entry[key] + tonumber(val, 10)
|
||||
elseif key == "src" or key == "dst" then
|
||||
if entry[key] == nil then
|
||||
entry[key] = luci.ip.new(val):string()
|
||||
end
|
||||
elseif key == "sport" or key == "dport" then
|
||||
if entry[key] == nil then
|
||||
entry[key] = val
|
||||
end
|
||||
elseif val then
|
||||
entry[key] = val
|
||||
end
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(entry)
|
||||
else
|
||||
connt[#connt+1] = entry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return callback and true or connt
|
||||
end
|
||||
|
||||
function net.devices()
|
||||
local devs = {}
|
||||
local seen = {}
|
||||
for k, v in ipairs(nixio.getifaddrs()) do
|
||||
if v.name and not seen[v.name] then
|
||||
seen[v.name] = true
|
||||
devs[#devs+1] = v.name
|
||||
end
|
||||
end
|
||||
return devs
|
||||
end
|
||||
|
||||
function net.duid_to_mac(duid)
|
||||
local b1, b2, b3, b4, b5, b6
|
||||
|
||||
if type(duid) == "string" then
|
||||
-- DUID-LLT / Ethernet
|
||||
if #duid == 28 then
|
||||
b1, b2, b3, b4, b5, b6 = duid:match("^00010001(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)%x%x%x%x%x%x%x%x$")
|
||||
|
||||
-- DUID-LL / Ethernet
|
||||
elseif #duid == 20 then
|
||||
b1, b2, b3, b4, b5, b6 = duid:match("^00030001(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$")
|
||||
|
||||
-- DUID-LL / Ethernet (Without Header)
|
||||
elseif #duid == 12 then
|
||||
b1, b2, b3, b4, b5, b6 = duid:match("^(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$")
|
||||
end
|
||||
end
|
||||
|
||||
return b1 and luci.ip.checkmac(table.concat({ b1, b2, b3, b4, b5, b6 }, ":"))
|
||||
end
|
||||
|
||||
process = {}
|
||||
|
||||
function process.info(key)
|
||||
local s = {uid = nixio.getuid(), gid = nixio.getgid()}
|
||||
return not key and s or s[key]
|
||||
end
|
||||
|
||||
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][<NW ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
|
||||
)
|
||||
|
||||
local idx = tonumber(pid)
|
||||
if idx and not cmd:match("top %-bn1") then
|
||||
data[idx] = {
|
||||
['PID'] = pid,
|
||||
['PPID'] = ppid,
|
||||
['USER'] = user,
|
||||
['STAT'] = stat,
|
||||
['VSZ'] = vsz,
|
||||
['%MEM'] = mem,
|
||||
['%CPU'] = cpu,
|
||||
['COMMAND'] = cmd
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function process.setgroup(gid)
|
||||
return nixio.setgid(gid)
|
||||
end
|
||||
|
||||
function process.setuser(uid)
|
||||
return nixio.setuid(uid)
|
||||
end
|
||||
|
||||
process.signal = nixio.kill
|
||||
|
||||
local function xclose(fd)
|
||||
if fd and fd:fileno() > 2 then
|
||||
fd:close()
|
||||
end
|
||||
end
|
||||
|
||||
function process.exec(command, stdout, stderr, nowait)
|
||||
local out_r, out_w, err_r, err_w
|
||||
if stdout then out_r, out_w = nixio.pipe() end
|
||||
if stderr then err_r, err_w = nixio.pipe() end
|
||||
|
||||
local pid = nixio.fork()
|
||||
if pid == 0 then
|
||||
nixio.chdir("/")
|
||||
|
||||
local null = nixio.open("/dev/null", "w+")
|
||||
if null then
|
||||
nixio.dup(out_w or null, nixio.stdout)
|
||||
nixio.dup(err_w or null, nixio.stderr)
|
||||
nixio.dup(null, nixio.stdin)
|
||||
xclose(out_w)
|
||||
xclose(out_r)
|
||||
xclose(err_w)
|
||||
xclose(err_r)
|
||||
xclose(null)
|
||||
end
|
||||
|
||||
nixio.exec(unpack(command))
|
||||
os.exit(-1)
|
||||
end
|
||||
|
||||
local _, pfds, rv = nil, {}, { code = -1, pid = pid }
|
||||
|
||||
xclose(out_w)
|
||||
xclose(err_w)
|
||||
|
||||
if out_r then
|
||||
pfds[#pfds+1] = {
|
||||
fd = out_r,
|
||||
cb = type(stdout) == "function" and stdout,
|
||||
name = "stdout",
|
||||
events = nixio.poll_flags("in", "err", "hup")
|
||||
}
|
||||
end
|
||||
|
||||
if err_r then
|
||||
pfds[#pfds+1] = {
|
||||
fd = err_r,
|
||||
cb = type(stderr) == "function" and stderr,
|
||||
name = "stderr",
|
||||
events = nixio.poll_flags("in", "err", "hup")
|
||||
}
|
||||
end
|
||||
|
||||
while #pfds > 0 do
|
||||
local nfds, err = nixio.poll(pfds, -1)
|
||||
if not nfds and err ~= nixio.const.EINTR then
|
||||
break
|
||||
end
|
||||
|
||||
local i
|
||||
for i = #pfds, 1, -1 do
|
||||
local rfd = pfds[i]
|
||||
if rfd.revents > 0 then
|
||||
local chunk, err = rfd.fd:read(4096)
|
||||
if chunk and #chunk > 0 then
|
||||
if rfd.cb then
|
||||
rfd.cb(chunk)
|
||||
else
|
||||
rfd.buf = rfd.buf or {}
|
||||
rfd.buf[#rfd.buf + 1] = chunk
|
||||
end
|
||||
else
|
||||
table.remove(pfds, i)
|
||||
if rfd.buf then
|
||||
rv[rfd.name] = table.concat(rfd.buf, "")
|
||||
end
|
||||
rfd.fd:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not nowait then
|
||||
_, _, rv.code = nixio.waitpid(pid)
|
||||
end
|
||||
|
||||
return rv
|
||||
end
|
||||
|
||||
|
||||
user = {}
|
||||
|
||||
-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
|
||||
user.getuser = nixio.getpw
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
function user.setpasswd(username, password)
|
||||
return os.execute("(echo %s; sleep 1; echo %s) | passwd %s >/dev/null 2>&1" %{
|
||||
luci.util.shellquote(password),
|
||||
luci.util.shellquote(password),
|
||||
luci.util.shellquote(username)
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
wifi = {}
|
||||
|
||||
function wifi.getiwinfo(ifname)
|
||||
ntm.init()
|
||||
|
||||
local wnet = ntm:get_wifinet(ifname)
|
||||
if wnet and wnet.iwinfo then
|
||||
return wnet.iwinfo
|
||||
end
|
||||
|
||||
local wdev = ntm:get_wifidev(ifname)
|
||||
if wdev and wdev.iwinfo then
|
||||
return wdev.iwinfo
|
||||
end
|
||||
|
||||
return { ifname = ifname }
|
||||
end
|
||||
|
||||
|
||||
init = {}
|
||||
init.dir = "/etc/init.d/"
|
||||
|
||||
function init.names()
|
||||
local names = { }
|
||||
for name in fs.glob(init.dir.."*") do
|
||||
names[#names+1] = fs.basename(name)
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function init.enabled(name)
|
||||
return (init_action("enabled", name) == 0)
|
||||
end
|
||||
|
||||
function init.enable(name)
|
||||
return (init_action("enable", name) == 1)
|
||||
end
|
||||
|
||||
function init.disable(name)
|
||||
return (init_action("disable", name) == 0)
|
||||
end
|
||||
|
||||
function init.start(name)
|
||||
return (init_action("start", name) == 0)
|
||||
end
|
||||
|
||||
function init.stop(name)
|
||||
return (init_action("stop", name) == 0)
|
||||
end
|
||||
|
||||
function init.restart(name)
|
||||
return (init_action("restart", name) == 0)
|
||||
end
|
||||
|
||||
function init.reload(name)
|
||||
return (init_action("reload", name) == 0)
|
||||
end
|
|
@ -1,441 +0,0 @@
|
|||
---[[
|
||||
LuCI Linux and POSIX system utilities.
|
||||
]]
|
||||
module "luci.sys"
|
||||
|
||||
---[[
|
||||
Execute a given shell command and return the error code
|
||||
|
||||
@class function
|
||||
@name call
|
||||
@param ... Command to call
|
||||
@return Error code of the command
|
||||
]]
|
||||
|
||||
---[[
|
||||
Execute a given shell command and capture its standard output
|
||||
|
||||
@class function
|
||||
@name exec
|
||||
@param command Command to call
|
||||
@return String containing the return the output of the command
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve information about currently mounted file systems.
|
||||
|
||||
@class function
|
||||
@name mounts
|
||||
@return Table containing mount information
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve environment variables. If no variable is given then a table
|
||||
|
||||
containing the whole environment is returned otherwise this function returns
|
||||
the corresponding string value for the given name or nil if no such variable
|
||||
exists.
|
||||
@class function
|
||||
@name getenv
|
||||
@param var Name of the environment variable to retrieve (optional)
|
||||
@return String containing the value of the specified variable
|
||||
@return Table containing all variables if no variable name is given
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get or set the current hostname.
|
||||
|
||||
@class function
|
||||
@name hostname
|
||||
@param String containing a new hostname to set (optional)
|
||||
@return String containing the system hostname
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the contents of a documented referred by an URL.
|
||||
|
||||
@class function
|
||||
@name httpget
|
||||
@param url The URL to retrieve
|
||||
@param stream Return a stream instead of a buffer
|
||||
@param target Directly write to target file name
|
||||
@return String containing the contents of given the URL
|
||||
]]
|
||||
|
||||
---[[
|
||||
Initiate a system reboot.
|
||||
|
||||
@class function
|
||||
@name reboot
|
||||
@return Return value of os.execute()
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieves the output of the "logread" command.
|
||||
|
||||
@class function
|
||||
@name syslog
|
||||
@return String containing the current log buffer
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieves the output of the "dmesg" command.
|
||||
|
||||
@class function
|
||||
@name dmesg
|
||||
@return String containing the current log buffer
|
||||
]]
|
||||
|
||||
---[[
|
||||
Generates a random id with specified length.
|
||||
|
||||
@class function
|
||||
@name uniqueid
|
||||
@param bytes Number of bytes for the unique id
|
||||
@return String containing hex encoded id
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the current system uptime stats.
|
||||
|
||||
@class function
|
||||
@name uptime
|
||||
@return String containing total uptime in seconds
|
||||
]]
|
||||
|
||||
---[[
|
||||
LuCI system utilities / network related functions.
|
||||
|
||||
@class module
|
||||
@name luci.sys.net
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the current arp-table entries as two-dimensional table.
|
||||
|
||||
@class function
|
||||
@name net.arptable
|
||||
@return Table of table containing the current arp entries.
|
||||
-- The following fields are defined for arp entry objects:
|
||||
-- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns a two-dimensional table of mac address hints.
|
||||
|
||||
@class function
|
||||
@name net.mac_hints
|
||||
@return Table of table containing known hosts from various sources.
|
||||
Each entry contains the values in the following order:
|
||||
[ "mac", "name" ]
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns a two-dimensional table of IPv4 address hints.
|
||||
|
||||
@class function
|
||||
@name net.ipv4_hints
|
||||
@return Table of table containing known hosts from various sources.
|
||||
Each entry contains the values in the following order:
|
||||
[ "ip", "name" ]
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns a two-dimensional table of IPv6 address hints.
|
||||
|
||||
@class function
|
||||
@name net.ipv6_hints
|
||||
@return Table of table containing known hosts from various sources.
|
||||
Each entry contains the values in the following order:
|
||||
[ "ip", "name" ]
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns a two-dimensional table of host hints.
|
||||
|
||||
@class function
|
||||
@name net.host_hints
|
||||
@return Table of table containing known hosts from various sources,
|
||||
indexed by mac address. Each subtable contains at least one
|
||||
of the fields "name", "ipv4" or "ipv6".
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns conntrack information
|
||||
|
||||
@class function
|
||||
@name net.conntrack
|
||||
@return Table with the currently tracked IP connections
|
||||
]]
|
||||
|
||||
---[[
|
||||
Determine the names of available network interfaces.
|
||||
|
||||
@class function
|
||||
@name net.devices
|
||||
@return Table containing all current interface names
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return information about available network interfaces.
|
||||
|
||||
@class function
|
||||
@name net.deviceinfo
|
||||
@return Table containing all current interface names and their information
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the current kernel routing table entries.
|
||||
|
||||
@class function
|
||||
@name net.routes
|
||||
@return Table of tables with properties of the corresponding routes.
|
||||
-- The following fields are defined for route entry tables:
|
||||
-- { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
|
||||
-- "flags", "device" }
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the current ipv6 kernel routing table entries.
|
||||
|
||||
@class function
|
||||
@name net.routes6
|
||||
@return Table of tables with properties of the corresponding routes.
|
||||
-- The following fields are defined for route entry tables:
|
||||
-- { "source", "dest", "nexthop", "metric", "refcount", "usecount",
|
||||
-- "flags", "device" }
|
||||
]]
|
||||
|
||||
---[[
|
||||
Tests whether the given host responds to ping probes.
|
||||
|
||||
@class function
|
||||
@name net.pingtest
|
||||
@param host String containing a hostname or IPv4 address
|
||||
@return Number containing 0 on success and >= 1 on error
|
||||
]]
|
||||
|
||||
---[[
|
||||
LuCI system utilities / process related functions.
|
||||
|
||||
@class module
|
||||
@name luci.sys.process
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the current process id.
|
||||
|
||||
@class function
|
||||
@name process.info
|
||||
@return Number containing the current pid
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve information about currently running processes.
|
||||
|
||||
@class function
|
||||
@name process.list
|
||||
@return Table containing process information
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the gid of a process identified by given pid.
|
||||
|
||||
@class function
|
||||
@name process.setgroup
|
||||
@param gid Number containing the Unix group id
|
||||
@return Boolean indicating successful operation
|
||||
@return String containing the error message if failed
|
||||
@return Number containing the error code if failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
Set the uid of a process identified by given pid.
|
||||
|
||||
@class function
|
||||
@name process.setuser
|
||||
@param uid Number containing the Unix user id
|
||||
@return Boolean indicating successful operation
|
||||
@return String containing the error message if failed
|
||||
@return Number containing the error code if failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
Send a signal to a process identified by given pid.
|
||||
|
||||
@class function
|
||||
@name process.signal
|
||||
@param pid Number containing the process id
|
||||
@param sig Signal to send (default: 15 [SIGTERM])
|
||||
@return Boolean indicating successful operation
|
||||
@return Number containing the error code if failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
Execute a process, optionally capturing stdio.
|
||||
|
||||
Executes the process specified by the given argv vector, e.g.
|
||||
`{ "/bin/sh", "-c", "echo 1" }` and waits for it to terminate unless a true
|
||||
value has been passed for the "nowait" parameter.
|
||||
|
||||
When a function value is passed for the stdout or stderr arguments, the passed
|
||||
function is repeatedly called for each chunk read from the corresponding stdio
|
||||
stream. The read data is passed as string containing at most 4096 bytes at a
|
||||
time.
|
||||
|
||||
When a true, non-function value is passed for the stdout or stderr arguments,
|
||||
the data of the corresponding stdio stream is read into an internal string
|
||||
buffer and returned as "stdout" or "stderr" field respectively in the result
|
||||
table.
|
||||
|
||||
When a true value is passed to the nowait parameter, the function does not
|
||||
await process termination but returns as soon as all captured stdio streams
|
||||
have been closed or - if no streams are captured - immediately after launching
|
||||
the process.
|
||||
|
||||
@class function
|
||||
@name process.exec
|
||||
@param commend Table containing the argv vector to execute
|
||||
@param stdout Callback function or boolean to indicate capturing (optional)
|
||||
@param stderr Callback function or boolean to indicate capturing (optional)
|
||||
@param nowait Don't wait for process termination when true (optional)
|
||||
@return Table containing at least the fields "code" which holds the exit
|
||||
status of the invoked process or "-1" on error and "pid", which
|
||||
contains the process id assigned to the spawned process. When
|
||||
stdout and/or stderr capturing has been requested, it additionally
|
||||
contains "stdout" and "stderr" fields respectively, holding the
|
||||
captured stdio data as string.
|
||||
]]
|
||||
|
||||
---[[
|
||||
LuCI system utilities / user related functions.
|
||||
|
||||
@class module
|
||||
@name luci.sys.user
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve user information for given uid.
|
||||
|
||||
@class function
|
||||
@name getuser
|
||||
@param uid Number containing the Unix user id
|
||||
@return Table containing the following fields:
|
||||
-- { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve the current user password hash.
|
||||
|
||||
@class function
|
||||
@name user.getpasswd
|
||||
@param username String containing the username to retrieve the password for
|
||||
@return String containing the hash or nil if no password is set.
|
||||
@return Password database entry
|
||||
]]
|
||||
|
||||
---[[
|
||||
Test whether given string matches the password of a given system user.
|
||||
|
||||
@class function
|
||||
@name user.checkpasswd
|
||||
@param username String containing the Unix user name
|
||||
@param pass String containing the password to compare
|
||||
@return Boolean indicating whether the passwords are equal
|
||||
]]
|
||||
|
||||
---[[
|
||||
Change the password of given user.
|
||||
|
||||
@class function
|
||||
@name user.setpasswd
|
||||
@param username String containing the Unix user name
|
||||
@param password String containing the password to compare
|
||||
@return Number containing 0 on success and >= 1 on error
|
||||
]]
|
||||
|
||||
---[[
|
||||
LuCI system utilities / wifi related functions.
|
||||
|
||||
@class module
|
||||
@name luci.sys.wifi
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get wireless information for given interface.
|
||||
|
||||
@class function
|
||||
@name wifi.getiwinfo
|
||||
@param ifname String containing the interface name
|
||||
@return A wrapped iwinfo object instance
|
||||
]]
|
||||
|
||||
---[[
|
||||
LuCI system utilities / init related functions.
|
||||
|
||||
@class module
|
||||
@name luci.sys.init
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the names of all installed init scripts
|
||||
|
||||
@class function
|
||||
@name init.names
|
||||
@return Table containing the names of all inistalled init scripts
|
||||
]]
|
||||
|
||||
---[[
|
||||
Get the index of he given init script
|
||||
|
||||
@class function
|
||||
@name init.index
|
||||
@param name Name of the init script
|
||||
@return Numeric index value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Test whether the given init script is enabled
|
||||
|
||||
@class function
|
||||
@name init.enabled
|
||||
@param name Name of the init script
|
||||
@return Boolean indicating whether init is enabled
|
||||
]]
|
||||
|
||||
---[[
|
||||
Enable the given init script
|
||||
|
||||
@class function
|
||||
@name init.enable
|
||||
@param name Name of the init script
|
||||
@return Boolean indicating success
|
||||
]]
|
||||
|
||||
---[[
|
||||
Disable the given init script
|
||||
|
||||
@class function
|
||||
@name init.disable
|
||||
@param name Name of the init script
|
||||
@return Boolean indicating success
|
||||
]]
|
||||
|
||||
---[[
|
||||
Start the given init script
|
||||
|
||||
@class function
|
||||
@name init.start
|
||||
@param name Name of the init script
|
||||
@return Boolean indicating success
|
||||
]]
|
||||
|
||||
---[[
|
||||
Stop the given init script
|
||||
|
||||
@class function
|
||||
@name init.stop
|
||||
@param name Name of the init script
|
||||
@return Boolean indicating success
|
||||
]]
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
-- Licensed to the public under the Apache 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
|
||||
})
|
|
@ -1,458 +0,0 @@
|
|||
-- Licensed to the public under the Apache 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/Cairo', 'EET-2' },
|
||||
{ 'Africa/Casablanca', '<+01>-1' },
|
||||
{ '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', '<+01>-1' },
|
||||
{ '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', 'CAT-2' },
|
||||
{ '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', 'CAT-2' },
|
||||
{ 'America/Adak', 'HST10HDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Anchorage', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Anguilla', 'AST4' },
|
||||
{ 'America/Antigua', 'AST4' },
|
||||
{ 'America/Araguaina', '<-03>3' },
|
||||
{ 'America/Argentina/Buenos Aires', '<-03>3' },
|
||||
{ 'America/Argentina/Catamarca', '<-03>3' },
|
||||
{ 'America/Argentina/Cordoba', '<-03>3' },
|
||||
{ 'America/Argentina/Jujuy', '<-03>3' },
|
||||
{ 'America/Argentina/La Rioja', '<-03>3' },
|
||||
{ 'America/Argentina/Mendoza', '<-03>3' },
|
||||
{ 'America/Argentina/Rio Gallegos', '<-03>3' },
|
||||
{ 'America/Argentina/Salta', '<-03>3' },
|
||||
{ 'America/Argentina/San Juan', '<-03>3' },
|
||||
{ 'America/Argentina/San Luis', '<-03>3' },
|
||||
{ 'America/Argentina/Tucuman', '<-03>3' },
|
||||
{ 'America/Argentina/Ushuaia', '<-03>3' },
|
||||
{ 'America/Aruba', 'AST4' },
|
||||
{ 'America/Asuncion', '<-04>4<-03>,M10.1.0/0,M3.4.0/0' },
|
||||
{ 'America/Atikokan', 'EST5' },
|
||||
{ 'America/Bahia', '<-03>3' },
|
||||
{ 'America/Bahia Banderas', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||
{ 'America/Barbados', 'AST4' },
|
||||
{ 'America/Belem', '<-03>3' },
|
||||
{ 'America/Belize', 'CST6' },
|
||||
{ 'America/Blanc-Sablon', 'AST4' },
|
||||
{ 'America/Boa Vista', '<-04>4' },
|
||||
{ 'America/Bogota', '<-05>5' },
|
||||
{ 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Campo Grande', '<-04>4' },
|
||||
{ 'America/Cancun', 'EST5' },
|
||||
{ 'America/Caracas', '<-04>4' },
|
||||
{ 'America/Cayenne', '<-03>3' },
|
||||
{ '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/Creston', 'MST7' },
|
||||
{ 'America/Cuiaba', '<-04>4' },
|
||||
{ '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', '<-05>5' },
|
||||
{ 'America/El Salvador', 'CST6' },
|
||||
{ 'America/Fort Nelson', 'MST7' },
|
||||
{ 'America/Fortaleza', '<-03>3' },
|
||||
{ 'America/Glace Bay', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Godthab', '<-03>3<-02>,M3.5.0/-2,M10.5.0/-1' },
|
||||
{ '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', '<-05>5' },
|
||||
{ 'America/Guyana', '<-04>4' },
|
||||
{ 'America/Halifax', 'AST4ADT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Havana', 'CST5CDT,M3.2.0/0,M11.1.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', '<-04>4' },
|
||||
{ 'America/Lima', '<-05>5' },
|
||||
{ 'America/Los Angeles', 'PST8PDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Lower Princes', 'AST4' },
|
||||
{ 'America/Maceio', '<-03>3' },
|
||||
{ 'America/Managua', 'CST6' },
|
||||
{ 'America/Manaus', '<-04>4' },
|
||||
{ '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', 'AKST9AKDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Mexico City', 'CST6CDT,M4.1.0,M10.5.0' },
|
||||
{ 'America/Miquelon', '<-03>3<-02>,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', '<-03>3' },
|
||||
{ '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', '<-02>2' },
|
||||
{ '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', '<-03>3' },
|
||||
{ 'America/Phoenix', 'MST7' },
|
||||
{ 'America/Port of Spain', 'AST4' },
|
||||
{ 'America/Port-au-Prince', 'EST5EDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Porto Velho', '<-04>4' },
|
||||
{ 'America/Puerto Rico', 'AST4' },
|
||||
{ 'America/Punta Arenas', '<-03>3' },
|
||||
{ 'America/Rainy River', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Rankin Inlet', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Recife', '<-03>3' },
|
||||
{ 'America/Regina', 'CST6' },
|
||||
{ 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
|
||||
{ 'America/Rio Branco', '<-05>5' },
|
||||
{ 'America/Santarem', '<-03>3' },
|
||||
{ 'America/Santiago', '<-04>4<-03>,M9.1.6/24,M4.1.6/24' },
|
||||
{ 'America/Santo Domingo', 'AST4' },
|
||||
{ 'America/Sao Paulo', '<-03>3' },
|
||||
{ 'America/Scoresbysund', '<-01>1<+00>,M3.5.0/0,M10.5.0/1' },
|
||||
{ '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', '<+08>-8' },
|
||||
{ 'Antarctica/Davis', '<+07>-7' },
|
||||
{ 'Antarctica/DumontDUrville', '<+10>-10' },
|
||||
{ 'Antarctica/Macquarie', '<+11>-11' },
|
||||
{ 'Antarctica/Mawson', '<+05>-5' },
|
||||
{ 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
|
||||
{ 'Antarctica/Palmer', '<-03>3' },
|
||||
{ 'Antarctica/Rothera', '<-03>3' },
|
||||
{ 'Antarctica/Syowa', '<+03>-3' },
|
||||
{ 'Antarctica/Troll', '<+00>0<+02>-2,M3.5.0/1,M10.5.0/3' },
|
||||
{ 'Antarctica/Vostok', '<+06>-6' },
|
||||
{ 'Arctic/Longyearbyen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Asia/Aden', '<+03>-3' },
|
||||
{ 'Asia/Almaty', '<+06>-6' },
|
||||
{ 'Asia/Amman', 'EET-2EEST,M3.5.4/24,M10.5.5/1' },
|
||||
{ 'Asia/Anadyr', '<+12>-12' },
|
||||
{ 'Asia/Aqtau', '<+05>-5' },
|
||||
{ 'Asia/Aqtobe', '<+05>-5' },
|
||||
{ 'Asia/Ashgabat', '<+05>-5' },
|
||||
{ 'Asia/Atyrau', '<+05>-5' },
|
||||
{ 'Asia/Baghdad', '<+03>-3' },
|
||||
{ 'Asia/Bahrain', '<+03>-3' },
|
||||
{ 'Asia/Baku', '<+04>-4' },
|
||||
{ 'Asia/Bangkok', '<+07>-7' },
|
||||
{ 'Asia/Barnaul', '<+07>-7' },
|
||||
{ 'Asia/Beirut', 'EET-2EEST,M3.5.0/0,M10.5.0/0' },
|
||||
{ 'Asia/Bishkek', '<+06>-6' },
|
||||
{ 'Asia/Brunei', '<+08>-8' },
|
||||
{ 'Asia/Chita', '<+09>-9' },
|
||||
{ 'Asia/Choibalsan', '<+08>-8' },
|
||||
{ 'Asia/Colombo', '<+0530>-5:30' },
|
||||
{ 'Asia/Damascus', 'EET-2EEST,M3.5.5/0,M10.5.5/0' },
|
||||
{ 'Asia/Dhaka', '<+06>-6' },
|
||||
{ 'Asia/Dili', '<+09>-9' },
|
||||
{ 'Asia/Dubai', '<+04>-4' },
|
||||
{ 'Asia/Dushanbe', '<+05>-5' },
|
||||
{ 'Asia/Famagusta', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||
{ 'Asia/Gaza', 'EET-2EEST,M3.5.5/0,M10.5.6/1' },
|
||||
{ 'Asia/Hebron', 'EET-2EEST,M3.5.5/0,M10.5.6/1' },
|
||||
{ 'Asia/Ho Chi Minh', '<+07>-7' },
|
||||
{ 'Asia/Hong Kong', 'HKT-8' },
|
||||
{ 'Asia/Hovd', '<+07>-7' },
|
||||
{ 'Asia/Irkutsk', '<+08>-8' },
|
||||
{ 'Asia/Jakarta', 'WIB-7' },
|
||||
{ 'Asia/Jayapura', 'WIT-9' },
|
||||
{ 'Asia/Jerusalem', 'IST-2IDT,M3.4.4/26,M10.5.0' },
|
||||
{ 'Asia/Kabul', '<+0430>-4:30' },
|
||||
{ 'Asia/Kamchatka', '<+12>-12' },
|
||||
{ 'Asia/Karachi', 'PKT-5' },
|
||||
{ 'Asia/Kathmandu', '<+0545>-5:45' },
|
||||
{ 'Asia/Khandyga', '<+09>-9' },
|
||||
{ 'Asia/Kolkata', 'IST-5:30' },
|
||||
{ 'Asia/Krasnoyarsk', '<+07>-7' },
|
||||
{ 'Asia/Kuala Lumpur', '<+08>-8' },
|
||||
{ 'Asia/Kuching', '<+08>-8' },
|
||||
{ 'Asia/Kuwait', '<+03>-3' },
|
||||
{ 'Asia/Macau', 'CST-8' },
|
||||
{ 'Asia/Magadan', '<+11>-11' },
|
||||
{ 'Asia/Makassar', 'WITA-8' },
|
||||
{ 'Asia/Manila', 'PST-8' },
|
||||
{ 'Asia/Muscat', '<+04>-4' },
|
||||
{ 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||
{ 'Asia/Novokuznetsk', '<+07>-7' },
|
||||
{ 'Asia/Novosibirsk', '<+07>-7' },
|
||||
{ 'Asia/Omsk', '<+06>-6' },
|
||||
{ 'Asia/Oral', '<+05>-5' },
|
||||
{ 'Asia/Phnom Penh', '<+07>-7' },
|
||||
{ 'Asia/Pontianak', 'WIB-7' },
|
||||
{ 'Asia/Pyongyang', 'KST-9' },
|
||||
{ 'Asia/Qatar', '<+03>-3' },
|
||||
{ 'Asia/Qostanay', '<+06>-6' },
|
||||
{ 'Asia/Qyzylorda', '<+05>-5' },
|
||||
{ 'Asia/Riyadh', '<+03>-3' },
|
||||
{ 'Asia/Sakhalin', '<+11>-11' },
|
||||
{ 'Asia/Samarkand', '<+05>-5' },
|
||||
{ 'Asia/Seoul', 'KST-9' },
|
||||
{ 'Asia/Shanghai', 'CST-8' },
|
||||
{ 'Asia/Singapore', '<+08>-8' },
|
||||
{ 'Asia/Srednekolymsk', '<+11>-11' },
|
||||
{ 'Asia/Taipei', 'CST-8' },
|
||||
{ 'Asia/Tashkent', '<+05>-5' },
|
||||
{ 'Asia/Tbilisi', '<+04>-4' },
|
||||
{ 'Asia/Tehran', '<+0330>-3:30<+0430>,J79/24,J263/24' },
|
||||
{ 'Asia/Thimphu', '<+06>-6' },
|
||||
{ 'Asia/Tokyo', 'JST-9' },
|
||||
{ 'Asia/Tomsk', '<+07>-7' },
|
||||
{ 'Asia/Ulaanbaatar', '<+08>-8' },
|
||||
{ 'Asia/Urumqi', '<+06>-6' },
|
||||
{ 'Asia/Ust-Nera', '<+10>-10' },
|
||||
{ 'Asia/Vientiane', '<+07>-7' },
|
||||
{ 'Asia/Vladivostok', '<+10>-10' },
|
||||
{ 'Asia/Yakutsk', '<+09>-9' },
|
||||
{ 'Asia/Yangon', '<+0630>-6:30' },
|
||||
{ 'Asia/Yekaterinburg', '<+05>-5' },
|
||||
{ 'Asia/Yerevan', '<+04>-4' },
|
||||
{ 'Atlantic/Azores', '<-01>1<+00>,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', '<-01>1' },
|
||||
{ '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', '<-02>2' },
|
||||
{ 'Atlantic/St Helena', 'GMT0' },
|
||||
{ 'Atlantic/Stanley', '<-03>3' },
|
||||
{ 'Australia/Adelaide', 'ACST-9:30ACDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Australia/Brisbane', 'AEST-10' },
|
||||
{ 'Australia/Broken Hill', 'ACST-9:30ACDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Australia/Currie', 'AEST-10AEDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Australia/Darwin', 'ACST-9:30' },
|
||||
{ 'Australia/Eucla', '<+0845>-8:45' },
|
||||
{ 'Australia/Hobart', 'AEST-10AEDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Australia/Lindeman', 'AEST-10' },
|
||||
{ 'Australia/Lord Howe', '<+1030>-10:30<+11>-11,M10.1.0,M4.1.0' },
|
||||
{ 'Australia/Melbourne', 'AEST-10AEDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Australia/Perth', 'AWST-8' },
|
||||
{ 'Australia/Sydney', 'AEST-10AEDT,M10.1.0,M4.1.0/3' },
|
||||
{ 'Etc/GMT', 'GMT0' },
|
||||
{ 'Etc/GMT+1', '<-01>1' },
|
||||
{ 'Etc/GMT+10', '<-10>10' },
|
||||
{ 'Etc/GMT+11', '<-11>11' },
|
||||
{ 'Etc/GMT+12', '<-12>12' },
|
||||
{ 'Etc/GMT+2', '<-02>2' },
|
||||
{ 'Etc/GMT+3', '<-03>3' },
|
||||
{ 'Etc/GMT+4', '<-04>4' },
|
||||
{ 'Etc/GMT+5', '<-05>5' },
|
||||
{ 'Etc/GMT+6', '<-06>6' },
|
||||
{ 'Etc/GMT+7', '<-07>7' },
|
||||
{ 'Etc/GMT+8', '<-08>8' },
|
||||
{ 'Etc/GMT+9', '<-09>9' },
|
||||
{ 'Etc/GMT-1', '<+01>-1' },
|
||||
{ 'Etc/GMT-10', '<+10>-10' },
|
||||
{ 'Etc/GMT-11', '<+11>-11' },
|
||||
{ 'Etc/GMT-12', '<+12>-12' },
|
||||
{ 'Etc/GMT-13', '<+13>-13' },
|
||||
{ 'Etc/GMT-14', '<+14>-14' },
|
||||
{ 'Etc/GMT-2', '<+02>-2' },
|
||||
{ 'Etc/GMT-3', '<+03>-3' },
|
||||
{ 'Etc/GMT-4', '<+04>-4' },
|
||||
{ 'Etc/GMT-5', '<+05>-5' },
|
||||
{ 'Etc/GMT-6', '<+06>-6' },
|
||||
{ 'Etc/GMT-7', '<+07>-7' },
|
||||
{ 'Etc/GMT-8', '<+08>-8' },
|
||||
{ 'Etc/GMT-9', '<+09>-9' },
|
||||
{ 'Europe/Amsterdam', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Andorra', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Astrakhan', '<+04>-4' },
|
||||
{ '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/Busingen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Chisinau', 'EET-2EEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Dublin', 'IST-1GMT0,M10.5.0,M3.5.0/1' },
|
||||
{ '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', '<+03>-3' },
|
||||
{ 'Europe/Jersey', 'GMT0BST,M3.5.0/1,M10.5.0' },
|
||||
{ 'Europe/Kaliningrad', 'EET-2' },
|
||||
{ 'Europe/Kiev', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
|
||||
{ 'Europe/Kirov', '<+03>-3' },
|
||||
{ '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', '<+03>-3' },
|
||||
{ 'Europe/Monaco', 'CET-1CEST,M3.5.0,M10.5.0/3' },
|
||||
{ 'Europe/Moscow', 'MSK-3' },
|
||||
{ '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', '<+04>-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/Saratov', '<+04>-4' },
|
||||
{ 'Europe/Simferopol', 'MSK-3' },
|
||||
{ '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/Ulyanovsk', '<+04>-4' },
|
||||
{ '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', '<+04>-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', '<+06>-6' },
|
||||
{ 'Indian/Christmas', '<+07>-7' },
|
||||
{ 'Indian/Cocos', '<+0630>-6:30' },
|
||||
{ 'Indian/Comoro', 'EAT-3' },
|
||||
{ 'Indian/Kerguelen', '<+05>-5' },
|
||||
{ 'Indian/Mahe', '<+04>-4' },
|
||||
{ 'Indian/Maldives', '<+05>-5' },
|
||||
{ 'Indian/Mauritius', '<+04>-4' },
|
||||
{ 'Indian/Mayotte', 'EAT-3' },
|
||||
{ 'Indian/Reunion', '<+04>-4' },
|
||||
{ 'Pacific/Apia', '<+13>-13<+14>,M9.5.0/3,M4.1.0/4' },
|
||||
{ 'Pacific/Auckland', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
|
||||
{ 'Pacific/Bougainville', '<+11>-11' },
|
||||
{ 'Pacific/Chatham', '<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45' },
|
||||
{ 'Pacific/Chuuk', '<+10>-10' },
|
||||
{ 'Pacific/Easter', '<-06>6<-05>,M9.1.6/22,M4.1.6/22' },
|
||||
{ 'Pacific/Efate', '<+11>-11' },
|
||||
{ 'Pacific/Enderbury', '<+13>-13' },
|
||||
{ 'Pacific/Fakaofo', '<+13>-13' },
|
||||
{ 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.2/123' },
|
||||
{ 'Pacific/Funafuti', '<+12>-12' },
|
||||
{ 'Pacific/Galapagos', '<-06>6' },
|
||||
{ 'Pacific/Gambier', '<-09>9' },
|
||||
{ 'Pacific/Guadalcanal', '<+11>-11' },
|
||||
{ 'Pacific/Guam', 'ChST-10' },
|
||||
{ 'Pacific/Honolulu', 'HST10' },
|
||||
{ 'Pacific/Kiritimati', '<+14>-14' },
|
||||
{ 'Pacific/Kosrae', '<+11>-11' },
|
||||
{ 'Pacific/Kwajalein', '<+12>-12' },
|
||||
{ 'Pacific/Majuro', '<+12>-12' },
|
||||
{ 'Pacific/Marquesas', '<-0930>9:30' },
|
||||
{ 'Pacific/Midway', 'SST11' },
|
||||
{ 'Pacific/Nauru', '<+12>-12' },
|
||||
{ 'Pacific/Niue', '<-11>11' },
|
||||
{ 'Pacific/Norfolk', '<+11>-11' },
|
||||
{ 'Pacific/Noumea', '<+11>-11' },
|
||||
{ 'Pacific/Pago Pago', 'SST11' },
|
||||
{ 'Pacific/Palau', '<+09>-9' },
|
||||
{ 'Pacific/Pitcairn', '<-08>8' },
|
||||
{ 'Pacific/Pohnpei', '<+11>-11' },
|
||||
{ 'Pacific/Port Moresby', '<+10>-10' },
|
||||
{ 'Pacific/Rarotonga', '<-10>10' },
|
||||
{ 'Pacific/Saipan', 'ChST-10' },
|
||||
{ 'Pacific/Tahiti', '<-10>10' },
|
||||
{ 'Pacific/Tarawa', '<+12>-12' },
|
||||
{ 'Pacific/Tongatapu', '<+13>-13' },
|
||||
{ 'Pacific/Wake', '<+12>-12' },
|
||||
{ 'Pacific/Wallis', '<+12>-12' },
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module "luci.sys.zoneinfo.tzoffset"
|
||||
|
||||
OFFSET = {
|
||||
gmt = 0, -- GMT
|
||||
eat = 10800, -- EAT
|
||||
cet = 3600, -- CET
|
||||
wat = 3600, -- WAT
|
||||
cat = 7200, -- CAT
|
||||
eet = 7200, -- EET
|
||||
sast = 7200, -- SAST
|
||||
hst = -36000, -- HST
|
||||
hdt = -32400, -- HDT
|
||||
akst = -32400, -- AKST
|
||||
akdt = -28800, -- AKDT
|
||||
ast = -14400, -- AST
|
||||
est = -18000, -- EST
|
||||
cst = -21600, -- CST
|
||||
cdt = -18000, -- CDT
|
||||
mst = -25200, -- MST
|
||||
mdt = -21600, -- MDT
|
||||
pst = -28800, -- PST
|
||||
pdt = -25200, -- PDT
|
||||
nst = -12600, -- NST
|
||||
ndt = -9000, -- NDT
|
||||
nzst = 43200, -- NZST
|
||||
nzdt = 46800, -- NZDT
|
||||
hkt = 28800, -- HKT
|
||||
wib = 25200, -- WIB
|
||||
wit = 32400, -- WIT
|
||||
ist = 7200, -- IST
|
||||
idt = 10800, -- IDT
|
||||
pkt = 18000, -- PKT
|
||||
wita = 28800, -- WITA
|
||||
kst = 32400, -- KST
|
||||
jst = 32400, -- JST
|
||||
wet = 0, -- WET
|
||||
acst = 34200, -- ACST
|
||||
acdt = 37800, -- ACDT
|
||||
aest = 36000, -- AEST
|
||||
awst = 28800, -- AWST
|
||||
msk = 10800, -- MSK
|
||||
sst = -39600, -- SST
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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
|
||||
|
||||
--- Render a template from a string.
|
||||
-- @param template Template string
|
||||
-- @param scope Scope to assign to template (optional)
|
||||
function render_string(template, scope)
|
||||
return Template(nil, template):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, template)
|
||||
if name then
|
||||
self.template = self.cache[name]
|
||||
self.name = name
|
||||
else
|
||||
self.name = "[string]"
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
if name then
|
||||
sourcefile = viewdir .. "/" .. name .. ".htm"
|
||||
self.template, _, err = tparser.parse(sourcefile)
|
||||
else
|
||||
sourcefile = "[string]"
|
||||
self.template, _, err = tparser.parse_string(template)
|
||||
end
|
||||
|
||||
-- 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"))
|
||||
elseif name then
|
||||
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 occurred: " .. tostring(err or "(nil)"))
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
-- Copyright 2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache 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
|
|
@ -1,290 +0,0 @@
|
|||
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module("luci.tools.status", package.seeall)
|
||||
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local ipc = require "luci.ip"
|
||||
|
||||
local function dhcp_leases_common(family)
|
||||
local rv = { }
|
||||
local nfs = require "nixio.fs"
|
||||
local sys = require "luci.sys"
|
||||
local leasefile = "/tmp/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+)")
|
||||
local expire = tonumber(ts) or 0
|
||||
if ts and mac and ip and name and duid then
|
||||
if family == 4 and not ip:match(":") then
|
||||
rv[#rv+1] = {
|
||||
expires = (expire ~= 0) and os.difftime(expire, os.time()),
|
||||
macaddr = ipc.checkmac(mac) or "00:00:00:00:00:00",
|
||||
ipaddr = ip,
|
||||
hostname = (name ~= "*") and name
|
||||
}
|
||||
elseif family == 6 and ip:match(":") then
|
||||
rv[#rv+1] = {
|
||||
expires = (expire ~= 0) and os.difftime(expire, os.time()),
|
||||
ip6addr = ip,
|
||||
duid = (duid ~= "*") and duid,
|
||||
hostname = (name ~= "*") and name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
fd:close()
|
||||
end
|
||||
|
||||
local lease6file = "/tmp/hosts/odhcpd"
|
||||
uci:foreach("dhcp", "odhcpd",
|
||||
function(t)
|
||||
if t.leasefile and nfs.access(t.leasefile) then
|
||||
lease6file = t.leasefile
|
||||
return false
|
||||
end
|
||||
end)
|
||||
local fd = io.open(lease6file, "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+) (.*)")
|
||||
local expire = tonumber(ts) or 0
|
||||
if ip and iaid ~= "ipv4" and family == 6 then
|
||||
rv[#rv+1] = {
|
||||
expires = (expire >= 0) and os.difftime(expire, os.time()),
|
||||
duid = duid,
|
||||
ip6addr = ip,
|
||||
hostname = (name ~= "-") and name
|
||||
}
|
||||
elseif ip and iaid == "ipv4" and family == 4 then
|
||||
rv[#rv+1] = {
|
||||
expires = (expire >= 0) and os.difftime(expire, os.time()),
|
||||
macaddr = sys.net.duid_to_mac(duid) or "00:00:00:00:00:00",
|
||||
ipaddr = ip,
|
||||
hostname = (name ~= "-") and name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
fd:close()
|
||||
end
|
||||
|
||||
if family == 6 then
|
||||
local _, lease
|
||||
local hosts = sys.net.host_hints()
|
||||
for _, lease in ipairs(rv) do
|
||||
local mac = sys.net.duid_to_mac(lease.duid)
|
||||
local host = mac and hosts[mac]
|
||||
if host then
|
||||
if not lease.name then
|
||||
lease.host_hint = host.name or host.ipv4 or host.ipv6
|
||||
elseif host.name and lease.hostname ~= host.name then
|
||||
lease.host_hint = host.name
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
local a, an = nil, 0
|
||||
for _, a in pairs(net:assoclist() or {}) do
|
||||
an = an + 1
|
||||
end
|
||||
|
||||
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(),
|
||||
country = net:country(),
|
||||
txpower = net:txpower(),
|
||||
txpoweroff = net:txpower_offset(),
|
||||
num_assoc = an,
|
||||
disabled = (dev:get("disabled") == "1" or
|
||||
net:get("disabled") == "1")
|
||||
}
|
||||
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(),
|
||||
country = net:country(),
|
||||
txpower = net:txpower(),
|
||||
txpoweroff = net:txpower_offset(),
|
||||
disabled = (dev:get("disabled") == "1" or
|
||||
net:get("disabled") == "1"),
|
||||
device = {
|
||||
up = dev:is_up(),
|
||||
device = dev:name(),
|
||||
name = dev:get_i18n()
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
return { }
|
||||
end
|
||||
|
||||
function wifi_assoclist()
|
||||
local sys = require "luci.sys"
|
||||
local ntm = require "luci.model.network".init()
|
||||
local hosts = sys.net.host_hints()
|
||||
|
||||
local assoc = {}
|
||||
local _, dev, net, bss
|
||||
|
||||
for _, dev in ipairs(ntm:get_wifidevs()) do
|
||||
local radioname = dev:get_i18n()
|
||||
|
||||
for _, net in ipairs(dev:get_wifinets()) do
|
||||
local netname = net:shortname()
|
||||
local netlink = net:adminlink()
|
||||
local ifname = net:ifname()
|
||||
|
||||
for _, bss in pairs(net:assoclist() or {}) do
|
||||
local host = hosts[_]
|
||||
|
||||
bss.bssid = _
|
||||
bss.ifname = ifname
|
||||
bss.radio = radioname
|
||||
bss.name = netname
|
||||
bss.link = netlink
|
||||
|
||||
bss.host_name = (host) and (host.name or host.ipv4 or host.ipv6)
|
||||
bss.host_hint = (host and host.name and (host.ipv4 or host.ipv6)) and (host.ipv4 or host.ipv6)
|
||||
|
||||
assoc[#assoc+1] = bss
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(assoc, function(a, b)
|
||||
if a.radio ~= b.radio then
|
||||
return a.radio < b.radio
|
||||
elseif a.ifname ~= b.ifname then
|
||||
return a.ifname < b.ifname
|
||||
else
|
||||
return a.bssid < b.bssid
|
||||
end
|
||||
end)
|
||||
|
||||
return assoc
|
||||
end
|
||||
|
||||
function switch_status(devs)
|
||||
local dev
|
||||
local switches = { }
|
||||
for dev in devs:gmatch("[^%s,]+") do
|
||||
local ports = { }
|
||||
local swc = io.popen("swconfig dev %s show"
|
||||
% luci.util.shellquote(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
|
|
@ -1,105 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module("luci.tools.webadmin", package.seeall)
|
||||
|
||||
local util = require "luci.util"
|
||||
local uci = require "luci.model.uci"
|
||||
local ip = require "luci.ip"
|
||||
|
||||
function byte_format(byte)
|
||||
local suff = {"B", "KB", "MB", "GB", "TB"}
|
||||
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 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)
|
||||
local _, n
|
||||
for _, n in ipairs(ip.neighbors({ family = 4 })) do
|
||||
if n.dest then
|
||||
field:value(n.dest:string())
|
||||
end
|
||||
end
|
||||
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 link = ip.link(tostring(iface))
|
||||
if link.master then
|
||||
iface = link.master
|
||||
end
|
||||
|
||||
local cur = uci.cursor()
|
||||
local dump = util.ubus("network.interface", "dump", { })
|
||||
if dump then
|
||||
local _, net
|
||||
for _, net in ipairs(dump.interface) do
|
||||
if net.l3_device == iface or net.device == iface then
|
||||
-- cross check with uci to filter out @name style aliases
|
||||
local uciname = cur:get("network", net.interface, "ifname")
|
||||
if type(uciname) == "string" and uciname:sub(1,1) ~= "@" or uciname then
|
||||
return net.interface
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,776 +0,0 @@
|
|||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
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 json = require "luci.jsonc"
|
||||
local lhttp = require "lucihttp"
|
||||
|
||||
local _ubus = require "ubus"
|
||||
local _ubus_connection = nil
|
||||
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
local rawget, rawset, unpack, select = rawget, rawset, unpack, select
|
||||
local tostring, type, assert, error = tostring, type, assert, error
|
||||
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
|
||||
|
||||
module "luci.util"
|
||||
|
||||
--
|
||||
-- Pythonic string formatting extension
|
||||
--
|
||||
getmetatable("").__mod = function(a, b)
|
||||
local ok, res
|
||||
|
||||
if not b then
|
||||
return a
|
||||
elseif type(b) == "table" then
|
||||
local k, _
|
||||
for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
|
||||
|
||||
ok, res = pcall(a.format, a, unpack(b))
|
||||
if not ok then
|
||||
error(res, 2)
|
||||
end
|
||||
return res
|
||||
else
|
||||
if type(b) == "userdata" then b = tostring(b) end
|
||||
|
||||
ok, res = pcall(a.format, a, b)
|
||||
if not ok then
|
||||
error(res, 2)
|
||||
end
|
||||
return res
|
||||
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
|
||||
|
||||
-- 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.
|
||||
function class(base)
|
||||
return setmetatable({}, {
|
||||
__call = _instantiate,
|
||||
__index = base
|
||||
})
|
||||
end
|
||||
|
||||
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
|
||||
--
|
||||
|
||||
coxpt = setmetatable({}, { __mode = "kv" })
|
||||
|
||||
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
|
||||
local r = rawget(self, c)
|
||||
if not r then
|
||||
rawset(self, c, { [key] = value })
|
||||
else
|
||||
r[key] = value
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
-- 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.
|
||||
function threadlocal(tbl)
|
||||
return setmetatable(tbl or {}, tl_meta)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Debugging routines
|
||||
--
|
||||
|
||||
function perror(obj)
|
||||
return io.stderr:write(tostring(obj) .. "\n")
|
||||
end
|
||||
|
||||
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
|
||||
--
|
||||
|
||||
function pcdata(value)
|
||||
return value and tparser.pcdata(tostring(value))
|
||||
end
|
||||
|
||||
function urlencode(value)
|
||||
if value ~= nil then
|
||||
local str = tostring(value)
|
||||
return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
|
||||
or str
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function urldecode(value, decode_plus)
|
||||
if value ~= nil then
|
||||
local flag = decode_plus and lhttp.DECODE_PLUS or 0
|
||||
local str = tostring(value)
|
||||
return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
|
||||
or str
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function striptags(value)
|
||||
return value and tparser.striptags(tostring(value))
|
||||
end
|
||||
|
||||
function shellquote(value)
|
||||
return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
|
||||
end
|
||||
|
||||
-- for bash, ash and similar shells single-quoted strings are taken
|
||||
-- literally except for single quotes (which terminate the string)
|
||||
-- (and the exception noted below for dash (-) at the start of a
|
||||
-- command line parameter).
|
||||
function shellsqescape(value)
|
||||
local res
|
||||
res, _ = string.gsub(value, "'", "'\\''")
|
||||
return res
|
||||
end
|
||||
|
||||
-- bash, ash and other similar shells interpret a dash (-) at the start
|
||||
-- of a command-line parameters as an option indicator regardless of
|
||||
-- whether it is inside a single-quoted string. It must be backlash
|
||||
-- escaped to resolve this. This requires in some funky special-case
|
||||
-- handling. It may actually be a property of the getopt function
|
||||
-- rather than the shell proper.
|
||||
function shellstartsqescape(value)
|
||||
res, _ = string.gsub(value, "^%-", "\\-")
|
||||
return shellsqescape(res)
|
||||
end
|
||||
|
||||
-- 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.
|
||||
-- pattern as regular expression (optional, default is false)
|
||||
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
|
||||
|
||||
function trim(str)
|
||||
return (str:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function cmatch(str, pat)
|
||||
local count = 0
|
||||
for _ in str:gmatch(pat) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
-- 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 iterator which aborts with the first invocation.
|
||||
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
|
||||
|
||||
-- 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)
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
function combine(...)
|
||||
return append({}, ...)
|
||||
end
|
||||
|
||||
function contains(table, value)
|
||||
for k, v in pairs(table) do
|
||||
if value == v then
|
||||
return k
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Both table are - in fact - merged together.
|
||||
function update(t, updates)
|
||||
for k, v in pairs(updates) do
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function keys(t)
|
||||
local keys = { }
|
||||
if t then
|
||||
for k, _ in kspairs(t) do
|
||||
keys[#keys+1] = k
|
||||
end
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
-- with loadstring().
|
||||
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
|
||||
|
||||
function restore_data(str)
|
||||
return loadstring("return " .. str)()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Byte code manipulation routines
|
||||
--
|
||||
|
||||
-- will be stripped before it is returned.
|
||||
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
|
||||
|
||||
-- numbers and debugging numbers will be discarded. Original version by
|
||||
-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
|
||||
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
|
||||
|
||||
-- the provided callback function.
|
||||
function spairs(t,f)
|
||||
return _sortiter( t, f )
|
||||
end
|
||||
|
||||
-- The table pairs are sorted by key.
|
||||
function kspairs(t)
|
||||
return _sortiter( t )
|
||||
end
|
||||
|
||||
-- The table pairs are sorted by value.
|
||||
function vspairs(t)
|
||||
return _sortiter( t, function (a,b) return t[a] < t[b] end )
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- System utility functions
|
||||
--
|
||||
|
||||
function bigendian()
|
||||
return string.byte(string.dump(function() end), 7) == 0
|
||||
end
|
||||
|
||||
function exec(command)
|
||||
local pp = io.popen(command)
|
||||
local data = pp:read("*a")
|
||||
pp:close()
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
||||
local ubus_codes = {
|
||||
"INVALID_COMMAND",
|
||||
"INVALID_ARGUMENT",
|
||||
"METHOD_NOT_FOUND",
|
||||
"NOT_FOUND",
|
||||
"NO_DATA",
|
||||
"PERMISSION_DENIED",
|
||||
"TIMEOUT",
|
||||
"NOT_SUPPORTED",
|
||||
"UNKNOWN_ERROR",
|
||||
"CONNECTION_FAILED"
|
||||
}
|
||||
|
||||
local function ubus_return(...)
|
||||
if select('#', ...) == 2 then
|
||||
local rv, err = select(1, ...), select(2, ...)
|
||||
if rv == nil and type(err) == "number" then
|
||||
return nil, err, ubus_codes[err]
|
||||
end
|
||||
end
|
||||
|
||||
return ...
|
||||
end
|
||||
|
||||
function ubus(object, method, data)
|
||||
if not _ubus_connection then
|
||||
_ubus_connection = _ubus.connect()
|
||||
assert(_ubus_connection, "Unable to establish ubus connection")
|
||||
end
|
||||
|
||||
if object and method then
|
||||
if type(data) ~= "table" then
|
||||
data = { }
|
||||
end
|
||||
return ubus_return(_ubus_connection:call(object, method, data))
|
||||
elseif object then
|
||||
return _ubus_connection:signatures(object)
|
||||
else
|
||||
return _ubus_connection:objects()
|
||||
end
|
||||
end
|
||||
|
||||
function serialize_json(x, cb)
|
||||
local js = json.stringify(x)
|
||||
if type(cb) == "function" then
|
||||
cb(js)
|
||||
else
|
||||
return js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function libpath()
|
||||
return require "nixio.fs".dirname(ldebug.__file__)
|
||||
end
|
||||
|
||||
function checklib(fullpathexe, wantedlib)
|
||||
local fs = require "nixio.fs"
|
||||
local haveldd = fs.access('/usr/bin/ldd')
|
||||
local haveexe = fs.access(fullpathexe)
|
||||
if not haveldd or not haveexe then
|
||||
return false
|
||||
end
|
||||
local libs = exec(string.format("/usr/bin/ldd %s", shellquote(fullpathexe)))
|
||||
if not libs then
|
||||
return false
|
||||
end
|
||||
for k, v in ipairs(split(libs)) do
|
||||
if v:find(wantedlib) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Coroutine safe xpcall and pcall versions
|
||||
--
|
||||
-- Encapsulates the protected calls with a coroutine based loop, so errors can
|
||||
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
|
||||
-- yielding inside the call to pcall or xpcall.
|
||||
--
|
||||
-- Authors: Roberto Ierusalimschy and Andre Carregal
|
||||
-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
|
||||
--
|
||||
-- Copyright 2005 - Kepler Project
|
||||
--
|
||||
-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Implements xpcall with coroutines
|
||||
-------------------------------------------------------------------------------
|
||||
local coromap = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function handleReturnValue(err, co, status, ...)
|
||||
if not status then
|
||||
return false, err(debug.traceback(co, (...)), ...)
|
||||
end
|
||||
if coroutine.status(co) == 'suspended' then
|
||||
return performResume(err, co, coroutine.yield(...))
|
||||
else
|
||||
return true, ...
|
||||
end
|
||||
end
|
||||
|
||||
function performResume(err, co, ...)
|
||||
return handleReturnValue(err, co, coroutine.resume(co, ...))
|
||||
end
|
||||
|
||||
local function id(trace, ...)
|
||||
return trace
|
||||
end
|
||||
|
||||
function coxpcall(f, err, ...)
|
||||
local current = coroutine.running()
|
||||
if not current then
|
||||
if err == id then
|
||||
return pcall(f, ...)
|
||||
else
|
||||
if select("#", ...) > 0 then
|
||||
local oldf, params = f, { ... }
|
||||
f = function() return oldf(unpack(params)) end
|
||||
end
|
||||
return xpcall(f, err)
|
||||
end
|
||||
else
|
||||
local res, co = pcall(coroutine.create, f)
|
||||
if not res then
|
||||
local newf = function(...) return f(...) end
|
||||
co = coroutine.create(newf)
|
||||
end
|
||||
coromap[co] = current
|
||||
coxpt[co] = coxpt[current] or current or 0
|
||||
return performResume(err, co, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function copcall(f, ...)
|
||||
return coxpcall(f, id, ...)
|
||||
end
|
|
@ -1,413 +0,0 @@
|
|||
---[[
|
||||
LuCI utility functions.
|
||||
]]
|
||||
module "luci.util"
|
||||
|
||||
---[[
|
||||
Create a Class object (Python-style object model).
|
||||
|
||||
The class object can be instantiated by calling itself.
|
||||
Any class functions or shared parameters can be attached to this object.
|
||||
Attaching a table to the class object makes this table shared between
|
||||
all instances of this class. For object parameters use the __init__ function.
|
||||
Classes can inherit member functions and values from a base class.
|
||||
Class can be instantiated by calling them. All parameters will be passed
|
||||
to the __init__ function of this class - if such a function exists.
|
||||
The __init__ function must be used to set any object parameters that are not shared
|
||||
with other objects of this class. Any return values will be ignored.
|
||||
|
||||
@class function
|
||||
@name class
|
||||
@param base The base class to inherit from (optional)
|
||||
@return A class object
|
||||
@see instanceof
|
||||
@see clone
|
||||
]]
|
||||
|
||||
---[[
|
||||
Test whether the given object is an instance of the given class.
|
||||
|
||||
@class function
|
||||
@name instanceof
|
||||
@param object Object instance
|
||||
@param class Class object to test against
|
||||
@return Boolean indicating whether the object is an instance
|
||||
@see class
|
||||
@see clone
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create a new or get an already existing thread local store associated with
|
||||
the current active coroutine.
|
||||
|
||||
A thread local store is private a table object
|
||||
whose values can't be accessed from outside of the running coroutine.
|
||||
|
||||
@class function
|
||||
@name threadlocal
|
||||
@return Table value representing the corresponding thread local store
|
||||
]]
|
||||
|
||||
---[[
|
||||
Write given object to stderr.
|
||||
|
||||
@class function
|
||||
@name perror
|
||||
@param obj Value to write to stderr
|
||||
@return Boolean indicating whether the write operation was successful
|
||||
]]
|
||||
|
||||
---[[
|
||||
Recursively dumps a table to stdout, useful for testing and debugging.
|
||||
|
||||
@class function
|
||||
@name dumptable
|
||||
@param t Table value to dump
|
||||
@param maxdepth Maximum depth
|
||||
@return Always nil
|
||||
]]
|
||||
|
||||
---[[
|
||||
Create valid XML PCDATA from given string.
|
||||
|
||||
@class function
|
||||
@name pcdata
|
||||
@param value String value containing the data to escape
|
||||
@return String value containing the escaped data
|
||||
]]
|
||||
|
||||
---[[
|
||||
Decode an URL-encoded string - optionally decoding the "+" sign to space.
|
||||
|
||||
@class function
|
||||
@name urldecode
|
||||
@param str Input string in x-www-urlencoded format
|
||||
@param decode_plus Decode "+" signs to spaces if true (optional)
|
||||
@return The decoded string
|
||||
@see urlencode
|
||||
]]
|
||||
|
||||
---[[
|
||||
URL-encode given string.
|
||||
|
||||
@class function
|
||||
@name urlencode
|
||||
@param str String to encode
|
||||
@return String containing the encoded data
|
||||
@see urldecode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Strip HTML tags from given string.
|
||||
|
||||
@class function
|
||||
@name striptags
|
||||
@param value String containing the HTML text
|
||||
@return String with HTML tags stripped of
|
||||
]]
|
||||
|
||||
---[[
|
||||
Safely quote value for use in shell commands.
|
||||
|
||||
@class function
|
||||
@name shellquote
|
||||
@param value String containing the value to quote
|
||||
@return Single-quote enclosed string with embedded quotes escaped
|
||||
]]
|
||||
|
||||
---[[
|
||||
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
|
||||
nterpreted as regular expression.
|
||||
|
||||
@class function
|
||||
@name split
|
||||
@param str String value containing the data to split up
|
||||
@param pat String with separator pattern (optional, defaults to "\n")
|
||||
@param max Maximum times to split (optional)
|
||||
@param regex Boolean indicating whether to interpret the separator
|
||||
-- pattern as regular expression (optional, default is false)
|
||||
@return Table containing the resulting substrings
|
||||
]]
|
||||
|
||||
---[[
|
||||
Remove leading and trailing whitespace from given string value.
|
||||
|
||||
@class function
|
||||
@name trim
|
||||
@param str String value containing whitespace padded data
|
||||
@return String value with leading and trailing space removed
|
||||
]]
|
||||
|
||||
---[[
|
||||
Count the occurrences of given substring in given string.
|
||||
|
||||
@class function
|
||||
@name cmatch
|
||||
@param str String to search in
|
||||
@param pattern String containing pattern to find
|
||||
@return Number of found occurrences
|
||||
]]
|
||||
|
||||
---[[
|
||||
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 iterator which aborts with the first invocation.
|
||||
|
||||
@class function
|
||||
@name imatch
|
||||
@param val The value to scan (table, string or nil)
|
||||
@return Iterator which returns one token per call
|
||||
]]
|
||||
|
||||
---[[
|
||||
Parse certain units from the given string and return the canonical integer
|
||||
value or 0 if the unit is unknown.
|
||||
|
||||
Upper- or lower case is irrelevant.
|
||||
Recognized units are:
|
||||
|
||||
-- o "y" - one year (60*60*24*366)
|
||||
o "m" - one month (60*60*24*31)
|
||||
o "w" - one week (60*60*24*7)
|
||||
o "d" - one day (60*60*24)
|
||||
o "h" - one hour (60*60)
|
||||
o "min" - one minute (60)
|
||||
o "kb" - one kilobyte (1024)
|
||||
o "mb" - one megabyte (1024*1024)
|
||||
o "gb" - one gigabyte (1024*1024*1024)
|
||||
o "kib" - one si kilobyte (1000)
|
||||
o "mib" - one si megabyte (1000*1000)
|
||||
o "gib" - one si gigabyte (1000*1000*1000)
|
||||
|
||||
@class function
|
||||
@name parse_units
|
||||
@param ustr String containing a numerical value with trailing unit
|
||||
@return Number containing the canonical value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Appends numerically indexed tables or single objects to a given table.
|
||||
|
||||
@class function
|
||||
@name append
|
||||
@param src Target table
|
||||
@param ... Objects to insert
|
||||
@return Target table
|
||||
]]
|
||||
|
||||
---[[
|
||||
Combines two or more numerically indexed tables and single objects into one table.
|
||||
|
||||
@class function
|
||||
@name combine
|
||||
@param tbl1 Table value to combine
|
||||
@param tbl2 Table value to combine
|
||||
@param ... More tables to combine
|
||||
@return Table value containing all values of given tables
|
||||
]]
|
||||
|
||||
---[[
|
||||
Checks whether the given table contains the given value.
|
||||
|
||||
@class function
|
||||
@name contains
|
||||
@param table Table value
|
||||
@param value Value to search within the given table
|
||||
@return Number indicating the first index at which the given value occurs
|
||||
-- within table or false.
|
||||
]]
|
||||
|
||||
---[[
|
||||
Update values in given table with the values from the second given table.
|
||||
|
||||
Both table are - in fact - merged together.
|
||||
|
||||
@class function
|
||||
@name update
|
||||
@param t Table which should be updated
|
||||
@param updates Table containing the values to update
|
||||
@return Always nil
|
||||
]]
|
||||
|
||||
---[[
|
||||
Retrieve all keys of given associative table.
|
||||
|
||||
@class function
|
||||
@name keys
|
||||
@param t Table to extract keys from
|
||||
@return Sorted table containing the keys
|
||||
]]
|
||||
|
||||
---[[
|
||||
Clones the given object and return it's copy.
|
||||
|
||||
@class function
|
||||
@name clone
|
||||
@param object Table value to clone
|
||||
@param deep Boolean indicating whether to do recursive cloning
|
||||
@return Cloned table value
|
||||
]]
|
||||
|
||||
---[[
|
||||
Recursively serialize given data to lua code, suitable for restoring
|
||||
with loadstring().
|
||||
|
||||
@class function
|
||||
@name serialize_data
|
||||
@param val Value containing the data to serialize
|
||||
@return String value containing the serialized code
|
||||
@see restore_data
|
||||
@see get_bytecode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Restore data previously serialized with serialize_data().
|
||||
|
||||
@class function
|
||||
@name restore_data
|
||||
@param str String containing the data to restore
|
||||
@return Value containing the restored data structure
|
||||
@see serialize_data
|
||||
@see get_bytecode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return the current runtime bytecode of the given data. The byte code
|
||||
will be stripped before it is returned.
|
||||
|
||||
@class function
|
||||
@name get_bytecode
|
||||
@param val Value to return as bytecode
|
||||
@return String value containing the bytecode of the given data
|
||||
]]
|
||||
|
||||
---[[
|
||||
Strips unnecessary lua bytecode from given string.
|
||||
|
||||
Information like line numbers and debugging numbers will be discarded.
|
||||
Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
|
||||
|
||||
@class function
|
||||
@name strip_bytecode
|
||||
@param code String value containing the original lua byte code
|
||||
@return String value containing the stripped lua byte code
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return a key, value iterator which returns the values sorted according to
|
||||
the provided callback function.
|
||||
|
||||
@class function
|
||||
@name spairs
|
||||
@param t The table to iterate
|
||||
@param f A callback function to decide the order of elements
|
||||
@return Function value containing the corresponding iterator
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return a key, value iterator for the given table.
|
||||
|
||||
The table pairs are sorted by key.
|
||||
|
||||
@class function
|
||||
@name kspairs
|
||||
@param t The table to iterate
|
||||
@return Function value containing the corresponding iterator
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return a key, value iterator for the given table.
|
||||
|
||||
The table pairs are sorted by value.
|
||||
|
||||
@class function
|
||||
@name vspairs
|
||||
@param t The table to iterate
|
||||
@return Function value containing the corresponding iterator
|
||||
]]
|
||||
|
||||
---[[
|
||||
Test whether the current system is operating in big endian mode.
|
||||
|
||||
@class function
|
||||
@name bigendian
|
||||
@return Boolean value indicating whether system is big endian
|
||||
]]
|
||||
|
||||
---[[
|
||||
Execute given commandline and gather stdout.
|
||||
|
||||
@class function
|
||||
@name exec
|
||||
@param command String containing command to execute
|
||||
@return String containing the command's stdout
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return a line-buffered iterator over the output of given command.
|
||||
|
||||
@class function
|
||||
@name execi
|
||||
@param command String containing the command to execute
|
||||
@return Iterator
|
||||
]]
|
||||
|
||||
---[[
|
||||
Issue an ubus call.
|
||||
|
||||
@class function
|
||||
@name ubus
|
||||
@param object String containing the ubus object to call
|
||||
@param method String containing the ubus method to call
|
||||
@param values Table containing the values to pass
|
||||
@return Table containin the ubus result
|
||||
]]
|
||||
|
||||
---[[
|
||||
Convert data structure to JSON
|
||||
|
||||
@class function
|
||||
@name serialize_json
|
||||
@param data The data to serialize
|
||||
@param writer A function to write a chunk of JSON data (optional)
|
||||
@return String containing the JSON if called without write callback
|
||||
]]
|
||||
|
||||
---[[
|
||||
Returns the absolute path to LuCI base directory.
|
||||
|
||||
@class function
|
||||
@name libpath
|
||||
@return String containing the directory path
|
||||
]]
|
||||
|
||||
---[[
|
||||
This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
|
||||
|
||||
@class function
|
||||
@name coxpcall
|
||||
@param f Lua function to be called protected
|
||||
@param err Custom error handler
|
||||
@param ... Parameters passed to the function
|
||||
@return A boolean whether the function call succeeded and the return
|
||||
-- values of either the function or the error handler
|
||||
]]
|
||||
|
||||
---[[
|
||||
This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
|
||||
|
||||
@class function
|
||||
@name copcall
|
||||
@param f Lua function to be called protected
|
||||
@param ... Parameters passed to the function
|
||||
@return A boolean whether the function call succeeded and the returns
|
||||
-- values of the function or the error object
|
||||
]]
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
module "luci.version"
|
||||
|
||||
distname = "Host System"
|
||||
distversion = "SDK"
|
||||
|
||||
luciname = "LuCI"
|
||||
luciversion = "SVN"
|
|
@ -1,10 +0,0 @@
|
|||
<%+cbi/valueheader%>
|
||||
|
||||
<input class="cbi-input-text" type="text"<%=
|
||||
attr("id", cbid) ..
|
||||
attr("name", cbid) ..
|
||||
attr("value", self:cfgvalue(section) or self.default) ..
|
||||
attr("data-browser", self.default_path or "")
|
||||
%> />
|
||||
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,7 +0,0 @@
|
|||
<%+cbi/valueheader%>
|
||||
<% if self:cfgvalue(section) ~= false then %>
|
||||
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
|
||||
<% else %>
|
||||
-
|
||||
<% end %>
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,2 +0,0 @@
|
|||
</div>
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
<%-
|
||||
local title = luci.util.trim(striptags(self.title))
|
||||
local descr = luci.util.trim(striptags(self.description))
|
||||
local ftype = self.typename or (self.template and self.template:gsub("^.+/", ""))
|
||||
-%>
|
||||
<div class="td cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %><% if self.password then %> nowrap<% end %>"<%=
|
||||
attr("data-name", self.option) ..
|
||||
ifattr(ftype and #ftype > 0, "data-type", ftype) ..
|
||||
ifattr(title and #title > 0, "data-title", title, true) ..
|
||||
ifattr(descr and #descr > 0, "data-description", descr, true)
|
||||
%>>
|
||||
<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
|
|
@ -1 +0,0 @@
|
|||
<%- self:render_children() %>
|
|
@ -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>
|