diff options
Diffstat (limited to 'modules')
10 files changed, 330 insertions, 60 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/firewall.js b/modules/luci-base/htdocs/luci-static/resources/firewall.js index d034d6e010..9ae14e16d9 100644 --- a/modules/luci-base/htdocs/luci-static/resources/firewall.js +++ b/modules/luci-base/htdocs/luci-static/resources/firewall.js @@ -375,6 +375,14 @@ Zone = AbstractFirewallItem.extend({ this.set('network', ' '); }, + getDevices: function() { + return L.toArray(this.get('device')); + }, + + getSubnets: function() { + return L.toArray(this.get('subnet')); + }, + getForwardingsBy: function(what) { var sections = uci.sections('firewall', 'forwarding'), forwards = []; diff --git a/modules/luci-base/htdocs/luci-static/resources/form.js b/modules/luci-base/htdocs/luci-static/resources/form.js index 508e2c4857..58d8f7100c 100644 --- a/modules/luci-base/htdocs/luci-static/resources/form.js +++ b/modules/luci-base/htdocs/luci-static/resources/form.js @@ -1461,6 +1461,7 @@ var CBIListValue = CBIValue.extend({ size: this.size, sort: this.keylist, optional: this.rmempty || this.optional, + placeholder: this.placeholder, validate: L.bind(this.validate, this, section_id) }); diff --git a/modules/luci-base/htdocs/luci-static/resources/tools/widgets.js b/modules/luci-base/htdocs/luci-static/resources/tools/widgets.js index 7f2997f173..39e5aa1655 100644 --- a/modules/luci-base/htdocs/luci-static/resources/tools/widgets.js +++ b/modules/luci-base/htdocs/luci-static/resources/tools/widgets.js @@ -30,8 +30,22 @@ var CBIZoneSelect = form.ListValue.extend({ renderWidget: function(section_id, option_index, cfgvalue) { var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), + isOutputOnly = false, choices = {}; + if (this.option == 'dest') { + for (var i = 0; i < this.section.children.length; i++) { + var opt = this.section.children[i]; + if (opt.option == 'src') { + var val = opt.cfgvalue(section_id) || opt.default; + isOutputOnly = (val == null || val == ''); + break; + } + } + + this.title = isOutputOnly ? _('Output zone') : _('Destination zone'); + } + if (this.allowlocal) { choices[''] = E('span', { 'class': 'zonebadge', @@ -39,7 +53,7 @@ var CBIZoneSelect = form.ListValue.extend({ }, [ E('strong', _('Device')), (this.allowany || this.allowlocal) - ? ' (%s)'.format(this.alias != 'dest' ? _('output') : _('input')) : '' + ? ' (%s)'.format(this.option != 'dest' ? _('output') : _('input')) : '' ]); } else if (!this.multiple && (this.rmempty || this.optional)) { @@ -55,7 +69,7 @@ var CBIZoneSelect = form.ListValue.extend({ 'style': 'background-color:' + firewall.getColorForName(null) }, [ E('strong', _('Any zone')), - (this.allowany && this.allowlocal) ? ' (%s)'.format(_('forward')) : '' + (this.allowany && this.allowlocal && !isOutputOnly) ? ' (%s)'.format(_('forward')) : '' ]); } @@ -120,7 +134,64 @@ var CBIZoneSelect = form.ListValue.extend({ '</li>' }); - return widget.render(); + var elem = widget.render(); + + if (this.option == 'src') { + elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) { + var opt = this.map.lookupOption('dest', section_id), + val = ev.detail.instance.getValue(); + + if (opt == null) + return; + + var cbid = opt[0].cbid(section_id), + label = document.querySelector('label[for="widget.%s"]'.format(cbid)), + node = document.getElementById(cbid); + + L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone')); + + if (val == '') { + if (L.dom.callClassMethod(node, 'getValue') == '') + L.dom.callClassMethod(node, 'setValue', '*'); + + var emptyval = node.querySelector('[data-value=""]'), + anyval = node.querySelector('[data-value="*"]'); + + L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone'))); + + if (emptyval != null) + emptyval.parentNode.removeChild(emptyval); + } + else { + var anyval = node.querySelector('[data-value="*"]'), + emptyval = node.querySelector('[data-value=""]'); + + if (emptyval == null) { + emptyval = anyval.cloneNode(true); + emptyval.removeAttribute('display'); + emptyval.removeAttribute('selected'); + emptyval.setAttribute('data-value', ''); + } + + L.dom.content(emptyval.querySelector('span'), [ + E('strong', _('Device')), ' (%s)'.format(_('input')) + ]); + + L.dom.content(anyval.querySelector('span'), [ + E('strong', _('Any zone')), ' (%s)'.format(_('forward')) + ]); + + anyval.parentNode.insertBefore(emptyval, anyval); + } + + }, this)); + } + else if (isOutputOnly) { + var emptyval = elem.querySelector('[data-value=""]'); + emptyval.parentNode.removeChild(emptyval); + } + + return elem; }, }); @@ -128,10 +199,16 @@ var CBIZoneForwards = form.DummyValue.extend({ __name__: 'CBI.ZoneForwards', load: function(section_id) { - return Promise.all([ firewall.getDefaults(), firewall.getZones(), network.getNetworks() ]).then(L.bind(function(dzn) { - this.defaults = dzn[0]; - this.zones = dzn[1]; - this.networks = dzn[2]; + return Promise.all([ + firewall.getDefaults(), + firewall.getZones(), + network.getNetworks(), + network.getDevices() + ]).then(L.bind(function(dznd) { + this.defaults = dznd[0]; + this.zones = dznd[1]; + this.networks = dznd[2]; + this.devices = dznd[3]; return this.super('load', section_id); }, this)); @@ -140,6 +217,8 @@ var CBIZoneForwards = form.DummyValue.extend({ renderZone: function(zone) { var name = zone.getName(), networks = zone.getNetworks(), + devices = zone.getDevices(), + subnets = zone.getSubnets(), ifaces = []; for (var j = 0; j < networks.length; j++) { @@ -152,21 +231,39 @@ var CBIZoneForwards = form.DummyValue.extend({ 'class': 'ifacebadge' + (network.getName() == this.network ? ' ifacebadge-active' : '') }, network.getName() + ': '); - var devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice()); + var subdevs = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice()); - for (var k = 0; k < devices.length && devices[k]; k++) { + for (var k = 0; k < subdevs.length && subdevs[k]; k++) { span.appendChild(E('img', { - 'title': devices[k].getI18n(), - 'src': L.resource('icons/%s%s.png'.format(devices[k].getType(), devices[k].isUp() ? '' : '_disabled')) + 'title': subdevs[k].getI18n(), + 'src': L.resource('icons/%s%s.png'.format(subdevs[k].getType(), subdevs[k].isUp() ? '' : '_disabled')) })); } - if (!devices.length) + if (!subdevs.length) span.appendChild(E('em', _('(empty)'))); ifaces.push(span); } + for (var i = 0; i < devices.length; i++) { + var device = this.devices.filter(function(dev) { return dev.getName() == devices[i] })[0], + title = device ? device.getI18n() : _('Absent Interface'), + type = device ? device.getType() : 'ethernet', + up = device ? device.isUp() : false; + + ifaces.push(E('span', { 'class': 'ifacebadge' }, [ + E('img', { + 'title': title, + 'src': L.resource('icons/%s%s.png'.format(type, up ? '' : '_disabled')) + }), + device ? device.getName() : devices[i] + ])); + } + + if (subnets.length > 0) + ifaces.push(E('span', { 'class': 'ifacebadge' }, [ '{ %s }'.format(subnets.join('; ')) ])); + if (!ifaces.length) ifaces.push(E('span', { 'class': 'ifacebadge' }, E('em', _('(empty)')))); @@ -319,9 +416,120 @@ var CBINetworkSelect = form.ListValue.extend({ }, }); +var CBIDeviceSelect = form.ListValue.extend({ + __name__: 'CBI.DeviceSelect', + + load: function(section_id) { + return network.getDevices().then(L.bind(function(devices) { + this.devices = devices; + + return this.super('load', section_id); + }, this)); + }, + + filter: function(section_id, value) { + return true; + }, + + renderWidget: function(section_id, option_index, cfgvalue) { + var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), + choices = {}, + checked = {}, + order = []; + + for (var i = 0; i < values.length; i++) + checked[values[i]] = true; + + values = []; + + if (!this.multiple && (this.rmempty || this.optional)) + choices[''] = E('em', _('unspecified')); + + for (var i = 0; i < this.devices.length; i++) { + var device = this.devices[i], + name = device.getName(), + type = device.getType(); + + if (name == 'lo' || name == this.exclude || !this.filter(section_id, name)) + continue; + + if (this.noaliases && type == 'alias') + continue; + + if (this.nobridges && type == 'bridge') + continue; + + if (this.noinactive && device.isUp() == false) + continue; + + var item = E([ + E('img', { + 'title': device.getI18n(), + 'src': L.resource('icons/%s%s.png'.format(type, device.isUp() ? '' : '_disabled')) + }), + E('span', { 'class': 'hide-open' }, [ name ]), + E('span', { 'class': 'hide-close'}, [ device.getI18n() ]) + ]); + + var networks = device.getNetworks(); + + if (networks.length > 0) + L.dom.append(item.lastChild, [ ' (', networks.join(', '), ')' ]); + + if (checked[name]) + values.push(name); + + choices[name] = item; + order.push(name); + } + + if (!this.nocreate) { + var keys = Object.keys(checked).sort(); + + for (var i = 0; i < keys.length; i++) { + if (choices.hasOwnProperty(keys[i])) + continue; + + choices[keys[i]] = E([ + E('img', { + 'title': _('Absent Interface'), + 'src': L.resource('icons/ethernet_disabled.png') + }), + E('span', { 'class': 'hide-open' }, [ keys[i] ]), + E('span', { 'class': 'hide-close'}, [ '%s: "%h"'.format(_('Absent Interface'), keys[i]) ]) + ]); + + values.push(keys[i]); + order.push(keys[i]); + } + } + + var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, { + id: this.cbid(section_id), + sort: order, + multiple: this.multiple, + optional: this.optional || this.rmempty, + select_placeholder: E('em', _('unspecified')), + display_items: this.display_size || this.size || 3, + dropdown_items: this.dropdown_size || this.size || 5, + validate: L.bind(this.validate, this, section_id), + create: !this.nocreate, + create_markup: '' + + '<li data-value="{{value}}">' + + '<img title="'+_('Custom Interface')+': "{{value}}"" src="'+L.resource('icons/ethernet_disabled.png')+'" />' + + '<span class="hide-open">{{value}}</span>' + + '<span class="hide-close">'+_('Custom Interface')+': "{{value}}"</span>' + + '</li>' + }); + + return widget.render(); + }, +}); + return L.Class.extend({ ZoneSelect: CBIZoneSelect, ZoneForwards: CBIZoneForwards, - NetworkSelect: CBINetworkSelect + NetworkSelect: CBINetworkSelect, + DeviceSelect: CBIDeviceSelect, }); diff --git a/modules/luci-base/htdocs/luci-static/resources/ui.js b/modules/luci-base/htdocs/luci-static/resources/ui.js index 9c16a9a0dd..43afc698f6 100644 --- a/modules/luci-base/htdocs/luci-static/resources/ui.js +++ b/modules/luci-base/htdocs/luci-static/resources/ui.js @@ -287,8 +287,8 @@ var UISelect = UIElement.extend({ this.node = frameEl; if (this.options.widget == 'select') { - this.setUpdateEvents(frameEl, 'change', 'click', 'blur'); - this.setChangeEvents(frameEl, 'change'); + this.setUpdateEvents(frameEl.firstChild, 'change', 'click', 'blur'); + this.setChangeEvents(frameEl.firstChild, 'change'); } else { var radioEls = frameEl.querySelectorAll('input[type="radio"]'); @@ -879,7 +879,7 @@ var UIDropdown = UIElement.extend({ else markup = '<li data-value="{{value}}">{{value}}</li>'; - new_item = E(markup.replace(/{{value}}/g, item)); + new_item = E(markup.replace(/{{value}}/g, '%h'.format(item))); if (sbox.options.multiple) { sbox.transformItem(sb, new_item); @@ -1797,7 +1797,7 @@ return L.Class.extend({ return chg[1]; case 4: - return "'" + chg[3].replace(/'/g, "'\"'\"'") + "'"; + return "'%h'".format(chg[3].replace(/'/g, "'\"'\"'")); default: return chg[m1-1]; @@ -1929,7 +1929,7 @@ return L.Class.extend({ method: 'post', timeout: L.env.apply_timeout * 1000, query: L.ui.changes.confirm_auth - }).then(call); + }).then(call, call); }, delay); }; diff --git a/modules/luci-base/po/ru/base.po b/modules/luci-base/po/ru/base.po index f47419134f..d4b5a82211 100644 --- a/modules/luci-base/po/ru/base.po +++ b/modules/luci-base/po/ru/base.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: LuCI: base\n" "POT-Creation-Date: 2010-05-09 01:01+0300\n" -"PO-Revision-Date: 2019-07-15 15:36+0300\n" +"PO-Revision-Date: 2019-07-20 15:31+0300\n" "Last-Translator: Anton Kikin <a.kikin@tano-systems.com>\n" "Language-Team: http://cyber-place.ru\n" "Language: ru\n" @@ -3489,7 +3489,7 @@ msgstr "Не задано имя сети" #: modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js:147 msgid "No networks in range" -msgstr "" +msgstr "Нет сетей в радиусе действия" #: themes/luci-theme-bootstrap/luasrc/view/themes/bootstrap/header.htm:173 #: themes/luci-theme-material/luasrc/view/themes/material/header.htm:211 diff --git a/modules/luci-base/po/uk/base.po b/modules/luci-base/po/uk/base.po index 60f42b07ee..724ab4ccc5 100644 --- a/modules/luci-base/po/uk/base.po +++ b/modules/luci-base/po/uk/base.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"PO-Revision-Date: 2019-07-16 20:35+0300\n" +"PO-Revision-Date: 2019-07-21 13:22+0300\n" "Last-Translator: Yurii <yuripet@gmail.com>\n" "Language-Team: none\n" "Language: uk\n" @@ -3516,7 +3516,7 @@ msgstr "Ім'я мережі не визначено" #: modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js:147 msgid "No networks in range" -msgstr "" +msgstr "Немає мереж у межах досяжності" #: themes/luci-theme-bootstrap/luasrc/view/themes/bootstrap/header.htm:173 #: themes/luci-theme-material/luasrc/view/themes/material/header.htm:211 diff --git a/modules/luci-base/root/usr/libexec/rpcd/luci b/modules/luci-base/root/usr/libexec/rpcd/luci index 55233d6d0a..7644745efd 100755 --- a/modules/luci-base/root/usr/libexec/rpcd/luci +++ b/modules/luci-base/root/usr/libexec/rpcd/luci @@ -285,6 +285,41 @@ local methods = { local fs = require "nixio.fs" return { offload_support = not not fs.access("/sys/module/xt_FLOWOFFLOAD/refcnt") } end + }, + + conntrack_helpers = { + call = function() + local fd = io.open("/usr/share/fw3/helpers.conf", "r") + local rv = {} + + local line, entry + while true do + line = fd:read("*l") + if not line then + break + end + + if line:match("^%s*config%s") then + if entry then + rv[#rv+1] = entry + end + entry = {} + else + local opt, val = line:match("^%s*option%s+(%S+)%s+(%S.*)$") + if opt and val then + opt = opt:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1") + val = val:gsub("^'(.+)'$", "%1"):gsub('^"(.+)"$', "%1") + entry[opt] = val + end + end + end + + if entry then + rv[#rv+1] = entry + end + + return { helpers = rv } + end } } diff --git a/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json b/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json index a9baef8f9c..de145ce784 100644 --- a/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json +++ b/modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json @@ -13,7 +13,7 @@ "read": { "ubus": { "iwinfo": [ "info" ], - "luci": [ "boardjson", "duid_hints", "host_hints", "ifaddrs", "initList", "getLocaltime", "leases", "leds", "netdevs", "offload_support", "usb" ], + "luci": [ "boardjson", "duid_hints", "host_hints", "ifaddrs", "initList", "getLocaltime", "leases", "leds", "netdevs", "usb" ], "network.device": [ "status" ], "network.interface": [ "dump" ], "network.wireless": [ "status" ], @@ -28,5 +28,17 @@ }, "uci": [ "*" ] } + }, + "luci-app-firewall": { + "description": "Grant access to firewall procedures", + "read": { + "ubus": { + "luci": [ "conntrack_helpers", "offload_support" ] + }, + "uci": [ "firewall" ] + }, + "write": { + "uci": [ "firewall" ] + } } } diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js index af926ab4a2..f30e47ec7c 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wifi_join.js @@ -144,7 +144,7 @@ function scan() { ]); }); - cbi_update_table(tbl, bss, E('em' {}, _('No networks in range'))); + cbi_update_table(tbl, bss, E('em', {}, _('No networks in range'))); } else { cbi_update_table(tbl, [], E('em', { class: 'spinning' }, _('No scan results available yet...'))); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js index adcf4a4f8b..c1109b5d64 100644 --- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js @@ -72,8 +72,10 @@ return L.view.extend({ trigger = s.option(form.ListValue, 'trigger', _('Trigger')); Object.keys(triggers).sort().forEach(function(t) { trigger.value(t, t.replace(/-/g, '')) }); - trigger.value('usbdev'); - trigger.value('usbport'); + if (usb.devices && usb.devices.length) + trigger.value('usbdev'); + if (usb.ports && usb.ports.length) + trigger.value('usbport'); o = s.option(form.Value, 'delayon', _('On-State Delay')); o.depends('trigger', 'timer'); @@ -100,42 +102,46 @@ return L.view.extend({ o.value('tx', _('Transmit')); o.value('rx', _('Receive')); - o = s.option(form.ListValue, '_usb_dev', _('USB Device')); - o.depends('trigger', 'usbdev'); - o.rmempty = true; - o.ucioption = 'dev'; - o.remove = function(section_id) { - var t = trigger.formvalue(section_id); - if (t != 'netdev' && t != 'usbdev') - uci.unset('system', section_id, 'dev'); + if (usb.devices && usb.devices.length) { + o = s.option(form.ListValue, '_usb_dev', _('USB Device')); + o.depends('trigger', 'usbdev'); + o.rmempty = true; + o.ucioption = 'dev'; + o.remove = function(section_id) { + var t = trigger.formvalue(section_id); + if (t != 'netdev' && t != 'usbdev') + uci.unset('system', section_id, 'dev'); + } + o.value(''); + usb.devices.forEach(function(usbdev) { + o.value(usbdev.id, '%s (%s - %s)'.format(usbdev.id, usbdev.vendor || '?', usbdev.product || '?')); + }); } - o.value(''); - (usb.devices || []).forEach(function(usbdev) { - o.value(usbdev.id, '%s (%s - %s)'.format(usbdev.id, usbdev.vendor || '?', usbdev.product || '?')); - }); - o = s.option(form.MultiValue, 'port', _('USB Ports')); - o.depends('trigger', 'usbport'); - o.rmempty = true; - o.cfgvalue = function(section_id) { - var ports = [], - value = uci.get('system', section_id, 'port'); - - if (!Array.isArray(value)) - value = String(value || '').split(/\s+/); - - for (var i = 0; i < value.length; i++) - if (value[i].match(/^usb(\d+)-port(\d+)$/)) - ports.push(value[i]); - else if (value[i].match(/^(\d+)-(\d+)$/)) - ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2)); - - return ports; - }; - (usb.ports || []).forEach(function(usbport) { - o.value('usb%d-port%d'.format(usbport.hub, usbport.port), - 'Hub %d, Port %d'.format(usbport.hub, usbport.port)); - }); + if (usb.ports && usb.ports.length) { + o = s.option(form.MultiValue, 'port', _('USB Ports')); + o.depends('trigger', 'usbport'); + o.rmempty = true; + o.cfgvalue = function(section_id) { + var ports = [], + value = uci.get('system', section_id, 'port'); + + if (!Array.isArray(value)) + value = String(value || '').split(/\s+/); + + for (var i = 0; i < value.length; i++) + if (value[i].match(/^usb(\d+)-port(\d+)$/)) + ports.push(value[i]); + else if (value[i].match(/^(\d+)-(\d+)$/)) + ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2)); + + return ports; + }; + usb.ports.forEach(function(usbport) { + o.value('usb%d-port%d'.format(usbport.hub, usbport.port), + 'Hub %d, Port %d'.format(usbport.hub, usbport.port)); + }); + } o = s.option(form.Value, 'port_mask', _('Switch Port Mask')); o.depends('trigger', 'switch0'); |