return data;
};
+ this.toArray = function(x)
+ {
+ switch (typeof(x))
+ {
+ case 'number':
+ case 'boolean':
+ return [ x ];
+
+ case 'string':
+ var r = [ ];
+ var l = x.split(/\s+/);
+ for (var i = 0; i < l.length; i++)
+ if (l[i].length > 0)
+ r.push(l[i]);
+ return r;
+
+ case 'object':
+ if ($.isArray(x))
+ {
+ var r = [ ];
+ for (var i = 0; i < x.length; i++)
+ r.push(x[i]);
+ return r;
+ }
+ else if ($.isPlainObject(x))
+ {
+ var r = [ ];
+ for (var k in x)
+ if (x.hasOwnProperty(k))
+ r.push(k);
+ return r.sort();
+ }
+ }
+
+ return [ ];
+ };
+
+ this.toObject = function(x)
+ {
+ switch (typeof(x))
+ {
+ case 'number':
+ case 'boolean':
+ return { x: true };
+
+ case 'string':
+ var r = { };
+ var l = x.split(/\x+/);
+ for (var i = 0; i < l.length; i++)
+ if (l[i].length > 0)
+ r[l[i]] = true;
+ return r;
+
+ case 'object':
+ if ($.isArray(x))
+ {
+ var r = { };
+ for (var i = 0; i < x.length; i++)
+ r[x[i]] = true;
+ return r;
+ }
+ else if ($.isPlainObject(x))
+ {
+ return x;
+ }
+ }
+
+ return { };
+ };
+
+ this.filterArray = function(array, item)
+ {
+ if (!$.isArray(array))
+ return [ ];
+
+ for (var i = 0; i < array.length; i++)
+ if (array[i] === item)
+ array.splice(i--, 1);
+
+ return array;
+ };
+
+ this.toClassName = function(str, suffix)
+ {
+ var n = '';
+ var l = str.split(/[\/.]/);
+
+ for (var i = 0; i < l.length; i++)
+ if (l[i].length > 0)
+ n += l[i].charAt(0).toUpperCase() + l[i].substr(1).toLowerCase();
+
+ if (typeof(suffix) == 'string')
+ n += suffix;
+
+ return n;
+ };
+
this.globals = {
timeout: 15000,
resource: '/luci2',
data: JSON.stringify(req),
dataType: 'json',
type: 'POST',
- timeout: _luci2.globals.timeout
- }).then(cb);
+ 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)
- throw 'Invalid JSON response';
+ if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !$.isArray(list))
+ list = [ ];
- return msg.result;
+ return $.Deferred().resolveWith(this, [ list ]);
},
_call_cb: function(msg)
{
var data = [ ];
var type = Object.prototype.toString;
+ var reqs = this._rpc_req;
- if (!$.isArray(msg))
+ if (!$.isArray(reqs))
+ {
msg = [ msg ];
+ reqs = [ reqs ];
+ }
for (var i = 0; i < msg.length; i++)
{
- /* verify message frame */
- if (typeof(msg[i]) != 'object' || msg[i].jsonrpc != '2.0' || !msg[i].id)
- throw 'Invalid JSON response';
-
/* fetch related request info */
- var req = _luci2.rpc._requests[msg[i].id];
+ 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;
- if ($.isArray(msg[i].result) && msg[i].result[0] == 0)
- ret = (msg[i].result.length > 1) ? msg[i].result[1] : msg[i].result[0];
+ /* 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)
{
data = ret;
/* delete request object */
- delete _luci2.rpc._requests[msg[i].id];
+ delete _luci2.rpc._requests[reqs[i].id];
}
- return data;
+ return $.Deferred().resolveWith(this, [ data ]);
},
list: function()
}
};
- this.uci = {
+ this.UCIContext = Class.extend({
- writable: function()
+ init: function()
{
- return _luci2.session.access('ubus', 'uci', 'commit');
+ this.state = {
+ newid: 0,
+ values: { },
+ creates: { },
+ changes: { },
+ deletes: { },
+ reorder: { }
+ };
},
- add: _luci2.rpc.declare({
+ _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: '' }
}),
- apply: function()
- {
-
- },
-
- configs: _luci2.rpc.declare({
+ _set: _luci2.rpc.declare({
object: 'uci',
- method: 'configs',
- expect: { configs: [ ] }
+ method: 'set',
+ params: [ 'config', 'section', 'values' ]
}),
- _changes: _luci2.rpc.declare({
+ _delete: _luci2.rpc.declare({
object: 'uci',
- method: 'changes',
- params: [ 'config' ],
- expect: { changes: [ ] }
+ method: 'delete',
+ params: [ 'config', 'section', 'options' ]
}),
- changes: function(config)
+ load: function(packages)
{
- if (typeof(config) == 'string')
- return this._changes(config);
+ var self = this;
+ var seen = { };
+ var pkgs = [ ];
- var configlist;
- return this.configs().then(function(configs) {
- _luci2.rpc.batch();
- configlist = configs;
+ if (!$.isArray(packages))
+ packages = [ packages ];
- for (var i = 0; i < configs.length; i++)
- _luci2.uci._changes(configs[i]);
+ _luci2.rpc.batch();
- return _luci2.rpc.flush();
- }).then(function(changes) {
- var rv = { };
+ for (var i = 0; i < packages.length; i++)
+ if (!seen[packages[i]])
+ {
+ pkgs.push(packages[i]);
+ seen[packages[i]] = true;
+ self._load(packages[i]);
+ }
- for (var i = 0; i < configlist.length; i++)
- if (changes[i].length)
- rv[configlist[i]] = changes[i];
+ return _luci2.rpc.flush().then(function(responses) {
+ for (var i = 0; i < responses.length; i++)
+ self.state.values[pkgs[i]] = responses[i];
- return rv;
+ return pkgs;
});
},
- commit: _luci2.rpc.declare({
- object: 'uci',
- method: 'commit',
- params: [ 'config' ]
- }),
-
- _delete_one: _luci2.rpc.declare({
- object: 'uci',
- method: 'delete',
- params: [ 'config', 'section', 'option' ]
- }),
+ unload: function(packages)
+ {
+ if (!$.isArray(packages))
+ packages = [ packages ];
- _delete_multiple: _luci2.rpc.declare({
- object: 'uci',
- method: 'delete',
- params: [ 'config', 'section', 'options' ]
- }),
+ for (var i = 0; i < packages.length; i++)
+ {
+ delete this.state.values[packages[i]];
+ delete this.state.creates[packages[i]];
+ delete this.state.changes[packages[i]];
+ delete this.state.deletes[packages[i]];
+ }
+ },
- 'delete': function(config, section, option)
+ add: function(conf, type, name)
{
- if ($.isArray(option))
- return this._delete_multiple(config, section, option);
- else
- return this._delete_one(config, section, option);
- },
+ var c = this.state.creates;
+ var s = '.new.%d'.format(this.state.newid++);
- delete_all: _luci2.rpc.declare({
- object: 'uci',
- method: 'delete',
- params: [ 'config', 'type', 'match' ]
- }),
+ if (!c[conf])
+ c[conf] = { };
- _foreach: _luci2.rpc.declare({
- object: 'uci',
- method: 'get',
- params: [ 'config', 'type' ],
- expect: { values: { } }
- }),
+ c[conf][s] = {
+ '.type': type,
+ '.name': s,
+ '.create': name,
+ '.anonymous': !name,
+ '.index': 1000 + this.state.newid
+ };
- foreach: function(config, type, cb)
- {
- return this._foreach(config, type).then(function(sections) {
- for (var s in sections)
- cb(sections[s]);
- });
+ return s;
},
- get: _luci2.rpc.declare({
- object: 'uci',
- method: 'get',
- params: [ 'config', 'section', 'option' ],
- expect: { '': { } },
- filter: function(data, params) {
- if (typeof(params.option) == 'undefined')
- return data.values ? data.values['.type'] : undefined;
- else
- return data.value;
+ remove: function(conf, sid)
+ {
+ 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)
+ {
+ if (n[conf])
+ delete n[conf][sid];
}
- }),
+ else
+ {
+ if (c[conf])
+ delete c[conf][sid];
- get_all: _luci2.rpc.declare({
- object: 'uci',
- method: 'get',
- params: [ 'config', 'section' ],
- expect: { values: { } },
- filter: function(data, params) {
- if (typeof(params.section) == 'string')
- data['.section'] = params.section;
- else if (typeof(params.config) == 'string')
- data['.package'] = params.config;
- return data;
+ if (!d[conf])
+ d[conf] = { };
+
+ d[conf][sid] = true;
}
- }),
+ },
- get_first: function(config, type, option)
+ sections: function(conf, type, cb)
{
- return this._foreach(config, type).then(function(sections) {
- for (var s in sections)
- {
- var val = (typeof(option) == 'string') ? sections[s][option] : sections[s]['.name'];
+ 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 (typeof(val) != 'undefined')
- return val;
- }
+ if (!v)
+ return sa;
- return undefined;
- });
- },
+ 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));
- section: _luci2.rpc.declare({
- object: 'uci',
- method: 'add',
- params: [ 'config', 'type', 'name', 'values' ],
- expect: { section: '' }
- }),
+ if (n)
+ for (var s in n)
+ if (!type || n[s]['.type'] == type)
+ sa.push(n[s]);
- _set: _luci2.rpc.declare({
- object: 'uci',
- method: 'set',
- params: [ 'config', 'section', 'values' ]
- }),
+ sa.sort(function(a, b) {
+ return a['.index'] - b['.index'];
+ });
- set: function(config, section, option, value)
- {
- if (typeof(value) == 'undefined' && typeof(option) == 'string')
- return this.section(config, section, option); /* option -> type */
- else if ($.isPlainObject(option))
- return this._set(config, section, option); /* option -> values */
+ for (var i = 0; i < sa.length; i++)
+ sa[i]['.index'] = i;
- var values = { };
- values[option] = value;
+ if (typeof(cb) == 'function')
+ for (var i = 0; i < sa.length; i++)
+ cb.call(this, sa[i], sa[i]['.name']);
- return this._set(config, section, values);
+ return sa;
},
- order: _luci2.rpc.declare({
- object: 'uci',
- method: 'order',
- params: [ 'config', 'sections' ]
- })
- };
-
- this.network = {
- listNetworkNames: function() {
- return _luci2.rpc.list('network.interface.*').then(function(list) {
- var names = [ ];
- for (var name in list)
- if (name != 'network.interface.loopback')
- names.push(name.substring(18));
- names.sort();
- return names;
- });
- },
+ get: function(conf, sid, opt)
+ {
+ var v = this.state.values;
+ var n = this.state.creates;
+ var c = this.state.changes;
+ var d = this.state.deletes;
- listDeviceNames: _luci2.rpc.declare({
- object: 'network.device',
- method: 'status',
- expect: { '': { } },
- filter: function(data) {
- var names = [ ];
- for (var name in data)
- if (name != 'lo')
- names.push(name);
- names.sort();
- return names;
- }
- }),
+ if (typeof(sid) == 'undefined')
+ return undefined;
- getNetworkStatus: function()
- {
- var nets = [ ];
- var devs = { };
+ /* requested option in a just created section */
+ if (sid.indexOf('.new.') == 0)
+ {
+ if (!n[conf])
+ return undefined;
- return this.listNetworkNames().then(function(names) {
- _luci2.rpc.batch();
+ if (typeof(opt) == 'undefined')
+ return n[conf][sid];
- for (var i = 0; i < names.length; i++)
- _luci2.network.getInterfaceStatus(names[i]);
+ return n[conf][sid][opt];
+ }
- return _luci2.rpc.flush();
- }).then(function(networks) {
- for (var i = 0; i < networks.length; i++)
+ /* requested an option value */
+ if (typeof(opt) != 'undefined')
+ {
+ /* check whether option was deleted */
+ if (d[conf] && d[conf][sid])
{
- var net = nets[i] = networks[i];
- var dev = net.l3_device || net.l2_device;
- if (dev)
- net.device = devs[dev] || (devs[dev] = { });
+ if (d[conf][sid] === true)
+ return undefined;
+
+ for (var i = 0; i < d[conf][sid].length; i++)
+ if (d[conf][sid][i] == opt)
+ return undefined;
}
- _luci2.rpc.batch();
+ /* check whether option was changed */
+ if (c[conf] && c[conf][sid] && typeof(c[conf][sid][opt]) != 'undefined')
+ return c[conf][sid][opt];
- for (var dev in devs)
- _luci2.network.getDeviceStatus(dev);
+ /* return base value */
+ if (v[conf] && v[conf][sid])
+ return v[conf][sid][opt];
- return _luci2.rpc.flush();
- }).then(function(devices) {
- _luci2.rpc.batch();
+ return undefined;
+ }
+
+ /* requested an entire section */
+ if (v[conf])
+ return v[conf][sid];
+
+ return undefined;
+ },
+
+ set: function(conf, sid, opt, val)
+ {
+ var n = this.state.creates;
+ var c = this.state.changes;
+ var d = this.state.deletes;
- for (var i = 0; i < devices.length; i++)
+ if (typeof(sid) == 'undefined' ||
+ typeof(opt) == 'undefined' ||
+ opt.charAt(0) == '.')
+ return;
+
+ if (sid.indexOf('.new.') == 0)
+ {
+ if (n[conf] && n[conf][sid])
{
- var brm = devices[i]['bridge-members'];
- delete devices[i]['bridge-members'];
+ 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;
- $.extend(devs[devices[i]['device']], devices[i]);
+ if (!c[conf])
+ c[conf] = { };
- if (!brm)
- continue;
+ if (!c[conf][sid])
+ c[conf][sid] = { };
- devs[devices[i]['device']].subdevices = [ ];
+ /* undelete option */
+ if (d[conf] && d[conf][sid])
+ d[conf][sid] = _luci2.filterArray(d[conf][sid], opt);
- for (var j = 0; j < brm.length; j++)
- {
- if (!devs[brm[j]])
- {
- devs[brm[j]] = { };
- _luci2.network.getDeviceStatus(brm[j]);
- }
+ c[conf][sid][opt] = val;
+ }
+ else
+ {
+ if (!d[conf])
+ d[conf] = { };
- devs[devices[i]['device']].subdevices[j] = devs[brm[j]];
- }
- }
+ if (!d[conf][sid])
+ d[conf][sid] = [ ];
- return _luci2.rpc.flush();
- }).then(function(subdevices) {
- for (var i = 0; i < subdevices.length; i++)
- $.extend(devs[subdevices[i]['device']], subdevices[i]);
+ if (d[conf][sid] !== true)
+ d[conf][sid].push(opt);
+ }
+ },
- _luci2.rpc.batch();
+ unset: function(conf, sid, opt)
+ {
+ return this.set(conf, sid, opt, undefined);
+ },
- for (var dev in devs)
- _luci2.wireless.getDeviceStatus(dev);
+ _reload: function()
+ {
+ var pkgs = [ ];
- return _luci2.rpc.flush();
- }).then(function(wifidevices) {
- for (var i = 0; i < wifidevices.length; i++)
- if (wifidevices[i])
- devs[wifidevices[i]['device']].wireless = wifidevices[i];
+ for (var pkg in this.state.values)
+ pkgs.push(pkg);
- nets.sort(function(a, b) {
- if (a['interface'] < b['interface'])
- return -1;
- else if (a['interface'] > b['interface'])
- return 1;
- else
- return 0;
- });
+ this.init();
- return nets;
- });
+ return this.load(pkgs);
},
- findWanInterfaces: function(cb)
+ _reorder: function()
{
- return this.listNetworkNames().then(function(names) {
- _luci2.rpc.batch();
+ var v = this.state.values;
+ var n = this.state.creates;
+ var r = this.state.reorder;
- for (var i = 0; i < names.length; i++)
- _luci2.network.getInterfaceStatus(names[i]);
+ if ($.isEmptyObject(r))
+ return _luci2.deferrable();
- return _luci2.rpc.flush();
- }).then(function(interfaces) {
- var rv = [ undefined, undefined ];
+ _luci2.rpc.batch();
+
+ /*
+ 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 = [ ];
- for (var i = 0; i < interfaces.length; i++)
+ 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)
{
- if (!interfaces[i].route)
- continue;
+ o.sort(function(a, b) {
+ return (a['.index'] - b['.index']);
+ });
- for (var j = 0; j < interfaces[i].route.length; j++)
- {
- var rt = interfaces[i].route[j];
+ var sids = [ ];
- if (typeof(rt.table) != 'undefined')
- continue;
+ for (var i = 0; i < o.length; i++)
+ sids.push(o[i]['.name']);
- if (rt.target == '0.0.0.0' && rt.mask == 0)
- rv[0] = interfaces[i];
- else if (rt.target == '::' && rt.mask == 0)
- rv[1] = interfaces[i];
- }
+ this._order(c, sids);
}
+ }
- return rv;
- });
+ this.state.reorder = { };
+ return _luci2.rpc.flush();
},
- getDHCPLeases: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'dhcp_leases',
- expect: { leases: [ ] }
- }),
+ 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;
- getDHCPv6Leases: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'dhcp6_leases',
- expect: { leases: [ ] }
- }),
+ if (isNaN(n1) || isNaN(n2))
+ return false;
- getRoutes: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'routes',
- expect: { routes: [ ] }
- }),
+ s1['.index'] = n2;
+ s2['.index'] = n1;
- getIPv6Routes: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'routes',
- expect: { routes: [ ] }
- }),
+ this.state.reorder[conf] = true;
- getARPTable: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'arp_table',
- expect: { entries: [ ] }
- }),
+ return true;
+ },
- getInterfaceStatus: _luci2.rpc.declare({
- object: 'network.interface',
- method: 'status',
- params: [ 'interface' ],
- expect: { '': { } },
- filter: function(data, params) {
- data['interface'] = params['interface'];
- data['l2_device'] = data['device'];
- delete data['device'];
- return data;
- }
- }),
+ save: function()
+ {
+ _luci2.rpc.batch();
- getDeviceStatus: _luci2.rpc.declare({
- object: 'network.device',
- method: 'status',
- params: [ 'name' ],
- expect: { '': { } },
- filter: function(data, params) {
- data['device'] = params['name'];
- return data;
- }
- }),
+ var self = this;
+ var snew = [ ];
- getConntrackCount: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'conntrack_count',
- expect: { '': { count: 0, limit: 0 } }
- }),
+ if (self.state.creates)
+ for (var c in self.state.creates)
+ for (var s in self.state.creates[c])
+ {
+ var r = {
+ config: c,
+ values: { }
+ };
- listSwitchNames: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'switch_list',
- expect: { switches: [ ] }
- }),
+ 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];
+ }
- getSwitchInfo: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'switch_info',
- params: [ 'switch' ],
- expect: { info: { } },
- filter: function(data, params) {
- data['attrs'] = data['switch'];
- data['vlan_attrs'] = data['vlan'];
- data['port_attrs'] = data['port'];
- data['switch'] = params['switch'];
+ snew.push(self.state.creates[c][s]);
- delete data.vlan;
- delete data.port;
+ self._add(r.config, r.type, r.name, r.values);
+ }
- return data;
- }
- }),
+ 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]);
- getSwitchStatus: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'switch_status',
- params: [ 'switch' ],
- expect: { ports: [ ] }
- }),
+ 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];
- runPing: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'ping',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
- }),
+ return self._reorder();
+ });
+ },
- runPing6: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'ping6',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
+ _apply: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'apply',
+ params: [ 'timeout', 'rollback' ]
}),
- runTraceroute: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'traceroute',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
+ _confirm: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'confirm'
}),
- runTraceroute6: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'traceroute6',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
- }),
+ 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;
+ },
- runNslookup: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'nslookup',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
+ changes: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'changes',
+ expect: { changes: { } }
}),
+ readable: function(conf)
+ {
+ return _luci2.session.hasACL('uci', conf, 'read');
+ },
- setUp: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'ifup',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
- }),
+ writable: function(conf)
+ {
+ return _luci2.session.hasACL('uci', conf, 'write');
+ }
+ });
- setDown: _luci2.rpc.declare({
- object: 'luci2.network',
- method: 'ifdown',
- params: [ 'data' ],
- expect: { '': { code: -1 } }
- })
- };
+ this.uci = new this.UCIContext();
this.wireless = {
listDeviceNames: _luci2.rpc.declare({
var self = this;
var zone = undefined;
- return _luci2.uci.foreach('firewall', 'zone', function(z) {
+ return _luci2.uci.sections('firewall', 'zone', function(z) {
if (!z.name || !z.network)
return;
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;
}
};
sections: function(cb)
{
- var s1 = this.map.ucisections(this.map.uci_package);
+ var s1 = _luci2.uci.sections(this.map.uci_package);
var s2 = [ ];
for (var i = 0; i < s1.length; i++)
var addb = text.next();
var errt = addb.next();
var name = text.val();
- var used = false;
if (!/^[a-zA-Z0-9_]*$/.test(name))
{
return false;
}
- for (var sid in self.map.uci.values[self.map.uci_package])
- if (sid == name)
- {
- used = true;
- break;
- }
-
- for (var sid in self.map.uci.creates[self.map.uci_package])
- if (sid == name)
- {
- used = true;
- break;
- }
-
- if (used)
+ if (_luci2.uci.get(self.map.uci_package, name))
{
errt.text(_luci2.tr('Name already used')).show();
text.addClass('error');
if (new_idx >= 0 && new_idx < s.length)
{
- var tmp = s[cur_idx]['.index'];
-
- s[cur_idx]['.index'] = s[new_idx]['.index'];
- s[new_idx]['.index'] = tmp;
-
- if (self.active_panel == cur_idx)
- self.active_panel = new_idx;
- else if (self.active_panel == new_idx)
- self.active_panel = cur_idx;
-
- self.map.uci.reorder = true;
+ _luci2.uci.swap(self.map.uci_package, s[cur_idx]['.name'], s[new_idx]['.name']);
self.map.save();
self.map.redraw();
sections: function(cb)
{
var sa = [ ];
- var pkg = this.map.uci.values[this.map.uci_package];
+ var sl = _luci2.uci.sections(this.map.uci_package);
- for (var s in pkg)
- if (pkg[s]['.name'] == this.uci_type)
+ for (var i = 0; i < sl.length; i++)
+ if (sl[i]['.name'] == this.uci_type)
{
- sa.push(pkg[s]);
+ sa.push(sl[i]);
break;
}
if (typeof(cb) == 'function' && sa.length > 0)
- cb.apply(this, [ sa[0] ]);
+ cb.call(this, sa[0]);
return sa;
}
this.sections = [ ];
this.options = _luci2.defaults(options, {
save: function() { },
- prepare: function() {
- return _luci2.uci.writable(function(writable) {
- self.options.readonly = !writable;
- });
- }
+ prepare: function() { }
});
},
- _load_cb: function(packages)
+ _load_cb: function()
{
- for (var i = 0; i < packages.length; i++)
- {
- this.uci.values[packages[i]['.package']] = packages[i];
- delete packages[i]['.package'];
- }
-
var deferreds = [ _luci2.deferrable(this.options.prepare()) ];
for (var i = 0; i < this.sections.length; i++)
load: function()
{
var self = this;
-
- this.uci = {
- newid: 0,
- values: { },
- creates: { },
- changes: { },
- deletes: { },
- reorder: false
- };
-
var packages = { };
for (var i = 0; i < this.sections.length; i++)
packages[this.uci_package] = true;
- _luci2.rpc.batch();
-
for (var pkg in packages)
- _luci2.uci.get_all(pkg);
+ if (!_luci2.uci.writable(pkg))
+ this.options.readonly = true;
- return _luci2.rpc.flush().then(function(packages) {
- return self._load_cb(packages);
+ return _luci2.uci.load(_luci2.toArray(packages)).then(function() {
+ return self._load_cb();
});
},
add: function(conf, type, name)
{
- var c = this.uci.creates;
- var s = '.new.%d'.format(this.uci.newid++);
-
- if (!c[conf])
- c[conf] = { };
-
- c[conf][s] = {
- '.type': type,
- '.name': s,
- '.create': name,
- '.anonymous': !name,
- '.index': 1000 + this.uci.newid
- };
-
- return s;
+ return _luci2.uci.add(conf, type, name);
},
remove: function(conf, sid)
{
- var n = this.uci.creates;
- var c = this.uci.changes;
- var d = this.uci.deletes;
-
- /* requested deletion of a just created section */
- if (sid.indexOf('.new.') == 0)
- {
- if (n[conf])
- delete n[conf][sid];
- }
- else
- {
- if (c[conf])
- delete c[conf][sid];
-
- if (!d[conf])
- d[conf] = { };
-
- d[conf][sid] = true;
- }
- },
-
- ucisections: function(conf, cb)
- {
- var sa = [ ];
- var pkg = this.uci.values[conf];
- var crt = this.uci.creates[conf];
- var del = this.uci.deletes[conf];
-
- if (!pkg)
- return sa;
-
- for (var s in pkg)
- if (!del || del[s] !== true)
- sa.push(pkg[s]);
-
- if (crt)
- for (var s in crt)
- sa.push(crt[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.apply(this, [ sa[i] ]);
-
- return sa;
+ return _luci2.uci.remove(conf, sid);
},
get: function(conf, sid, opt)
{
- var v = this.uci.values;
- var n = this.uci.creates;
- var c = this.uci.changes;
- var d = this.uci.deletes;
-
- /* requested option in a just created section */
- if (sid.indexOf('.new.') == 0)
- {
- 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 (d[conf][sid] === true)
- return undefined;
-
- for (var i = 0; i < d[conf][sid].length; i++)
- if (d[conf][sid][i] == opt)
- return undefined;
- }
-
- /* check whether option was changed */
- if (c[conf] && c[conf][sid] && 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];
-
- return undefined;
- }
-
- /* requested an entire section */
- if (v[conf])
- return (v[conf][sid] || { });
-
- return undefined;
+ return _luci2.uci.get(conf, sid, opt);
},
set: function(conf, sid, opt, val)
{
- var n = this.uci.creates;
- var c = this.uci.changes;
- var d = this.uci.deletes;
-
- 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')
- {
- if (!c[conf])
- c[conf] = { };
-
- if (!c[conf][sid])
- c[conf][sid] = { };
-
- c[conf][sid][opt] = val;
- }
- else
- {
- if (!d[conf])
- d[conf] = { };
-
- if (!d[conf][sid])
- d[conf][sid] = [ ];
-
- d[conf][sid].push(opt);
- }
+ return _luci2.uci.set(conf, sid, opt, val);
},
validate: function()
save: function()
{
- if (this.options.readonly)
+ var self = this;
+
+ if (self.options.readonly)
return _luci2.deferrable();
- var deferreds = [ _luci2.deferrable(this.options.save()) ];
+ var deferreds = [ ];
- for (var i = 0; i < this.sections.length; i++)
+ for (var i = 0; i < self.sections.length; i++)
{
- if (this.sections[i].options.readonly)
+ if (self.sections[i].options.readonly)
continue;
- for (var f in this.sections[i].fields)
+ for (var f in self.sections[i].fields)
{
- if (typeof(this.sections[i].fields[f].save) != 'function')
+ if (typeof(self.sections[i].fields[f].save) != 'function')
continue;
- var s = this.sections[i].sections();
+ var s = self.sections[i].sections();
for (var j = 0; j < s.length; j++)
{
- var rv = this.sections[i].fields[f].save(s[j]['.name']);
+ var rv = self.sections[i].fields[f].save(s[j]['.name']);
if (_luci2.isDeferred(rv))
deferreds.push(rv);
}
}
}
- return $.when.apply($, deferreds);
- },
-
- _send_uci_reorder: function()
- {
- if (!this.uci.reorder)
- return _luci2.deferrable();
-
- _luci2.rpc.batch();
-
- /*
- gather all created and existing sections, sort them according
- to their index value and issue an uci order call
- */
- for (var c in this.uci.values)
- {
- var o = [ ];
-
- if (this.uci.creates && this.uci.creates[c])
- for (var s in this.uci.creates[c])
- o.push(this.uci.creates[c][s]);
-
- for (var s in this.uci.values[c])
- o.push(this.uci.values[c][s]);
-
- if (o.length > 0)
- {
- o.sort(function(a, b) {
- return (a['.index'] - b['.index']);
- });
-
- var sids = [ ];
-
- for (var i = 0; i < o.length; i++)
- sids.push(o[i]['.name']);
-
- _luci2.uci.order(c, sids);
- }
- }
-
- return _luci2.rpc.flush();
- },
-
- _send_uci: function()
- {
- _luci2.rpc.batch();
-
- var self = this;
- var snew = [ ];
-
- if (this.uci.creates)
- for (var c in this.uci.creates)
- for (var s in this.uci.creates[c])
- {
- var r = {
- config: c,
- values: { }
- };
-
- for (var k in this.uci.creates[c][s])
- {
- if (k == '.type')
- r.type = this.uci.creates[c][s][k];
- else if (k == '.create')
- r.name = this.uci.creates[c][s][k];
- else if (k.charAt(0) != '.')
- r.values[k] = this.uci.creates[c][s][k];
- }
-
- snew.push(this.uci.creates[c][s]);
-
- _luci2.uci.add(r.config, r.type, r.name, r.values);
- }
-
- if (this.uci.changes)
- for (var c in this.uci.changes)
- for (var s in this.uci.changes[c])
- _luci2.uci.set(c, s, this.uci.changes[c][s]);
-
- if (this.uci.deletes)
- for (var c in this.uci.deletes)
- for (var s in this.uci.deletes[c])
- {
- var o = this.uci.deletes[c][s];
- _luci2.uci['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._send_uci_reorder();
+ return $.when.apply($, deferreds).then(function() {
+ return _luci2.deferrable(self.options.save());
});
},
_luci2.ui.loading(true);
return this.save().then(function() {
- return self._send_uci();
+ return _luci2.uci.save();
}).then(function() {
return _luci2.ui.updateChanges();
}).then(function() {