1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-03-09 15:40:03 +00:00

Update luci and theme

This commit is contained in:
Ycarus 2019-05-28 21:51:29 +02:00
parent fe03553aae
commit 4d7962337f
165 changed files with 74180 additions and 13802 deletions

View file

@ -14,13 +14,17 @@ 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
LUCI_LUASRCDIET_VERSION:=1.0.0
PKG_SOURCE:=v1.0.0.tar.gz
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet/archive/
PKG_HASH:=48162e63e77d009f5848f18a5cabffbdfc867d0e5e73c6d407f6af5d6880151b
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet.git
PKG_SOURCE_VERSION:=f138fc9359821d9201cd6b57cfa2fcbed5b9af97
PKG_SOURCE_SUBDIR:=luasrcdiet-$(LUCI_LUASRCDIET_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
PKG_MIRROR_HASH:=a5c9d098549fbef618e6022b701e66c8c6fb16c910e63219adad3a4e71341f72
PKG_LICENSE:=MIT
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/luasrcdiet-1.0.0
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/$(PKG_SOURCE_SUBDIR)
include $(INCLUDE_DIR)/host-build.mk
@ -36,13 +40,14 @@ define Host/Configure
endef
define Host/Compile
$(MAKE) -C src/ clean po2lmo
$(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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,511 @@
(function(window, document, undefined) {
var modalDiv = null,
tooltipDiv = null,
tooltipTimeout = null,
dummyElem = null,
domParser = null;
LuCI.prototype = {
/* URL construction helpers */
path: function(prefix, parts) {
var url = [ prefix || '' ];
for (var i = 0; i < parts.length; i++)
if (/^(?:[a-zA-Z0-9_.%,;-]+\/)*[a-zA-Z0-9_.%,;-]+$/.test(parts[i]))
url.push('/', parts[i]);
if (url.length === 1)
url.push('/');
return url.join('');
},
url: function() {
return this.path(this.env.scriptname, arguments);
},
resource: function() {
return this.path(this.env.resource, arguments);
},
location: function() {
return this.path(this.env.scriptname, this.env.requestpath);
},
/* HTTP resource fetching */
get: function(url, args, cb) {
return this.poll(0, url, args, cb, false);
},
post: function(url, args, cb) {
return this.poll(0, url, args, cb, true);
},
poll: function(interval, url, args, cb, post) {
var data = post ? { token: this.env.token } : null;
if (!/^(?:\/|\S+:\/\/)/.test(url))
url = this.url(url);
if (typeof(args) === 'object' && args !== null) {
data = data || {};
for (var key in args)
if (args.hasOwnProperty(key))
switch (typeof(args[key])) {
case 'string':
case 'number':
case 'boolean':
data[key] = args[key];
break;
case 'object':
data[key] = JSON.stringify(args[key]);
break;
}
}
if (interval > 0)
return XHR.poll(interval, url, data, cb, post);
else if (post)
return XHR.post(url, data, cb);
else
return XHR.get(url, data, cb);
},
stop: function(entry) { XHR.stop(entry) },
halt: function() { XHR.halt() },
run: function() { XHR.run() },
/* Modal dialog */
showModal: function(title, children) {
var dlg = modalDiv.firstElementChild;
dlg.setAttribute('class', 'modal');
this.dom.content(dlg, this.dom.create('h4', {}, title));
this.dom.append(dlg, children);
document.body.classList.add('modal-overlay-active');
return dlg;
},
hideModal: function() {
document.body.classList.remove('modal-overlay-active');
},
/* Tooltip */
showTooltip: function(ev) {
var target = findParent(ev.target, '[data-tooltip]');
if (!target)
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
var rect = target.getBoundingClientRect(),
x = rect.left + window.pageXOffset,
y = rect.top + rect.height + window.pageYOffset;
tooltipDiv.className = 'cbi-tooltip';
tooltipDiv.innerHTML = '▲ ';
tooltipDiv.firstChild.data += target.getAttribute('data-tooltip');
if (target.hasAttribute('data-tooltip-style'))
tooltipDiv.classList.add(target.getAttribute('data-tooltip-style'));
if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
y -= (tooltipDiv.offsetHeight + target.offsetHeight);
tooltipDiv.firstChild.data = '▼ ' + tooltipDiv.firstChild.data.substr(2);
}
tooltipDiv.style.top = y + 'px';
tooltipDiv.style.left = x + 'px';
tooltipDiv.style.opacity = 1;
tooltipDiv.dispatchEvent(new CustomEvent('tooltip-open', {
bubbles: true,
detail: { target: target }
}));
},
hideTooltip: function(ev) {
if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv ||
tooltipDiv.contains(ev.target) || tooltipDiv.contains(ev.relatedTarget))
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
tooltipDiv.style.opacity = 0;
tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
tooltipDiv.dispatchEvent(new CustomEvent('tooltip-close', { bubbles: true }));
},
/* Widget helper */
itemlist: function(node, items, separators) {
var children = [];
if (!Array.isArray(separators))
separators = [ separators || E('br') ];
for (var i = 0; i < items.length; i += 2) {
if (items[i+1] !== null && items[i+1] !== undefined) {
var sep = separators[(i/2) % separators.length],
cld = [];
children.push(E('span', { class: 'nowrap' }, [
items[i] ? E('strong', items[i] + ': ') : '',
items[i+1]
]));
if ((i+2) < items.length)
children.push(this.dom.elem(sep) ? sep.cloneNode(true) : sep);
}
}
this.dom.content(node, children);
return node;
}
};
/* Tabs */
LuCI.prototype.tabs = {
init: function() {
var groups = [], prevGroup = null, currGroup = null;
document.querySelectorAll('[data-tab]').forEach(function(tab) {
var parent = tab.parentNode;
if (!parent.hasAttribute('data-tab-group'))
parent.setAttribute('data-tab-group', groups.length);
currGroup = +parent.getAttribute('data-tab-group');
if (currGroup !== prevGroup) {
prevGroup = currGroup;
if (!groups[currGroup])
groups[currGroup] = [];
}
groups[currGroup].push(tab);
});
for (var i = 0; i < groups.length; i++)
this.initTabGroup(groups[i]);
document.addEventListener('dependency-update', this.updateTabs.bind(this));
this.updateTabs();
if (!groups.length)
this.setActiveTabId(-1, -1);
},
initTabGroup: function(panes) {
if (!Array.isArray(panes) || panes.length === 0)
return;
var menu = E('ul', { 'class': 'cbi-tabmenu' }),
group = panes[0].parentNode,
groupId = +group.getAttribute('data-tab-group'),
selected = null;
for (var i = 0, pane; pane = panes[i]; i++) {
var name = pane.getAttribute('data-tab'),
title = pane.getAttribute('data-tab-title'),
active = pane.getAttribute('data-tab-active') === 'true';
menu.appendChild(E('li', {
'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
'data-tab': name
}, E('a', {
'href': '#',
'click': this.switchTab.bind(this)
}, title)));
if (active)
selected = i;
}
group.parentNode.insertBefore(menu, group);
if (selected === null) {
selected = this.getActiveTabId(groupId);
if (selected < 0 || selected >= panes.length)
selected = 0;
menu.childNodes[selected].classList.add('cbi-tab');
menu.childNodes[selected].classList.remove('cbi-tab-disabled');
panes[selected].setAttribute('data-tab-active', 'true');
this.setActiveTabId(groupId, selected);
}
},
getActiveTabState: function() {
var page = document.body.getAttribute('data-page');
try {
var val = JSON.parse(window.sessionStorage.getItem('tab'));
if (val.page === page && Array.isArray(val.groups))
return val;
}
catch(e) {}
window.sessionStorage.removeItem('tab');
return { page: page, groups: [] };
},
getActiveTabId: function(groupId) {
return +this.getActiveTabState().groups[groupId] || 0;
},
setActiveTabId: function(groupId, tabIndex) {
try {
var state = this.getActiveTabState();
state.groups[groupId] = tabIndex;
window.sessionStorage.setItem('tab', JSON.stringify(state));
}
catch (e) { return false; }
return true;
},
updateTabs: function(ev) {
document.querySelectorAll('[data-tab-title]').forEach(function(pane) {
var menu = pane.parentNode.previousElementSibling,
tab = menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))),
n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
if (!pane.firstElementChild) {
tab.style.display = 'none';
tab.classList.remove('flash');
}
else if (tab.style.display === 'none') {
tab.style.display = '';
requestAnimationFrame(function() { tab.classList.add('flash') });
}
if (n_errors) {
tab.setAttribute('data-errors', n_errors);
tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors));
tab.setAttribute('data-tooltip-style', 'error');
}
else {
tab.removeAttribute('data-errors');
tab.removeAttribute('data-tooltip');
}
});
},
switchTab: function(ev) {
var tab = ev.target.parentNode,
name = tab.getAttribute('data-tab'),
menu = tab.parentNode,
group = menu.nextElementSibling,
groupId = +group.getAttribute('data-tab-group'),
index = 0;
ev.preventDefault();
if (!tab.classList.contains('cbi-tab-disabled'))
return;
menu.querySelectorAll('[data-tab]').forEach(function(tab) {
tab.classList.remove('cbi-tab');
tab.classList.remove('cbi-tab-disabled');
tab.classList.add(
tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled');
});
group.childNodes.forEach(function(pane) {
if (L.dom.matches(pane, '[data-tab]')) {
if (pane.getAttribute('data-tab') === name) {
pane.setAttribute('data-tab-active', 'true');
L.tabs.setActiveTabId(groupId, index);
}
else {
pane.setAttribute('data-tab-active', 'false');
}
index++;
}
});
}
};
/* DOM manipulation */
LuCI.prototype.dom = {
elem: function(e) {
return (typeof(e) === 'object' && e !== null && 'nodeType' in e);
},
parse: function(s) {
var elem;
try {
domParser = domParser || new DOMParser();
elem = domParser.parseFromString(s, 'text/html').body.firstChild;
}
catch(e) {}
if (!elem) {
try {
dummyElem = dummyElem || document.createElement('div');
dummyElem.innerHTML = s;
elem = dummyElem.firstChild;
}
catch (e) {}
}
return elem || null;
},
matches: function(node, selector) {
var m = this.elem(node) ? node.matches || node.msMatchesSelector : null;
return m ? m.call(node, selector) : false;
},
parent: function(node, selector) {
if (this.elem(node) && node.closest)
return node.closest(selector);
while (this.elem(node))
if (this.matches(node, selector))
return node;
else
node = node.parentNode;
return null;
},
append: function(node, children) {
if (!this.elem(node))
return null;
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++)
if (this.elem(children[i]))
node.appendChild(children[i]);
else if (children !== null && children !== undefined)
node.appendChild(document.createTextNode('' + children[i]));
return node.lastChild;
}
else if (typeof(children) === 'function') {
return this.append(node, children(node));
}
else if (this.elem(children)) {
return node.appendChild(children);
}
else if (children !== null && children !== undefined) {
node.innerHTML = '' + children;
return node.lastChild;
}
return null;
},
content: function(node, children) {
if (!this.elem(node))
return null;
while (node.firstChild)
node.removeChild(node.firstChild);
return this.append(node, children);
},
attr: function(node, key, val) {
if (!this.elem(node))
return null;
var attr = null;
if (typeof(key) === 'object' && key !== null)
attr = key;
else if (typeof(key) === 'string')
attr = {}, attr[key] = val;
for (key in attr) {
if (!attr.hasOwnProperty(key) || attr[key] === null || attr[key] === undefined)
continue;
switch (typeof(attr[key])) {
case 'function':
node.addEventListener(key, attr[key]);
break;
case 'object':
node.setAttribute(key, JSON.stringify(attr[key]));
break;
default:
node.setAttribute(key, attr[key]);
}
}
},
create: function() {
var html = arguments[0],
attr = (arguments[1] instanceof Object && !Array.isArray(arguments[1])) ? arguments[1] : null,
data = attr ? arguments[2] : arguments[1],
elem;
if (this.elem(html))
elem = html;
else if (html.charCodeAt(0) === 60)
elem = this.parse(html);
else
elem = document.createElement(html);
if (!elem)
return null;
this.attr(elem, attr);
this.append(elem, data);
return elem;
}
};
/* Setup */
LuCI.prototype.setupDOM = function(ev) {
this.tabs.init();
};
function LuCI(env) {
this.env = env;
modalDiv = document.body.appendChild(
this.dom.create('div', { id: 'modal_overlay' },
this.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
tooltipDiv = document.body.appendChild(this.dom.create('div', { class: 'cbi-tooltip' }));
document.addEventListener('mouseover', this.showTooltip.bind(this), true);
document.addEventListener('mouseout', this.hideTooltip.bind(this), true);
document.addEventListener('focus', this.showTooltip.bind(this), true);
document.addEventListener('blur', this.hideTooltip.bind(this), true);
document.addEventListener('DOMContentLoaded', this.setupDOM.bind(this));
}
window.LuCI = LuCI;
})(window, document);

View file

@ -1,24 +1,65 @@
/*
* xhr.js - XMLHttpRequest helper class
* (c) 2008-2010 Jo-Philipp Wich
* (c) 2008-2018 Jo-Philipp Wich <jo@mein.io>
*/
XHR = function()
{
this.reinit = function()
{
if (window.XMLHttpRequest) {
this._xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else {
alert("xhr.js: XMLHttpRequest is not supported by this browser!");
}
}
XHR.prototype = {
_encode: function(obj) {
obj = obj ? obj : { };
obj['_'] = Math.random();
this.busy = function() {
if (typeof obj == 'object') {
var code = '';
var self = this;
for (var k in obj)
code += (code ? '&' : '') +
k + '=' + encodeURIComponent(obj[k]);
return code;
}
return obj;
},
_response: function(callback, ts) {
if (this._xmlHttp.readyState !== 4)
return;
var status = this._xmlHttp.status,
login = this._xmlHttp.getResponseHeader("X-LuCI-Login-Required"),
type = this._xmlHttp.getResponseHeader("Content-Type"),
json = null;
if (status === 403 && login === 'yes') {
XHR.halt();
showModal(_('Session expired'), [
E('div', { class: 'alert-message warning' },
_('A new login is required since the authentication session expired.')),
E('div', { class: 'right' },
E('div', {
class: 'btn primary',
click: function() {
var loc = window.location;
window.location = loc.protocol + '//' + loc.host + loc.pathname + loc.search;
}
}, _('To login…')))
]);
}
else if (type && type.toLowerCase().match(/^application\/json\b/)) {
try {
json = JSON.parse(this._xmlHttp.responseText);
}
catch(e) {
json = null;
}
}
callback(this._xmlHttp, json, Date.now() - ts);
},
busy: function() {
if (!this._xmlHttp)
return false;
@ -32,20 +73,18 @@ XHR = function()
default:
return false;
}
}
},
this.abort = function() {
abort: function() {
if (this.busy())
this._xmlHttp.abort();
}
},
this.get = function(url,data,callback,timeout)
{
this.reinit();
get: function(url, data, callback, timeout) {
this._xmlHttp = new XMLHttpRequest();
var ts = Date.now();
var xhr = this._xmlHttp;
var code = this._encode(data);
var xhr = this._xmlHttp,
code = this._encode(data);
url = location.protocol + '//' + location.host + url;
@ -60,83 +99,51 @@ XHR = function()
if (!isNaN(timeout))
xhr.timeout = timeout;
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4) {
var json = null;
if (xhr.getResponseHeader("Content-Type") == "application/json") {
try { json = JSON.parse(xhr.responseText); }
catch(e) { json = null; }
}
callback(xhr, json, Date.now() - ts);
}
}
xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.send(null);
}
},
this.post = function(url,data,callback,timeout)
{
this.reinit();
post: function(url, data, callback, timeout) {
this._xmlHttp = new XMLHttpRequest();
var ts = Date.now();
var xhr = this._xmlHttp;
var code = this._encode(data);
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4) {
var json = null;
if (xhr.getResponseHeader("Content-Type") == "application/json") {
try { json = JSON.parse(xhr.responseText); }
catch(e) { json = null; }
}
callback(xhr, json, Date.now() - ts);
}
}
var xhr = this._xmlHttp,
code = this._encode(data);
xhr.open('POST', url, true);
if (!isNaN(timeout))
xhr.timeout = timeout;
xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(code);
}
},
this.cancel = function()
{
this._xmlHttp.onreadystatechange = function(){};
cancel: function() {
this._xmlHttp.onreadystatechange = function() {};
this._xmlHttp.abort();
}
},
this.send_form = function(form,callback,extra_values)
{
send_form: function(form, callback, extra_values) {
var code = '';
for (var i = 0; i < form.elements.length; i++)
{
for (var i = 0; i < form.elements.length; i++) {
var e = form.elements[i];
if (e.options)
{
if (e.options) {
code += (code ? '&' : '') +
form.elements[i].name + '=' + encodeURIComponent(
e.options[e.selectedIndex].value
);
}
else if (e.length)
{
else if (e.length) {
for (var j = 0; j < e.length; j++)
if (e[j].name) {
code += (code ? '&' : '') +
e[j].name + '=' + encodeURIComponent(e[j].value);
}
}
else
{
else {
code += (code ? '&' : '') +
e.name + '=' + encodeURIComponent(e.value);
}
@ -147,46 +154,25 @@ XHR = function()
code += (code ? '&' : '') +
key + '=' + encodeURIComponent(extra_values[key]);
return(
(form.method == 'get')
? this.get(form.getAttribute('action'), code, callback)
: this.post(form.getAttribute('action'), code, callback)
);
}
this._encode = function(obj)
{
obj = obj ? obj : { };
obj['_'] = Math.random();
if (typeof obj == 'object')
{
var code = '';
var self = this;
for (var k in obj)
code += (code ? '&' : '') +
k + '=' + encodeURIComponent(obj[k]);
return code;
}
return obj;
return (form.method == 'get'
? this.get(form.getAttribute('action'), code, callback)
: this.post(form.getAttribute('action'), code, callback));
}
}
XHR.get = function(url, data, callback)
{
XHR.get = function(url, data, callback) {
(new XHR()).get(url, data, callback);
}
XHR.poll = function(interval, url, data, callback, post)
{
if (isNaN(interval) || interval < 1)
interval = 5;
XHR.post = function(url, data, callback) {
(new XHR()).post(url, data, callback);
}
if (!XHR._q)
{
XHR.poll = function(interval, url, data, callback, post) {
if (isNaN(interval) || interval <= 0)
interval = L.env.pollinterval;
if (!XHR._q) {
XHR._t = 0;
XHR._q = [ ];
XHR._r = function() {
@ -213,8 +199,7 @@ XHR.poll = function(interval, url, data, callback, post)
return e;
}
XHR.stop = function(e)
{
XHR.stop = function(e) {
for (var i = 0; XHR._q && XHR._q[i]; i++) {
if (XHR._q[i] === e) {
e.xhr.cancel();
@ -226,10 +211,8 @@ XHR.stop = function(e)
return false;
}
XHR.halt = function()
{
if (XHR._i)
{
XHR.halt = function() {
if (XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@ -242,10 +225,8 @@ XHR.halt = function()
}
}
XHR.run = function()
{
if (XHR._r && !XHR._i)
{
XHR.run = function() {
if (XHR._r && !XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@ -260,9 +241,10 @@ XHR.run = function()
}
}
XHR.running = function()
{
XHR.running = function() {
return !!(XHR._r && XHR._i);
}
function XHR() {}
document.addEventListener('DOMContentLoaded', XHR.run);

View file

@ -1199,19 +1199,20 @@ function TypedSection.parse(self, novld)
if name then
-- Ignore if it already exists
if self:cfgvalue(name) then
name = nil;
end
name = self:checkscope(name)
if not name then
name = nil
self.err_invalid = true
end
else
name = self:checkscope(name)
if name and #name > 0 then
created = self:create(name, origin) and name
if not created then
self.invalid_cts = true
if not name then
self.err_invalid = true
end
if name and #name > 0 then
created = self:create(name, origin) and name
if not created then
self.invalid_cts = true
end
end
end
end

View file

@ -132,6 +132,10 @@ function ip6prefix(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("^([^/]+)/([^/]+)$")
@ -460,3 +464,7 @@ function dateyyyymmdd(val)
end
return false
end
function unique(val)
return true
end

View file

@ -0,0 +1,164 @@
-- 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", "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
-- 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
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

View file

@ -40,6 +40,28 @@ function build_url(...)
return table.concat(url, "")
end
function _ordered_children(node)
local name, child, children = nil, nil, {}
for name, child in pairs(node.nodes) do
children[#children+1] = {
name = name,
node = child,
order = child.order or 100
}
end
table.sort(children, function(a, b)
if a.order == b.order then
return a.name < b.name
else
return a.order < b.order
end
end)
return children
end
function node_visible(node)
if node then
return not (
@ -55,15 +77,10 @@ end
function node_childs(node)
local rv = { }
if node then
local k, v
for k, v in util.spairs(node.nodes,
function(a, b)
return (node.nodes[a].order or 100)
< (node.nodes[b].order or 100)
end)
do
if node_visible(v) then
rv[#rv+1] = k
local _, child
for _, child in ipairs(_ordered_children(node)) do
if node_visible(child.node) then
rv[#rv+1] = child.name
end
end
end
@ -296,10 +313,6 @@ function dispatch(request)
ctx.requestpath = ctx.requestpath or freq
ctx.path = preq
if track.i18n then
i18n.loadc(track.i18n)
end
-- Init template engine
if (c and c.index) or not track.notemplate then
local tpl = require("luci.template")
@ -315,7 +328,7 @@ function dispatch(request)
assert(media, "No valid theme found")
end
local function _ifattr(cond, key, val)
local function _ifattr(cond, key, val, noescape)
if cond then
local env = getfenv(3)
local scope = (type(env.self) == "table") and env.self
@ -326,13 +339,16 @@ function dispatch(request)
val = util.serialize_json(val)
end
end
return string.format(
' %s="%s"', tostring(key),
util.pcdata(tostring( val
or (type(env[key]) ~= "function" and env[key])
or (scope and type(scope[key]) ~= "function" and scope[key])
or "" ))
)
val = tostring(val or
(type(env[key]) ~= "function" and env[key]) or
(scope and type(scope[key]) ~= "function" and scope[key]) or "")
if noescape ~= true then
val = util.pcdata(val)
end
return string.format(' %s="%s"', tostring(key), val)
else
return ''
end
@ -421,6 +437,7 @@ function dispatch(request)
context.path = {}
http.status(403, "Forbidden")
http.header("X-LuCI-Login-Required", "yes")
tmpl.render(track.sysauth_template or "sysauth", {
duser = default_user,
fuser = user
@ -437,6 +454,7 @@ function dispatch(request)
if not sid or not sdat then
http.status(403, "Forbidden")
http.header("X-LuCI-Login-Required", "yes")
return
end
@ -597,14 +615,9 @@ function createtree()
local ctx = context
local tree = {nodes={}, inreq=true}
local modi = {}
ctx.treecache = setmetatable({}, {__mode="v"})
ctx.tree = tree
ctx.modifiers = modi
-- Load default translation
require "luci.i18n".loadc("base")
local scope = setmetatable({}, {__index = luci.dispatcher})
@ -614,28 +627,9 @@ function createtree()
v()
end
local function modisort(a,b)
return modi[a].order < modi[b].order
end
for _, v in util.spairs(modi, modisort) do
scope._NAME = v.module
setfenv(v.func, scope)
v.func()
end
return tree
end
function modifier(func, order)
context.modifiers[#context.modifiers+1] = {
func = func,
order = order or 0,
module
= getfenv(2)._NAME
}
end
function assign(path, clone, title, order)
local obj = node(unpack(path))
obj.nodes = nil
@ -724,32 +718,66 @@ end
-- Subdispatchers --
function _find_eligible_node(root, prefix, deep, types, descend)
local children = _ordered_children(root)
if not root.leaf and deep ~= nil then
local sub_path = { unpack(prefix) }
if deep == false then
deep = nil
end
local _, child
for _, child in ipairs(children) do
sub_path[#prefix+1] = child.name
local res_path = _find_eligible_node(child.node, sub_path,
deep, types, true)
if res_path then
return res_path
end
end
end
if descend and
(not types or
(type(root.target) == "table" and
util.contains(types, root.target.type)))
then
return prefix
end
end
function _find_node(recurse, types)
local path = { unpack(context.path) }
local name = table.concat(path, ".")
local node = context.treecache[name]
path = _find_eligible_node(node, path, recurse, types)
if path then
dispatch(path)
else
require "luci.template".render("empty_node_placeholder")
end
end
function _firstchild()
local path = { unpack(context.path) }
local name = table.concat(path, ".")
local node = context.treecache[name]
local lowest
if node and node.nodes and next(node.nodes) then
local k, v
for k, v in pairs(node.nodes) do
if not lowest or
(v.order or 100) < (node.nodes[lowest].order or 100)
then
lowest = k
end
end
end
assert(lowest ~= nil,
"The requested node contains no childs, unable to redispatch")
path[#path+1] = lowest
dispatch(path)
return _find_node(false, nil)
end
function firstchild()
return { type = "firstchild", target = _firstchild }
return { type = "firstchild", target = _firstchild }
end
function _firstnode()
return _find_node(true, { "cbi", "form", "template", "arcombine" })
end
function firstnode()
return { type = "firstnode", target = _firstnode }
end
function alias(...)

View file

@ -22,7 +22,7 @@ Check whether a dispatch node shall be visible
]]
---[[
Return a sorted table of visible childs within a given node
Return a sorted table of visible children within a given node
@class function
@name node_childs
@ -81,15 +81,6 @@ Build the index before if it does not exist yet.
@name createtree
]]
---[[
Register a tree modifier.
@class function
@name modifier
@param func Modifier function
@param order Modifier order value (optional)
]]
---[[
Clone a node of the dispatching tree to another position.

View file

@ -335,13 +335,13 @@ 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 feeded with the
-- 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 wheather the current chunk is the last one (eof)
-- 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)

View file

@ -204,13 +204,13 @@ 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 feeded with the
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 wheather the current chunk is the last one (eof)
o Boolean which indicates whether the current chunk is the last one (eof)
@class function
@name mimedecode_message_body

View file

@ -1,37 +1,43 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.i18n", package.seeall)
require("luci.util")
local tparser = require "luci.template.parser"
local util = require "luci.util"
local tostring = tostring
local tparser = require "luci.template.parser"
module "luci.i18n"
table = {}
i18ndir = luci.util.libpath() .. "/i18n/"
loaded = {}
context = luci.util.threadlocal()
i18ndir = util.libpath() .. "/i18n/"
context = util.threadlocal()
default = "en"
function clear()
end
function load(file, lang, force)
end
-- Alternatively load the translation of the fallback language.
function loadc(file, force)
end
function setlanguage(lang)
context.lang = lang:gsub("_", "-")
context.parent = (context.lang:match("^([a-z][a-z])_"))
if not tparser.load_catalog(context.lang, i18ndir) then
if context.parent then
tparser.load_catalog(context.parent, i18ndir)
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 context.lang
return nil
end
function translate(key)
@ -42,14 +48,8 @@ function translatef(key, ...)
return tostring(translate(key)):format(...)
end
-- and ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translate(...))</code>
function string(key)
return tostring(translate(key))
end
-- Ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translatef(...))</code>
function stringf(key, ...)
return tostring(translate(key)):format(...)
function dump()
local rv = {}
tparser.get_translations(function(k, v) rv[k] = v end)
return rv
end

View file

@ -3,41 +3,13 @@ LuCI translation library.
]]
module "luci.i18n"
---[[
Clear the translation table.
@class function
@name clear
]]
---[[
Load a translation and copy its data into the translation table.
@class function
@name load
@param file Language file
@param lang Two-letter language code
@param force Force reload even if already loaded (optional)
@return Success status
]]
---[[
Load a translation file using the default translation language.
Alternatively load the translation of the fallback language.
@class function
@name loadc
@param file Language file
@param force Force reload even if already loaded (optional)
]]
---[[
Set the context default translation language.
@class function
@name setlanguage
@param lang Two-letter language code
@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
]]
---[[
@ -60,25 +32,11 @@ Return the translated value for a specific translation key and use it as sprintf
]]
---[[
Return the translated value for a specific translation key
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.
and ensure that the returned value is a Lua string value.
This is the same as calling <code>tostring(translate(...))</code>
@class function
@name string
@param key Default translation text
@return Translated string
@name dump
@return Key-value translation string table.
]]
---[[
Return the translated value for a specific translation key and use it as sprintf pattern.
Ensure that the returned value is a Lua string value.
This is the same as calling <code>tostring(translatef(...))</code>
@class function
@name stringf
@param key Default translation text
@param ... Format parameters
@return Translated and formatted string
]]

View file

@ -53,6 +53,7 @@ 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",

View file

@ -4,17 +4,93 @@
local map, section, net = ...
local ifc = net:get_interface()
local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
local mtu, metric
local netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
local mtu, metric, usecidr, ipaddr_single, ipaddr_multi
ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
ipaddr.datatype = "ip4addr"
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
netmask = section:taboption("general", Value, "netmask",
translate("IPv4 netmask"))
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")
@ -48,8 +124,9 @@ if luci.model.network:has_ipv6() then
translate("Assign prefix parts using this hexadecimal subprefix ID for this interface."))
for i=33,64 do ip6hint:depends("ip6assign", i) end
ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
ip6addr = section:taboption("general", DynamicList, "ip6addr", translate("IPv6 address"))
ip6addr.datatype = "ip6addr"
ip6addr.placeholder = translate("Add IPv6 address…")
ip6addr:depends("ip6assign", "")
@ -83,14 +160,8 @@ mtu.placeholder = "1500"
mtu.datatype = "max(9200)"
--metric = section:taboption("advanced", Value, "metric",
-- translate("Use gateway metric"))
--metric.default = "1"
--metric.datatype = "uinteger"
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
--local nw = require "luci.model.network".init()
--for _, network in ipairs(nw:get_networks()) do
-- if network:proto() == "static" and network:type() == "macvlan" and tonumber(network:metric()) >= tonumber(metric.default) then
-- metric.default = network:metric() + 1
-- end
--end
metric.placeholder = "0"
metric.datatype = "uinteger"

View file

@ -1,247 +0,0 @@
-- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.
local os = require "os"
local io = require "io"
local fs = require "nixio.fs"
local util = require "luci.util"
local type = type
local pairs = pairs
local error = error
local table = table
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
local icfg = "/etc/opkg.conf"
module "luci.model.ipkg"
-- Internal action function
local function _action(cmd, ...)
local cmdline = { ipkg, cmd }
local k, v
for k, v in pairs({...}) do
cmdline[#cmdline+1] = util.shellquote(v)
end
local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ")
local r = os.execute(c)
local e = fs.readfile("/tmp/opkg.stderr")
local o = fs.readfile("/tmp/opkg.stdout")
fs.unlink("/tmp/opkg.stderr")
fs.unlink("/tmp/opkg.stdout")
return r, o or "", e or ""
end
-- Internal parser function
local function _parselist(rawdata)
if type(rawdata) ~= "function" then
error("OPKG: Invalid rawdata given")
end
local data = {}
local c = {}
local l = nil
for line in rawdata do
if line:sub(1, 1) ~= " " then
local key, val = line:match("(.-): ?(.*)%s*")
if key and val then
if key == "Package" then
c = {Package = val}
data[val] = c
elseif key == "Status" then
c.Status = {}
for j in val:gmatch("([^ ]+)") do
c.Status[j] = true
end
else
c[key] = val
end
l = key
end
else
-- Multi-line field
c[l] = c[l] .. "\n" .. line
end
end
return data
end
-- Internal lookup function
local function _lookup(cmd, pkg)
local cmdline = { ipkg, cmd }
if pkg then
cmdline[#cmdline+1] = util.shellquote(pkg)
end
-- OPKG sometimes kills the whole machine because it sucks
-- Therefore we have to use a sucky approach too and use
-- tmpfiles instead of directly reading the output
local tmpfile = os.tmpname()
os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile })
local data = _parselist(io.lines(tmpfile))
os.remove(tmpfile)
return data
end
function info(pkg)
return _lookup("info", pkg)
end
function status(pkg)
return _lookup("status", pkg)
end
function install(...)
return _action("install", ...)
end
function installed(pkg)
local p = status(pkg)[pkg]
return (p and p.Status and p.Status.installed)
end
function remove(...)
return _action("remove", ...)
end
function update()
return _action("update")
end
function upgrade()
return _action("upgrade")
end
-- List helper
local function _list(action, pat, cb)
local cmdline = { ipkg, action }
if pat then
cmdline[#cmdline+1] = util.shellquote(pat)
end
local fd = io.popen(table.concat(cmdline, " "))
if fd then
local name, version, sz, desc
while true do
local line = fd:read("*l")
if not line then break end
name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)")
if not name then
name, version, sz = line:match("^(.-) %- (.-) %- (.+)")
desc = ""
end
if name and version then
if #version > 26 then
version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
end
cb(name, version, sz, desc)
end
name = nil
version = nil
sz = nil
desc = nil
end
fd:close()
end
end
function list_all(pat, cb)
_list("list --size", pat, cb)
end
function list_installed(pat, cb)
_list("list_installed --size", pat, cb)
end
function find(pat, cb)
_list("find --size", pat, cb)
end
function overlay_root()
local od = "/"
local fd = io.open(icfg, "r")
if fd then
local ln
repeat
ln = fd:read("*l")
if ln and ln:match("^%s*option%s+overlay_root%s+") then
od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
local s = fs.stat(od)
if not s or s.type ~= "dir" then
od = "/"
end
break
end
until not ln
fd:close()
end
return od
end
function compare_versions(ver1, comp, ver2)
if not ver1 or not ver2
or not comp or not (#comp > 0) then
error("Invalid parameters")
return nil
end
-- correct compare string
if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
elseif comp == "=" or comp == "==" then comp = "=="
elseif comp == "<<" then comp = "<"
elseif comp == ">>" then comp = ">"
else
error("Invalid compare string")
return nil
end
local av1 = util.split(ver1, "[%.%-]", nil, true)
local av2 = util.split(ver2, "[%.%-]", nil, true)
local max = table.getn(av1)
if (table.getn(av1) < table.getn(av2)) then
max = table.getn(av2)
end
for i = 1, max, 1 do
local s1 = av1[i] or ""
local s2 = av2[i] or ""
-- first "not equal" found return true
if comp == "~=" and (s1 ~= s2) then return true end
-- first "lower" found return true
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
-- first "greater" found return true
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
-- not equal then return false
if (s1 ~= s2) then return false end
end
-- all equal and not compare greater or lower then true
return not (comp == "<" or comp == ">")
end

View file

@ -1,125 +0,0 @@
---[[
LuCI OPKG call abstraction library
]]
module "luci.model.ipkg"
---[[
Return information about installed and available packages.
@class function
@name info
@param pkg Limit output to a (set of) packages
@return Table containing package information
]]
---[[
Return the package status of one or more packages.
@class function
@name status
@param pkg Limit output to a (set of) packages
@return Table containing package status information
]]
---[[
Install one or more packages.
@class function
@name install
@param ... List of packages to install
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Determine whether a given package is installed.
@class function
@name installed
@param pkg Package
@return Boolean
]]
---[[
Remove one or more packages.
@class function
@name remove
@param ... List of packages to install
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Update package lists.
@class function
@name update
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Upgrades all installed packages.
@class function
@name upgrade
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
List all packages known to opkg.
@class function
@name list_all
@param pat Only find packages matching this pattern, nil lists all packages
@param cb Callback function invoked for each package, receives name, version and description as arguments
@return nothing
]]
---[[
List installed packages.
@class function
@name list_installed
@param pat Only find packages matching this pattern, nil lists all packages
@param cb Callback function invoked for each package, receives name, version and description as arguments
@return nothing
]]
---[[
Find packages that match the given pattern.
@class function
@name find
@param pat Find packages whose names or descriptions match this pattern, nil results in zero results
@param cb Callback function invoked for each patckage, receives name, version and description as arguments
@return nothing
]]
---[[
Determines the overlay root used by opkg.
@class function
@name overlay_root
@return String containing the directory path of the overlay root.
]]
---[[
lua version of opkg compare-versions
@class function
@name compare_versions
@param ver1 string version 1
@param ver2 string version 2
@param comp string compare versions using
"<=" or "<" lower-equal
">" or ">=" greater-equal
"=" equal
"<<" lower
">>" greater
"~=" not equal
@return Boolean indicating the status of the compare
]]

View file

@ -20,7 +20,7 @@ module "luci.model.network"
IFACE_PATTERNS_VIRTUAL = { }
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$" }
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$", "^teql%d" }
IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
IFACE_ERRORS = {
@ -622,6 +622,12 @@ function del_network(self, n)
_uci:delete("wireless", s['.name'], "network")
end
end)
local ok, fw = pcall(require, "luci.model.firewall")
if ok then
fw.init()
fw:del_network(n)
end
end
return r
end
@ -813,6 +819,7 @@ function del_wifinet(self, net)
end
function get_status_by_route(self, addr, mask)
local route_statuses = { }
local _, object
for _, object in ipairs(utl.ubus()) do
local net = object:match("^network%.interface%.(.+)")
@ -822,12 +829,14 @@ function get_status_by_route(self, addr, mask)
local rt
for _, rt in ipairs(s.route) do
if not rt.table and rt.target == addr and rt.mask == mask then
return net, s
route_statuses[net] = s
end
end
end
end
end
return route_statuses
end
function get_status_by_address(self, addr)
@ -852,28 +861,40 @@ function get_status_by_address(self, addr)
end
end
end
if s and s['ipv6-prefix-assignment'] then
local a
for _, a in ipairs(s['ipv6-prefix-assignment']) do
if a and a['local-address'] and a['local-address'].address == addr then
return net, s
end
end
end
end
end
end
function get_wannet(self)
local net, stat = self:get_status_by_route("0.0.0.0", 0)
return net and network(net, stat.proto)
function get_wan_networks(self)
local k, v
local wan_nets = { }
local route_statuses = self:get_status_by_route("0.0.0.0", 0)
for k, v in pairs(route_statuses) do
wan_nets[#wan_nets+1] = network(k, v.proto)
end
return wan_nets
end
function get_wandev(self)
local _, stat = self:get_status_by_route("0.0.0.0", 0)
return stat and interface(stat.l3_device or stat.device)
end
function get_wan6_networks(self)
local k, v
local wan6_nets = { }
local route_statuses = self:get_status_by_route("::", 0)
function get_wan6net(self)
local net, stat = self:get_status_by_route("::", 0)
return net and network(net, stat.proto)
end
for k, v in pairs(route_statuses) do
wan6_nets[#wan6_nets+1] = network(k, v.proto)
end
function get_wan6dev(self)
local _, stat = self:get_status_by_route("::", 0)
return stat and interface(stat.l3_device or stat.device)
return wan6_nets
end
function get_switch_topologies(self)
@ -1144,6 +1165,10 @@ function protocol.is_dynamic(self)
return (self:_ubus("dynamic") == true)
end
function protocol.is_auto(self)
return (self:_get("auto") ~= "0")
end
function protocol.is_alias(self)
local ifn, parent = nil, nil

View file

@ -15,7 +15,7 @@ local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
-- save the changes to the staging area via Cursor.save and finally
-- Cursor.commit the data to the actual config files.
-- LuCI then needs to Cursor.apply the changes so deamons etc. are
-- LuCI then needs to Cursor.apply the changes so daemons etc. are
-- reloaded.
module "luci.model.uci"
@ -95,41 +95,15 @@ end
function changes(self, config)
local rv = call("changes", { config = config })
local res = {}
local rv, err = call("changes", { config = config })
if type(rv) == "table" and type(rv.changes) == "table" then
local package, changes
for package, changes in pairs(rv.changes) do
res[package] = {}
local _, change
for _, change in ipairs(changes) do
local operation, section, option, value = unpack(change)
if option and operation ~= "add" then
res[package][section] = res[package][section] or { }
if operation == "list-add" then
local v = res[package][section][option]
if type(v) == "table" then
v[#v+1] = value or ""
elseif v ~= nil then
res[package][section][option] = { v, value }
else
res[package][section][option] = { value }
end
else
res[package][section][option] = value or ""
end
else
res[package][section] = res[package][section] or {}
res[package][section][".type"] = option or ""
end
end
end
return rv.changes
elseif err then
return nil, ERRSTR[err]
else
return { }
end
return res
end

View file

@ -5,7 +5,7 @@ The typical workflow for UCI is: Get a cursor instance from the
cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
save the changes to the staging area via Cursor.save and finally
Cursor.commit the data to the actual config files.
LuCI then needs to Cursor.apply the changes so deamons etc. are
LuCI then needs to Cursor.apply the changes so daemons etc. are
reloaded.
@cstyle instance
]]
@ -172,7 +172,7 @@ has the same effect as deleting the option.
---[[
Create a sub-state of this cursor.
The sub-state is tied to the parent curser, means it the parent unloads or
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
@ -339,7 +339,7 @@ Set the configuration directory.
]]
---[[
Set the directory for uncommited changes.
Set the directory for uncommitted changes.
@class function
@name Cursor.set_savedir

View file

@ -13,8 +13,8 @@ local luci = {}
luci.util = require "luci.util"
luci.ip = require "luci.ip"
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack
module "luci.sys"
@ -70,6 +70,24 @@ function mounts()
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.
@ -87,9 +105,9 @@ end
function httpget(url, stream, target)
if not target then
local source = stream and io.popen or luci.util.exec
return source("wget -4 -T 20 -qO- %s" % luci.util.shellquote(url))
return source("wget -qO- %s" % luci.util.shellquote(url))
else
return os.execute("wget -4 -T 20 -qO %s %s" %
return os.execute("wget -qO %s %s" %
{luci.util.shellquote(target), luci.util.shellquote(url)})
end
end
@ -386,8 +404,8 @@ function process.list()
end
for line in ps do
local pid, ppid, user, stat, vsz, mem, cpun, cpu, cmd = line:match(
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+.%d) +(%d+) +(%d+.%d) +(.+)"
local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
)
local idx = tonumber(pid)
@ -418,6 +436,96 @@ 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 = {}

View file

@ -18,7 +18,7 @@ Execute a given shell command and capture its standard output
@class function
@name exec
@param command Command to call
@return String containg the return the output of the command
@return String containing the return the output of the command
]]
---[[
@ -38,7 +38,7 @@ exists.
@class function
@name getenv
@param var Name of the environment variable to retrieve (optional)
@return String containg the value of the specified variable
@return String containing the value of the specified variable
@return Table containing all variables if no variable name is given
]]
@ -271,6 +271,42 @@ Send a signal to a process identified by given pid.
@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.
@ -279,7 +315,7 @@ LuCI system utilities / user related functions.
]]
---[[
Retrieve user informations for given uid.
Retrieve user information for given uid.
@class function
@name getuser
@ -305,7 +341,7 @@ Test whether given string matches the password of a given system user.
@name user.checkpasswd
@param username String containing the Unix user name
@param pass String containing the password to compare
@return Boolean indicating wheather the passwords are equal
@return Boolean indicating whether the passwords are equal
]]
---[[

View file

@ -16,14 +16,12 @@ TZ = {
{ 'Africa/Brazzaville', 'WAT-1' },
{ 'Africa/Bujumbura', 'CAT-2' },
{ 'Africa/Cairo', 'EET-2' },
{ 'Africa/Casablanca', 'WET0WEST,M3.5.0,M10.5.0/3' },
{ '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', 'WET0WEST,M3.5.0,M10.5.0/3' },
{ 'Africa/Freetown', 'GMT0' },
{ 'Africa/Gaborone', 'CAT-2' },
{ 'Africa/Harare', 'CAT-2' },
@ -51,7 +49,7 @@ TZ = {
{ 'Africa/Nouakchott', 'GMT0' },
{ 'Africa/Ouagadougou', 'GMT0' },
{ 'Africa/Porto-Novo', 'WAT-1' },
{ 'Africa/Sao Tome', 'WAT-1' },
{ 'Africa/Sao Tome', 'GMT0' },
{ 'Africa/Tripoli', 'EET-2' },
{ 'Africa/Tunis', 'CET-1' },
{ 'Africa/Windhoek', 'CAT-2' },
@ -179,7 +177,7 @@ TZ = {
{ 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Rio Branco', '<-05>5' },
{ 'America/Santarem', '<-03>3' },
{ 'America/Santiago', '<-04>4<-03>,M8.2.6/24,M5.2.6/24' },
{ 'America/Santiago', '<-04>4<-03>,M9.1.6/24,M4.1.6/24' },
{ 'America/Santo Domingo', 'AST4' },
{ 'America/Sao Paulo', '<-03>3<-02>,M11.1.0/0,M2.3.0/0' },
{ 'America/Scoresbysund', '<-01>1<+00>,M3.5.0/0,M10.5.0/1' },
@ -261,7 +259,7 @@ TZ = {
{ 'Asia/Macau', 'CST-8' },
{ 'Asia/Magadan', '<+11>-11' },
{ 'Asia/Makassar', 'WITA-8' },
{ 'Asia/Manila', '<+08>-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' },
@ -270,9 +268,10 @@ TZ = {
{ 'Asia/Oral', '<+05>-5' },
{ 'Asia/Phnom Penh', '<+07>-7' },
{ 'Asia/Pontianak', 'WIB-7' },
{ 'Asia/Pyongyang', 'KST-8:30' },
{ 'Asia/Pyongyang', 'KST-9' },
{ 'Asia/Qatar', '<+03>-3' },
{ 'Asia/Qyzylorda', '<+06>-6' },
{ 'Asia/Qostanay', '<+06>-6' },
{ 'Asia/Qyzylorda', '<+05>-5' },
{ 'Asia/Riyadh', '<+03>-3' },
{ 'Asia/Sakhalin', '<+11>-11' },
{ 'Asia/Samarkand', '<+05>-5' },
@ -283,7 +282,7 @@ TZ = {
{ 'Asia/Taipei', 'CST-8' },
{ 'Asia/Tashkent', '<+05>-5' },
{ 'Asia/Tbilisi', '<+04>-4' },
{ 'Asia/Tehran', '<+0330>-3:30<+0430>,J80/0,J264/0' },
{ 'Asia/Tehran', '<+0330>-3:30<+0430>,J79/24,J263/24' },
{ 'Asia/Thimphu', '<+06>-6' },
{ 'Asia/Tokyo', 'JST-9' },
{ 'Asia/Tomsk', '<+07>-7' },
@ -358,7 +357,7 @@ TZ = {
{ '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', 'GMT0IST,M3.5.0/1,M10.5.0' },
{ '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' },
@ -400,7 +399,7 @@ TZ = {
{ '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', '<+03>-3' },
{ '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' },
@ -421,11 +420,11 @@ TZ = {
{ '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>,M8.2.6/22,M5.2.6/22' },
{ '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.1/147' },
{ 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.2/123' },
{ 'Pacific/Funafuti', '<+12>-12' },
{ 'Pacific/Galapagos', '<-06>6' },
{ 'Pacific/Gambier', '<-09>9' },

View file

@ -9,7 +9,6 @@ OFFSET = {
wat = 3600, -- WAT
cat = 7200, -- CAT
eet = 7200, -- EET
wet = 0, -- WET
sast = 7200, -- SAST
hst = -36000, -- HST
hdt = -32400, -- HDT
@ -34,8 +33,9 @@ OFFSET = {
idt = 10800, -- IDT
pkt = 18000, -- PKT
wita = 28800, -- WITA
kst = 30600, -- KST
kst = 32400, -- KST
jst = 32400, -- JST
wet = 0, -- WET
acst = 34200, -- ACST
acdt = 37800, -- ACDT
aest = 36000, -- AEST

View file

@ -95,6 +95,6 @@ function Template.render(self, scope)
local stat, err = util.copcall(self.template)
if not stat then
error("Failed to execute template '" .. self.name .. "'.\n" ..
"A runtime error occured: " .. tostring(err or "(nil)"))
"A runtime error occurred: " .. tostring(err or "(nil)"))
end
end

View file

@ -262,7 +262,7 @@ 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 interator which aborts with the first invocation.
-- will result in a valid iterator which aborts with the first invocation.
function imatch(v)
if type(v) == "table" then
local k = nil

View file

@ -158,7 +158,7 @@ Return a matching iterator for the given value.
The iterator will return one token per invocation, the tokens are separated by
whitespace. If the input value is a table, it is transformed into a string first.
A nil value will result in a valid interator which aborts with the first invocation.
A nil value will result in a valid iterator which aborts with the first invocation.
@class function
@name imatch
@ -289,7 +289,7 @@ will be stripped before it is returned.
]]
---[[
Strips unnescessary lua bytecode from given string.
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)

View file

@ -0,0 +1,66 @@
<%#
Copyright 2010 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-%>
<% export("uci_changelog", function(changes) -%>
<div class="cbi-section">
<strong><%:Legend:%></strong>
<div class="uci-change-legend">
<div class="uci-change-legend-label"><ins>&#160;</ins> <%:Section added%></div>
<div class="uci-change-legend-label"><del>&#160;</del> <%:Section removed%></div>
<div class="uci-change-legend-label"><var><ins>&#160;</ins></var> <%:Option changed%></div>
<div class="uci-change-legend-label"><var><del>&#160;</del></var> <%:Option removed%></div>
<br style="clear:both" />
</div>
<br />
<div class="uci-change-list"><%
local util = luci.util
local tpl = {
["add-3"] = "<ins>uci add %0 <strong>%3</strong> # =%2</ins>",
["set-3"] = "<ins>uci set %0.<strong>%2</strong>=%3</ins>",
["set-4"] = "<var><ins>uci set %0.%2.%3=<strong>%4</strong></ins></var>",
["remove-2"] = "<del>uci del %0.<strong>%2</strong></del>",
["remove-3"] = "<var><del>uci del %0.%2.<strong>%3</strong></del></var>",
["order-3"] = "<var>uci reorder %0.%2=<strong>%3</strong></var>",
["list-add-4"] = "<var><ins>uci add_list %0.%2.%3=<strong>%4</strong></ins></var>",
["list-del-4"] = "<var><del>uci del_list %0.%2.%3=<strong>%4</strong></del></var>",
["rename-3"] = "<var>uci rename %0.%2=<strong>%3</strong></var>",
["rename-4"] = "<var>uci rename %0.%2.%3=<strong>%4</strong></var>"
}
local conf, deltas
for conf, deltas in util.kspairs(changes) do
write("<h3># /etc/config/%s</h3>" % conf)
local _, delta, added
for _, delta in pairs(deltas) do
local t = tpl["%s-%d" %{ delta[1], #delta }]
write(t:gsub("%%(%d)", function(n)
if n == "0" then
return conf
elseif n == "2" then
if added and delta[2] == added[1] then
return "@%s[-1]" % added[2]
else
return delta[2]
end
elseif n == "4" then
return util.shellquote(delta[4])
else
return delta[tonumber(n)]
end
end))
if delta[1] == "add" then
added = { delta[2], delta[3] }
end
end
write("<br />")
end
%></div>
</div>
<%- end) %>

View file

@ -1,49 +1,4 @@
<% export("cbi_apply_widget", function(redirect_ok, rollback_token) -%>
<style type="text/css">
#cbi_apply_overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
display: none;
z-index: 20000;
}
#cbi_apply_overlay .alert-message {
position: relative;
top: 10%;
width: 60%;
margin: auto;
display: flex;
flex-wrap: wrap;
min-height: 32px;
align-items: center;
}
#cbi_apply_overlay .alert-message > h4,
#cbi_apply_overlay .alert-message > p,
#cbi_apply_overlay .alert-message > div {
flex-basis: 100%;
}
#cbi_apply_overlay .alert-message > img {
margin-right: 1em;
flex-basis: 32px;
}
body.apply-overlay-active {
overflow: hidden;
height: 100vh;
}
body.apply-overlay-active #cbi_apply_overlay {
display: block;
}
</style>
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.138.59467-72fe5dd"></script>
<script type="text/javascript">//<![CDATA[
var xhr = new XHR(),
uci_apply_auth = { sid: '<%=luci.dispatcher.context.authsession%>', token: '<%=token%>' },
@ -55,28 +10,22 @@
was_xhr_poll_running = false;
function uci_status_message(type, content) {
var overlay = document.getElementById('cbi_apply_overlay') || document.body.appendChild(E('<div id="cbi_apply_overlay"><div class="alert-message"></div></div>')),
message = overlay.querySelector('.alert-message');
if (type) {
var message = showModal('', '');
if (message && type) {
if (!message.classList.contains(type)) {
message.classList.remove('notice');
message.classList.remove('warning');
message.classList.add(type);
}
message.classList.add('alert-message');
DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
if (content)
message.innerHTML = content;
document.body.classList.add('apply-overlay-active');
if (!was_xhr_poll_running) {
was_xhr_poll_running = XHR.running();
XHR.halt();
}
}
else {
document.body.classList.remove('apply-overlay-active');
hideModal();
if (was_xhr_poll_running)
XHR.run();
@ -85,19 +34,18 @@
function uci_rollback(checked) {
if (checked) {
uci_status_message('warning',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Failed to confirm apply within %ds, waiting for rollback…%>'.format(uci_apply_rollback));
uci_status_message('warning spinning',
'<p><%:Failed to confirm apply within %ds, waiting for rollback…%></p>'.format(uci_apply_rollback));
var call = function(r, data, duration) {
if (r.status === 204) {
uci_status_message('warning',
'<h4><%:Configuration has been rolled back!%></h4>' +
'<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
'<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, proceed by applying anyway. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
'<div class="right">' +
'<input type="button" class="btn" onclick="uci_status_message(false)" value="<%:Dismiss%>" /> ' +
'<input type="button" class="btn cbi-button-action important" onclick="uci_revert()" value="<%:Revert changes%>" /> ' +
'<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply unchecked%>" />' +
'<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply anyway%>" />' +
'</div>');
return;
@ -126,6 +74,7 @@
var call = function(r, data, duration) {
if (Date.now() >= deadline) {
window.clearTimeout(tt);
uci_rollback(checked);
return;
}
@ -133,7 +82,7 @@
var indicator = document.querySelector('.uci_change_indicator');
if (indicator) indicator.style.display = 'none';
uci_status_message('notice', '<%:Configuration has been applied.%>');
uci_status_message('notice', '<p><%:Configuration has been applied.%></p>');
window.clearTimeout(tt);
window.setTimeout(function() {
@ -156,9 +105,8 @@
var tick = function() {
var now = Date.now();
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Waiting for configuration to get applied… %ds%>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
uci_status_message('notice spinning',
'<p><%:Waiting for configuration to be applied… %ds%></p>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
if (now >= deadline)
return;
@ -174,9 +122,7 @@
}
function uci_apply(checked) {
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Starting configuration apply…%>');
uci_status_message('notice spinning', '<p><%:Starting configuration apply…%></p>');
xhr.post('<%=url("admin/uci")%>/' + (checked ? 'apply_rollback' : 'apply_unchecked'), uci_apply_auth, function(r, tok) {
if (r.status === (checked ? 200 : 204)) {
@ -186,7 +132,7 @@
uci_confirm(checked, Date.now() + uci_apply_rollback * 1000);
}
else if (checked && r.status === 204) {
uci_status_message('notice', '<%:There are no changes to apply.%>');
uci_status_message('notice', '<p><%:There are no changes to apply.%></p>');
window.setTimeout(function() {
<% if redirect_ok then -%>
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
@ -196,20 +142,18 @@
}, uci_apply_display * 1000);
}
else {
uci_status_message('warning', '<%_Apply request failed with status <code>%h</code>%>'.format(r.responseText || r.statusText || r.status));
uci_status_message('warning', '<p><%_Apply request failed with status <code>%h</code>%></p>'.format(r.responseText || r.statusText || r.status));
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
}
});
}
function uci_revert() {
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Reverting configuration…%>');
uci_status_message('notice spinning', '<p><%:Reverting configuration…%></p>');
xhr.post('<%=url("admin/uci/revert")%>', uci_apply_auth, function(r) {
if (r.status === 200) {
uci_status_message('notice', '<%:Changes have been reverted.%>');
uci_status_message('notice', '<p><%:Changes have been reverted.%></p>');
window.setTimeout(function() {
<% if redirect_ok then -%>
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
@ -219,7 +163,7 @@
}, uci_apply_display * 1000);
}
else {
uci_status_message('warning', '<%_Revert request failed with status <code>%h</code>%>'.format(r.statusText || r.status));
uci_status_message('warning', '<p><%_Revert request failed with status <code>%h</code>%></p>'.format(r.statusText || r.status));
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
}
});

View file

@ -1,8 +1,10 @@
<% local v = self:cfgvalue(section) -%>
<%+cbi/valueheader%>
<input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
<script type="text/javascript">
cbi_init()
cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=url('admin/filebrowser')%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
</script>
<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%>

View file

@ -6,7 +6,7 @@
<div class="td cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>"<%=
attr("data-name", self.option) ..
ifattr(ftype and #ftype > 0, "data-type", ftype) ..
ifattr(title and #title > 0, "data-title", title) ..
ifattr(descr and #descr > 0, "data-description", descr)
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))%>">

View file

@ -30,7 +30,7 @@
<li<%=
attr("data-index", i) ..
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
attr("value", key) ..
attr("data-value", key) ..
ifattr(selected[key], "selected", "selected")
%>>
<%=pcdata(self.vallist[i])%>

View file

@ -6,22 +6,8 @@
self.keylist, self.vallist,
self.datatype, self.optional or self.rmempty
})) ..
attr("data-values", luci.util.serialize_json(self:cfgvalue(section))) ..
ifattr(self.size, "data-size", self.size) ..
ifattr(self.placeholder, "data-placeholder", self.placeholder)
%>>
<%
local vals = self:cfgvalue(section) or {}
for i=1, #vals + 1 do
local val = vals[i]
if (val and #val > 0) or (i == 1) then
%>
<input class="cbi-input-text" value="<%=pcdata(val)%>" data-update="change" type="text"<%=
attr("id", cbid .. "." .. i) ..
attr("name", cbid) ..
ifattr(self.size, "size") ..
ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder)
%> /><br />
<% end end %>
</div>
%>></div>
<%+cbi/valuefooter%>

View file

@ -63,7 +63,7 @@
if empty then
%>
<label class="zonebadge zonebadge-empty">
<strong><%=zone:forward():upper()%></strong>
<strong><%=def:forward():upper()%></strong>
</label>
<% end %>
</div>

View file

@ -30,7 +30,7 @@
ifattr(self.rmempty or self.optional, "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<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>
@ -38,7 +38,7 @@
--></script>
<ul>
<% if self.allowlocal then %>
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
<li data-value=""<%=ifattr(checked[""], "selected", "selected")%>>
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
<strong><%:Device%></strong>
<% if self.allowany and self.allowlocal then -%>
@ -48,14 +48,14 @@
</span>
</li>
<% elseif self.widget ~= "checkbox" and (self.rmempty or self.optional) then %>
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
<li data-value=""<%=ifattr(checked[""], "selected", "selected")%>>
<span class="zonebadge">
<em><%:unspecified%></em>
</span>
</li>
<% end %>
<% if self.allowany then %>
<li value="*"<%=ifattr(checked["*"], "selected", "selected")%>>
<li data-value="*"<%=ifattr(checked["*"], "selected", "selected")%>>
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
<strong><%:Any zone%></strong>
<% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
@ -67,7 +67,7 @@
if zone:name() ~= self.exclude then
selected = selected or (value == zone:name())
%>
<li<%=attr("value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
<li<%=attr("data-value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
<span style="background-color:<%=zone:get_color()%>" class="zonebadge">
<strong><%=zone:name()%>:</strong>
<%-
@ -94,11 +94,11 @@
<% end end %>
<% if self.widget ~= "checkbox" and not self.nocreate then %>
<li value="-">
<li data-value="-">
<span class="zonebadge">
<em><%:create%>:</em>
<input type="password" style="display:none" />
<input class="create-item-input" type="text" />
<input class="create-item-input" type="text" data-type="and(uciname,maxlength(11))" data-optional="true" />
</span>
</li>
<% end %>

View file

@ -0,0 +1,27 @@
<%+cbi/valueheader%>
<script type="text/javascript">
function switchToCIDRList(ev) {
var input = ev.target.previousElementSibling,
usecidr = document.getElementById(input.id + '_usecidr');
ev.preventDefault();
usecidr.value = '1';
cbi_d_update();
}
</script>
<input data-update="change"<%=
attr("id", cbid) ..
attr("name", cbid) ..
attr("type", "text") ..
attr("class", "cbi-input-text") ..
attr("value", self:cfgvalue(section) or self.default) ..
ifattr(self.size, "size") ..
ifattr(self.placeholder, "placeholder") ..
ifattr(self.datatype, "data-type", self.datatype) ..
ifattr(self.datatype, "data-optional", self.optional or self.rmempty) ..
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
%> /><!--
--><button class="cbi-button cbi-button-neutral" title="<%:Switch to CIDR list notation%>" aria-label="<%:Switch to CIDR list notation%>" onclick="switchToCIDRList(event)"></button>
<%+cbi/valuefooter%>

View file

@ -3,25 +3,25 @@
<%- end end -%>
<div class="cbi-map" id="cbi-<%=self.config%>">
<% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
<% if self.title and #self.title > 0 then %>
<h2 name="content"><%=self.title%></h2>
<% end %>
<% if self.description and #self.description > 0 then %>
<div class="cbi-map-descr"><%=self.description%></div>
<% end %>
<% if self.tabbed then %>
<ul class="cbi-tabmenu map">
<%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
<% for i, section in ipairs(self.children) do %>
<%- if not self.selected_tab then self.selected_tab = section.sectiontype end %>
<li id="tab.m-<%=self.config%>.<%=section.section or section.sectiontype%>" class="cbi-tab<%=(section.sectiontype == self.selected_tab) and '' or '-disabled'%>">
<a onclick="this.blur(); return cbi_t_switch('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')" href="<%=REQUEST_URI%>?tab.m-<%=self.config%>=<%=section.section or section.sectiontype%>"><%=section.title or section.section or section.sectiontype %></a>
<% if section.sectiontype == self.selected_tab then %><input type="hidden" id="tab.m-<%=self.config%>" name="tab.m-<%=self.config%>" value="<%=section.section or section.sectiontype%>" /><% end %>
</li>
<div>
<% for i, section in ipairs(self.children) do
tab = section.section or section.sectiontype %>
<div class="cbi-tabcontainer"<%=
attr("id", "container.m-%s.%s" %{ self.config, tab }) ..
attr("data-tab", tab) ..
attr("data-tab-title", section.title or tab)
%>>
<% section:render() %>
</div>
<% end %>
</ul>
<% for i, section in ipairs(self.children) do %>
<div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>>
<% section:render() %>
</div>
<script type="text/javascript">cbi_t_add('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')</script>
<% end %>
</div>
<% if not self.save then -%>
<div class="cbi-section-error">

View file

@ -41,13 +41,13 @@
<input type="hidden" name="<%=cbeid%>" value="1" />
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%=
attr("name", cbid) ..
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
ifattr(self.widget == "checkbox", "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<li data-value="{{value}}">
<img title="<%:Custom Interface%>: &quot;{{value}}&quot;" src="<%=resource%>/icons/ethernet_disabled.png" />
<span class="hide-open">{{value}}</span>
<span class="hide-close"><%:Custom Interface%>: "{{value}}"</span>
@ -61,7 +61,7 @@
iface:name() ~= self.exclude
then %>
<li<%=
attr("value", iface:name()) ..
attr("data-value", iface:name()) ..
ifattr(checked[iface:name()], "selected", "selected")
%>>
<img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
@ -78,7 +78,7 @@
</li>
<% end end %>
<% if not self.nocreate then %>
<li value="">
<li data-value="">
<img title="<%:Custom Interface%>" src="<%=resource%>/icons/ethernet_disabled.png" />
<span><%:Custom Interface%>:</span>
<input type="password" style="display:none" />

View file

@ -20,13 +20,13 @@
end
-%>
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%=
attr("name", cbid) ..
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
ifattr(self.widget == "checkbox", "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<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>
@ -34,7 +34,7 @@
--></script>
<ul>
<% if self.widget ~= "checkbox" then %>
<li value=""<%= ifattr(not value, "selected", "selected") %>>
<li data-value=""<%= ifattr(not value, "selected", "selected") %>>
<em><%:unspecified%></em>
</li>
<% end %>
@ -44,7 +44,7 @@
(net:name() ~= self.exclude) and
(not self.novirtual or not net:is_virtual())
then %>
<li<%= attr("value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>>
<li<%= attr("data-value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>>
<span class="ifacebadge"><%=net:name()%>:
<%
local empty = true
@ -63,7 +63,7 @@
<% end end %>
<% if not self.nocreate then %>
<li value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
<li data-value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
<em>
<%- if self.widget == "checkbox" then -%>
<%:create:%>

View file

@ -11,7 +11,6 @@
<input type="submit" class="cbi-button" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:Delete%>" />
</div>
<%- end %>
<%+cbi/tabmenu%>
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
<%+cbi/ucisection%>
</div>

View file

@ -1,7 +1,14 @@
<% for tab, data in pairs(self.tabs) do %>
<div class="cbi-tabcontainer" id="container.<%=self.config%>.<%=section%>.<%=tab%>"<% if tab ~= self.selected_tab then %> style="display:none"<% end %>>
<% if data.description then %><div class="cbi-tab-descr"><%=data.description%></div><% end %>
<% for _, tab in ipairs(self.tab_names) do data = self.tabs[tab] %>
<div class="cbi-tabcontainer"<%=
attr("id", "container.%s.%s.%s" %{ self.config, section, tab }) ..
attr("data-tab", tab) ..
attr("data-tab-title", data.title) ..
attr("data-tab-active", tostring(tab == self.selected_tab))
%>>
<% if data.description then %>
<div class="cbi-tab-descr"><%=data.description%></div>
<% end %>
<% self:render_tab(tab, section, scope or {}) %>
</div>
<script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script>
<% end %>

View file

@ -1,12 +0,0 @@
<%- if self.tabs then %>
<ul class="cbi-tabmenu">
<%- self.selected_tab = luci.http.formvalue("tab." .. self.config .. "." .. section) %>
<%- for _, tab in ipairs(self.tab_names) do if #self.tabs[tab].childs > 0 then %>
<%- if not self.selected_tab then self.selected_tab = tab end %>
<li id="tab.<%=self.config%>.<%=section%>.<%=tab%>" class="cbi-tab<%=(tab == self.selected_tab) and '' or '-disabled'%>">
<a onclick="this.blur(); return cbi_t_switch('<%=self.config%>.<%=section%>', '<%=tab%>')" href="<%=REQUEST_URI%>?tab.<%=self.config%>.<%=section%>=<%=tab%>"><%=self.tabs[tab].title%></a>
<% if tab == self.selected_tab then %><input type="hidden" id="tab.<%=self.config%>.<%=section%>" name="tab.<%=self.config%>.<%=section%>" value="<%=tab%>" /><% end %>
</li>
<% end end -%>
</ul>
<% end -%>

View file

@ -127,7 +127,7 @@ end
section = k
local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k)
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname)
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname, true)
local colorclass = (self.extedit or self.rowcolors) and rowstyle() or ""
local scope = {
valueheader = "cbi/cell_valueheader",

View file

@ -2,6 +2,11 @@
<% if self.title and #self.title > 0 then -%>
<legend><%=self.title%></legend>
<%- end %>
<% if self.error_msg and #self.error_msg > 0 then -%>
<div class="cbi-section-error">
<%=self.error_msg%>
</div>
<%- end %>
<% if self.description and #self.description > 0 then -%>
<div class="cbi-section-descr"><%=self.description%></div>
<%- end %>
@ -18,8 +23,6 @@
<h3><%=section:upper()%></h3>
<%- end %>
<%+cbi/tabmenu%>
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
<%+cbi/ucisection%>
</div>

View file

@ -1,6 +1,6 @@
<%+cbi/valueheader%>
<%- if self.password then -%>
<input type="password" style="position:absolute; left:-4000px"<%=
<input type="password" style="position:absolute; left:-1000px" aria-hidden="true" tabindex="-1"<%=
attr("name", "password." .. cbid)
%> />
<%- end -%>
@ -21,6 +21,6 @@
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
%> />
<%- if self.password then -%>
<div class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'"></div>
<button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'; event.preventDefault()"></button>
<% end %>
<%+cbi/valuefooter%>

View file

@ -0,0 +1,11 @@
<%#
Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
Copyright 2018 Daniel F. Dickinson <cshored@thecshore.com>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<p>Component not present.</p>
<%+footer%>

View file

@ -10,3 +10,15 @@
luci.dispatcher.context.template_header_sent = true
end
%>
<script type="text/javascript" src="<%=resource%>/luci.js"></script>
<script type="text/javascript">
L = new LuCI(<%= luci.http.write_json({
token = token,
resource = resource,
scriptname = luci.http.getenv("SCRIPT_NAME"),
pathinfo = luci.http.getenv("PATH_INFO"),
requestpath = luci.dispatcher.context.requestpath,
pollinterval = luci.config.main.pollinterval or 5
}) %>);
</script>

View file

@ -1,5 +1,5 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url('admin/network/dhcplease_status')%>', null,
XHR.poll(-1, '<%=url('admin/dhcplease_status')%>', null,
function(x, st)
{
var tb = document.getElementById('lease_status_table');

View file

@ -1,4 +1,21 @@
<%
local supports_deauth = {}
local _, v
for _, v in ipairs(luci.util.ubus()) do
local iface = v:match("^hostapd%.(.+)$")
if iface then
local funcs = luci.util.ubus(v)
if type(funcs) == "table" and funcs.del_client then
supports_deauth[iface] = true
end
end
end
%>
<script type="text/javascript">//<![CDATA[
var supports_deauth = <%= luci.http.write_json(supports_deauth) %>;
function wifirate(bss, rx) {
var p = rx ? 'rx_' : 'tx_',
s = '%.1f <%:Mbit/s%>, %d<%:MHz%>'
@ -17,7 +34,17 @@
return s;
}
XHR.poll(5, '<%=url('admin/network/wireless_assoclist')%>', null,
function handleDeauth(ev) {
(new XHR()).post('<%=url('admin/wireless_deauth')%>', {
token: '<%=token%>',
iface: ev.target.getAttribute('data-iface'),
bssid: ev.target.getAttribute('data-bssid')
}, function() {
ev.target.disabled = true;
});
}
XHR.poll(-1, '<%=url('admin/wireless_assoclist')%>', null,
function(x, st)
{
var tb = document.getElementById('wifi_assoclist_table');
@ -58,7 +85,15 @@
E('span', wifirate(bss, true)),
E('br'),
E('span', wifirate(bss, false))
])
]),
supports_deauth[bss.ifname] ? E('input', {
type: 'button',
class: 'cbi-button cbi-button-remove',
value: '<%:Disconnect%>',
'data-bssid': bss.bssid,
'data-iface': bss.ifname,
click: handleDeauth
}) : '-'
]);
});
@ -75,6 +110,9 @@
<div class="th nowrap"><%:Host%></div>
<div class="th nowrap"><%:Signal%> / <%:Noise%></div>
<div class="th nowrap"><%:RX Rate%> / <%:TX Rate%></div>
<% if next(supports_deauth) then %>
<div class="th right"><%:Disconnect%></div>
<% end %>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,9 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci/" />
</head>
<body style="background-color: white">
<a style="color: black; font-family: arial, helvetica, sans-serif;" href="/cgi-bin/luci">LuCI - Lua Configuration Interface</a>
<a style="color: black; font-family: arial, helvetica, sans-serif;" href="/cgi-bin/luci/">LuCI - Lua Configuration Interface</a>
</body>
</html>

View file

@ -4,6 +4,9 @@
clean:
rm -f po2lmo parser.so version.lua *.o
jsmin: jsmin.o
$(CC) $(LDFLAGS) -o $@ $^
po2lmo: po2lmo.o template_lmo.o
$(CC) $(LDFLAGS) -o $@ $^

292
luci-base/src/jsmin.c Normal file
View file

@ -0,0 +1,292 @@
/* jsmin.c
2011-09-30
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
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 shall be used for Good, not Evil.
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.
*/
#include <stdlib.h>
#include <stdio.h>
static int theA;
static int theB;
static int theLookahead = EOF;
/* isAlphanum -- return true if the character is a letter, digit, underscore,
dollar sign, or non-ASCII character.
*/
static int
isAlphanum(int c)
{
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
c > 126);
}
/* get -- return the next character from stdin. Watch out for lookahead. If
the character is a control character, translate it to a space or
linefeed.
*/
static int
get()
{
int c = theLookahead;
theLookahead = EOF;
if (c == EOF) {
c = getc(stdin);
}
if (c >= ' ' || c == '\n' || c == EOF) {
return c;
}
if (c == '\r') {
return '\n';
}
return ' ';
}
/* peek -- get the next character without getting it.
*/
static int
peek()
{
theLookahead = get();
return theLookahead;
}
/* next -- get the next character, excluding comments. peek() is used to see
if a '/' is followed by a '/' or '*'.
*/
static int
next()
{
int c = get();
if (c == '/') {
switch (peek()) {
case '/':
for (;;) {
c = get();
if (c <= '\n') {
return c;
}
}
case '*':
get();
for (;;) {
switch (get()) {
case '*':
if (peek() == '/') {
get();
return ' ';
}
break;
case EOF:
fprintf(stderr, "Error: JSMIN Unterminated comment.\n");
exit(1);
}
}
default:
return c;
}
}
return c;
}
/* action -- do something! What you do is determined by the argument:
1 Output A. Copy B to A. Get the next B.
2 Copy B to A. Get the next B. (Delete A).
3 Get the next B. (Delete B).
action treats a string as a single character. Wow!
action recognizes a regular expression if it is preceded by ( or , or =.
*/
static void
action(int d)
{
switch (d) {
case 1:
putc(theA, stdout);
case 2:
theA = theB;
if (theA == '\'' || theA == '"' || theA == '`') {
for (;;) {
putc(theA, stdout);
theA = get();
if (theA == theB) {
break;
}
if (theA == '\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr, "Error: JSMIN unterminated string literal.");
exit(1);
}
}
}
case 3:
theB = next();
if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
theA == ':' || theA == '[' || theA == '!' ||
theA == '&' || theA == '|' || theA == '?' ||
theA == '{' || theA == '}' || theA == ';' ||
theA == '\n')) {
putc(theA, stdout);
putc(theB, stdout);
for (;;) {
theA = get();
if (theA == '[') {
for (;;) {
putc(theA, stdout);
theA = get();
if (theA == ']') {
break;
}
if (theA == '\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr,
"Error: JSMIN unterminated set in Regular Expression literal.\n");
exit(1);
}
}
} else if (theA == '/') {
break;
} else if (theA =='\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr,
"Error: JSMIN unterminated Regular Expression literal.\n");
exit(1);
}
putc(theA, stdout);
}
theB = next();
}
}
}
/* jsmin -- Copy the input to the output, deleting the characters which are
insignificant to JavaScript. Comments will be removed. Tabs will be
replaced with spaces. Carriage returns will be replaced with linefeeds.
Most spaces and linefeeds will be removed.
*/
static void
jsmin()
{
theA = '\n';
action(3);
while (theA != EOF) {
switch (theA) {
case ' ':
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
break;
case '\n':
switch (theB) {
case '{':
case '[':
case '(':
case '+':
case '-':
action(1);
break;
case ' ':
action(3);
break;
default:
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
}
break;
default:
switch (theB) {
case ' ':
if (isAlphanum(theA)) {
action(1);
break;
}
action(3);
break;
case '\n':
switch (theA) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case '\'':
case '`':
action(1);
break;
default:
if (isAlphanum(theA)) {
action(1);
} else {
action(3);
}
}
break;
default:
action(1);
break;
}
}
}
}
/* main -- Output any command line arguments as comments
and then minify the input.
*/
extern int
main(int argc, char* argv[])
{
int i;
for (i = 1; i < argc; i += 1) {
fprintf(stdout, "// %s\n", argv[i]);
}
jsmin();
return 0;
}

View file

@ -46,14 +46,14 @@ uint32_t sfh_hash(const char *data, int len)
switch (rem) {
case 3: hash += sfh_get16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash ^= (signed char)data[sizeof(uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
case 1: hash += (signed char)*data;
hash ^= hash << 10;
hash += hash >> 1;
}
@ -216,7 +216,7 @@ int lmo_load_catalog(const char *lang, const char *dir)
if (!_lmo_active_catalog)
_lmo_active_catalog = cat;
return 0;
return cat->archives ? 0 : -1;
err:
if (dh) closedir(dh);
@ -301,6 +301,20 @@ int lmo_translate(const char *key, int keylen, char **out, int *outlen)
return -1;
}
void lmo_iterate(lmo_iterate_cb_t cb, void *priv)
{
unsigned int i;
lmo_entry_t *e;
lmo_archive_t *ar;
if (!_lmo_active_catalog)
return;
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
for (i = 0, e = &ar->index[0]; i < ar->length; e = &ar->index[++i])
cb(ntohl(e->key_id), ar->mmap + ntohl(e->offset), ntohl(e->length), priv);
}
void lmo_close_catalog(const char *lang)
{
lmo_archive_t *ar, *next;

View file

@ -73,6 +73,7 @@ struct lmo_catalog {
typedef struct lmo_catalog lmo_catalog_t;
typedef void (*lmo_iterate_cb_t)(uint32_t, const char *, int, void *);
uint32_t sfh_hash(const char *data, int len);
uint32_t lmo_canon_hash(const char *data, int len);
@ -87,6 +88,7 @@ extern lmo_catalog_t *_lmo_active_catalog;
int lmo_load_catalog(const char *lang, const char *dir);
int lmo_change_catalog(const char *lang);
int lmo_translate(const char *key, int keylen, char **out, int *outlen);
void lmo_iterate(lmo_iterate_cb_t cb, void *priv);
void lmo_close_catalog(const char *lang);
#endif

View file

@ -129,6 +129,24 @@ static int template_L_change_catalog(lua_State *L) {
return 1;
}
static void template_L_get_translations_cb(uint32_t key, const char *val, int len, void *priv) {
lua_State *L = priv;
char hex[9];
luaL_checktype(L, 1, LUA_TFUNCTION);
snprintf(hex, sizeof(hex), "%08x", key);
lua_pushvalue(L, 1);
lua_pushstring(L, hex);
lua_pushlstring(L, val, len);
lua_call(L, 2, 0);
}
static int template_L_get_translations(lua_State *L) {
lmo_iterate(template_L_get_translations_cb, L);
return 0;
}
static int template_L_translate(lua_State *L) {
size_t len;
char *tr;
@ -168,6 +186,7 @@ static const luaL_reg R[] = {
{ "load_catalog", template_L_load_catalog },
{ "close_catalog", template_L_close_catalog },
{ "change_catalog", template_L_change_catalog },
{ "get_translations", template_L_get_translations },
{ "translate", template_L_translate },
{ "hash", template_L_hash },
{ NULL, NULL }

View file

@ -258,7 +258,7 @@ static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
break;
}
/* advance beyound the last found valid continuation char */
/* advance beyond the last found valid continuation char */
o = v;
ptr += v;
}

View file

@ -1,6 +1,5 @@
#
# Copyright (C) 2008-2014 The LuCI Team <luci@lists.subsignal.org>
# Copyright (C) 2017-2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
@ -8,9 +7,8 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI Administration - full-featured for full control
LUCI_DEPENDS:=+luci-base +luci-app-firewall +luci-lib-iptparser
LUCI_DEPENDS:=+luci-base +luci-mod-status +luci-mod-system +luci-mod-network
PKG_BUILD_DEPENDS:=iwinfo
PKG_LICENSE:=Apache-2.0
include ../luci/luci.mk

View file

@ -1,16 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="rx" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
<polyline id="tx" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
<line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
<text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
<text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
<text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,17 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="tcp" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
<polyline id="udp" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
<polyline id="other" points="" style="fill:red;fill-opacity:0.4;stroke:red;stroke-width:1" />
<line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
<text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
<text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
<text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,17 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="load01" points="" style="fill:#ff0000;fill-opacity:0.4;stroke:#ff0000;stroke-width:1" />
<polyline id="load05" points="" style="fill:#ff6600;fill-opacity:0.4;stroke:#ff6600;stroke-width:1" />
<polyline id="load15" points="" style="fill:#ffaa00;fill-opacity:0.4;stroke:#ffaa00;stroke-width:1" />
<line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
<text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
<text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
<text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,15 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="rate" points="" style="fill:green;fill-opacity:0.4;stroke:green;stroke-width:1" />
<line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
<text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
<text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
<text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
</svg>

Before

Width:  |  Height:  |  Size: 973 B

View file

@ -1,16 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="rssi" points="" style="fill:blue;fill-opacity:0.4;stroke:blue;stroke-width:1" />
<polyline id="noise" points="" style="fill:red;fill-opacity:0.4;stroke:red;stroke-width:1" />
<line x1="0" y1="25%" x2="100%" y2="25%" style="stroke:black;stroke-width:0.1" />
<text id="label_75" x="20" y="24%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="50%" x2="100%" y2="50%" style="stroke:black;stroke-width:0.1" />
<text id="label_50" x="20" y="49%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
<line x1="0" y1="75%" x2="100%" y2="75%" style="stroke:black;stroke-width:0.1" />
<text id="label_25" x="20" y="74%" style="fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000"> </text>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,42 +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()
local root = node()
if not root.target then
root.target = alias("admin")
root.index = true
end
local page = node("admin")
page.target = firstchild()
page.title = _("Administration")
page.order = 10
page.sysauth = "root"
page.sysauth_authenticator = "htmlauth"
page.ucidata = true
page.index = true
-- Empty services menu to be populated by addons
entry({"admin", "services"}, firstchild(), _("Services"), 40).index = true
entry({"admin", "logout"}, call("action_logout"), _("Logout"), 90)
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/" %{
sid, 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url()
})
end
luci.http.redirect(dsp.build_url())
end

View file

@ -1,154 +0,0 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.admin.status", package.seeall)
function index()
entry({"admin", "status"}, alias("admin", "status", "overview"), _("Status"), 20).index = true
entry({"admin", "status", "overview"}, template("admin_status/index"), _("Overview"), 1)
entry({"admin", "status", "iptables"}, template("admin_status/iptables"), _("Firewall"), 2).leaf = true
entry({"admin", "status", "iptables_action"}, post("action_iptables")).leaf = true
entry({"admin", "status", "routes"}, template("admin_status/routes"), _("Routes"), 3)
entry({"admin", "status", "syslog"}, call("action_syslog"), _("System Log"), 4)
entry({"admin", "status", "dmesg"}, call("action_dmesg"), _("Kernel Log"), 5)
entry({"admin", "status", "processes"}, form("admin_status/processes"), _("Processes"), 6)
entry({"admin", "status", "realtime"}, alias("admin", "status", "realtime", "load"), _("Realtime Graphs"), 7)
entry({"admin", "status", "realtime", "load"}, template("admin_status/load"), _("Load"), 1).leaf = true
entry({"admin", "status", "realtime", "load_status"}, call("action_load")).leaf = true
entry({"admin", "status", "realtime", "bandwidth"}, template("admin_status/bandwidth"), _("Traffic"), 2).leaf = true
entry({"admin", "status", "realtime", "bandwidth_status"}, call("action_bandwidth")).leaf = true
if nixio.fs.access("/etc/config/wireless") then
entry({"admin", "status", "realtime", "wireless"}, template("admin_status/wireless"), _("Wireless"), 3).leaf = true
entry({"admin", "status", "realtime", "wireless_status"}, call("action_wireless")).leaf = true
end
entry({"admin", "status", "realtime", "connections"}, template("admin_status/connections"), _("Connections"), 4).leaf = true
entry({"admin", "status", "realtime", "connections_status"}, call("action_connections")).leaf = true
entry({"admin", "status", "nameinfo"}, call("action_nameinfo")).leaf = true
end
function action_syslog()
local syslog = luci.sys.syslog()
luci.template.render("admin_status/syslog", {syslog=syslog})
end
function action_dmesg()
local dmesg = luci.sys.dmesg()
luci.template.render("admin_status/dmesg", {dmesg=dmesg})
end
function action_iptables()
if luci.http.formvalue("zero") then
if luci.http.formvalue("family") == "6" then
luci.util.exec("/usr/sbin/ip6tables -Z")
else
luci.util.exec("/usr/sbin/iptables -Z")
end
elseif luci.http.formvalue("restart") then
luci.util.exec("/etc/init.d/firewall restart")
end
luci.http.redirect(luci.dispatcher.build_url("admin/status/iptables"))
end
function action_bandwidth(iface)
luci.http.prepare_content("application/json")
local bwc = io.popen("luci-bwc -i %s 2>/dev/null"
% luci.util.shellquote(iface))
if bwc then
luci.http.write("[")
while true do
local ln = bwc:read("*l")
if not ln then break end
luci.http.write(ln)
end
luci.http.write("]")
bwc:close()
end
end
function action_wireless(iface)
luci.http.prepare_content("application/json")
local bwc = io.popen("luci-bwc -r %s 2>/dev/null"
% luci.util.shellquote(iface))
if bwc then
luci.http.write("[")
while true do
local ln = bwc:read("*l")
if not ln then break end
luci.http.write(ln)
end
luci.http.write("]")
bwc:close()
end
end
function action_load()
luci.http.prepare_content("application/json")
local bwc = io.popen("luci-bwc -l 2>/dev/null")
if bwc then
luci.http.write("[")
while true do
local ln = bwc:read("*l")
if not ln then break end
luci.http.write(ln)
end
luci.http.write("]")
bwc:close()
end
end
function action_connections()
local sys = require "luci.sys"
luci.http.prepare_content("application/json")
luci.http.write('{ "connections": ')
luci.http.write_json(sys.net.conntrack())
local bwc = io.popen("luci-bwc -c 2>/dev/null")
if bwc then
luci.http.write(', "statistics": [')
while true do
local ln = bwc:read("*l")
if not ln then break end
luci.http.write(ln)
end
luci.http.write("]")
bwc:close()
end
luci.http.write(" }")
end
function action_nameinfo(...)
local util = require "luci.util"
luci.http.prepare_content("application/json")
luci.http.write_json(util.ubus("network.rrdns", "lookup", {
addrs = { ... },
timeout = 5000,
limit = 1000
}) or { })
end

View file

@ -1,448 +0,0 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.admin.system", package.seeall)
function index()
local fs = require "nixio.fs"
entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)
entry({"admin", "system", "clock_status"}, post_on({ set = true }, "action_clock_status"))
entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
if fs.access("/bin/opkg") then
entry({"admin", "system", "packages"}, post_on({ exec = "1" }, "action_packages"), _("Software"), 10)
entry({"admin", "system", "packages", "ipkg"}, form("admin_system/ipkg"))
end
entry({"admin", "system", "startup"}, form("admin_system/startup"), _("Startup"), 45)
entry({"admin", "system", "crontab"}, form("admin_system/crontab"), _("Scheduled Tasks"), 46)
if fs.access("/sbin/block") and fs.access("/etc/config/fstab") then
entry({"admin", "system", "fstab"}, cbi("admin_system/fstab"), _("Mount Points"), 50)
entry({"admin", "system", "fstab", "mount"}, cbi("admin_system/fstab/mount"), nil).leaf = true
entry({"admin", "system", "fstab", "swap"}, cbi("admin_system/fstab/swap"), nil).leaf = true
end
local nodes, number = fs.glob("/sys/class/leds/*")
if number > 0 then
entry({"admin", "system", "leds"}, cbi("admin_system/leds"), _("<abbr title=\"Light Emitting Diode\">LED</abbr> Configuration"), 60)
end
entry({"admin", "system", "flashops"}, call("action_flashops"), _("Backup / Flash Firmware"), 70)
entry({"admin", "system", "flashops", "reset"}, post("action_reset"))
entry({"admin", "system", "flashops", "backup"}, post("action_backup"))
entry({"admin", "system", "flashops", "backupfiles"}, form("admin_system/backupfiles"))
-- call() instead of post() due to upload handling!
entry({"admin", "system", "flashops", "restore"}, call("action_restore"))
entry({"admin", "system", "flashops", "sysupgrade"}, call("action_sysupgrade"))
entry({"admin", "system", "reboot"}, template("admin_system/reboot"), _("Reboot"), 90)
entry({"admin", "system", "reboot", "call"}, post("action_reboot"))
end
function action_clock_status()
local set = tonumber(luci.http.formvalue("set"))
if set ~= nil and set > 0 then
local date = os.date("*t", set)
if date then
luci.sys.call("date -s '%04d-%02d-%02d %02d:%02d:%02d'" %{
date.year, date.month, date.day, date.hour, date.min, date.sec
})
luci.sys.call("/etc/init.d/sysfixtime restart")
end
end
luci.http.prepare_content("application/json")
luci.http.write_json({ timestring = os.date("%c") })
end
function action_packages()
local fs = require "nixio.fs"
local ipkg = require "luci.model.ipkg"
local submit = (luci.http.formvalue("exec") == "1")
local update, upgrade
local changes = false
local install = { }
local remove = { }
local stdout = { "" }
local stderr = { "" }
local out, err
-- Display
local display = luci.http.formvalue("display") or "available"
-- Letter
local letter = string.byte(luci.http.formvalue("letter") or "A", 1)
letter = (letter == 35 or (letter >= 65 and letter <= 90)) and letter or 65
-- Search query
local query = luci.http.formvalue("query")
query = (query ~= '') and query or nil
-- Modifying actions
if submit then
-- Packets to be installed
local ninst = luci.http.formvalue("install")
local uinst = nil
-- Install from URL
local url = luci.http.formvalue("url")
if url and url ~= '' then
uinst = url
end
-- Do install
if ninst then
install[ninst], out, err = ipkg.install(ninst)
stdout[#stdout+1] = out
stderr[#stderr+1] = err
changes = true
end
if uinst then
local pkg
for pkg in luci.util.imatch(uinst) do
install[uinst], out, err = ipkg.install(pkg)
stdout[#stdout+1] = out
stderr[#stderr+1] = err
changes = true
end
end
-- Remove packets
local rem = luci.http.formvalue("remove")
if rem then
remove[rem], out, err = ipkg.remove(rem)
stdout[#stdout+1] = out
stderr[#stderr+1] = err
changes = true
end
-- Update all packets
update = luci.http.formvalue("update")
if update then
update, out, err = ipkg.update()
stdout[#stdout+1] = out
stderr[#stderr+1] = err
end
-- Upgrade all packets
upgrade = luci.http.formvalue("upgrade")
if upgrade then
upgrade, out, err = ipkg.upgrade()
stdout[#stdout+1] = out
stderr[#stderr+1] = err
end
end
-- List state
local no_lists = true
local old_lists = false
if fs.access("/var/opkg-lists/") then
local list
for list in fs.dir("/var/opkg-lists/") do
no_lists = false
if (fs.stat("/var/opkg-lists/"..list, "mtime") or 0) < (os.time() - (24 * 60 * 60)) then
old_lists = true
break
end
end
end
luci.template.render("admin_system/packages", {
display = display,
letter = letter,
query = query,
install = install,
remove = remove,
update = update,
upgrade = upgrade,
no_lists = no_lists,
old_lists = old_lists,
stdout = table.concat(stdout, ""),
stderr = table.concat(stderr, "")
})
-- Remove index cache
if changes then
fs.unlink("/tmp/luci-indexcache")
end
end
local function image_supported(image)
return (os.execute("sysupgrade -T %q >/dev/null" % image) == 0)
end
local function image_checksum(image)
return (luci.sys.exec("md5sum %q" % image):match("^([^%s]+)"))
end
local function image_sha256_checksum(image)
return (luci.sys.exec("sha256sum %q" % image):match("^([^%s]+)"))
end
local function supports_sysupgrade()
return nixio.fs.access("/lib/upgrade/platform.sh")
end
local function supports_reset()
return (os.execute([[grep -sq "^overlayfs:/overlay / overlay " /proc/mounts]]) == 0)
end
local function storage_size()
local size = 0
if nixio.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 n == "linux" or n == "firmware" then
size = tonumber(s, 16)
break
end
end
elseif nixio.fs.access("/proc/partitions") then
for l in io.lines("/proc/partitions") do
local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
if b and n and not n:match('[0-9]') then
size = tonumber(b) * 1024
break
end
end
end
return size
end
function action_flashops()
--
-- Overview
--
luci.template.render("admin_system/flashops", {
reset_avail = supports_reset(),
upgrade_avail = supports_sysupgrade()
})
end
function action_sysupgrade()
local fs = require "nixio.fs"
local http = require "luci.http"
local image_tmp = "/tmp/firmware.img"
local fp
http.setfilehandler(
function(meta, chunk, eof)
if not fp and meta and meta.name == "image" then
fp = io.open(image_tmp, "w")
end
if fp and chunk then
fp:write(chunk)
end
if fp and eof then
fp:close()
end
end
)
if not luci.dispatcher.test_post_security() then
fs.unlink(image_tmp)
return
end
--
-- Cancel firmware flash
--
if http.formvalue("cancel") then
fs.unlink(image_tmp)
http.redirect(luci.dispatcher.build_url('admin/system/flashops'))
return
end
--
-- Initiate firmware flash
--
local step = tonumber(http.formvalue("step") or 1)
if step == 1 then
if image_supported(image_tmp) then
luci.template.render("admin_system/upgrade", {
checksum = image_checksum(image_tmp),
sha256ch = image_sha256_checksum(image_tmp),
storage = storage_size(),
size = (fs.stat(image_tmp, "size") or 0),
keep = (not not http.formvalue("keep"))
})
else
fs.unlink(image_tmp)
luci.template.render("admin_system/flashops", {
reset_avail = supports_reset(),
upgrade_avail = supports_sysupgrade(),
image_invalid = true
})
end
--
-- Start sysupgrade flash
--
elseif step == 2 then
local keep = (http.formvalue("keep") == "1") and "" or "-n"
luci.template.render("admin_system/applyreboot", {
title = luci.i18n.translate("Flashing..."),
msg = luci.i18n.translate("The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
addr = (#keep > 0) and "192.168.1.1" or nil
})
fork_exec("sleep 1; killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
end
end
function action_backup()
local reader = ltn12_popen("sysupgrade --create-backup - 2>/dev/null")
luci.http.header(
'Content-Disposition', 'attachment; filename="backup-%s-%s.tar.gz"' %{
luci.sys.hostname(),
os.date("%Y-%m-%d")
})
luci.http.prepare_content("application/x-targz")
luci.ltn12.pump.all(reader, luci.http.write)
end
function action_restore()
local fs = require "nixio.fs"
local http = require "luci.http"
local archive_tmp = "/tmp/restore.tar.gz"
local fp
http.setfilehandler(
function(meta, chunk, eof)
if not fp and meta and meta.name == "archive" then
fp = io.open(archive_tmp, "w")
end
if fp and chunk then
fp:write(chunk)
end
if fp and eof then
fp:close()
end
end
)
if not luci.dispatcher.test_post_security() then
fs.unlink(archive_tmp)
return
end
local upload = http.formvalue("archive")
if upload and #upload > 0 then
if os.execute("gunzip -t %q >/dev/null 2>&1" % archive_tmp) == 0 then
luci.template.render("admin_system/applyreboot")
os.execute("tar -C / -xzf %q >/dev/null 2>&1" % archive_tmp)
luci.sys.reboot()
else
luci.template.render("admin_system/flashops", {
reset_avail = supports_reset(),
upgrade_avail = supports_sysupgrade(),
backup_invalid = true
})
end
return
end
http.redirect(luci.dispatcher.build_url('admin/system/flashops'))
end
function action_reset()
if supports_reset() then
luci.template.render("admin_system/applyreboot", {
title = luci.i18n.translate("Erasing..."),
msg = luci.i18n.translate("The system is erasing the configuration partition now and will reboot itself when finished."),
addr = "192.168.1.1"
})
fork_exec("sleep 1; killall dropbear uhttpd; sleep 1; jffs2reset -y && reboot")
return
end
http.redirect(luci.dispatcher.build_url('admin/system/flashops'))
end
function action_passwd()
local p1 = luci.http.formvalue("pwd1")
local p2 = luci.http.formvalue("pwd2")
local stat = nil
if p1 or p2 then
if p1 == p2 then
stat = luci.sys.user.setpasswd("root", p1)
else
stat = 10
end
end
luci.template.render("admin_system/passwd", {stat=stat})
end
function action_reboot()
luci.sys.reboot()
end
function fork_exec(command)
local pid = nixio.fork()
if pid > 0 then
return
elseif pid == 0 then
-- change to root dir
nixio.chdir("/")
-- patch stdin, out, err to /dev/null
local null = nixio.open("/dev/null", "w+")
if null then
nixio.dup(null, nixio.stderr)
nixio.dup(null, nixio.stdout)
nixio.dup(null, nixio.stdin)
if null:fileno() > 2 then
null:close()
end
end
-- replace with target command
nixio.exec("/bin/sh", "-c", command)
end
end
function ltn12_popen(command)
local fdi, fdo = nixio.pipe()
local pid = nixio.fork()
if pid > 0 then
fdo:close()
local close
return function()
local buffer = fdi:read(2048)
local wpid, stat = nixio.waitpid(pid, "nohang")
if not close and wpid and stat == "exited" then
close = true
end
if buffer and #buffer > 0 then
return buffer
elseif close then
fdi:close()
return nil
end
end
elseif pid == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exec("/bin/sh", "-c", command)
end
end

View file

@ -1,67 +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 device, apn, service, pincode, username, password
local ipv6, maxwait, defaultroute, metric, peerdns, dns,
keepalive_failure, keepalive_interval, demand
mca = s:taboption("ahcp", Value, "multicast_address", translate("Multicast address"))
mca.optional = true
mca.placeholder = "ff02::cca6:c0f9:e182:5359"
mca.datatype = "ip6addr"
mca:depends("proto", "ahcp")
port = s:taboption("ahcp", Value, "port", translate("Port"))
port.optional = true
port.placeholder = 5359
port.datatype = "port"
port:depends("proto", "ahcp")
fam = s:taboption("ahcp", ListValue, "_family", translate("Protocol family"))
fam:value("", translate("IPv4 and IPv6"))
fam:value("ipv4", translate("IPv4 only"))
fam:value("ipv6", translate("IPv6 only"))
fam:depends("proto", "ahcp")
function fam.cfgvalue(self, section)
local v4 = m.uci:get_bool("network", section, "ipv4_only")
local v6 = m.uci:get_bool("network", section, "ipv6_only")
if v4 then
return "ipv4"
elseif v6 then
return "ipv6"
end
return ""
end
function fam.write(self, section, value)
if value == "ipv4" then
m.uci:set("network", section, "ipv4_only", "true")
m.uci:delete("network", section, "ipv6_only")
elseif value == "ipv6" then
m.uci:set("network", section, "ipv6_only", "true")
m.uci:delete("network", section, "ipv4_only")
end
end
function fam.remove(self, section)
m.uci:delete("network", section, "ipv4_only")
m.uci:delete("network", section, "ipv6_only")
end
nodns = s:taboption("ahcp", Flag, "no_dns", translate("Disable DNS setup"))
nodns.optional = true
nodns.enabled = "true"
nodns.disabled = "false"
nodns.default = nodns.disabled
nodns:depends("proto", "ahcp")
ltime = s:taboption("ahcp", Value, "lease_time", translate("Lease validity time"))
ltime.optional = true
ltime.placeholder = 3666
ltime.datatype = "uinteger"
ltime:depends("proto", "ahcp")

View file

@ -1,223 +0,0 @@
-- Copyright 2018 Jo-Philipp Wich <jo@mein.io>
-- Licensed to the public under the Apache License 2.0.
local fs = require "nixio.fs"
local utl = require "luci.util"
local tpl = require "luci.template"
local ntm = require "luci.model.network"
local has_iwinfo = pcall(require, "iwinfo")
function guess_wifi_hw(dev)
local bands = ""
local ifname = dev:name()
local name, idx = ifname:match("^([a-z]+)(%d+)")
idx = tonumber(idx)
if has_iwinfo then
local bl = dev.iwinfo.hwmodelist
if bl and next(bl) then
if bl.a then bands = bands .. "a" end
if bl.b then bands = bands .. "b" end
if bl.g then bands = bands .. "g" end
if bl.n then bands = bands .. "n" end
if bl.ac then bands = bands .. "ac" end
end
local hw = dev.iwinfo.hardware_name
if hw then
return "%s 802.11%s" %{ hw, bands }
end
end
-- wl.o
if name == "wl" then
local name = translatef("Broadcom 802.11%s Wireless Controller", bands)
local nm = 0
local fd = nixio.open("/proc/bus/pci/devices", "r")
if fd then
local ln
for ln in fd:linesource() do
if ln:match("wl$") then
if nm == idx then
local version = ln:match("^%S+%s+%S%S%S%S([0-9a-f]+)")
name = translatef(
"Broadcom BCM%04x 802.11 Wireless Controller",
tonumber(version, 16)
)
break
else
nm = nm + 1
end
end
end
fd:close()
end
return name
-- dunno yet
else
return translatef("Generic 802.11%s Wireless Controller", bands)
end
end
local tpl_radio = tpl.Template(nil, [[
<div class="cbi-section-node">
<div class="table">
<!-- physical device -->
<div class="tr cbi-rowstyle-2">
<div class="td col-2 center middle">
<span class="ifacebadge"><img src="<%=resource%>/icons/wifi_disabled.png" id="<%=dev:name()%>-iw-upstate" /> <%=dev:name()%></span>
</div>
<div class="td col-7 left middle">
<big><strong><%=hw%></strong></big><br />
<span id="<%=dev:name()%>-iw-devinfo"></span>
</div>
<div class="td middle cbi-section-actions">
<div>
<input type="button" class="cbi-button cbi-button-neutral" title="<%:Restart radio interface%>" value="<%:Restart%>" data-radio="<%=dev:name()%>" onclick="wifi_restart(event)" />
<input type="button" class="cbi-button cbi-button-action important" title="<%:Find and join network%>" value="<%:Scan%>" onclick="cbi_submit(this, 'device', '<%=dev:name()%>', '<%=url('admin/network/wireless_join')%>')" />
<input type="button" class="cbi-button cbi-button-add" title="<%:Provide new network%>" value="<%:Add%>" onclick="cbi_submit(this, 'device', '<%=dev:name()%>', '<%=url('admin/network/wireless_add')%>')" />
</div>
</div>
</div>
<!-- /physical device -->
<!-- network list -->
<% if #wnets > 0 then %>
<% for i, net in ipairs(wnets) do local disabled = (dev:get("disabled") == "1" or net:get("disabled") == "1") %>
<div class="tr cbi-rowstyle-<%=1 + ((i-1) % 2)%>">
<div class="td col-2 center middle" id="<%=net:id()%>-iw-signal">
<span class="ifacebadge" title="<%:Not associated%>"><img src="<%=resource%>/icons/signal-<%= disabled and "none" or "0" %>.png" /> 0%</span>
</div>
<div class="td col-7 left middle" id="<%=net:id()%>-iw-status" data-network="<%=net:id()%>" data-disabled="<%= disabled and "true" or "false" %>">
<em><%= disabled and translate("Wireless is disabled") or translate("Collecting data...") %></em>
</div>
<div class="td middle cbi-section-actions">
<div>
<% if disabled then %>
<input name="cbid.wireless.<%=net:name()%>.__disable__" type="hidden" value="1" />
<input name="cbi.apply" type="submit" class="cbi-button cbi-button-neutral" title="<%:Enable this network%>" value="<%:Enable%>" onclick="this.previousElementSibling.value='0'" />
<% else %>
<input name="cbid.wireless.<%=net:name()%>.__disable__" type="hidden" value="0" />
<input name="cbi.apply" type="submit" class="cbi-button cbi-button-neutral" title="<%:Disable this network%>" value="<%:Disable%>" onclick="this.previousElementSibling.value='1'" />
<% end %>
<input type="button" class="cbi-button cbi-button-action important" onclick="location.href='<%=net:adminlink()%>'" title="<%:Edit this network%>" value="<%:Edit%>" />
<input name="cbid.wireless.<%=net:name()%>.__delete__" type="hidden" value="" />
<input name="cbi.apply" type="submit" class="cbi-button cbi-button-negative" title="<%:Delete this network%>" value="<%:Remove%>" onclick="wifi_delete(event)" />
</div>
</div>
</div>
<% end %>
<% else %>
<div class="tr placeholder">
<div class="td">
<em><%:No network configured on this device%></em>
</div>
</div>
<% end %>
<!-- /network list -->
</div>
</div>
]])
m = Map("wireless", translate("Wireless Overview"))
m:chain("network")
m.pageaction = false
if not has_iwinfo then
s = m:section(NamedSection, "__warning__")
function s.render(self)
tpl.render_string([[
<div class="alert-message warning">
<h4><%:Package libiwinfo required!%></h4>
<p><%_The <em>libiwinfo-lua</em> package is not installed. You must install this component for working wireless configuration!%></p>
</div>
]])
end
end
local _, dev, net
for _, dev in ipairs(ntm:get_wifidevs()) do
s = m:section(TypedSection)
s.wnets = dev:get_wifinets()
function s.render(self, sid)
tpl_radio:render({
hw = guess_wifi_hw(dev),
dev = dev,
wnets = self.wnets
})
end
function s.cfgsections(self)
local _, net, sl = nil, nil, { }
for _, net in ipairs(self.wnets) do
sl[#sl+1] = net:name()
self.wnets[net:name()] = net
end
return sl
end
o = s:option(Value, "__disable__")
function o.cfgvalue(self, sid)
local wnet = self.section.wnets[sid]
local wdev = wnet:get_device()
return ((wnet and wnet:get("disabled") == "1") or
(wdev and wdev:get("disabled") == "1")) and "1" or "0"
end
function o.write(self, sid, value)
local wnet = self.section.wnets[sid]
local wdev = wnet:get_device()
if value ~= "1" then
wnet:set("disabled", nil)
wdev:set("disabled", nil)
else
wnet:set("disabled", "1")
end
end
o.remove = o.write
o = s:option(Value, "__delete__")
function o.write(self, sid, value)
local wnet = self.section.wnets[sid]
local nets = wnet:get_networks()
ntm:del_wifinet(wnet:id())
local _, net
for _, net in ipairs(nets) do
if net:is_empty() then
ntm:del_network(net:name())
end
end
end
end
s = m:section(NamedSection, "__script__")
s.template = "admin_network/wifi_overview_status"
s = m:section(NamedSection, "__assoclist__")
function s.render(self, sid)
tpl.render_string([[
<h2><%:Associated Stations%></h2>
<%+admin_network/wifi_assoclist%>
]])
end
return m

View file

@ -1,34 +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.
f = SimpleForm("processes", translate("Processes"), translate("This list gives an overview over currently running system processes and their status."))
f.reset = false
f.submit = false
t = f:section(Table, luci.sys.process.list())
t:option(DummyValue, "PID", translate("PID"))
t:option(DummyValue, "USER", translate("Owner"))
t:option(DummyValue, "COMMAND", translate("Command"))
t:option(DummyValue, "%CPU", translate("CPU usage (%)"))
t:option(DummyValue, "%MEM", translate("Memory usage (%)"))
hup = t:option(Button, "_hup", translate("Hang Up"))
hup.inputstyle = "reload"
function hup.write(self, section)
null, self.tag_error[section] = luci.sys.process.signal(section, 1)
end
term = t:option(Button, "_term", translate("Terminate"))
term.inputstyle = "remove"
function term.write(self, section)
null, self.tag_error[section] = luci.sys.process.signal(section, 15)
end
kill = t:option(Button, "_kill", translate("Kill"))
kill.inputstyle = "reset"
function kill.write(self, section)
null, self.tag_error[section] = luci.sys.process.signal(section, 9)
end
return f

View file

@ -1,122 +0,0 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
local fs = require "nixio.fs"
m = Map("system", translate("Router Password"),
translate("Changes the administrator password for accessing the device"))
s = m:section(TypedSection, "_dummy", "")
s.addremove = false
s.anonymous = true
pw1 = s:option(Value, "pw1", translate("Password"))
pw1.password = true
pw2 = s:option(Value, "pw2", translate("Confirmation"))
pw2.password = true
function s.cfgsections()
return { "_pass" }
end
function m.parse(map)
local v1 = pw1:formvalue("_pass")
local v2 = pw2:formvalue("_pass")
if v1 and v2 and #v1 > 0 and #v2 > 0 then
if v1 == v2 then
if luci.sys.user.setpasswd(luci.dispatcher.context.authuser, v1) == 0 then
m.message = translate("Password successfully changed!")
else
m.message = translate("Unknown Error, password not changed!")
end
else
m.message = translate("Given password confirmation did not match, password not changed!")
end
end
Map.parse(map)
end
if fs.access("/etc/config/dropbear") then
m2 = Map("dropbear", translate("SSH Access"),
translate("Dropbear offers <abbr title=\"Secure Shell\">SSH</abbr> network shell access and an integrated <abbr title=\"Secure Copy\">SCP</abbr> server"))
s = m2:section(TypedSection, "dropbear", translate("Dropbear Instance"))
s.anonymous = true
s.addremove = true
ni = s:option(Value, "Interface", translate("Interface"),
translate("Listen only on the given interface or, if unspecified, on all"))
ni.template = "cbi/network_netlist"
ni.nocreate = true
ni.unspecified = true
pt = s:option(Value, "Port", translate("Port"),
translate("Specifies the listening port of this <em>Dropbear</em> instance"))
pt.datatype = "port"
pt.default = 22
pa = s:option(Flag, "PasswordAuth", translate("Password authentication"),
translate("Allow <abbr title=\"Secure Shell\">SSH</abbr> password authentication"))
pa.enabled = "on"
pa.disabled = "off"
pa.default = pa.enabled
pa.rmempty = false
ra = s:option(Flag, "RootPasswordAuth", translate("Allow root logins with password"),
translate("Allow the <em>root</em> user to login with password"))
ra.enabled = "on"
ra.disabled = "off"
ra.default = ra.enabled
gp = s:option(Flag, "GatewayPorts", translate("Gateway ports"),
translate("Allow remote hosts to connect to local SSH forwarded ports"))
gp.enabled = "on"
gp.disabled = "off"
gp.default = gp.disabled
s2 = m2:section(TypedSection, "_dummy", translate("SSH-Keys"),
translate("Here you can paste public SSH-Keys (one per line) for SSH public-key authentication."))
s2.addremove = false
s2.anonymous = true
s2.template = "cbi/tblsection"
function s2.cfgsections()
return { "_keys" }
end
keys = s2:option(TextValue, "_data", "")
keys.wrap = "off"
keys.rows = 3
function keys.cfgvalue()
return fs.readfile("/etc/dropbear/authorized_keys") or ""
end
function keys.write(self, section, value)
return fs.writefile("/etc/dropbear/authorized_keys", value:gsub("\r\n", "\n"))
end
function keys.remove(self, section, value)
return fs.writefile("/etc/dropbear/authorized_keys", "")
end
end
return m, m2

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