Merge pull request #2983 from dibdot/adblock
authorDirk Brenken <dev@brenken.org>
Fri, 16 Aug 2019 04:15:32 +0000 (06:15 +0200)
committerGitHub <noreply@github.com>
Fri, 16 Aug 2019 04:15:32 +0000 (06:15 +0200)
luci-app-adblock: sync with adblock 3.8.0

17 files changed:
applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/forwards.js
applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js
applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/zones.js
applications/luci-app-statistics/luasrc/statistics/rrdtool/definitions/cpufreq.lua
modules/luci-base/htdocs/luci-static/resources/firewall.js
modules/luci-base/htdocs/luci-static/resources/form.js
modules/luci-base/htdocs/luci-static/resources/luci.js
modules/luci-base/htdocs/luci-static/resources/network.js
modules/luci-base/htdocs/luci-static/resources/tools/widgets.js
modules/luci-base/htdocs/luci-static/resources/ui.js
modules/luci-base/luasrc/controller/admin/index.lua
modules/luci-base/root/usr/libexec/rpcd/luci
modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json
modules/luci-mod-network/htdocs/luci-static/resources/view/network/dhcp.js
modules/luci-mod-network/htdocs/luci-static/resources/view/network/hosts.js
modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js
modules/luci-mod-system/htdocs/luci-static/resources/view/system/system.js

index 63af69f8a939609c38c360300c6ae3ee252ad39e..80938711e9d416f121cd7f0ea8b886597652c3d7 100644 (file)
@@ -75,7 +75,8 @@ function forward_via_txt(s) {
 return L.view.extend({
        callHostHints: rpc.declare({
                object: 'luci',
-               method: 'host_hints'
+               method: 'getHostHints',
+               expect: { '': {} }
        }),
 
        load: function() {
index a7924b10769c6f9da0b9ce304debf08c27b3dbd6..6df3bc7f8550bf425674ee44f1bb8b82d168504e 100644 (file)
@@ -112,7 +112,8 @@ function rule_target_txt(s) {
 return L.view.extend({
        callHostHints: rpc.declare({
                object: 'luci',
-               method: 'host_hints'
+               method: 'getHostHints',
+               expect: { '': {} }
        }),
 
        load: function() {
index 4d13752b3a2e17485b0effecff3ddce52b1a4dce..62b792da1f860ac44d091648f38242cea423434e 100644 (file)
@@ -7,28 +7,22 @@
 'require tools.widgets as widgets';
 
 return L.view.extend({
-       callOffloadSupport: rpc.declare({
-               object: 'luci',
-               method: 'offload_support',
-               expect: { offload_support: false }
-       }),
-
        callConntrackHelpers: rpc.declare({
                object: 'luci',
-               method: 'conntrack_helpers',
-               expect: { helpers: [] }
+               method: 'getConntrackHelpers',
+               expect: { result: [] }
        }),
 
        load: function() {
                return Promise.all([
-                       this.callOffloadSupport(),
-                       this.callConntrackHelpers()
+                       this.callConntrackHelpers(),
+                       firewall.getDefaults()
                ]);
        },
 
        render: function(data) {
-               var hasOffloading = data[0],
-                   ctHelpers = data[1],
+               var ctHelpers = data[0],
+                   fwDefaults = data[1],
                    m, s, o, inp, out;
 
                m = new form.Map('firewall', _('Firewall - Zone Settings'),
@@ -55,7 +49,7 @@ return L.view.extend({
 
                /* Netfilter flow offload support */
 
-               if (hasOffloading) {
+               if (L.hasSystemFeature('offloading')) {
                        s = m.section(form.TypedSection, 'defaults', _('Routing/NAT Offloading'),
                                _('Experimental feature. Not fully compatible with QoS/SQM.'));
 
@@ -126,6 +120,10 @@ return L.view.extend({
                        p[i].editable = true;
                }
 
+               p[0].default = fwDefaults.getInput();
+               p[1].default = fwDefaults.getOutput();
+               p[2].default = fwDefaults.getForward();
+
                o = s.taboption('general', form.Flag, 'masq', _('Masquerading'));
                o.editable = true;
 
index d3596637b22330223f4abf5ea138527b30ccd48b..cb7ae53afa6c76c20bb699ccf90ba24a3458e089 100644 (file)
@@ -48,12 +48,12 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
                data = {
                        types = { "percent" },
                        options = {
-                               percent = { title = "%di Hz", negweight = true },
+                               percent = { title = "%di kHz", negweight = true },
                        }
                }
        }
 
-       return { cpufreq, transitions, percentage }
+       return { cpufreq, percentage, transitions }
     else
        return { cpufreq }
     end
index 9ae14e16d9910d77c4c9e58d71698a2620f74ac9..8f6be1813dc85c50a8791a0afe75f2c20fe52f75 100644 (file)
@@ -99,7 +99,7 @@ Firewall = L.Class.extend({
                        if (name == null || !/^[a-zA-Z0-9_]+$/.test(name))
                                return null;
 
-                       if (this.getZone(name) != null)
+                       if (lookupZone(name) != null)
                                return null;
 
                        var d = new Defaults(),
index ab0998943ce6d3351496fb5511baee352870337e..44a2df22f17d13c938748b0a84e01bc93b65ddab 100644 (file)
@@ -57,6 +57,23 @@ var CBINode = Class.extend({
 
                var x = E('div', {}, s);
                return x.textContent || x.innerText || '';
+       },
+
+       titleFn: function(attr /*, ... */) {
+               var s = null;
+
+               if (typeof(this[attr]) == 'function')
+                       s = this[attr].apply(this, this.varargs(arguments, 1));
+               else if (typeof(this[attr]) == 'string')
+                       s = (arguments.length > 1) ? ''.format.apply(this[attr], this.varargs(arguments, 1)) : this[attr];
+
+               if (s != null)
+                       s = this.stripTags(String(s)).trim();
+
+               if (s == null || s == '')
+                       return null;
+
+               return s;
        }
 });
 
@@ -115,10 +132,11 @@ var CBIMap = CBINode.extend({
                return Promise.all(tasks);
        },
 
-       save: function() {
+       save: function(cb) {
                this.checkDepends();
 
                return this.parse()
+                       .then(cb)
                        .then(uci.save.bind(uci))
                        .then(this.load.bind(this))
                        .then(this.renderContents.bind(this))
@@ -156,7 +174,10 @@ var CBIMap = CBINode.extend({
                        if (this.description != null && this.description != '')
                                mapEl.appendChild(E('div', { 'class': 'cbi-map-descr' }, this.description));
 
-                       L.dom.append(mapEl, nodes);
+                       if (this.tabbed)
+                               L.dom.append(mapEl, E('div', { 'class': 'cbi-map-tabbed' }, nodes));
+                       else
+                               L.dom.append(mapEl, nodes);
 
                        if (!initialRender) {
                                mapEl.classList.remove('flash');
@@ -168,17 +189,22 @@ var CBIMap = CBINode.extend({
 
                        this.checkDepends();
 
+                       var tabGroups = mapEl.querySelectorAll('.cbi-map-tabbed, .cbi-section-node-tabbed');
+
+                       for (var i = 0; i < tabGroups.length; i++)
+                               ui.tabs.initTabGroup(tabGroups[i].childNodes);
+
                        return mapEl;
                }, this));
        },
 
-       lookupOption: function(name, section_id) {
+       lookupOption: function(name, section_id, config_name) {
                var id, elem, sid, inst;
 
                if (name.indexOf('.') > -1)
                        id = 'cbid.%s'.format(name);
                else
-                       id = 'cbid.%s.%s.%s'.format(this.config, section_id, name);
+                       id = 'cbid.%s.%s.%s'.format(config_name || this.config, section_id, name);
 
                elem = this.findElement('data-field', id);
                sid  = elem ? id.split(/\./)[2] : null;
@@ -437,7 +463,11 @@ var CBIAbstractValue = CBINode.extend({
                                                else if (k.indexOf('.') !== -1)
                                                        dep['cbid.%s'.format(k)] = list[i][k];
                                                else
-                                                       dep['cbid.%s.%s.%s'.format(this.config, this.ucisection || section_id, k)] = list[i][k];
+                                                       dep['cbid.%s.%s.%s'.format(
+                                                               this.uciconfig || this.section.uciconfig || this.map.config,
+                                                               this.ucisection || section_id,
+                                                               k
+                                                       )] = list[i][k];
                                        }
                                }
 
@@ -484,7 +514,8 @@ var CBIAbstractValue = CBINode.extend({
                                        istat = false;
                                }
                                else {
-                                       var res = this.map.lookupOption(dep, section_id),
+                                       var conf = this.uciconfig || this.section.uciconfig || this.map.config,
+                                           res = this.map.lookupOption(dep, section_id, conf),
                                            val = res ? res[0].formvalue(res[1]) : null;
 
                                        istat = (istat && isEqual(val, this.deps[i][dep]));
@@ -502,7 +533,9 @@ var CBIAbstractValue = CBINode.extend({
                if (section_id == null)
                        L.error('TypeError', 'Section ID required');
 
-               return 'cbid.%s.%s.%s'.format(this.map.config, section_id, this.option);
+               return 'cbid.%s.%s.%s'.format(
+                       this.uciconfig || this.section.uciconfig || this.map.config,
+                       section_id, this.option);
        },
 
        load: function(section_id) {
@@ -510,7 +543,7 @@ var CBIAbstractValue = CBINode.extend({
                        L.error('TypeError', 'Section ID required');
 
                return uci.get(
-                       this.uciconfig || this.map.config,
+                       this.uciconfig || this.section.uciconfig || this.map.config,
                        this.ucisection || section_id,
                        this.ucioption || this.option);
        },
@@ -598,7 +631,7 @@ var CBIAbstractValue = CBINode.extend({
 
        write: function(section_id, formvalue) {
                return uci.set(
-                       this.uciconfig || this.map.config,
+                       this.uciconfig || this.section.uciconfig || this.map.config,
                        this.ucisection || section_id,
                        this.ucioption || this.option,
                        formvalue);
@@ -606,7 +639,7 @@ var CBIAbstractValue = CBINode.extend({
 
        remove: function(section_id) {
                return uci.unset(
-                       this.uciconfig || this.map.config,
+                       this.uciconfig || this.section.uciconfig || this.map.config,
                        this.ucisection || section_id,
                        this.ucioption || this.option);
        }
@@ -640,7 +673,8 @@ var CBITypedSection = CBIAbstractSection.extend({
                        return E([]);
 
                var createEl = E('div', { 'class': 'cbi-section-create' }),
-                   config_name = this.uciconfig || this.map.config;
+                   config_name = this.uciconfig || this.map.config,
+                   btn_title = this.titleFn('addbtntitle');
 
                if (extra_class != null)
                        createEl.classList.add(extra_class);
@@ -649,8 +683,8 @@ var CBITypedSection = CBIAbstractSection.extend({
                        createEl.appendChild(E('input', {
                                'type': 'submit',
                                'class': 'cbi-button cbi-button-add',
-                               'value': _('Add'),
-                               'title': _('Add'),
+                               'value': btn_title || _('Add'),
+                               'title': btn_title || _('Add'),
                                'click': L.bind(this.handleAdd, this)
                        }));
                }
@@ -665,8 +699,8 @@ var CBITypedSection = CBIAbstractSection.extend({
                                E('input', {
                                        'class': 'cbi-button cbi-button-add',
                                        'type': 'submit',
-                                       'value': _('Add'),
-                                       'title': _('Add'),
+                                       'value': btn_title || _('Add'),
+                                       'title': btn_title || _('Add'),
                                        'click': L.bind(function(ev) {
                                                if (nameEl.classList.contains('cbi-input-invalid'))
                                                        return;
@@ -682,12 +716,21 @@ var CBITypedSection = CBIAbstractSection.extend({
                return createEl;
        },
 
+       renderSectionPlaceholder: function() {
+               return E([
+                       E('em', _('This section contains no values yet')),
+                       E('br'), E('br')
+               ]);
+       },
+
        renderContents: function(cfgsections, nodes) {
                var section_id = null,
                    config_name = this.uciconfig || this.map.config,
                    sectionEl = E('div', {
                                'id': 'cbi-%s-%s'.format(config_name, this.sectiontype),
-                               'class': 'cbi-section'
+                               'class': 'cbi-section',
+                               'data-tab': this.map.tabbed ? this.sectiontype : null,
+                               'data-tab-title': this.map.tabbed ? this.title || this.sectiontype : null
                        });
 
                if (this.title != null && this.title != '')
@@ -716,18 +759,13 @@ var CBITypedSection = CBIAbstractSection.extend({
                        sectionEl.appendChild(E('div', {
                                'id': 'cbi-%s-%s'.format(config_name, cfgsections[i]),
                                'class': this.tabs
-                                       ? 'cbi-section-node cbi-section-node-tabbed' : 'cbi-section-node'
+                                       ? 'cbi-section-node cbi-section-node-tabbed' : 'cbi-section-node',
+                               'data-section-id': cfgsections[i]
                        }, nodes[i]));
-
-                       if (this.tabs)
-                               ui.tabs.initTabGroup(sectionEl.lastChild.childNodes);
                }
 
                if (nodes.length == 0)
-                       L.dom.append(sectionEl, [
-                               E('em', _('This section contains no values yet')),
-                               E('br'), E('br')
-                       ]);
+                       sectionEl.appendChild(this.renderSectionPlaceholder());
 
                sectionEl.appendChild(this.renderSectionAdd());
 
@@ -761,7 +799,9 @@ var CBITableSection = CBITypedSection.extend({
                    has_more = max_cols < this.children.length,
                    sectionEl = E('div', {
                                'id': 'cbi-%s-%s'.format(config_name, this.sectiontype),
-                               'class': 'cbi-section cbi-tblsection'
+                               'class': 'cbi-section cbi-tblsection',
+                               'data-tab': this.map.tabbed ? this.sectiontype : null,
+                               'data-tab-title': this.map.tabbed ? this.title || this.sectiontype : null
                        }),
                        tableEl = E('div', {
                                'class': 'table cbi-section-table'
@@ -776,8 +816,7 @@ var CBITableSection = CBITypedSection.extend({
                tableEl.appendChild(this.renderHeaderRows(max_cols));
 
                for (var i = 0; i < nodes.length; i++) {
-                       var sectionname = this.stripTags((typeof(this.sectiontitle) == 'function')
-                               ? String(this.sectiontitle(cfgsections[i]) || '') : cfgsections[i]).trim();
+                       var sectionname = this.titleFn('sectiontitle', cfgsections[i]);
 
                        var trEl = E('div', {
                                'id': 'cbi-%s-%s'.format(config_name, cfgsections[i]),
@@ -791,7 +830,8 @@ var CBITableSection = CBITypedSection.extend({
                                'dragleave': this.sortable ? L.bind(this.handleDragLeave, this) : null,
                                'dragend': this.sortable ? L.bind(this.handleDragEnd, this) : null,
                                'drop': this.sortable ? L.bind(this.handleDrop, this) : null,
-                               'data-title': (sectionname && (!this.anonymous || this.sectiontitle)) ? sectionname : null
+                               'data-title': (sectionname && (!this.anonymous || this.sectiontitle)) ? sectionname : null,
+                               'data-section-id': cfgsections[i]
                        });
 
                        if (this.extedit || this.rowcolors)
@@ -954,11 +994,13 @@ var CBITableSection = CBITypedSection.extend({
                }
 
                if (this.addremove) {
+                       var btn_title = this.titleFn('removebtntitle', section_id);
+
                        L.dom.append(tdEl.lastElementChild,
                                E('input', {
                                        'type': 'submit',
-                                       'value': _('Delete'),
-                                       'title': _('Delete'),
+                                       'value': btn_title || _('Delete'),
+                                       'title': btn_title || _('Delete'),
                                        'class': 'cbi-button cbi-button-remove',
                                        'click': L.bind(function(sid, ev) {
                                                uci.remove(config_name, sid);
@@ -1077,16 +1119,16 @@ var CBITableSection = CBITypedSection.extend({
                    name = null,
                    m = new CBIMap(this.map.config, null, null),
                    s = m.section(CBINamedSection, section_id, this.sectiontype);
-                   s.tabs = this.tabs;
-                   s.tab_names = this.tab_names;
 
-               if (typeof(this.sectiontitle) == 'function')
-                       name = this.stripTags(String(this.sectiontitle(section_id) || ''));
-               else if (!this.anonymous)
-                       name = section_id;
+               s.tabs = this.tabs;
+               s.tab_names = this.tab_names;
 
-               if (name)
-                       title += ' - ' + name;
+               if ((name = this.titleFn('modaltitle', section_id)) != null)
+                       title = name;
+               else if ((name = this.titleFn('sectiontitle', section_id)) != null)
+                       title = '%s - %s'.format(parent.title, name);
+               else if (!this.anonymous)
+                       title = '%s - %s'.format(parent.title, section_id);
 
                for (var i = 0; i < this.children.length; i++) {
                        var o1 = this.children[i];
@@ -1258,7 +1300,9 @@ var CBINamedSection = CBIAbstractSection.extend({
                    config_name = this.uciconfig || this.map.config,
                    sectionEl = E('div', {
                                'id': ucidata ? null : 'cbi-%s-%s'.format(config_name, section_id),
-                               'class': 'cbi-section'
+                               'class': 'cbi-section',
+                               'data-tab': this.map.tabbed ? this.sectiontype : null,
+                               'data-tab-title': this.map.tabbed ? this.title || this.sectiontype : null
                        });
 
                if (typeof(this.title) === 'string' && this.title !== '')
@@ -1282,11 +1326,9 @@ var CBINamedSection = CBIAbstractSection.extend({
                        sectionEl.appendChild(E('div', {
                                'id': 'cbi-%s-%s'.format(config_name, section_id),
                                'class': this.tabs
-                                       ? 'cbi-section-node cbi-section-node-tabbed' : 'cbi-section-node'
+                                       ? 'cbi-section-node cbi-section-node-tabbed' : 'cbi-section-node',
+                               'data-section-id': section_id
                        }, nodes));
-
-                       if (this.tabs)
-                               ui.tabs.initTabGroup(sectionEl.lastChild.childNodes);
                }
                else if (this.addremove) {
                        sectionEl.appendChild(
@@ -1332,7 +1374,7 @@ var CBIValue = CBIAbstractValue.extend({
        },
 
        renderFrame: function(section_id, in_table, option_index, nodes) {
-               var config_name = this.uciconfig || this.map.config,
+               var config_name = this.uciconfig || this.section.uciconfig || this.map.config,
                    depend_list = this.transformDepList(section_id),
                    optionEl;
 
@@ -1583,28 +1625,30 @@ var CBIButtonValue = CBIValue.extend({
        __name__: 'CBI.ButtonValue',
 
        renderWidget: function(section_id, option_index, cfgvalue) {
-               var value = (cfgvalue != null) ? cfgvalue : this.default;
+               var value = (cfgvalue != null) ? cfgvalue : this.default,
+                   hiddenEl = new ui.Hiddenfield(value, { id: this.cbid(section_id) }),
+                   outputEl = E('div'),
+                   btn_title = this.titleFn('inputtitle', section_id) || this.titleFn('title', section_id);
 
                if (value !== false)
-                       return E([
-                               E('input', {
-                                       'type': 'hidden',
-                                       'id': this.cbid(section_id)
-                               }),
+                       L.dom.content(outputEl, [
                                E('input', {
                                        'class': 'cbi-button cbi-button-%s'.format(this.inputstyle || 'button'),
-                                       'type': 'submit',
-                                       //'id': this.cbid(section_id),
-                                       //'name': this.cbid(section_id),
-                                       'value': this.inputtitle || this.title,
-                                       'click': L.bind(function(ev) {
+                                       'type': 'button',
+                                       'value': btn_title,
+                                       'click': L.bind(this.onclick || function(ev) {
                                                ev.target.previousElementSibling.value = ev.target.value;
                                                this.map.save();
                                        }, this)
                                })
                        ]);
                else
-                       return document.createTextNode(' - ');
+                       L.dom.content(outputEl, ' - ');
+
+               return E([
+                       outputEl,
+                       hiddenEl.render()
+               ]);
        }
 });
 
@@ -1645,7 +1689,7 @@ var CBISectionValue = CBIValue.extend({
 
        checkDepends: function(section_id) {
                this.subsection.checkDepends();
-               return this.super('checkDepends');
+               return CBIValue.prototype.checkDepends.apply(this, [ section_id ]);
        },
 
        write: function() {},
index 66f32d72237d68abec63e76a692efafae31b63c1..d72764b11449be885466bba6a438406ef3881932 100644 (file)
            domParser = null,
            originalCBIInit = null,
            rpcBaseURL = null,
+           sysFeatures = null,
            classes = {};
 
        var LuCI = Class.extend({
                        return Promise.resolve(rpcBaseURL);
                },
 
+               probeSystemFeatures: function() {
+                       if (sysFeatures == null) {
+                               try {
+                                       sysFeatures = JSON.parse(window.sessionStorage.getItem('sysFeatures'));
+                               }
+                               catch (e) {}
+                       }
+
+                       if (!this.isObject(sysFeatures)) {
+                               sysFeatures = classes.rpc.declare({
+                                       object: 'luci',
+                                       method: 'getFeatures',
+                                       expect: { '': {} }
+                               })().then(function(features) {
+                                       try {
+                                               window.sessionStorage.setItem('sysFeatures', JSON.stringify(features));
+                                       }
+                                       catch (e) {}
+
+                                       sysFeatures = features;
+
+                                       return features;
+                               });
+                       }
+
+                       return Promise.resolve(sysFeatures);
+               },
+
+               hasSystemFeature: function() {
+                       var ft = sysFeatures[arguments[0]];
+
+                       if (arguments.length == 2)
+                               return this.isObject(ft) ? ft[arguments[1]] : null;
+
+                       return (ft != null && ft != false);
+               },
+
                setupDOM: function(res) {
                        var domEv = res[0],
                            uiClass = res[1],
                                throw 'Session expired';
                        });
 
-                       originalCBIInit();
+                       return this.probeSystemFeatures().finally(this.initDOM);
+               },
 
+               initDOM: function() {
+                       originalCBIInit();
                        Poll.start();
-
                        document.dispatchEvent(new CustomEvent('luci-loaded'));
                },
 
index d3d9a1cf57d516303749bdba972709234ecd982c..d6a97408a253706c81ff998454a9493d7af724d5 100644 (file)
@@ -51,18 +51,19 @@ var callNetworkWirelessStatus = rpc.declare({
 
 var callLuciNetdevs = rpc.declare({
        object: 'luci',
-       method: 'netdevs'
+       method: 'getNetworkDevices',
+       expect: { '': {} }
 });
 
 var callLuciIfaddrs = rpc.declare({
        object: 'luci',
-       method: 'ifaddrs',
+       method: 'getIfaddrs',
        expect: { result: [] }
 });
 
 var callLuciBoardjson = rpc.declare({
        object: 'luci',
-       method: 'boardjson'
+       method: 'getBoardJSON'
 });
 
 var callIwinfoInfo = rpc.declare({
@@ -83,95 +84,104 @@ var callNetworkDeviceStatus = rpc.declare({
        expect: { '': {} }
 });
 
-var _cache = {},
-    _state = null,
-    _protocols = {};
-
-function getWifiState() {
-       if (_cache.wifi == null)
-               return callNetworkWirelessStatus().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.wifi = state);
-               }).catch(function() {
-                       return (_cache.wifi = {});
-               });
+var callGetProtoHandlers = rpc.declare({
+       object: 'network',
+       method: 'get_proto_handlers',
+       expect: { '': {} }
+});
 
-       return Promise.resolve(_cache.wifi);
+var _init = null,
+    _state = null,
+    _protocols = {},
+    _protospecs = {};
+
+function getWifiState(cache) {
+       return callNetworkWirelessStatus().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getInterfaceState() {
-       if (_cache.interfacedump == null)
-               return callNetworkInterfaceStatus().then(function(state) {
-                       if (!Array.isArray(state))
-                               throw !1;
-                       return (_cache.interfacedump = state);
-               }).catch(function() {
-                       return (_cache.interfacedump = []);
-               });
-
-       return Promise.resolve(_cache.interfacedump);
+function getInterfaceState(cache) {
+       return callNetworkInterfaceStatus().then(function(state) {
+               if (!Array.isArray(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return [];
+       });
 }
 
-function getDeviceState() {
-       if (_cache.devicedump == null)
-               return callNetworkDeviceStatus().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.devicedump = state);
-               }).catch(function() {
-                       return (_cache.devicedump = {});
-               });
+function getDeviceState(cache) {
+       return callNetworkDeviceStatus().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
+}
 
-       return Promise.resolve(_cache.devicedump);
+function getIfaddrState(cache) {
+       return callLuciIfaddrs().then(function(addrs) {
+               if (!Array.isArray(addrs))
+                       throw !1;
+               return addrs;
+       }).catch(function() {
+               return [];
+       });
 }
 
-function getIfaddrState() {
-       if (_cache.ifaddrs == null)
-               return callLuciIfaddrs().then(function(addrs) {
-                       if (!Array.isArray(addrs))
-                               throw !1;
-                       return (_cache.ifaddrs = addrs);
-               }).catch(function() {
-                       return (_cache.ifaddrs = []);
-               });
+function getNetdevState(cache) {
+       return callLuciNetdevs().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
+}
 
-       return Promise.resolve(_cache.ifaddrs);
+function getBoardState(cache) {
+       return callLuciBoardjson().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getNetdevState() {
-       if (_cache.devices == null)
-               return callLuciNetdevs().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.devices = state);
-               }).catch(function() {
-                       return (_cache.devices = {});
-               });
+function getProtocolHandlers(cache) {
+       return callGetProtoHandlers().then(function(protos) {
+               if (!L.isObject(protos))
+                       throw !1;
 
-       return Promise.resolve(_cache.devices);
-}
+               Object.assign(_protospecs, protos);
 
-function getBoardState() {
-       if (_cache.board == null)
-               return callLuciBoardjson().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.board = state);
-               }).catch(function() {
-                       return (_cache.board = {});
+               return Promise.all(Object.keys(protos).map(function(p) {
+                       return Promise.resolve(L.require('protocol.%s'.format(p))).catch(function(err) {
+                               if (L.isObject(err) && err.name != 'NetworkError')
+                                       L.error(err);
+                       });
+               })).then(function() {
+                       return protos;
                });
-
-       return Promise.resolve(_cache.board);
+       }).catch(function() {
+               return {};
+       });
 }
 
 function getWifiStateBySid(sid) {
        var s = uci.get('wireless', sid);
 
        if (s != null && s['.type'] == 'wifi-iface') {
-               for (var radioname in _cache.wifi) {
-                       for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) {
-                               var netstate = _cache.wifi[radioname].interfaces[i];
+               for (var radioname in _state.radios) {
+                       for (var i = 0; i < _state.radios[radioname].interfaces.length; i++) {
+                               var netstate = _state.radios[radioname].interfaces[i];
 
                                if (typeof(netstate.section) != 'string')
                                        continue;
@@ -179,7 +189,7 @@ function getWifiStateBySid(sid) {
                                var s2 = uci.get('wireless', netstate.section);
 
                                if (s2 != null && s['.type'] == s2['.type'] && s['.name'] == s2['.name'])
-                                       return [ radioname, _cache.wifi[radioname], netstate ];
+                                       return [ radioname, _state.radios[radioname], netstate ];
                        }
                }
        }
@@ -188,15 +198,15 @@ function getWifiStateBySid(sid) {
 }
 
 function getWifiStateByIfname(ifname) {
-       for (var radioname in _cache.wifi) {
-               for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) {
-                       var netstate = _cache.wifi[radioname].interfaces[i];
+       for (var radioname in _state.radios) {
+               for (var i = 0; i < _state.radios[radioname].interfaces.length; i++) {
+                       var netstate = _state.radios[radioname].interfaces[i];
 
                        if (typeof(netstate.ifname) != 'string')
                                continue;
 
                        if (netstate.ifname == ifname)
-                               return [ radioname, _cache.wifi[radioname], netstate ];
+                               return [ radioname, _state.radios[radioname], netstate ];
                }
        }
 
@@ -336,7 +346,7 @@ function appendValue(config, section, option, value) {
            rv = false;
 
        if (isArray == false)
-               values = String(values || '').split(/\s+/);
+               values = L.toArray(values);
 
        if (values.indexOf(value) == -1) {
                values.push(value);
@@ -354,7 +364,7 @@ function removeValue(config, section, option, value) {
            rv = false;
 
        if (isArray == false)
-               values = String(values || '').split(/\s+/);
+               values = L.toArray(values);
 
        for (var i = values.length - 1; i >= 0; i--) {
                if (values[i] == value) {
@@ -413,17 +423,19 @@ function maskToPrefix(mask, v6) {
        return bits;
 }
 
-function initNetworkState() {
-       if (_state == null)
-               return (_state = Promise.all([
+function initNetworkState(refresh) {
+       if (_state == null || refresh) {
+               _init = _init || Promise.all([
                        getInterfaceState(), getDeviceState(), getBoardState(),
-                       getWifiState(), getIfaddrState(), getNetdevState(),
+                       getWifiState(), getIfaddrState(), getNetdevState(), getProtocolHandlers(),
                        uci.load('network'), uci.load('wireless'), uci.load('luci')
-               ]).finally(function() {
-                       var ifaddrs = _cache.ifaddrs,
-                           devices = _cache.devices,
-                           board = _cache.board,
-                           s = { isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {}, interfaces: {}, bridges: {}, switches: {} };
+               ]).then(function(data) {
+                       var board = data[2], ifaddrs = data[4], devices = data[5];
+                       var s = {
+                               isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {},
+                               ifaces: data[0], devices: data[1], radios: data[3],
+                               netdevs: {}, bridges: {}, switches: {}
+                       };
 
                        for (var i = 0, a; (a = ifaddrs[i]) != null; i++) {
                                var name = a.name.replace(/:.+$/, '');
@@ -432,7 +444,7 @@ function initNetworkState() {
                                        s.isTunnel[name] = true;
 
                                if (s.isTunnel[name] || !(isIgnoredIfname(name) || isVirtualIfname(name))) {
-                                       s.interfaces[name] = s.interfaces[name] || {
+                                       s.netdevs[name] = s.netdevs[name] || {
                                                idx:      a.ifindex || i,
                                                name:     name,
                                                rawname:  a.name,
@@ -442,15 +454,15 @@ function initNetworkState() {
                                        };
 
                                        if (a.family == 'packet') {
-                                               s.interfaces[name].flags   = a.flags;
-                                               s.interfaces[name].stats   = a.data;
-                                               s.interfaces[name].macaddr = a.addr;
+                                               s.netdevs[name].flags   = a.flags;
+                                               s.netdevs[name].stats   = a.data;
+                                               s.netdevs[name].macaddr = a.addr;
                                        }
                                        else if (a.family == 'inet') {
-                                               s.interfaces[name].ipaddrs.push(a.addr + '/' + a.netmask);
+                                               s.netdevs[name].ipaddrs.push(a.addr + '/' + a.netmask);
                                        }
                                        else if (a.family == 'inet6') {
-                                               s.interfaces[name].ip6addrs.push(a.addr + '/' + a.netmask);
+                                               s.netdevs[name].ip6addrs.push(a.addr + '/' + a.netmask);
                                        }
                                }
                        }
@@ -467,7 +479,7 @@ function initNetworkState() {
                                        };
 
                                        for (var i = 0; dev.ports && i < dev.ports.length; i++) {
-                                               var subdev = s.interfaces[dev.ports[i]];
+                                               var subdev = s.netdevs[dev.ports[i]];
 
                                                if (subdev == null)
                                                        continue;
@@ -477,6 +489,16 @@ function initNetworkState() {
                                        }
 
                                        s.bridges[devname] = b;
+                                       s.isBridge[devname] = true;
+                               }
+
+                               if (s.netdevs.hasOwnProperty(devname)) {
+                                       Object.assign(s.netdevs[devname], {
+                                               macaddr: dev.mac,
+                                               type:    dev.type,
+                                               mtu:     dev.mtu,
+                                               qlen:    dev.qlen
+                                       });
                                }
                        }
 
@@ -544,17 +566,28 @@ function initNetworkState() {
                                }
                        }
 
+                       if (L.isObject(board.dsl) && L.isObject(board.dsl.modem)) {
+                               s.hasDSLModem = board.dsl.modem;
+                       }
+
+                       _init = null;
+
                        return (_state = s);
-               }));
+               });
+       }
 
-       return Promise.resolve(_state);
+       return (_state != null ? Promise.resolve(_state) : _init);
 }
 
 function ifnameOf(obj) {
-       if (obj instanceof Interface)
-               return obj.name();
-       else if (obj instanceof Protocol)
-               return obj.ifname();
+       if (obj instanceof Protocol)
+               return obj.getIfname();
+       else if (obj instanceof Device)
+               return obj.getName();
+       else if (obj instanceof WifiDevice)
+               return obj.getName();
+       else if (obj instanceof WifiNetwork)
+               return obj.getIfname();
        else if (typeof(obj) == 'string')
                return obj.replace(/:.+$/, '');
 
@@ -580,10 +613,18 @@ function deviceSort(a, b) {
 var Network, Protocol, Device, WifiDevice, WifiNetwork;
 
 Network = L.Class.extend({
+       prefixToMask: prefixToMask,
+       maskToPrefix: maskToPrefix,
+
+       flushCache: function() {
+               initNetworkState(true);
+               return _init;
+       },
+
        getProtocol: function(protoname, netname) {
                var v = _protocols[protoname];
                if (v != null)
-                       return v(netname || '__dummy__');
+                       return new v(netname || '__dummy__');
 
                return null;
        },
@@ -592,18 +633,35 @@ Network = L.Class.extend({
                var rv = [];
 
                for (var protoname in _protocols)
-                       rv.push(_protocols[protoname]('__dummy__'));
+                       rv.push(new _protocols[protoname]('__dummy__'));
 
                return rv;
        },
 
        registerProtocol: function(protoname, methods) {
-               var proto = Protocol.extend(Object.assign({}, methods, {
+               var spec = L.isObject(_protospecs) ? _protospecs[protoname] : null;
+               var proto = Protocol.extend(Object.assign({
+                       getI18n: function() {
+                               return protoname;
+                       },
+
+                       isFloating: function() {
+                               return false;
+                       },
+
+                       isVirtual: function() {
+                               return (L.isObject(spec) && spec.no_device == true);
+                       },
+
+                       renderFormOptions: function(section) {
+
+                       }
+               }, methods, {
                        __init__: function(name) {
                                this.sid = name;
                        },
 
-                       proto: function() {
+                       getProtocol: function() {
                                return protoname;
                        }
                }));
@@ -661,9 +719,9 @@ Network = L.Class.extend({
                                return this.instantiateNetwork(name);
                        }
                        else if (name != null) {
-                               for (var i = 0; i < _cache.interfacedump.length; i++)
-                                       if (_cache.interfacedump[i].interface == name)
-                                               return this.instantiateNetwork(name, _cache.interfacedump[i].proto);
+                               for (var i = 0; i < _state.ifaces.length; i++)
+                                       if (_state.ifaces[i].interface == name)
+                                               return this.instantiateNetwork(name, _state.ifaces[i].proto);
                        }
 
                        return null;
@@ -678,10 +736,10 @@ Network = L.Class.extend({
                        for (var i = 0; i < uciInterfaces.length; i++)
                                networks[uciInterfaces[i]['.name']] = this.instantiateNetwork(uciInterfaces[i]['.name']);
 
-                       for (var i = 0; i < _cache.interfacedump.length; i++)
-                               if (networks[_cache.interfacedump[i].interface] == null)
-                                       networks[_cache.interfacedump[i].interface] =
-                                               this.instantiateNetwork(_cache.interfacedump[i].interface, _cache.interfacedump[i].proto);
+                       for (var i = 0; i < _state.ifaces.length; i++)
+                               if (networks[_state.ifaces[i].interface] == null)
+                                       networks[_state.ifaces[i].interface] =
+                                               this.instantiateNetwork(_state.ifaces[i].interface, _state.ifaces[i].proto);
 
                        var rv = [];
 
@@ -795,7 +853,7 @@ Network = L.Class.extend({
                        if (name == null)
                                return null;
 
-                       if (_state.interfaces.hasOwnProperty(name) || isWifiIfname(name))
+                       if (_state.netdevs.hasOwnProperty(name) || isWifiIfname(name))
                                return this.instantiateDevice(name);
 
                        var netid = getWifiNetidBySid(name);
@@ -826,7 +884,7 @@ Network = L.Class.extend({
                                }
                        }
 
-                       for (var ifname in _state.interfaces) {
+                       for (var ifname in _state.netdevs) {
                                if (devices.hasOwnProperty(ifname))
                                        continue;
 
@@ -1017,7 +1075,7 @@ Network = L.Class.extend({
                        var radioname = existingDevice['.name'],
                            netid = getWifiNetidBySid(sid) || [];
 
-                       return this.instantiateWifiNetwork(sid, radioname, _cache.wifi[radioname], netid[0], null, { ifname: netid });
+                       return this.instantiateWifiNetwork(sid, radioname, _state.radios[radioname], netid[0], null, { ifname: netid });
                }, this));
        },
 
@@ -1037,20 +1095,24 @@ Network = L.Class.extend({
                return initNetworkState().then(L.bind(function() {
                        var rv = [];
 
-                       for (var i = 0; i < _state.interfacedump.length; i++) {
-                               if (!Array.isArray(_state.interfacedump[i].route))
+                       for (var i = 0; i < _state.ifaces.length; i++) {
+                               if (!Array.isArray(_state.ifaces[i].route))
                                        continue;
 
-                               for (var j = 0; j < _state.interfacedump[i].route.length; j++) {
-                                       if (typeof(_state.interfacedump[i].route[j]) != 'object' ||
-                                           typeof(_state.interfacedump[i].route[j].target) != 'string' ||
-                                           typeof(_state.interfacedump[i].route[j].mask) != 'number')
+                               for (var j = 0; j < _state.ifaces[i].route.length; j++) {
+                                       if (typeof(_state.ifaces[i].route[j]) != 'object' ||
+                                           typeof(_state.ifaces[i].route[j].target) != 'string' ||
+                                           typeof(_state.ifaces[i].route[j].mask) != 'number')
                                            continue;
 
-                                       if (_state.interfacedump[i].route[j].table)
+                                       if (_state.ifaces[i].route[j].table)
                                                continue;
 
-                                       rv.push(_state.interfacedump[i]);
+                                       if (_state.ifaces[i].route[j].target != addr ||
+                                           _state.ifaces[i].route[j].mask != mask)
+                                           continue;
+
+                                       rv.push(_state.ifaces[i]);
                                }
                        }
 
@@ -1062,25 +1124,25 @@ Network = L.Class.extend({
                return initNetworkState().then(L.bind(function() {
                        var rv = [];
 
-                       for (var i = 0; i < _state.interfacedump.length; i++) {
-                               if (Array.isArray(_state.interfacedump[i]['ipv4-address']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv4-address'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv4-address'][j]) == 'object' &&
-                                                   _state.interfacedump[i]['ipv4-address'][j].address == addr)
-                                                       return _state.interfacedump[i];
-
-                               if (Array.isArray(_state.interfacedump[i]['ipv6-address']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv6-address'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv6-address'][j]) == 'object' &&
-                                                   _state.interfacedump[i]['ipv6-address'][j].address == addr)
-                                                       return _state.interfacedump[i];
-
-                               if (Array.isArray(_state.interfacedump[i]['ipv6-prefix-assignment']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv6-prefix-assignment'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]) == 'object' &&
-                                                       typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' &&
-                                                   _state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr)
-                                                       return _state.interfacedump[i];
+                       for (var i = 0; i < _state.ifaces.length; i++) {
+                               if (Array.isArray(_state.ifaces[i]['ipv4-address']))
+                                       for (var j = 0; j < _state.ifaces[i]['ipv4-address'].length; j++)
+                                               if (typeof(_state.ifaces[i]['ipv4-address'][j]) == 'object' &&
+                                                   _state.ifaces[i]['ipv4-address'][j].address == addr)
+                                                       return _state.ifaces[i];
+
+                               if (Array.isArray(_state.ifaces[i]['ipv6-address']))
+                                       for (var j = 0; j < _state.ifaces[i]['ipv6-address'].length; j++)
+                                               if (typeof(_state.ifaces[i]['ipv6-address'][j]) == 'object' &&
+                                                   _state.ifaces[i]['ipv6-address'][j].address == addr)
+                                                       return _state.ifaces[i];
+
+                               if (Array.isArray(_state.ifaces[i]['ipv6-prefix-assignment']))
+                                       for (var j = 0; j < _state.ifaces[i]['ipv6-prefix-assignment'].length; j++)
+                                               if (typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]) == 'object' &&
+                                                       typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' &&
+                                                   _state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr)
+                                                       return _state.ifaces[i];
                        }
 
                        return null;
@@ -1135,6 +1197,16 @@ Network = L.Class.extend({
 
        instantiateWifiNetwork: function(sid, radioname, radiostate, netid, netstate, iwinfo) {
                return new WifiNetwork(sid, radioname, radiostate, netid, netstate, iwinfo);
+       },
+
+       getIfnameOf: function(obj) {
+               return ifnameOf(obj);
+       },
+
+       getDSLModemType: function() {
+               return initNetworkState().then(function() {
+                       return _state.hasDSLModem ? _state.hasDSLModem.type : null;
+               });
        }
 });
 
@@ -1153,11 +1225,11 @@ Protocol = L.Class.extend({
        },
 
        _ubus: function(field) {
-               for (var i = 0; i < _cache.interfacedump.length; i++) {
-                       if (_cache.interfacedump[i].interface != this.sid)
+               for (var i = 0; i < _state.ifaces.length; i++) {
+                       if (_state.ifaces[i].interface != this.sid)
                                continue;
 
-                       return (field != null ? _cache.interfacedump[i][field] : _cache.interfacedump[i]);
+                       return (field != null ? _state.ifaces[i][field] : _state.ifaces[i]);
                }
        },
 
@@ -1175,7 +1247,7 @@ Protocol = L.Class.extend({
                if (this.isFloating())
                        ifname = this._ubus('l3_device');
                else
-                       ifname = this._ubus('device');
+                       ifname = this._ubus('device') || this._ubus('l3_device');
 
                if (ifname != null)
                        return ifname;
@@ -1185,7 +1257,7 @@ Protocol = L.Class.extend({
        },
 
        getProtocol: function() {
-               return 'none';
+               return null;
        },
 
        getI18n: function() {
@@ -1455,11 +1527,6 @@ Protocol = L.Class.extend({
                        return new Device(ifname, this);
                }
                else {
-                       var ifname = this._ubus('l3_device') || this._ubus('device');
-
-                       if (ifname != null)
-                               return L.network.instantiateDevice(ifname, this);
-
                        var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
 
                        for (var i = 0; i < ifnames.length; i++) {
@@ -1473,6 +1540,16 @@ Protocol = L.Class.extend({
                }
        },
 
+       getL2Device: function() {
+               var ifname = this._ubus('device');
+               return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
+       },
+
+       getL3Device: function() {
+               var ifname = this._ubus('l3_device');
+               return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
+       },
+
        getDevices: function() {
                var rv = [];
 
@@ -1559,12 +1636,12 @@ Device = L.Class.extend({
                }
 
                this.ifname  = this.ifname || ifname;
-               this.dev     = _state.interfaces[this.ifname];
+               this.dev     = _state.netdevs[this.ifname];
                this.network = network;
        },
 
        _ubus: function(field) {
-               var dump = _cache.devicedump[this.ifname] || {};
+               var dump = _state.devices[this.ifname] || {};
 
                return (field != null ? dump[field] : dump);
        },
@@ -1574,7 +1651,15 @@ Device = L.Class.extend({
        },
 
        getMAC: function() {
-               return this._ubus('macaddr');
+               var mac = (this.dev != null ? this.dev.macaddr : null);
+               if (mac == null)
+                       mac = this._ubus('macaddr');
+
+               return mac ? mac.toUpperCase() : null;
+       },
+
+       getMTU: function() {
+               return this.dev ? this.dev.mtu : null;
        },
 
        getIPAddrs: function() {
@@ -1655,7 +1740,9 @@ Device = L.Class.extend({
                        return null;
 
                for (var i = 0; i < br.ifnames.length; i++)
-                       rv.push(L.network.instantiateDevice(br.ifnames[i]));
+                       rv.push(L.network.instantiateDevice(br.ifnames[i].name));
+
+               rv.sort(deviceSort);
 
                return rv;
        },
@@ -1786,8 +1873,8 @@ WifiDevice = L.Class.extend({
        },
 
        isUp: function() {
-               if (L.isObject(_cache.wifi[this.sid]))
-                       return (_cache.wifi[this.sid].up == true);
+               if (L.isObject(_state.radios[this.sid]))
+                       return (_state.radios[this.sid].up == true);
 
                return false;
        },
index 39e5aa165560d679acd9024648bc3bff638f171f..2da9e7435ffaa7fd05f347332471e7b5b92a0b24 100644 (file)
@@ -420,8 +420,12 @@ var CBIDeviceSelect = form.ListValue.extend({
        __name__: 'CBI.DeviceSelect',
 
        load: function(section_id) {
-               return network.getDevices().then(L.bind(function(devices) {
-                       this.devices = devices;
+               return Promise.all([
+                       network.getDevices(),
+                       this.noaliases ? null : network.getNetworks()
+               ]).then(L.bind(function(data) {
+                       this.devices = data[0];
+                       this.networks = data[1];
 
                        return this.super('load', section_id);
                }, this));
@@ -483,6 +487,35 @@ var CBIDeviceSelect = form.ListValue.extend({
                        order.push(name);
                }
 
+               if (this.networks != null) {
+                       for (var i = 0; i < this.networks.length; i++) {
+                               var net = this.networks[i],
+                                   device = network.instantiateDevice('@%s'.format(net.getName()), net),
+                                   name = device.getName();
+
+                               if (name == '@loopback' || name == this.exclude || !this.filter(section_id, name))
+                                       continue;
+
+                               if (this.noinactive && net.isUp() == false)
+                                       continue;
+
+                               var item = E([
+                                       E('img', {
+                                               'title': device.getI18n(),
+                                               'src': L.resource('icons/alias%s.png'.format(net.isUp() ? '' : '_disabled'))
+                                       }),
+                                       E('span', { 'class': 'hide-open' }, [ name ]),
+                                       E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
+                               ]);
+
+                               if (checked[name])
+                                       values.push(name);
+
+                               choices[name] = item;
+                               order.push(name);
+                       }
+               }
+
                if (!this.nocreate) {
                        var keys = Object.keys(checked).sort();
 
index 29233dec02472f3ff5a3c7de28c5bf0b8c5bc182..e00868171081f02611fb0da27b5b563d916aadef 100644 (file)
@@ -207,7 +207,7 @@ var UICheckbox = UIElement.extend({
 
 var UISelect = UIElement.extend({
        __init__: function(value, choices, options) {
-               if (typeof(choices) != 'object')
+               if (!L.isObject(choices))
                        choices = {};
 
                if (!Array.isArray(value))
@@ -1199,7 +1199,7 @@ var UIDynamicList = UIElement.extend({
                                        'name': this.options.name,
                                        'value': value })]);
 
-               dl.querySelectorAll('.item, .add-item').forEach(function(item) {
+               dl.querySelectorAll('.item').forEach(function(item) {
                        if (exists)
                                return;
 
@@ -1210,10 +1210,13 @@ var UIDynamicList = UIElement.extend({
 
                        if (hidden && hidden.value === value)
                                exists = true;
-                       else if (!hidden || hidden.value >= value)
-                               exists = !!item.parentNode.insertBefore(new_item, item);
                });
 
+               if (!exists) {
+                       var ai = dl.querySelector('.add-item');
+                       ai.parentNode.insertBefore(new_item, ai);
+               }
+
                dl.dispatchEvent(new CustomEvent('cbi-dynlist-change', {
                        bubbles: true,
                        detail: {
@@ -1553,9 +1556,6 @@ return L.Class.extend({
                        document.addEventListener('dependency-update', this.updateTabs.bind(this));
 
                        this.updateTabs();
-
-                       if (!groups.length)
-                               this.setActiveTabId(-1, -1);
                },
 
                initTabGroup: function(panes) {
@@ -1573,6 +1573,7 @@ return L.Class.extend({
                                    active = pane.getAttribute('data-tab-active') === 'true';
 
                                menu.appendChild(E('li', {
+                                       'style': L.dom.isEmpty(pane) ? 'display:none' : null,
                                        'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
                                        'data-tab': name
                                }, E('a', {
@@ -1587,7 +1588,7 @@ return L.Class.extend({
                        group.parentNode.insertBefore(menu, group);
 
                        if (selected === null) {
-                               selected = this.getActiveTabId(groupId);
+                               selected = this.getActiveTabId(panes[0]);
 
                                if (selected < 0 || selected >= panes.length || L.dom.isEmpty(panes[selected])) {
                                        for (var i = 0; i < panes.length; i++) {
@@ -1602,8 +1603,24 @@ return L.Class.extend({
                                menu.childNodes[selected].classList.remove('cbi-tab-disabled');
                                panes[selected].setAttribute('data-tab-active', 'true');
 
-                               this.setActiveTabId(groupId, selected);
+                               this.setActiveTabId(panes[selected], selected);
+                       }
+               },
+
+               getPathForPane: function(pane) {
+                       var path = [], node = null;
+
+                       for (node = pane ? pane.parentNode : null;
+                            node != null && node.hasAttribute != null;
+                            node = node.parentNode)
+                       {
+                               if (node.hasAttribute('data-tab'))
+                                       path.unshift(node.getAttribute('data-tab'));
+                               else if (node.hasAttribute('data-section-id'))
+                                       path.unshift(node.getAttribute('data-section-id'));
                        }
+
+                       return path.join('/');
                },
 
                getActiveTabState: function() {
@@ -1611,23 +1628,26 @@ return L.Class.extend({
 
                        try {
                                var val = JSON.parse(window.sessionStorage.getItem('tab'));
-                               if (val.page === page && Array.isArray(val.groups))
+                               if (val.page === page && L.isObject(val.paths))
                                        return val;
                        }
                        catch(e) {}
 
                        window.sessionStorage.removeItem('tab');
-                       return { page: page, groups: [] };
+                       return { page: page, paths: {} };
                },
 
-               getActiveTabId: function(groupId) {
-                       return +this.getActiveTabState().groups[groupId] || 0;
+               getActiveTabId: function(pane) {
+                       var path = this.getPathForPane(pane);
+                       return +this.getActiveTabState().paths[path] || 0;
                },
 
-               setActiveTabId: function(groupId, tabIndex) {
+               setActiveTabId: function(pane, tabIndex) {
+                       var path = this.getPathForPane(pane);
+
                        try {
                                var state = this.getActiveTabState();
-                                   state.groups[groupId] = tabIndex;
+                                   state.paths[path] = tabIndex;
 
                            window.sessionStorage.setItem('tab', JSON.stringify(state));
                        }
@@ -1639,9 +1659,12 @@ return L.Class.extend({
                updateTabs: function(ev, root) {
                        (root || document).querySelectorAll('[data-tab-title]').forEach(function(pane) {
                                var menu = pane.parentNode.previousElementSibling,
-                                   tab = menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))),
+                                   tab = menu ? menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))) : null,
                                    n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
 
+                               if (!menu || !tab)
+                                       return;
+
                                if (L.dom.isEmpty(pane)) {
                                        tab.style.display = 'none';
                                        tab.classList.remove('flash');
@@ -1687,7 +1710,7 @@ return L.Class.extend({
                                if (L.dom.matches(pane, '[data-tab]')) {
                                        if (pane.getAttribute('data-tab') === name) {
                                                pane.setAttribute('data-tab-active', 'true');
-                                               L.ui.tabs.setActiveTabId(groupId, index);
+                                               L.ui.tabs.setActiveTabId(pane, index);
                                        }
                                        else {
                                                pane.setAttribute('data-tab-active', 'false');
@@ -2058,6 +2081,29 @@ return L.Class.extend({
                catch (e) { }
        },
 
+       createHandlerFn: function(ctx, fn /*, ... */) {
+               if (typeof(fn) == 'string')
+                       fn = ctx[fn];
+
+               if (typeof(fn) != 'function')
+                       return null;
+
+               return Function.prototype.bind.apply(function() {
+                       var t = arguments[arguments.length - 1].target;
+
+                       t.classList.add('spinning');
+                       t.disabled = true;
+
+                       if (t.blur)
+                               t.blur();
+
+                       Promise.resolve(fn.apply(ctx, arguments)).then(function() {
+                               t.classList.remove('spinning');
+                               t.disabled = false;
+                       });
+               }, this.varargs(arguments, 2, ctx));
+       },
+
        /* Widgets */
        Textfield: UITextfield,
        Checkbox: UICheckbox,
index 3f2b465879500cdc2e11a450b37e9adbe062458f..b0427d6c058515197bae8ff61133dffbd0baa80f 100644 (file)
@@ -96,6 +96,7 @@ function index()
        page.leaf = true
 
        page = entry({"admin", "ubus"}, call("action_ubus"), nil)
+       page.sysauth = false
        page.leaf = true
 
        -- Logout is last
@@ -165,6 +166,17 @@ local ubus_types = {
        "double"
 }
 
+local function ubus_access(sid, obj, fun)
+       local res, code = luci.util.ubus("session", "access", {
+               ubus_rpc_session = sid,
+               scope            = "ubus",
+               object           = obj,
+               ["function"]     = fun
+       })
+
+       return (type(res) == "table" and res.access == true)
+end
+
 local function ubus_request(req)
        if type(req) ~= "table" or type(req.method) ~= "string" or type(req.params) ~= "table" or
           #req.params < 2 or req.jsonrpc ~= "2.0" or req.id == nil then
@@ -177,10 +189,14 @@ local function ubus_request(req)
                        return ubus_reply(req.id, nil, -32602, "Invalid parameters")
                end
 
-               if sid == "00000000000000000000000000000000" then
+               if sid == "00000000000000000000000000000000" and luci.dispatcher.context.authsession then
                        sid = luci.dispatcher.context.authsession
                end
 
+               if not ubus_access(sid, obj, fun) then
+                       return ubus_reply(req.id, nil, -32002, "Access denied")
+               end
+
                arg.ubus_rpc_session = sid
 
                local res, code = luci.util.ubus(obj, fun, arg)
index 7644745efd6edcf92f23f0e26ddb3dcc056ce328..b8950137368e6e58ef3620227e6d20a5df7b14f1 100755 (executable)
@@ -9,7 +9,7 @@ local function readfile(path)
 end
 
 local methods = {
-       initList = {
+       getInitList = {
                args = { name = "name" },
                call = function(args)
                        local sys = require "luci.sys"
@@ -22,11 +22,11 @@ local methods = {
                                        return { error = "No such init script" }
                                end
                        end
-                       return { result = scripts }
+                       return scripts
                end
        },
 
-       initCall = {
+       setInitAction = {
                args = { name = "name", action = "action" },
                call = function(args)
                        local sys = require "luci.sys"
@@ -39,7 +39,7 @@ local methods = {
 
        getLocaltime = {
                call = function(args)
-                       return { localtime = os.time() }
+                       return { result = os.time() }
                end
        },
 
@@ -52,11 +52,11 @@ local methods = {
                                sys.call("date -s '%04d-%02d-%02d %02d:%02d:%02d' >/dev/null" %{ date.year, date.month, date.day, date.hour, date.min, date.sec })
                                sys.call("/etc/init.d/sysfixtime restart >/dev/null")
                        end
-                       return { localtime = args.localtime }
+                       return { result = args.localtime }
                end
        },
 
-       timezone = {
+       getTimezones = {
                call = function(args)
                        local util  = require "luci.util"
                        local zones = require "luci.sys.zoneinfo"
@@ -76,11 +76,11 @@ local methods = {
                                        active = (res and res.value == zone[1]) and true or nil
                                }
                        end
-                       return { result = result }
+                       return result
                end
        },
 
-       leds = {
+       getLEDs = {
                call = function()
                        local iter   = fs.dir("/sys/class/leds")
                        local result = { }
@@ -115,7 +115,7 @@ local methods = {
                end
        },
 
-       usb = {
+       getUSBDevices = {
                call = function()
                        local fs     = require "nixio.fs"
                        local iter   = fs.glob("/sys/bus/usb/devices/[0-9]*/manufacturer")
@@ -126,7 +126,7 @@ local methods = {
 
                                local p
                                for p in iter do
-                                       local id = p:match("%d+-%d+")
+                                       local id = p:match("/([^/]+)/manufacturer$")
 
                                        result.devices[#result.devices+1] = {
                                                id      = id,
@@ -139,18 +139,19 @@ local methods = {
                                end
                        end
 
-                       iter = fs.glob("/sys/bus/usb/devices/*/usb[0-9]*-port[0-9]*")
+                       iter = fs.glob("/sys/bus/usb/devices/*/*-port[0-9]*")
 
                        if iter then
                                result.ports = {}
 
                                local p
                                for p in iter do
-                                       local bus, port = p:match("usb(%d+)-port(%d+)")
+                                       local port = p:match("([^/]+)$")
+                                       local link = fs.readlink(p.."/device")
 
                                        result.ports[#result.ports+1] = {
-                                               hub  = tonumber(bus),
-                                               port = tonumber(port)
+                                               port   = port,
+                                               device = link and fs.basename(link)
                                        }
                                end
                        end
@@ -159,20 +160,20 @@ local methods = {
                end
        },
 
-       ifaddrs = {
+       getIfaddrs = {
                call = function()
                        return { result = nixio.getifaddrs() }
                end
        },
 
-       host_hints = {
+       getHostHints = {
                call = function()
                        local sys = require "luci.sys"
                        return sys.net.host_hints()
                end
        },
 
-       duid_hints = {
+       getDUIDHints = {
                call = function()
                        local fp = io.open('/var/hosts/odhcpd')
                        local result = { }
@@ -192,7 +193,7 @@ local methods = {
                end
        },
 
-       leases = {
+       getDHCPLeases = {
                args = { family = 0 },
                call = function(args)
                        local s = require "luci.tools.status"
@@ -210,7 +211,7 @@ local methods = {
                end
        },
 
-       netdevs = {
+       getNetworkDevices = {
                call = function(args)
                        local dir = fs.dir("/sys/class/net")
                        local result = { }
@@ -273,52 +274,138 @@ local methods = {
                end
        },
 
-       boardjson = {
+       getBoardJSON = {
                call = function(args)
                        local jsc = require "luci.jsonc"
                        return jsc.parse(fs.readfile("/etc/board.json") or "")
                end
        },
 
-       offload_support = {
+       getConntrackHelpers = {
                call = function()
-                       local fs = require "nixio.fs"
-                       return { offload_support = not not fs.access("/sys/module/xt_FLOWOFFLOAD/refcnt") }
+                       local ok, fd = pcall(io.open, "/usr/share/fw3/helpers.conf", "r")
+                       local rv = {}
+
+                       if ok then
+                               local entry
+
+                               while true do
+                                       local 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
+
+                               fd:close()
+                       end
+
+                       return { result = rv }
                end
        },
 
-       conntrack_helpers = {
+       getFeatures = {
                call = function()
-                       local fd = io.open("/usr/share/fw3/helpers.conf", "r")
+                       local fs = require "nixio.fs"
                        local rv = {}
+                       local ok, fd
+
+                       rv.firewall      = fs.access("/sbin/fw3")
+                       rv.opkg          = fs.access("/bin/opkg")
+                       rv.offloading    = fs.access("/sys/module/xt_FLOWOFFLOAD/refcnt")
+                       rv.br2684ctl     = fs.access("/usr/sbin/br2684ctl")
+                       rv.swconfig      = fs.access("/sbin/swconfig")
+                       rv.odhcpd        = fs.access("/usr/sbin/odhcpd")
+                       rv.zram          = fs.access("/sys/class/zram-control")
+                       rv.sysntpd       = fs.readlink("/usr/sbin/ntpd") and true
+
+                       local wifi_features = { "eap", "11n", "11ac", "11r", "11w", "acs", "sae", "owe", "suiteb192" }
+
+                       if fs.access("/usr/sbin/hostapd") then
+                               rv.hostapd = {}
+
+                               local _, feature
+                               for _, feature in ipairs(wifi_features) do
+                                       rv.hostapd[feature] =
+                                               (os.execute(string.format("/usr/sbin/hostapd -v%s >/dev/null 2>/dev/null", feature)) == 0)
+                               end
+                       end
 
-                       local line, entry
-                       while true do
-                               line = fd:read("*l")
-                               if not line then
-                                       break
+                       if fs.access("/usr/sbin/wpa_supplicant") then
+                               rv.wpasupplicant = {}
+
+                               local _, feature
+                               for _, feature in ipairs(wifi_features) do
+                                       rv.wpasupplicant[feature] =
+                                               (os.execute(string.format("/usr/sbin/wpa_supplicant -v%s >/dev/null 2>/dev/null", feature)) == 0)
                                end
+                       end
 
-                               if line:match("^%s*config%s") then
-                                       if entry then
-                                               rv[#rv+1] = entry
+                       ok, fd = pcall(io.popen, "dnsmasq --version 2>/dev/null")
+                       if ok then
+                               rv.dnsmasq = {}
+
+                               while true do
+                                       local line = fd:read("*l")
+                                       if not line then
+                                               break
                                        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
+
+                                       local opts = line:match("^Compile time options: (.+)$")
+                                       if opts then
+                                               local opt
+                                               for opt in opts:gmatch("%S+") do
+                                                       local no = opt:match("^no%-(%S+)$")
+                                                       rv.dnsmasq[string.lower(no or opt)] = not no
+                                               end
+                                               break
                                        end
                                end
+
+                               fd:close()
                        end
 
-                       if entry then
-                               rv[#rv+1] = entry
+                       ok, fd = pcall(io.popen, "ipset --help 2>/dev/null")
+                       if ok then
+                               rv.ipset = {}
+
+                               local sets = false
+
+                               while true do
+                                       local line = fd:read("*l")
+                                       if not line then
+                                               break
+                                       elseif line:match("^Supported set types:") then
+                                               sets = true
+                                       elseif sets then
+                                               local set, ver = line:match("^%s+(%S+)%s+(%d+)")
+                                               if set and not rv.ipset[set] then
+                                                       rv.ipset[set] = tonumber(ver)
+                                               end
+                                       end
+                               end
+
+                               fd:close()
                        end
 
-                       return { helpers = rv }
+                       return rv
                end
        }
 }
index de145ce784592c089d549657ecf1937751f857f1..5ffcbdc2e6d70cf200228c7a36d86bf784ed5984 100644 (file)
@@ -1,4 +1,13 @@
 {
+       "unauthenticated": {
+               "description": "Allow system feature probing",
+               "read": {
+                       "ubus": {
+                               "luci": [ "getFeatures" ]
+                       }
+               }
+       },
+
        "uci-access": {
                "description": "Grant uci write access to all configurations",
                "read": {
                "read": {
                        "ubus": {
                                "iwinfo": [ "info" ],
-                               "luci": [ "boardjson", "duid_hints", "host_hints", "ifaddrs", "initList", "getLocaltime", "leases", "leds", "netdevs", "usb" ],
+                               "luci": [ "getBoardJSON", "getDUIDHints", "getHostHints", "getIfaddrs", "getInitList", "getLocaltime", "getTimezones", "getDHCPLeases", "getLEDs", "getNetworkDevices", "getUSBDevices" ],
                                "network.device": [ "status" ],
                                "network.interface": [ "dump" ],
                                "network.wireless": [ "status" ],
+                               "network": [ "get_proto_handlers" ],
                                "uci": [ "changes", "get" ]
                        },
                        "uci": [ "*" ]
                },
                "write": {
                        "ubus": {
-                               "luci": [ "initCall", "setLocaltime", "timezone" ],
+                               "luci": [ "setInitAction", "setLocaltime" ],
                                "uci": [ "add", "apply", "confirm", "delete", "order", "set" ]
                        },
                        "uci": [ "*" ]
@@ -33,7 +43,7 @@
                "description": "Grant access to firewall procedures",
                "read": {
                        "ubus": {
-                               "luci": [ "conntrack_helpers", "offload_support" ]
+                               "luci": [ "getConntrackHelpers" ]
                        },
                        "uci": [ "firewall" ]
                },
index 7035dc4769c53161d93ad4d50df5c541055c03db..1e9c402e0c1f93e1c6424d7aac8e39599178dcf6 100644 (file)
@@ -7,17 +7,19 @@ var callHostHints, callDUIDHints, callDHCPLeases, CBILeaseStatus;
 
 callHostHints = rpc.declare({
        object: 'luci',
-       method: 'host_hints'
+       method: 'getHostHints',
+       expect: { '': {} }
 });
 
 callDUIDHints = rpc.declare({
        object: 'luci',
-       method: 'duid_hints'
+       method: 'getDUIDHints',
+       expect: { '': {} }
 });
 
 callDHCPLeases = rpc.declare({
        object: 'luci',
-       method: 'leases',
+       method: 'getDHCPLeases',
        params: [ 'family' ],
        expect: { dhcp_leases: [] }
 });
@@ -57,7 +59,6 @@ return L.view.extend({
                    m, s, o, ss, so;
 
                m = new form.Map('dhcp', _('DHCP and DNS'), _('Dnsmasq is a combined <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server and <abbr title="Domain Name System">DNS</abbr>-Forwarder for <abbr title="Network Address Translation">NAT</abbr> firewalls'));
-               m.tabbed = true;
 
                s = m.section(form.TypedSection, 'dnsmasq', _('Server Settings'));
                s.anonymous = true;
index 2a49b04817e4323c74ceed1de11ac09a192f6d97..3cdea8adbeb8a9571039c93c881e8433e4ae3fe8 100644 (file)
@@ -5,7 +5,8 @@
 return L.view.extend({
        callHostHints: rpc.declare({
                object: 'luci',
-               method: 'host_hints'
+               method: 'getHostHints',
+               expect: { '': {} }
        }),
 
        load: function() {
index c1109b5d64ac7783590a62b4057db6d117aa16a8..a5bda0576173589b5a5788dcafafd6486f092a98 100644 (file)
@@ -3,28 +3,23 @@
 'require rpc';
 'require form';
 
-var callInitAction, callLeds, callUSB, callNetdevs;
-
-callInitAction = rpc.declare({
-       object: 'luci',
-       method: 'initCall',
-       params: [ 'name', 'action' ],
-       expect: { result: false }
-});
+var callLeds, callUSB, callNetdevs;
 
 callLeds = rpc.declare({
        object: 'luci',
-       method: 'leds'
+       method: 'getLEDs',
+       expect: { '': {} }
 });
 
 callUSB = rpc.declare({
        object: 'luci',
-       method: 'usb'
+       method: 'getUSBDevices',
+       expect: { '': {} }
 });
 
 callNetdevs = rpc.declare({
        object: 'luci',
-       method: 'ifaddrs',
+       method: 'getIfaddrs',
        expect: { result: [] },
        filter: function(res) {
                var devs = {};
@@ -130,16 +125,23 @@ return L.view.extend({
                                        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+)$/))
+                                       if (value[i].match(/^(\d+)-(\d+)$/))
                                                ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2));
+                                       else
+                                               ports.push(value[i]);
 
                                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));
+                               var dev = (usbport.device && Array.isArray(usb.devices))
+                                       ? usb.devices.filter(function(d) { return d.id == usbport.device })[0] : null;
+
+                               var label = _('Port %s').format(usbport.port);
+
+                               if (dev)
+                                       label += ' (%s - %s)'.format(dev.vendor || '?', dev.product || '?');
+
+                               o.value(usbport.port, label);
                        });
                }
 
index 6db973a8df9ef40e9316a33c8baee2e1f70214c7..1ed8f64d8f0d1226529cd6a8a75f0b136b915802 100644 (file)
@@ -8,9 +8,9 @@ var callInitList, callInitAction, callTimezone,
 
 callInitList = rpc.declare({
        object: 'luci',
-       method: 'initList',
+       method: 'getInitList',
        params: [ 'name' ],
-       expect: { result: {} },
+       expect: { '': {} },
        filter: function(res) {
                for (var k in res)
                        return +res[k].enabled;
@@ -20,7 +20,7 @@ callInitList = rpc.declare({
 
 callInitAction = rpc.declare({
        object: 'luci',
-       method: 'initCall',
+       method: 'setInitAction',
        params: [ 'name', 'action' ],
        expect: { result: false }
 });
@@ -28,20 +28,20 @@ callInitAction = rpc.declare({
 callGetLocaltime = rpc.declare({
        object: 'luci',
        method: 'getLocaltime',
-       expect: { localtime: 0 }
+       expect: { result: 0 }
 });
 
 callSetLocaltime = rpc.declare({
        object: 'luci',
        method: 'setLocaltime',
        params: [ 'localtime' ],
-       expect: { localtime: 0 }
+       expect: { result: 0 }
 });
 
 callTimezone = rpc.declare({
        object: 'luci',
-       method: 'timezone',
-       expect: { result: {} }
+       method: 'getTimezones',
+       expect: { '': {} }
 });
 
 CBILocalTime = form.DummyValue.extend({
@@ -103,7 +103,6 @@ return L.view.extend({
                        _('Here you can configure the basic aspects of your device like its hostname or the timezone.'));
 
                m.chain('luci');
-               m.tabbed = true;
 
                s = m.section(form.TypedSection, 'system', _('System Properties'));
                s.anonymous = true;