X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci2%2Fui.git;a=blobdiff_plain;f=luci2%2Fhtdocs%2Fluci2%2Fluci2.js;h=3e5af4a75408809b50b77d4a0e429ef7be887ac2;hp=996c18de8c6fd8607303cf748b457b646fb7a45c;hb=e28f8f6a5a654a86dc6840f8bb1b16b888e24430;hpb=8054344747e63d78b79cc3ba58287febf4b78e46 diff --git a/luci2/htdocs/luci2/luci2.js b/luci2/htdocs/luci2/luci2.js index 996c18d..3e5af4a 100644 --- a/luci2/htdocs/luci2/luci2.js +++ b/luci2/htdocs/luci2/luci2.js @@ -1,7 +1,7 @@ /* LuCI2 - OpenWrt Web Interface - Copyright 2013 Jo-Philipp Wich + Copyright 2013-2014 Jo-Philipp Wich Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -173,9 +173,16 @@ String.prototype.format = function() return out + str; } +if (!window.location.origin) + window.location.origin = '%s//%s%s'.format( + window.location.protocol, + window.location.hostname, + (window.location.port ? ':' + window.location.port : '') + ); + function LuCI2() { - var _luci2 = this; + var L = this; var Class = function() { }; @@ -219,6 +226,55 @@ function LuCI2() return _class; }; + Class.require = function(name) + { + var path = '/' + name.replace(/\./g, '/') + '.js'; + + return $.ajax(path, { + method: 'GET', + async: false, + cache: true, + dataType: 'text' + }).then(function(text) { + var code = '%s\n\n//@ sourceURL=%s/%s'.format(text, window.location.origin, path); + var construct = eval(code); + + var parts = name.split(/\./); + var cparent = L.Class || (L.Class = { }); + + for (var i = 1; i < parts.length - 1; i++) + { + cparent = cparent[parts[i]]; + + if (!cparent) + throw "Missing parent class"; + } + + cparent[parts[i]] = construct; + }); + }; + + Class.instantiate = function(name) + { + Class.require(name).then(function() { + var parts = name.split(/\./); + var iparent = L; + var construct = L.Class; + + for (var i = 1; i < parts.length - 1; i++) + { + iparent = iparent[parts[i]]; + construct = construct[parts[i]]; + + if (!iparent) + throw "Missing parent class"; + } + + if (construct[parts[i]]) + iparent[parts[i]] = new construct[parts[i]](); + }); + }; + this.defaults = function(obj, def) { for (var key in def) @@ -253,42 +309,42 @@ function LuCI2() plural: function(n) { return 0 + (n != 1) }, init: function() { - if (_luci2.i18n.loaded) + if (L.i18n.loaded) return; var lang = (navigator.userLanguage || navigator.language || 'en').toLowerCase(); var langs = (lang.indexOf('-') > -1) ? [ lang, lang.split(/-/)[0] ] : [ lang ]; for (var i = 0; i < langs.length; i++) - $.ajax('%s/i18n/base.%s.json'.format(_luci2.globals.resource, langs[i]), { + $.ajax('%s/i18n/base.%s.json'.format(L.globals.resource, langs[i]), { async: false, cache: true, dataType: 'json', success: function(data) { - $.extend(_luci2.i18n.catalog, data); + $.extend(L.i18n.catalog, data); - var pe = _luci2.i18n.catalog['']; + var pe = L.i18n.catalog['']; if (pe) { - delete _luci2.i18n.catalog['']; + delete L.i18n.catalog['']; try { var pf = new Function('n', 'return 0 + (' + pe + ')'); - _luci2.i18n.plural = pf; + L.i18n.plural = pf; } catch (e) { }; } } }); - _luci2.i18n.loaded = true; + L.i18n.loaded = true; } }; this.tr = function(msgid) { - _luci2.i18n.init(); + L.i18n.init(); - var msgstr = _luci2.i18n.catalog[msgid]; + var msgstr = L.i18n.catalog[msgid]; if (typeof(msgstr) == 'undefined') return msgid; @@ -300,23 +356,23 @@ function LuCI2() this.trp = function(msgid, msgid_plural, count) { - _luci2.i18n.init(); + L.i18n.init(); - var msgstr = _luci2.i18n.catalog[msgid]; + var msgstr = L.i18n.catalog[msgid]; if (typeof(msgstr) == 'undefined') return (count == 1) ? msgid : msgid_plural; else if (typeof(msgstr) == 'string') return msgstr; else - return msgstr[_luci2.i18n.plural(count)]; + return msgstr[L.i18n.plural(count)]; }; this.trc = function(msgctx, msgid) { - _luci2.i18n.init(); + L.i18n.init(); - var msgstr = _luci2.i18n.catalog[msgid + '\u0004' + msgctx]; + var msgstr = L.i18n.catalog[msgid + '\u0004' + msgctx]; if (typeof(msgstr) == 'undefined') return msgid; @@ -328,16 +384,16 @@ function LuCI2() this.trcp = function(msgctx, msgid, msgid_plural, count) { - _luci2.i18n.init(); + L.i18n.init(); - var msgstr = _luci2.i18n.catalog[msgid + '\u0004' + msgctx]; + var msgstr = L.i18n.catalog[msgid + '\u0004' + msgctx]; if (typeof(msgstr) == 'undefined') return (count == 1) ? msgid : msgid_plural; else if (typeof(msgstr) == 'string') return msgstr; else - return msgstr[_luci2.i18n.plural(count)]; + return msgstr[L.i18n.plural(count)]; }; this.setHash = function(key, value) @@ -485,7018 +541,165 @@ function LuCI2() return n; }; - this.globals = { - timeout: 15000, - resource: '/luci2', - sid: '00000000000000000000000000000000' - }; - - this.rpc = { - - _id: 1, - _batch: undefined, - _requests: { }, - - _call: function(req, cb) - { - return $.ajax('/ubus', { - cache: false, - contentType: 'application/json', - data: JSON.stringify(req), - dataType: 'json', - type: 'POST', - timeout: _luci2.globals.timeout, - _rpc_req: req - }).then(cb, cb); - }, - - _list_cb: function(msg) - { - var list = msg.result; - - /* verify message frame */ - if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !$.isArray(list)) - list = [ ]; - - return $.Deferred().resolveWith(this, [ list ]); - }, - - _call_cb: function(msg) - { - var data = [ ]; - var type = Object.prototype.toString; - var reqs = this._rpc_req; - - if (!$.isArray(reqs)) - { - msg = [ msg ]; - reqs = [ reqs ]; - } - - for (var i = 0; i < msg.length; i++) - { - /* fetch related request info */ - var req = _luci2.rpc._requests[reqs[i].id]; - if (typeof(req) != 'object') - throw 'No related request for JSON response'; - - /* fetch response attribute and verify returned type */ - var ret = undefined; - - /* verify message frame */ - if (typeof(msg[i]) == 'object' && msg[i].jsonrpc == '2.0') - if ($.isArray(msg[i].result) && msg[i].result[0] == 0) - ret = (msg[i].result.length > 1) ? msg[i].result[1] : msg[i].result[0]; - - if (req.expect) - { - for (var key in req.expect) - { - if (typeof(ret) != 'undefined' && key != '') - ret = ret[key]; - - if (typeof(ret) == 'undefined' || type.call(ret) != type.call(req.expect[key])) - ret = req.expect[key]; - - break; - } - } - - /* apply filter */ - if (typeof(req.filter) == 'function') - { - req.priv[0] = ret; - req.priv[1] = req.params; - ret = req.filter.apply(_luci2.rpc, req.priv); - } - - /* store response data */ - if (typeof(req.index) == 'number') - data[req.index] = ret; - else - data = ret; - - /* delete request object */ - delete _luci2.rpc._requests[reqs[i].id]; - } - - return $.Deferred().resolveWith(this, [ data ]); - }, - - list: function() - { - var params = [ ]; - for (var i = 0; i < arguments.length; i++) - params[i] = arguments[i]; + this.toColor = function(str) + { + if (typeof(str) != 'string' || str.length == 0) + return '#CCCCCC'; - var msg = { - jsonrpc: '2.0', - id: this._id++, - method: 'list', - params: (params.length > 0) ? params : undefined - }; + if (str == 'wan') + return '#F09090'; + else if (str == 'lan') + return '#90F090'; - return this._call(msg, this._list_cb); - }, + var i = 0, hash = 0; - batch: function() - { - if (!$.isArray(this._batch)) - this._batch = [ ]; - }, + while (i < str.length) + hash = str.charCodeAt(i++) + ((hash << 5) - hash); - flush: function() - { - if (!$.isArray(this._batch)) - return _luci2.deferrable([ ]); + var r = (hash & 0xFF) % 128; + var g = ((hash >> 8) & 0xFF) % 128; - var req = this._batch; - delete this._batch; + var min = 0; + var max = 128; - /* call rpc */ - return this._call(req, this._call_cb); - }, + if ((r + g) < 128) + min = 128 - r - g; + else + max = 255 - r - g; - declare: function(options) - { - var _rpc = this; - - return function() { - /* build parameter object */ - var p_off = 0; - var params = { }; - if ($.isArray(options.params)) - for (p_off = 0; p_off < options.params.length; p_off++) - params[options.params[p_off]] = arguments[p_off]; - - /* all remaining arguments are private args */ - var priv = [ undefined, undefined ]; - for (; p_off < arguments.length; p_off++) - priv.push(arguments[p_off]); - - /* store request info */ - var req = _rpc._requests[_rpc._id] = { - expect: options.expect, - filter: options.filter, - params: params, - priv: priv - }; - - /* build message object */ - var msg = { - jsonrpc: '2.0', - id: _rpc._id++, - method: 'call', - params: [ - _luci2.globals.sid, - options.object, - options.method, - params - ] - }; - - /* when a batch is in progress then store index in request data - * and push message object onto the stack */ - if ($.isArray(_rpc._batch)) - { - req.index = _rpc._batch.push(msg) - 1; - return _luci2.deferrable(msg); - } + var b = min + (((hash >> 16) & 0xFF) % (max - min)); - /* call rpc */ - return _rpc._call(msg, _rpc._call_cb); - }; - } + return '#%02X%02X%02X'.format(0xFF - r, 0xFF - g, 0xFF - b); }; - this.UCIContext = Class.extend({ - - init: function() - { - this.state = { - newid: 0, - values: { }, - creates: { }, - changes: { }, - deletes: { }, - reorder: { } - }; - }, - - _load: _luci2.rpc.declare({ - object: 'uci', - method: 'get', - params: [ 'config' ], - expect: { values: { } } - }), - - _order: _luci2.rpc.declare({ - object: 'uci', - method: 'order', - params: [ 'config', 'sections' ] - }), - - _add: _luci2.rpc.declare({ - object: 'uci', - method: 'add', - params: [ 'config', 'type', 'name', 'values' ], - expect: { section: '' } - }), - - _set: _luci2.rpc.declare({ - object: 'uci', - method: 'set', - params: [ 'config', 'section', 'values' ] - }), - - _delete: _luci2.rpc.declare({ - object: 'uci', - method: 'delete', - params: [ 'config', 'section', 'options' ] - }), - - load: function(packages) - { - var self = this; - var seen = { }; - var pkgs = [ ]; - - if (!$.isArray(packages)) - packages = [ packages ]; - - _luci2.rpc.batch(); - - for (var i = 0; i < packages.length; i++) - if (!seen[packages[i]]) - { - pkgs.push(packages[i]); - seen[packages[i]] = true; - self._load(packages[i]); - } - - return _luci2.rpc.flush().then(function(responses) { - for (var i = 0; i < responses.length; i++) - self.state.values[pkgs[i]] = responses[i]; + this.parseIPv4 = function(str) + { + if ((typeof(str) != 'string' && !(str instanceof String)) || + !str.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) + return undefined; - return pkgs; - }); - }, + var num = [ ]; + var parts = str.split(/\./); - unload: function(packages) + for (var i = 0; i < parts.length; i++) { - if (!$.isArray(packages)) - packages = [ packages ]; - - for (var i = 0; i < packages.length; i++) - { - delete this.state.values[packages[i]]; - delete this.state.creates[packages[i]]; - delete this.state.changes[packages[i]]; - delete this.state.deletes[packages[i]]; - } - }, + var n = parseInt(parts[i], 10); + if (isNaN(n) || n > 255) + return undefined; - add: function(conf, type, name) - { - var c = this.state.creates; - var s = '.new.%d'.format(this.state.newid++); + num.push(n); + } - if (!c[conf]) - c[conf] = { }; + return num; + }; - c[conf][s] = { - '.type': type, - '.name': s, - '.create': name, - '.anonymous': !name, - '.index': 1000 + this.state.newid - }; + this.parseIPv6 = function(str) + { + if ((typeof(str) != 'string' && !(str instanceof String)) || + !str.match(/^[a-fA-F0-9:]+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/)) + return undefined; - return s; - }, + var parts = str.split(/::/); + if (parts.length == 0 || parts.length > 2) + return undefined; - remove: function(conf, sid) + var lnum = [ ]; + if (parts[0].length > 0) { - var n = this.state.creates; - var c = this.state.changes; - var d = this.state.deletes; - - /* requested deletion of a just created section */ - if (sid.indexOf('.new.') == 0) + var left = parts[0].split(/:/); + for (var i = 0; i < left.length; i++) { - if (n[conf]) - delete n[conf][sid]; - } - else - { - if (c[conf]) - delete c[conf][sid]; - - if (!d[conf]) - d[conf] = { }; + var n = parseInt(left[i], 16); + if (isNaN(n)) + return undefined; - d[conf][sid] = true; + lnum.push((n / 256) >> 0); + lnum.push(n % 256); } - }, - - sections: function(conf, type, cb) - { - var sa = [ ]; - var v = this.state.values[conf]; - var n = this.state.creates[conf]; - var c = this.state.changes[conf]; - var d = this.state.deletes[conf]; - - if (!v) - return sa; - - for (var s in v) - if (!d || d[s] !== true) - if (!type || v[s]['.type'] == type) - sa.push($.extend({ }, v[s], c ? c[s] : undefined)); - - if (n) - for (var s in n) - if (!type || n[s]['.type'] == type) - sa.push(n[s]); - - sa.sort(function(a, b) { - return a['.index'] - b['.index']; - }); - - for (var i = 0; i < sa.length; i++) - sa[i]['.index'] = i; - - if (typeof(cb) == 'function') - for (var i = 0; i < sa.length; i++) - cb.call(this, sa[i], sa[i]['.name']); - - return sa; - }, + } - get: function(conf, sid, opt) + var rnum = [ ]; + if (parts.length > 1 && parts[1].length > 0) { - var v = this.state.values; - var n = this.state.creates; - var c = this.state.changes; - var d = this.state.deletes; - - if (typeof(sid) == 'undefined') - return undefined; + var right = parts[1].split(/:/); - /* requested option in a just created section */ - if (sid.indexOf('.new.') == 0) + for (var i = 0; i < right.length; i++) { - if (!n[conf]) - return undefined; - - if (typeof(opt) == 'undefined') - return n[conf][sid]; - - return n[conf][sid][opt]; - } - - /* requested an option value */ - if (typeof(opt) != 'undefined') - { - /* check whether option was deleted */ - if (d[conf] && d[conf][sid]) + if (right[i].indexOf('.') > 0) { - if (d[conf][sid] === true) + var addr = L.parseIPv4(right[i]); + if (!addr) return undefined; - for (var i = 0; i < d[conf][sid].length; i++) - if (d[conf][sid][i] == opt) - return undefined; + rnum.push.apply(rnum, addr); + continue; } - /* check whether option was changed */ - if (c[conf] && c[conf][sid] && typeof(c[conf][sid][opt]) != 'undefined') - return c[conf][sid][opt]; - - /* return base value */ - if (v[conf] && v[conf][sid]) - return v[conf][sid][opt]; + var n = parseInt(right[i], 16); + if (isNaN(n)) + return undefined; - return undefined; + rnum.push((n / 256) >> 0); + rnum.push(n % 256); } + } - /* requested an entire section */ - if (v[conf]) - return v[conf][sid]; - + if (rnum.length > 0 && (lnum.length + rnum.length) > 15) return undefined; - }, - - set: function(conf, sid, opt, val) - { - var n = this.state.creates; - var c = this.state.changes; - var d = this.state.deletes; - - if (typeof(sid) == 'undefined' || - typeof(opt) == 'undefined' || - opt.charAt(0) == '.') - return; - - if (sid.indexOf('.new.') == 0) - { - if (n[conf] && n[conf][sid]) - { - if (typeof(val) != 'undefined') - n[conf][sid][opt] = val; - else - delete n[conf][sid][opt]; - } - } - else if (typeof(val) != 'undefined') - { - /* do not set within deleted section */ - if (d[conf] && d[conf][sid] === true) - return; - - if (!c[conf]) - c[conf] = { }; - - if (!c[conf][sid]) - c[conf][sid] = { }; - /* undelete option */ - if (d[conf] && d[conf][sid]) - d[conf][sid] = _luci2.filterArray(d[conf][sid], opt); + var num = [ ]; - c[conf][sid][opt] = val; - } - else - { - if (!d[conf]) - d[conf] = { }; - - if (!d[conf][sid]) - d[conf][sid] = [ ]; - - if (d[conf][sid] !== true) - d[conf][sid].push(opt); - } - }, - - unset: function(conf, sid, opt) - { - return this.set(conf, sid, opt, undefined); - }, - - _reload: function() - { - var pkgs = [ ]; - - for (var pkg in this.state.values) - pkgs.push(pkg); - - this.init(); - - return this.load(pkgs); - }, - - _reorder: function() - { - var v = this.state.values; - var n = this.state.creates; - var r = this.state.reorder; - - if ($.isEmptyObject(r)) - return _luci2.deferrable(); - - _luci2.rpc.batch(); + num.push.apply(num, lnum); - /* - gather all created and existing sections, sort them according - to their index value and issue an uci order call - */ - for (var c in r) - { - var o = [ ]; - - if (n && n[c]) - for (var s in n[c]) - o.push(n[c][s]); - - for (var s in v[c]) - o.push(v[c][s]); - - if (o.length > 0) - { - o.sort(function(a, b) { - return (a['.index'] - b['.index']); - }); - - var sids = [ ]; + for (var i = 0; i < (16 - lnum.length - rnum.length); i++) + num.push(0); - for (var i = 0; i < o.length; i++) - sids.push(o[i]['.name']); - - this._order(c, sids); - } - } + num.push.apply(num, rnum); - this.state.reorder = { }; - return _luci2.rpc.flush(); - }, + if (num.length > 16) + return undefined; - swap: function(conf, sid1, sid2) - { - var s1 = this.get(conf, sid1); - var s2 = this.get(conf, sid2); - var n1 = s1 ? s1['.index'] : NaN; - var n2 = s2 ? s2['.index'] : NaN; + return num; + }; - if (isNaN(n1) || isNaN(n2)) - return false; + this.isNetmask = function(addr) + { + if (!$.isArray(addr)) + return false; - s1['.index'] = n2; - s2['.index'] = n1; + var c; - this.state.reorder[conf] = true; + for (c = 0; (c < addr.length) && (addr[c] == 255); c++); + if (c == addr.length) return true; - }, - - save: function() - { - _luci2.rpc.batch(); - var self = this; - var snew = [ ]; - - if (self.state.creates) - for (var c in self.state.creates) - for (var s in self.state.creates[c]) - { - var r = { - config: c, - values: { } - }; - - for (var k in self.state.creates[c][s]) - { - if (k == '.type') - r.type = self.state.creates[c][s][k]; - else if (k == '.create') - r.name = self.state.creates[c][s][k]; - else if (k.charAt(0) != '.') - r.values[k] = self.state.creates[c][s][k]; - } - - snew.push(self.state.creates[c][s]); - - self._add(r.config, r.type, r.name, r.values); - } - - if (self.state.changes) - for (var c in self.state.changes) - for (var s in self.state.changes[c]) - self._set(c, s, self.state.changes[c][s]); - - if (self.state.deletes) - for (var c in self.state.deletes) - for (var s in self.state.deletes[c]) - { - var o = self.state.deletes[c][s]; - self._delete(c, s, (o === true) ? undefined : o); - } - - return _luci2.rpc.flush().then(function(responses) { - /* - array "snew" holds references to the created uci sections, - use it to assign the returned names of the new sections - */ - for (var i = 0; i < snew.length; i++) - snew[i]['.name'] = responses[i]; - - return self._reorder(); - }); - }, - - _apply: _luci2.rpc.declare({ - object: 'uci', - method: 'apply', - params: [ 'timeout', 'rollback' ] - }), - - _confirm: _luci2.rpc.declare({ - object: 'uci', - method: 'confirm' - }), - - apply: function(timeout) - { - var self = this; - var date = new Date(); - var deferred = $.Deferred(); - - if (typeof(timeout) != 'number' || timeout < 1) - timeout = 10; - - self._apply(timeout, true).then(function(rv) { - if (rv != 0) - { - deferred.rejectWith(self, [ rv ]); - return; - } - - var try_deadline = date.getTime() + 1000 * timeout; - var try_confirm = function() - { - return self._confirm().then(function(rv) { - if (rv != 0) - { - if (date.getTime() < try_deadline) - window.setTimeout(try_confirm, 250); - else - deferred.rejectWith(self, [ rv ]); - - return; - } - - deferred.resolveWith(self, [ rv ]); - }); - }; - - window.setTimeout(try_confirm, 1000); - }); - - return deferred; - }, - - changes: _luci2.rpc.declare({ - object: 'uci', - method: 'changes', - expect: { changes: { } } - }), - - readable: function(conf) + if ((addr[c] == 254) || (addr[c] == 252) || (addr[c] == 248) || + (addr[c] == 240) || (addr[c] == 224) || (addr[c] == 192) || + (addr[c] == 128) || (addr[c] == 0)) { - return _luci2.session.hasACL('uci', conf, 'read'); - }, + for (c++; (c < addr.length) && (addr[c] == 0); c++); - writable: function(conf) - { - return _luci2.session.hasACL('uci', conf, 'write'); + if (c == addr.length) + return true; } - }); - - this.uci = new this.UCIContext(); - - this.wireless = { - listDeviceNames: _luci2.rpc.declare({ - object: 'iwinfo', - method: 'devices', - expect: { 'devices': [ ] }, - filter: function(data) { - data.sort(); - return data; - } - }), - - getDeviceStatus: _luci2.rpc.declare({ - object: 'iwinfo', - method: 'info', - params: [ 'device' ], - expect: { '': { } }, - filter: function(data, params) { - if (!$.isEmptyObject(data)) - { - data['device'] = params['device']; - return data; - } - return undefined; - } - }), - - getAssocList: _luci2.rpc.declare({ - object: 'iwinfo', - method: 'assoclist', - params: [ 'device' ], - expect: { results: [ ] }, - filter: function(data, params) { - for (var i = 0; i < data.length; i++) - data[i]['device'] = params['device']; - - data.sort(function(a, b) { - if (a.bssid < b.bssid) - return -1; - else if (a.bssid > b.bssid) - return 1; - else - return 0; - }); - - return data; - } - }), - - getWirelessStatus: function() { - return this.listDeviceNames().then(function(names) { - _luci2.rpc.batch(); - - for (var i = 0; i < names.length; i++) - _luci2.wireless.getDeviceStatus(names[i]); - - return _luci2.rpc.flush(); - }).then(function(networks) { - var rv = { }; - - var phy_attrs = [ - 'country', 'channel', 'frequency', 'frequency_offset', - 'txpower', 'txpower_offset', 'hwmodes', 'hardware', 'phy' - ]; - - var net_attrs = [ - 'ssid', 'bssid', 'mode', 'quality', 'quality_max', - 'signal', 'noise', 'bitrate', 'encryption' - ]; - - for (var i = 0; i < networks.length; i++) - { - var phy = rv[networks[i].phy] || ( - rv[networks[i].phy] = { networks: [ ] } - ); - - var net = { - device: networks[i].device - }; - - for (var j = 0; j < phy_attrs.length; j++) - phy[phy_attrs[j]] = networks[i][phy_attrs[j]]; - - for (var j = 0; j < net_attrs.length; j++) - net[net_attrs[j]] = networks[i][net_attrs[j]]; - - phy.networks.push(net); - } - - return rv; - }); - }, - - getAssocLists: function() - { - return this.listDeviceNames().then(function(names) { - _luci2.rpc.batch(); - - for (var i = 0; i < names.length; i++) - _luci2.wireless.getAssocList(names[i]); - - return _luci2.rpc.flush(); - }).then(function(assoclists) { - var rv = [ ]; - - for (var i = 0; i < assoclists.length; i++) - for (var j = 0; j < assoclists[i].length; j++) - rv.push(assoclists[i][j]); - - return rv; - }); - }, - - formatEncryption: function(enc) - { - var format_list = function(l, s) - { - var rv = [ ]; - for (var i = 0; i < l.length; i++) - rv.push(l[i].toUpperCase()); - return rv.join(s ? s : ', '); - } - - if (!enc || !enc.enabled) - return _luci2.tr('None'); - - if (enc.wep) - { - if (enc.wep.length == 2) - return _luci2.tr('WEP Open/Shared') + ' (%s)'.format(format_list(enc.ciphers, ', ')); - else if (enc.wep[0] == 'shared') - return _luci2.tr('WEP Shared Auth') + ' (%s)'.format(format_list(enc.ciphers, ', ')); - else - return _luci2.tr('WEP Open System') + ' (%s)'.format(format_list(enc.ciphers, ', ')); - } - else if (enc.wpa) - { - if (enc.wpa.length == 2) - return _luci2.tr('mixed WPA/WPA2') + ' %s (%s)'.format( - format_list(enc.authentication, '/'), - format_list(enc.ciphers, ', ') - ); - else if (enc.wpa[0] == 2) - return 'WPA2 %s (%s)'.format( - format_list(enc.authentication, '/'), - format_list(enc.ciphers, ', ') - ); - else - return 'WPA %s (%s)'.format( - format_list(enc.authentication, '/'), - format_list(enc.ciphers, ', ') - ); - } - return _luci2.tr('Unknown'); - } + return false; }; - this.firewall = { - getZoneColor: function(zone) - { - if ($.isPlainObject(zone)) - zone = zone.name; - - if (zone == 'lan') - return '#90f090'; - else if (zone == 'wan') - return '#f09090'; - - for (var i = 0, hash = 0; - i < zone.length; - hash = zone.charCodeAt(i++) + ((hash << 5) - hash)); - - for (var i = 0, color = '#'; - i < 3; - color += ('00' + ((hash >> i++ * 8) & 0xFF).tostring(16)).slice(-2)); - - return color; - }, - - findZoneByNetwork: function(network) - { - var self = this; - var zone = undefined; - - return _luci2.uci.sections('firewall', 'zone', function(z) { - if (!z.name || !z.network) - return; - - if (!$.isArray(z.network)) - z.network = z.network.split(/\s+/); - - for (var i = 0; i < z.network.length; i++) - { - if (z.network[i] == network) - { - zone = z; - break; - } - } - }).then(function() { - if (zone) - zone.color = self.getZoneColor(zone); - - return zone; - }); - } + this.globals = { + timeout: 15000, + resource: '/luci2', + sid: '00000000000000000000000000000000' }; - this.NetworkModel = { - _device_blacklist: [ - /^gre[0-9]+$/, - /^gretap[0-9]+$/, - /^ifb[0-9]+$/, - /^ip6tnl[0-9]+$/, - /^sit[0-9]+$/, - /^wlan[0-9]+\.sta[0-9]+$/ - ], - - _cache_functions: [ - 'protolist', 0, _luci2.rpc.declare({ - object: 'network', - method: 'get_proto_handlers', - expect: { '': { } } - }), - 'ifstate', 1, _luci2.rpc.declare({ - object: 'network.interface', - method: 'dump', - expect: { 'interface': [ ] } - }), - 'devstate', 2, _luci2.rpc.declare({ - object: 'network.device', - method: 'status', - expect: { '': { } } - }), - 'wifistate', 0, _luci2.rpc.declare({ - object: 'network.wireless', - method: 'status', - expect: { '': { } } - }), - 'bwstate', 2, _luci2.rpc.declare({ - object: 'luci2.network.bwmon', - method: 'statistics', - expect: { 'statistics': { } } - }), - 'devlist', 2, _luci2.rpc.declare({ - object: 'luci2.network', - method: 'device_list', - expect: { 'devices': [ ] } - }), - 'swlist', 0, _luci2.rpc.declare({ - object: 'luci2.network', - method: 'switch_list', - expect: { 'switches': [ ] } - }) - ], - - _fetch_protocol: function(proto) - { - var url = _luci2.globals.resource + '/proto/' + proto + '.js'; - var self = _luci2.NetworkModel; - - var def = $.Deferred(); - - $.ajax(url, { - method: 'GET', - cache: true, - dataType: 'text' - }).then(function(data) { - try { - var protoConstructorSource = ( - '(function(L, $) { ' + - 'return %s' + - '})(_luci2, $);\n\n' + - '//@ sourceURL=%s' - ).format(data, url); - - var protoClass = eval(protoConstructorSource); - - self._protos[proto] = new protoClass(); - } - catch(e) { - alert('Unable to instantiate proto "%s": %s'.format(url, e)); - }; - - def.resolve(); - }).fail(function() { - def.resolve(); - }); - - return def; - }, - - _fetch_protocols: function() - { - var self = _luci2.NetworkModel; - var deferreds = [ ]; - - for (var proto in self._cache.protolist) - deferreds.push(self._fetch_protocol(proto)); - - return $.when.apply($, deferreds); - }, - - _fetch_swstate: _luci2.rpc.declare({ - object: 'luci2.network', - method: 'switch_info', - params: [ 'switch' ], - expect: { 'info': { } } - }), - - _fetch_swstate_cb: function(responses) { - var self = _luci2.NetworkModel; - var swlist = self._cache.swlist; - var swstate = self._cache.swstate = { }; - - for (var i = 0; i < responses.length; i++) - swstate[swlist[i]] = responses[i]; - }, - - _fetch_cache_cb: function(level) - { - var self = _luci2.NetworkModel; - var name = '_fetch_cache_cb_' + level; - - return self[name] || ( - self[name] = function(responses) - { - for (var i = 0; i < self._cache_functions.length; i += 3) - if (!level || self._cache_functions[i + 1] == level) - self._cache[self._cache_functions[i]] = responses.shift(); - - if (!level) - { - _luci2.rpc.batch(); - - for (var i = 0; i < self._cache.swlist.length; i++) - self._fetch_swstate(self._cache.swlist[i]); - - return _luci2.rpc.flush().then(self._fetch_swstate_cb); - } - - return _luci2.deferrable(); - } - ); - }, - - _fetch_cache: function(level) - { - var self = _luci2.NetworkModel; - - return _luci2.uci.load(['network', 'wireless']).then(function() { - _luci2.rpc.batch(); - - for (var i = 0; i < self._cache_functions.length; i += 3) - if (!level || self._cache_functions[i + 1] == level) - self._cache_functions[i + 2](); - - return _luci2.rpc.flush().then(self._fetch_cache_cb(level || 0)); - }); - }, - - _get: function(pkg, sid, key) - { - return _luci2.uci.get(pkg, sid, key); - }, - - _set: function(pkg, sid, key, val) - { - return _luci2.uci.set(pkg, sid, key, val); - }, - - _is_blacklisted: function(dev) - { - for (var i = 0; i < this._device_blacklist.length; i++) - if (dev.match(this._device_blacklist[i])) - return true; - - return false; - }, - - _sort_devices: function(a, b) - { - if (a.options.kind < b.options.kind) - return -1; - else if (a.options.kind > b.options.kind) - return 1; - - if (a.options.name < b.options.name) - return -1; - else if (a.options.name > b.options.name) - return 1; - - return 0; - }, - - _get_dev: function(ifname) - { - var alias = (ifname.charAt(0) == '@'); - return this._devs[ifname] || ( - this._devs[ifname] = { - ifname: ifname, - kind: alias ? 'alias' : 'ethernet', - type: alias ? 0 : 1, - up: false, - changed: { } - } - ); - }, - - _get_iface: function(name) - { - return this._ifaces[name] || ( - this._ifaces[name] = { - name: name, - proto: this._protos.none, - changed: { } - } - ); - }, - - _parse_devices: function() - { - var self = _luci2.NetworkModel; - var wificount = { }; - - for (var ifname in self._cache.devstate) - { - if (self._is_blacklisted(ifname)) - continue; - - var dev = self._cache.devstate[ifname]; - var entry = self._get_dev(ifname); - - entry.up = dev.up; - - switch (dev.type) - { - case 'IP tunnel': - entry.kind = 'tunnel'; - break; - - case 'Bridge': - entry.kind = 'bridge'; - //entry.ports = dev['bridge-members'].sort(); - break; - } - } - - for (var i = 0; i < self._cache.devlist.length; i++) - { - var dev = self._cache.devlist[i]; - - if (self._is_blacklisted(dev.device)) - continue; - - var entry = self._get_dev(dev.device); - - entry.up = dev.is_up; - entry.type = dev.type; - - switch (dev.type) - { - case 1: /* Ethernet */ - if (dev.is_bridge) - entry.kind = 'bridge'; - else if (dev.is_tuntap) - entry.kind = 'tunnel'; - else if (dev.is_wireless) - entry.kind = 'wifi'; - break; - - case 512: /* PPP */ - case 768: /* IP-IP Tunnel */ - case 769: /* IP6-IP6 Tunnel */ - case 776: /* IPv6-in-IPv4 */ - case 778: /* GRE over IP */ - entry.kind = 'tunnel'; - break; - } - } - - var net = _luci2.uci.sections('network'); - for (var i = 0; i < net.length; i++) - { - var s = net[i]; - var sid = s['.name']; - - if (s['.type'] == 'device' && s.name) - { - var entry = self._get_dev(s.name); - - switch (s.type) - { - case 'macvlan': - case 'tunnel': - entry.kind = 'tunnel'; - break; - } - - entry.sid = sid; - } - else if (s['.type'] == 'interface' && !s['.anonymous'] && s.ifname) - { - var ifnames = _luci2.toArray(s.ifname); - - for (var j = 0; j < ifnames.length; j++) - self._get_dev(ifnames[j]); - - if (s['.name'] != 'loopback') - { - var entry = self._get_dev('@%s'.format(s['.name'])); - - entry.type = 0; - entry.kind = 'alias'; - entry.sid = sid; - } - } - else if (s['.type'] == 'switch_vlan' && s.device) - { - var sw = self._cache.swstate[s.device]; - var vid = parseInt(s.vid || s.vlan); - var ports = _luci2.toArray(s.ports); - - if (!sw || !ports.length || isNaN(vid)) - continue; - - var ifname = undefined; - - for (var j = 0; j < ports.length; j++) - { - var port = parseInt(ports[j]); - var tag = (ports[j].replace(/[^tu]/g, '') == 't'); - - if (port == sw.cpu_port) - { - // XXX: need a way to map switch to netdev - if (tag) - ifname = 'eth0.%d'.format(vid); - else - ifname = 'eth0'; - - break; - } - } - - if (!ifname) - continue; - - var entry = self._get_dev(ifname); - - entry.kind = 'vlan'; - entry.sid = sid; - entry.vsw = sw; - entry.vid = vid; - } - } - - var wifi = _luci2.uci.sections('wireless'); - for (var i = 0; i < wifi.length; i++) - { - var s = wifi[i]; - var sid = s['.name']; - - if (s['.type'] == 'wifi-iface' && s.device) - { - var r = parseInt(s.device.replace(/^[^0-9]+/, '')); - var n = wificount[s.device] = (wificount[s.device] || 0) + 1; - var id = 'radio%d.network%d'.format(r, n); - var ifname = id; - - if (self._cache.wifistate[s.device]) - { - var ifcs = self._cache.wifistate[s.device].interfaces; - for (var ifc in ifcs) - { - if (ifcs[ifc].section == sid) - { - ifname = ifcs[ifc].ifname; - break; - } - } - } - - var entry = self._get_dev(ifname); - - entry.kind = 'wifi'; - entry.sid = sid; - entry.wid = id; - entry.wdev = s.device; - entry.wmode = s.mode; - entry.wssid = s.ssid; - entry.wbssid = s.bssid; - } - } - - for (var i = 0; i < net.length; i++) - { - var s = net[i]; - var sid = s['.name']; - - if (s['.type'] == 'interface' && !s['.anonymous'] && s.type == 'bridge') - { - var ifnames = _luci2.toArray(s.ifname); - - for (var ifname in self._devs) - { - var dev = self._devs[ifname]; - - if (dev.kind != 'wifi') - continue; - - var wnets = _luci2.toArray(_luci2.uci.get('wireless', dev.sid, 'network')); - if ($.inArray(sid, wnets) > -1) - ifnames.push(ifname); - } - - entry = self._get_dev('br-%s'.format(s['.name'])); - entry.type = 1; - entry.kind = 'bridge'; - entry.sid = sid; - entry.ports = ifnames.sort(); - } - } - }, - - _parse_interfaces: function() - { - var self = _luci2.NetworkModel; - var net = _luci2.uci.sections('network'); - - for (var i = 0; i < net.length; i++) - { - var s = net[i]; - var sid = s['.name']; - - if (s['.type'] == 'interface' && !s['.anonymous'] && s.proto) - { - var entry = self._get_iface(s['.name']); - var proto = self._protos[s.proto] || self._protos.none; - - var l3dev = undefined; - var l2dev = undefined; - - var ifnames = _luci2.toArray(s.ifname); - - for (var ifname in self._devs) - { - var dev = self._devs[ifname]; - - if (dev.kind != 'wifi') - continue; - - var wnets = _luci2.toArray(_luci2.uci.get('wireless', dev.sid, 'network')); - if ($.inArray(entry.name, wnets) > -1) - ifnames.push(ifname); - } - - if (proto.virtual) - l3dev = '%s-%s'.format(s.proto, entry.name); - else if (s.type == 'bridge') - l3dev = 'br-%s'.format(entry.name); - else - l3dev = ifnames[0]; - - if (!proto.virtual && s.type == 'bridge') - l2dev = 'br-%s'.format(entry.name); - else if (!proto.virtual) - l2dev = ifnames[0]; - - entry.proto = proto; - entry.sid = sid; - entry.l3dev = l3dev; - entry.l2dev = l2dev; - } - } - - for (var i = 0; i < self._cache.ifstate.length; i++) - { - var iface = self._cache.ifstate[i]; - var entry = self._get_iface(iface['interface']); - var proto = self._protos[iface.proto] || self._protos.none; - - /* this is a virtual interface, either deleted from config but - not applied yet or set up from external tools (6rd) */ - if (!entry.sid) - { - entry.proto = proto; - entry.l2dev = iface.device; - entry.l3dev = iface.l3_device; - } - } - }, - - init: function() - { - var self = this; - - if (self._cache) - return _luci2.deferrable(); - - self._cache = { }; - self._devs = { }; - self._ifaces = { }; - self._protos = { }; - - return self._fetch_cache() - .then(self._fetch_protocols) - .then(self._parse_devices) - .then(self._parse_interfaces); - }, - - update: function() - { - delete this._cache; - return this.init(); - }, - - refreshInterfaceStatus: function() - { - return this._fetch_cache(1).then(this._parse_interfaces); - }, - - refreshDeviceStatus: function() - { - return this._fetch_cache(2).then(this._parse_devices); - }, - - refreshStatus: function() - { - return this._fetch_cache(1) - .then(this._fetch_cache(2)) - .then(this._parse_devices) - .then(this._parse_interfaces); - }, - - getDevices: function() - { - var devs = [ ]; - - for (var ifname in this._devs) - if (ifname != 'lo') - devs.push(new _luci2.NetworkModel.Device(this._devs[ifname])); - - return devs.sort(this._sort_devices); - }, - - getDeviceByInterface: function(iface) - { - if (iface instanceof _luci2.NetworkModel.Interface) - iface = iface.name(); - - if (this._ifaces[iface]) - return this.getDevice(this._ifaces[iface].l3dev) || - this.getDevice(this._ifaces[iface].l2dev); - - return undefined; - }, - - getDevice: function(ifname) - { - if (this._devs[ifname]) - return new _luci2.NetworkModel.Device(this._devs[ifname]); - - return undefined; - }, - - createDevice: function(name) - { - return new _luci2.NetworkModel.Device(this._get_dev(name)); - }, - - getInterfaces: function() - { - var ifaces = [ ]; - - for (var name in this._ifaces) - if (name != 'loopback') - ifaces.push(this.getInterface(name)); - - ifaces.sort(function(a, b) { - if (a.name() < b.name()) - return -1; - else if (a.name() > b.name()) - return 1; - else - return 0; - }); - - return ifaces; - }, - - getInterfacesByDevice: function(dev) - { - var ifaces = [ ]; - - if (dev instanceof _luci2.NetworkModel.Device) - dev = dev.name(); - - for (var name in this._ifaces) - { - var iface = this._ifaces[name]; - if (iface.l2dev == dev || iface.l3dev == dev) - ifaces.push(this.getInterface(name)); - } - - ifaces.sort(function(a, b) { - if (a.name() < b.name()) - return -1; - else if (a.name() > b.name()) - return 1; - else - return 0; - }); - - return ifaces; - }, - - getInterface: function(iface) - { - if (this._ifaces[iface]) - return new _luci2.NetworkModel.Interface(this._ifaces[iface]); - - return undefined; - }, - - getProtocols: function() - { - var rv = [ ]; - - for (var proto in this._protos) - { - var pr = this._protos[proto]; - - rv.push({ - name: proto, - description: pr.description, - virtual: pr.virtual, - tunnel: pr.tunnel - }); - } - - return rv.sort(function(a, b) { - if (a.name < b.name) - return -1; - else if (a.name > b.name) - return 1; - else - return 0; - }); - }, - - _find_wan: function(ipaddr) - { - for (var i = 0; i < this._cache.ifstate.length; i++) - { - var ifstate = this._cache.ifstate[i]; - - if (!ifstate.route) - continue; - - for (var j = 0; j < ifstate.route.length; j++) - if (ifstate.route[j].mask == 0 && - ifstate.route[j].target == ipaddr && - typeof(ifstate.route[j].table) == 'undefined') - { - return this.getInterface(ifstate['interface']); - } - } - - return undefined; - }, - - findWAN: function() - { - return this._find_wan('0.0.0.0'); - }, - - findWAN6: function() - { - return this._find_wan('::'); - }, - - resolveAlias: function(ifname) - { - if (ifname instanceof _luci2.NetworkModel.Device) - ifname = ifname.name(); - - var dev = this._devs[ifname]; - var seen = { }; - - while (dev && dev.kind == 'alias') - { - // loop - if (seen[dev.ifname]) - return undefined; - - var ifc = this._ifaces[dev.sid]; - - seen[dev.ifname] = true; - dev = ifc ? this._devs[ifc.l3dev] : undefined; - } - - return dev ? this.getDevice(dev.ifname) : undefined; - } - }; - - this.NetworkModel.Device = Class.extend({ - _wifi_modes: { - ap: _luci2.tr('Master'), - sta: _luci2.tr('Client'), - adhoc: _luci2.tr('Ad-Hoc'), - monitor: _luci2.tr('Monitor'), - wds: _luci2.tr('Static WDS') - }, - - _status: function(key) - { - var s = _luci2.NetworkModel._cache.devstate[this.options.ifname]; - - if (s) - return key ? s[key] : s; - - return undefined; - }, - - get: function(key) - { - var sid = this.options.sid; - var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network'; - return _luci2.NetworkModel._get(pkg, sid, key); - }, - - set: function(key, val) - { - var sid = this.options.sid; - var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network'; - return _luci2.NetworkModel._set(pkg, sid, key, val); - }, - - init: function() - { - if (typeof(this.options.type) == 'undefined') - this.options.type = 1; - - if (typeof(this.options.kind) == 'undefined') - this.options.kind = 'ethernet'; - - if (typeof(this.options.networks) == 'undefined') - this.options.networks = [ ]; - }, - - name: function() - { - return this.options.ifname; - }, - - description: function() - { - switch (this.options.kind) - { - case 'alias': - return _luci2.tr('Alias for network "%s"').format(this.options.ifname.substring(1)); - - case 'bridge': - return _luci2.tr('Network bridge'); - - case 'ethernet': - return _luci2.tr('Network device'); - - case 'tunnel': - switch (this.options.type) - { - case 1: /* tuntap */ - return _luci2.tr('TAP device'); - - case 512: /* PPP */ - return _luci2.tr('PPP tunnel'); - - case 768: /* IP-IP Tunnel */ - return _luci2.tr('IP-in-IP tunnel'); - - case 769: /* IP6-IP6 Tunnel */ - return _luci2.tr('IPv6-in-IPv6 tunnel'); - - case 776: /* IPv6-in-IPv4 */ - return _luci2.tr('IPv6-over-IPv4 tunnel'); - break; - - case 778: /* GRE over IP */ - return _luci2.tr('GRE-over-IP tunnel'); - - default: - return _luci2.tr('Tunnel device'); - } - - case 'vlan': - return _luci2.tr('VLAN %d on %s').format(this.options.vid, this.options.vsw.model); - - case 'wifi': - var o = this.options; - return _luci2.trc('(Wifi-Mode) "(SSID)" on (radioX)', '%s "%h" on %s').format( - o.wmode ? this._wifi_modes[o.wmode] : _luci2.tr('Unknown mode'), - o.wssid || '?', o.wdev - ); - } - - return _luci2.tr('Unknown device'); - }, - - icon: function(up) - { - var kind = this.options.kind; - - if (kind == 'alias') - kind = 'ethernet'; - - if (typeof(up) == 'undefined') - up = this.isUp(); - - return _luci2.globals.resource + '/icons/%s%s.png'.format(kind, up ? '' : '_disabled'); - }, - - isUp: function() - { - var l = _luci2.NetworkModel._cache.devlist; - - for (var i = 0; i < l.length; i++) - if (l[i].device == this.options.ifname) - return (l[i].is_up === true); - - return false; - }, - - isAlias: function() - { - return (this.options.kind == 'alias'); - }, - - isBridge: function() - { - return (this.options.kind == 'bridge'); - }, - - isBridgeable: function() - { - return (this.options.type == 1 && this.options.kind != 'bridge'); - }, - - isWireless: function() - { - return (this.options.kind == 'wifi'); - }, - - isInNetwork: function(net) - { - if (!(net instanceof _luci2.NetworkModel.Interface)) - net = _luci2.NetworkModel.getInterface(net); - - if (net) - { - if (net.options.l3dev == this.options.ifname || - net.options.l2dev == this.options.ifname) - return true; - - var dev = _luci2.NetworkModel._devs[net.options.l2dev]; - if (dev && dev.kind == 'bridge' && dev.ports) - return ($.inArray(this.options.ifname, dev.ports) > -1); - } - - return false; - }, - - getMTU: function() - { - var dev = _luci2.NetworkModel._cache.devstate[this.options.ifname]; - if (dev && !isNaN(dev.mtu)) - return dev.mtu; - - return undefined; - }, - - getMACAddress: function() - { - if (this.options.type != 1) - return undefined; - - var dev = _luci2.NetworkModel._cache.devstate[this.options.ifname]; - if (dev && dev.macaddr) - return dev.macaddr.toUpperCase(); - - return undefined; - }, - - getInterfaces: function() - { - return _luci2.NetworkModel.getInterfacesByDevice(this.options.name); - }, - - getStatistics: function() - { - var s = this._status('statistics') || { }; - return { - rx_bytes: (s.rx_bytes || 0), - tx_bytes: (s.tx_bytes || 0), - rx_packets: (s.rx_packets || 0), - tx_packets: (s.tx_packets || 0) - }; - }, - - getTrafficHistory: function() - { - var def = new Array(120); - - for (var i = 0; i < 120; i++) - def[i] = 0; - - var h = _luci2.NetworkModel._cache.bwstate[this.options.ifname] || { }; - return { - rx_bytes: (h.rx_bytes || def), - tx_bytes: (h.tx_bytes || def), - rx_packets: (h.rx_packets || def), - tx_packets: (h.tx_packets || def) - }; - }, - - removeFromInterface: function(iface) - { - if (!(iface instanceof _luci2.NetworkModel.Interface)) - iface = _luci2.NetworkModel.getInterface(iface); - - if (!iface) - return; - - var ifnames = _luci2.toArray(iface.get('ifname')); - if ($.inArray(this.options.ifname, ifnames) > -1) - iface.set('ifname', _luci2.filterArray(ifnames, this.options.ifname)); - - if (this.options.kind != 'wifi') - return; - - var networks = _luci2.toArray(this.get('network')); - if ($.inArray(iface.name(), networks) > -1) - this.set('network', _luci2.filterArray(networks, iface.name())); - }, - - attachToInterface: function(iface) - { - if (!(iface instanceof _luci2.NetworkModel.Interface)) - iface = _luci2.NetworkModel.getInterface(iface); - - if (!iface) - return; - - if (this.options.kind != 'wifi') - { - var ifnames = _luci2.toArray(iface.get('ifname')); - if ($.inArray(this.options.ifname, ifnames) < 0) - { - ifnames.push(this.options.ifname); - iface.set('ifname', (ifnames.length > 1) ? ifnames : ifnames[0]); - } - } - else - { - var networks = _luci2.toArray(this.get('network')); - if ($.inArray(iface.name(), networks) < 0) - { - networks.push(iface.name()); - this.set('network', (networks.length > 1) ? networks : networks[0]); - } - } - } - }); - - this.NetworkModel.Interface = Class.extend({ - _status: function(key) - { - var s = _luci2.NetworkModel._cache.ifstate; - - for (var i = 0; i < s.length; i++) - if (s[i]['interface'] == this.options.name) - return key ? s[i][key] : s[i]; - - return undefined; - }, - - get: function(key) - { - return _luci2.NetworkModel._get('network', this.options.name, key); - }, - - set: function(key, val) - { - return _luci2.NetworkModel._set('network', this.options.name, key, val); - }, - - name: function() - { - return this.options.name; - }, - - protocol: function() - { - return (this.get('proto') || 'none'); - }, - - isUp: function() - { - return (this._status('up') === true); - }, - - isVirtual: function() - { - return (typeof(this.options.sid) != 'string'); - }, - - getProtocol: function() - { - var prname = this.get('proto') || 'none'; - return _luci2.NetworkModel._protos[prname] || _luci2.NetworkModel._protos.none; - }, - - getUptime: function() - { - var uptime = this._status('uptime'); - return isNaN(uptime) ? 0 : uptime; - }, - - getDevice: function(resolveAlias) - { - if (this.options.l3dev) - return _luci2.NetworkModel.getDevice(this.options.l3dev); - - return undefined; - }, - - getPhysdev: function() - { - if (this.options.l2dev) - return _luci2.NetworkModel.getDevice(this.options.l2dev); - - return undefined; - }, - - getSubdevices: function() - { - var rv = [ ]; - var dev = this.options.l2dev ? - _luci2.NetworkModel._devs[this.options.l2dev] : undefined; - - if (dev && dev.kind == 'bridge' && dev.ports && dev.ports.length) - for (var i = 0; i < dev.ports.length; i++) - rv.push(_luci2.NetworkModel.getDevice(dev.ports[i])); - - return rv; - }, - - getIPv4Addrs: function(mask) - { - var rv = [ ]; - var addrs = this._status('ipv4-address'); - - if (addrs) - for (var i = 0; i < addrs.length; i++) - if (!mask) - rv.push(addrs[i].address); - else - rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask)); - - return rv; - }, - - getIPv6Addrs: function(mask) - { - var rv = [ ]; - var addrs; - - addrs = this._status('ipv6-address'); - - if (addrs) - for (var i = 0; i < addrs.length; i++) - if (!mask) - rv.push(addrs[i].address); - else - rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask)); - - addrs = this._status('ipv6-prefix-assignment'); - - if (addrs) - for (var i = 0; i < addrs.length; i++) - if (!mask) - rv.push('%s1'.format(addrs[i].address)); - else - rv.push('%s1/%d'.format(addrs[i].address, addrs[i].mask)); - - return rv; - }, - - getDNSAddrs: function() - { - var rv = [ ]; - var addrs = this._status('dns-server'); - - if (addrs) - for (var i = 0; i < addrs.length; i++) - rv.push(addrs[i]); - - return rv; - }, - - getIPv4DNS: function() - { - var rv = [ ]; - var dns = this._status('dns-server'); - - if (dns) - for (var i = 0; i < dns.length; i++) - if (dns[i].indexOf(':') == -1) - rv.push(dns[i]); - - return rv; - }, - - getIPv6DNS: function() - { - var rv = [ ]; - var dns = this._status('dns-server'); - - if (dns) - for (var i = 0; i < dns.length; i++) - if (dns[i].indexOf(':') > -1) - rv.push(dns[i]); - - return rv; - }, - - getIPv4Gateway: function() - { - var rt = this._status('route'); - - if (rt) - for (var i = 0; i < rt.length; i++) - if (rt[i].target == '0.0.0.0' && rt[i].mask == 0) - return rt[i].nexthop; - - return undefined; - }, - - getIPv6Gateway: function() - { - var rt = this._status('route'); - - if (rt) - for (var i = 0; i < rt.length; i++) - if (rt[i].target == '::' && rt[i].mask == 0) - return rt[i].nexthop; - - return undefined; - }, - - getStatistics: function() - { - var dev = this.getDevice() || new _luci2.NetworkModel.Device({}); - return dev.getStatistics(); - }, - - getTrafficHistory: function() - { - var dev = this.getDevice() || new _luci2.NetworkModel.Device({}); - return dev.getTrafficHistory(); - }, - - setDevices: function(devs) - { - var dev = this.getPhysdev(); - var old_devs = [ ]; - var changed = false; - - if (dev && dev.isBridge()) - old_devs = this.getSubdevices(); - else if (dev) - old_devs = [ dev ]; - - if (old_devs.length != devs.length) - changed = true; - else - for (var i = 0; i < old_devs.length; i++) - { - var dev = devs[i]; - - if (dev instanceof _luci2.NetworkModel.Device) - dev = dev.name(); - - if (!dev || old_devs[i].name() != dev) - { - changed = true; - break; - } - } - - if (changed) - { - for (var i = 0; i < old_devs.length; i++) - old_devs[i].removeFromInterface(this); - - for (var i = 0; i < devs.length; i++) - { - var dev = devs[i]; - - if (!(dev instanceof _luci2.NetworkModel.Device)) - dev = _luci2.NetworkModel.getDevice(dev); - - if (dev) - dev.attachToInterface(this); - } - } - }, - - changeProtocol: function(proto) - { - var pr = _luci2.NetworkModel._protos[proto]; - - if (!pr) - return; - - for (var opt in (this.get() || { })) - { - switch (opt) - { - case 'type': - case 'ifname': - case 'macaddr': - if (pr.virtual) - this.set(opt, undefined); - break; - - case 'auto': - case 'mtu': - break; - - case 'proto': - this.set(opt, pr.protocol); - break; - - default: - this.set(opt, undefined); - break; - } - } - }, - - createForm: function(mapwidget) - { - var self = this; - var proto = self.getProtocol(); - var device = self.getDevice(); - - if (!mapwidget) - mapwidget = _luci2.cbi.Map; - - var map = new mapwidget('network', { - caption: _luci2.tr('Configure "%s"').format(self.name()) - }); - - var section = map.section(_luci2.cbi.SingleSection, self.name(), { - anonymous: true - }); - - section.tab({ - id: 'general', - caption: _luci2.tr('General Settings') - }); - - section.tab({ - id: 'advanced', - caption: _luci2.tr('Advanced Settings') - }); - - section.tab({ - id: 'ipv6', - caption: _luci2.tr('IPv6') - }); - - section.tab({ - id: 'physical', - caption: _luci2.tr('Physical Settings') - }); - - - section.taboption('general', _luci2.cbi.CheckboxValue, 'auto', { - caption: _luci2.tr('Start on boot'), - optional: true, - initial: true - }); - - var pr = section.taboption('general', _luci2.cbi.ListValue, 'proto', { - caption: _luci2.tr('Protocol') - }); - - pr.ucivalue = function(sid) { - return self.get('proto') || 'none'; - }; - - var ok = section.taboption('general', _luci2.cbi.ButtonValue, '_confirm', { - caption: _luci2.tr('Really switch?'), - description: _luci2.tr('Changing the protocol will clear all configuration for this interface!'), - text: _luci2.tr('Change protocol') - }); - - ok.on('click', function(ev) { - self.changeProtocol(pr.formvalue(ev.data.sid)); - self.createForm(mapwidget).show(); - }); - - var protos = _luci2.NetworkModel.getProtocols(); - - for (var i = 0; i < protos.length; i++) - pr.value(protos[i].name, protos[i].description); - - proto.populateForm(section, self); - - if (!proto.virtual) - { - var br = section.taboption('physical', _luci2.cbi.CheckboxValue, 'type', { - caption: _luci2.tr('Network bridge'), - description: _luci2.tr('Merges multiple devices into one logical bridge'), - optional: true, - enabled: 'bridge', - disabled: '', - initial: '' - }); - - section.taboption('physical', _luci2.cbi.DeviceList, '__iface_multi', { - caption: _luci2.tr('Devices'), - multiple: true, - bridges: false - }).depends('type', true); - - section.taboption('physical', _luci2.cbi.DeviceList, '__iface_single', { - caption: _luci2.tr('Device'), - multiple: false, - bridges: true - }).depends('type', false); - - var mac = section.taboption('physical', _luci2.cbi.InputValue, 'macaddr', { - caption: _luci2.tr('Override MAC'), - optional: true, - placeholder: device ? device.getMACAddress() : undefined, - datatype: 'macaddr' - }) - - mac.ucivalue = function(sid) - { - if (device) - return device.get('macaddr'); - - return this.callSuper('ucivalue', sid); - }; - - mac.save = function(sid) - { - if (!this.changed(sid)) - return false; - - if (device) - device.set('macaddr', this.formvalue(sid)); - else - this.callSuper('set', sid); - - return true; - }; - } - - section.taboption('physical', _luci2.cbi.InputValue, 'mtu', { - caption: _luci2.tr('Override MTU'), - optional: true, - placeholder: device ? device.getMTU() : undefined, - datatype: 'range(1, 9000)' - }); - - section.taboption('physical', _luci2.cbi.InputValue, 'metric', { - caption: _luci2.tr('Override Metric'), - optional: true, - placeholder: 0, - datatype: 'uinteger' - }); - - for (var field in section.fields) - { - switch (field) - { - case 'proto': - break; - - case '_confirm': - for (var i = 0; i < protos.length; i++) - if (protos[i].name != (this.get('proto') || 'none')) - section.fields[field].depends('proto', protos[i].name); - break; - - default: - section.fields[field].depends('proto', this.get('proto') || 'none', true); - break; - } - } - - return map; - } - }); - - this.NetworkModel.Protocol = this.NetworkModel.Interface.extend({ - description: '__unknown__', - tunnel: false, - virtual: false, - - populateForm: function(section, iface) - { - - } - }); - - this.system = { - getSystemInfo: _luci2.rpc.declare({ - object: 'system', - method: 'info', - expect: { '': { } } - }), - - getBoardInfo: _luci2.rpc.declare({ - object: 'system', - method: 'board', - expect: { '': { } } - }), - - getDiskInfo: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'diskfree', - expect: { '': { } } - }), - - getInfo: function(cb) - { - _luci2.rpc.batch(); - - this.getSystemInfo(); - this.getBoardInfo(); - this.getDiskInfo(); - - return _luci2.rpc.flush().then(function(info) { - var rv = { }; - - $.extend(rv, info[0]); - $.extend(rv, info[1]); - $.extend(rv, info[2]); - - return rv; - }); - }, - - getProcessList: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'process_list', - expect: { processes: [ ] }, - filter: function(data) { - data.sort(function(a, b) { return a.pid - b.pid }); - return data; - } - }), - - getSystemLog: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'syslog', - expect: { log: '' } - }), - - getKernelLog: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'dmesg', - expect: { log: '' } - }), - - getZoneInfo: function(cb) - { - return $.getJSON(_luci2.globals.resource + '/zoneinfo.json', cb); - }, - - sendSignal: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'process_signal', - params: [ 'pid', 'signal' ], - filter: function(data) { - return (data == 0); - } - }), - - initList: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'init_list', - expect: { initscripts: [ ] }, - filter: function(data) { - data.sort(function(a, b) { return (a.start || 0) - (b.start || 0) }); - return data; - } - }), - - initEnabled: function(init, cb) - { - return this.initList().then(function(list) { - for (var i = 0; i < list.length; i++) - if (list[i].name == init) - return !!list[i].enabled; - - return false; - }); - }, - - initRun: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'init_action', - params: [ 'name', 'action' ], - filter: function(data) { - return (data == 0); - } - }), - - initStart: function(init, cb) { return _luci2.system.initRun(init, 'start', cb) }, - initStop: function(init, cb) { return _luci2.system.initRun(init, 'stop', cb) }, - initRestart: function(init, cb) { return _luci2.system.initRun(init, 'restart', cb) }, - initReload: function(init, cb) { return _luci2.system.initRun(init, 'reload', cb) }, - initEnable: function(init, cb) { return _luci2.system.initRun(init, 'enable', cb) }, - initDisable: function(init, cb) { return _luci2.system.initRun(init, 'disable', cb) }, - - - getRcLocal: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'rclocal_get', - expect: { data: '' } - }), - - setRcLocal: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'rclocal_set', - params: [ 'data' ] - }), - - - getCrontab: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'crontab_get', - expect: { data: '' } - }), - - setCrontab: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'crontab_set', - params: [ 'data' ] - }), - - - getSSHKeys: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'sshkeys_get', - expect: { keys: [ ] } - }), - - setSSHKeys: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'sshkeys_set', - params: [ 'keys' ] - }), - - - setPassword: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'password_set', - params: [ 'user', 'password' ] - }), - - - listLEDs: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'led_list', - expect: { leds: [ ] } - }), - - listUSBDevices: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'usb_list', - expect: { devices: [ ] } - }), - - - testUpgrade: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'upgrade_test', - expect: { '': { } } - }), - - startUpgrade: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'upgrade_start', - params: [ 'keep' ] - }), - - cleanUpgrade: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'upgrade_clean' - }), - - - restoreBackup: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'backup_restore' - }), - - cleanBackup: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'backup_clean' - }), - - - getBackupConfig: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'backup_config_get', - expect: { config: '' } - }), - - setBackupConfig: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'backup_config_set', - params: [ 'data' ] - }), - - - listBackup: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'backup_list', - expect: { files: [ ] } - }), - - - testReset: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'reset_test', - expect: { supported: false } - }), - - startReset: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'reset_start' - }), - - - performReboot: _luci2.rpc.declare({ - object: 'luci2.system', - method: 'reboot' - }) - }; - - this.opkg = { - updateLists: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'update', - expect: { '': { } } - }), - - _allPackages: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'list', - params: [ 'offset', 'limit', 'pattern' ], - expect: { '': { } } - }), - - _installedPackages: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'list_installed', - params: [ 'offset', 'limit', 'pattern' ], - expect: { '': { } } - }), - - _findPackages: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'find', - params: [ 'offset', 'limit', 'pattern' ], - expect: { '': { } } - }), - - _fetchPackages: function(action, offset, limit, pattern) - { - var packages = [ ]; - - return action(offset, limit, pattern).then(function(list) { - if (!list.total || !list.packages) - return { length: 0, total: 0 }; - - packages.push.apply(packages, list.packages); - packages.total = list.total; - - if (limit <= 0) - limit = list.total; - - if (packages.length >= limit) - return packages; - - _luci2.rpc.batch(); - - for (var i = offset + packages.length; i < limit; i += 100) - action(i, (Math.min(i + 100, limit) % 100) || 100, pattern); - - return _luci2.rpc.flush(); - }).then(function(lists) { - for (var i = 0; i < lists.length; i++) - { - if (!lists[i].total || !lists[i].packages) - continue; - - packages.push.apply(packages, lists[i].packages); - packages.total = lists[i].total; - } - - return packages; - }); - }, - - listPackages: function(offset, limit, pattern) - { - return _luci2.opkg._fetchPackages(_luci2.opkg._allPackages, offset, limit, pattern); - }, - - installedPackages: function(offset, limit, pattern) - { - return _luci2.opkg._fetchPackages(_luci2.opkg._installedPackages, offset, limit, pattern); - }, - - findPackages: function(offset, limit, pattern) - { - return _luci2.opkg._fetchPackages(_luci2.opkg._findPackages, offset, limit, pattern); - }, - - installPackage: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'install', - params: [ 'package' ], - expect: { '': { } } - }), - - removePackage: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'remove', - params: [ 'package' ], - expect: { '': { } } - }), - - getConfig: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'config_get', - expect: { config: '' } - }), - - setConfig: _luci2.rpc.declare({ - object: 'luci2.opkg', - method: 'config_set', - params: [ 'data' ] - }) - }; - - this.session = { - - login: _luci2.rpc.declare({ - object: 'session', - method: 'login', - params: [ 'username', 'password' ], - expect: { '': { } } - }), - - access: _luci2.rpc.declare({ - object: 'session', - method: 'access', - params: [ 'scope', 'object', 'function' ], - expect: { access: false } - }), - - isAlive: function() - { - return _luci2.session.access('ubus', 'session', 'access'); - }, - - startHeartbeat: function() - { - this._hearbeatInterval = window.setInterval(function() { - _luci2.session.isAlive().then(function(alive) { - if (!alive) - { - _luci2.session.stopHeartbeat(); - _luci2.ui.login(true); - } - - }); - }, _luci2.globals.timeout * 2); - }, - - stopHeartbeat: function() - { - if (typeof(this._hearbeatInterval) != 'undefined') - { - window.clearInterval(this._hearbeatInterval); - delete this._hearbeatInterval; - } - }, - - - _acls: { }, - - _fetch_acls: _luci2.rpc.declare({ - object: 'session', - method: 'access', - expect: { '': { } } - }), - - _fetch_acls_cb: function(acls) - { - _luci2.session._acls = acls; - }, - - updateACLs: function() - { - return _luci2.session._fetch_acls() - .then(_luci2.session._fetch_acls_cb); - }, - - hasACL: function(scope, object, func) - { - var acls = _luci2.session._acls; - - if (typeof(func) == 'undefined') - return (acls && acls[scope] && acls[scope][object]); - - if (acls && acls[scope] && acls[scope][object]) - for (var i = 0; i < acls[scope][object].length; i++) - if (acls[scope][object][i] == func) - return true; - - return false; - } - }; - - this.ui = { - - saveScrollTop: function() - { - this._scroll_top = $(document).scrollTop(); - }, - - restoreScrollTop: function() - { - if (typeof(this._scroll_top) == 'undefined') - return; - - $(document).scrollTop(this._scroll_top); - - delete this._scroll_top; - }, - - loading: function(enable) - { - var win = $(window); - var body = $('body'); - - var state = _luci2.ui._loading || (_luci2.ui._loading = { - modal: $('
') - .css('z-index', 2000) - .addClass('modal fade') - .append($('
') - .addClass('modal-dialog') - .append($('
') - .addClass('modal-content luci2-modal-loader') - .append($('
') - .addClass('modal-body') - .text(_luci2.tr('Loading data…'))))) - .appendTo(body) - .modal({ - backdrop: 'static', - keyboard: false - }) - }); - - state.modal.modal(enable ? 'show' : 'hide'); - }, - - dialog: function(title, content, options) - { - var win = $(window); - var body = $('body'); - - var state = _luci2.ui._dialog || (_luci2.ui._dialog = { - dialog: $('
') - .addClass('modal fade') - .append($('
') - .addClass('modal-dialog') - .append($('
') - .addClass('modal-content') - .append($('
') - .addClass('modal-header') - .append('

') - .addClass('modal-title')) - .append($('
') - .addClass('modal-body')) - .append($('
') - .addClass('modal-footer') - .append(_luci2.ui.button(_luci2.tr('Close'), 'primary') - .click(function() { - $(this).parents('div.modal').modal('hide'); - }))))) - .appendTo(body) - }); - - if (typeof(options) != 'object') - options = { }; - - if (title === false) - { - state.dialog.modal('hide'); - - return state.dialog; - } - - var cnt = state.dialog.children().children().children('div.modal-body'); - var ftr = state.dialog.children().children().children('div.modal-footer'); - - ftr.empty().show(); - - if (options.style == 'confirm') - { - ftr.append(_luci2.ui.button(_luci2.tr('Ok'), 'primary') - .click(options.confirm || function() { _luci2.ui.dialog(false) })); - - ftr.append(_luci2.ui.button(_luci2.tr('Cancel'), 'default') - .click(options.cancel || function() { _luci2.ui.dialog(false) })); - } - else if (options.style == 'close') - { - ftr.append(_luci2.ui.button(_luci2.tr('Close'), 'primary') - .click(options.close || function() { _luci2.ui.dialog(false) })); - } - else if (options.style == 'wait') - { - ftr.append(_luci2.ui.button(_luci2.tr('Close'), 'primary') - .attr('disabled', true)); - } - - if (options.wide) - { - state.dialog.addClass('wide'); - } - else - { - state.dialog.removeClass('wide'); - } - - state.dialog.find('h4:first').text(title); - state.dialog.modal('show'); - - cnt.empty().append(content); - - return state.dialog; - }, - - upload: function(title, content, options) - { - var state = _luci2.ui._upload || (_luci2.ui._upload = { - form: $('
') - .attr('method', 'post') - .attr('action', '/cgi-bin/luci-upload') - .attr('enctype', 'multipart/form-data') - .attr('target', 'cbi-fileupload-frame') - .append($('

')) - .append($('') - .attr('type', 'hidden') - .attr('name', 'sessionid')) - .append($('') - .attr('type', 'hidden') - .attr('name', 'filename')) - .append($('') - .attr('type', 'file') - .attr('name', 'filedata') - .addClass('cbi-input-file')) - .append($('

') - .css('width', '100%') - .addClass('progress progress-striped active') - .append($('
') - .addClass('progress-bar') - .css('width', '100%'))) - .append($('