luci-base: static.js: fix validation of netmask option
[project/luci.git] / modules / luci-base / htdocs / luci-static / resources / protocol / static.js
1 'use strict';
2 'require form';
3 'require network';
4 'require validation';
5
6 function isCIDR(value) {
7 return Array.isArray(value) || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(\d{1,2}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.test(value);
8 }
9
10 function calculateBroadcast(s, use_cfgvalue) {
11 var readfn = use_cfgvalue ? 'cfgvalue' : 'formvalue',
12 addropt = s.children.filter(function(o) { return o.option == 'ipaddr'})[0],
13 addrvals = addropt ? L.toArray(addropt[readfn](s.section)) : [],
14 maskopt = s.children.filter(function(o) { return o.option == 'netmask'})[0],
15 maskval = maskopt ? maskopt[readfn](s.section) : null,
16 firstsubnet = maskval ? addrvals[0] + '/' + maskval : addrvals.filter(function(a) { return a.indexOf('/') > 0 })[0];
17
18 if (firstsubnet == null)
19 return null;
20
21 var addr_mask = firstsubnet.split('/'),
22 addr = validation.parseIPv4(addr_mask[0]),
23 mask = addr_mask[1];
24
25 if (!isNaN(mask))
26 mask = validation.parseIPv4(network.prefixToMask(+mask));
27 else
28 mask = validation.parseIPv4(mask);
29
30 var bc = [
31 addr[0] | (~mask[0] >>> 0 & 255),
32 addr[1] | (~mask[1] >>> 0 & 255),
33 addr[2] | (~mask[2] >>> 0 & 255),
34 addr[3] | (~mask[3] >>> 0 & 255)
35 ];
36
37 return bc.join('.');
38 }
39
40 function validateBroadcast(section_id, value) {
41 var opt = this.map.lookupOption('broadcast', section_id),
42 node = opt ? this.map.findElement('id', opt[0].cbid(section_id)) : null,
43 addr = node ? calculateBroadcast(this.section, false) : null;
44
45 if (node != null) {
46 if (addr != null)
47 node.querySelector('input').setAttribute('placeholder', addr);
48 else
49 node.querySelector('input').removeAttribute('placeholder');
50 }
51
52 return true;
53 }
54
55 return network.registerProtocol('static', {
56 CBIIPValue: form.Value.extend({
57 handleSwitch: function(section_id, option_index, ev) {
58 var maskopt = this.map.lookupOption('netmask', section_id);
59
60 if (maskopt == null || !this.isValid(section_id))
61 return;
62
63 var maskval = maskopt[0].formvalue(section_id),
64 addrval = this.formvalue(section_id),
65 prefix = maskval ? network.maskToPrefix(maskval) : 32;
66
67 if (prefix == null)
68 return;
69
70 this.datatype = 'or(cidr4,ipmask4)';
71
72 var parent = L.dom.parent(ev.target, '.cbi-value-field');
73 L.dom.content(parent, form.DynamicList.prototype.renderWidget.apply(this, [
74 section_id,
75 option_index,
76 addrval ? '%s/%d'.format(addrval, prefix) : ''
77 ]));
78
79 var masknode = this.map.findElement('id', maskopt[0].cbid(section_id));
80 if (masknode) {
81 parent = L.dom.parent(masknode, '.cbi-value');
82 parent.parentNode.removeChild(parent);
83 }
84 },
85
86 renderWidget: function(section_id, option_index, cfgvalue) {
87 var maskopt = this.map.lookupOption('netmask', section_id),
88 widget = isCIDR(cfgvalue) ? 'DynamicList' : 'Value';
89
90 if (widget == 'DynamicList') {
91 this.datatype = 'or(cidr4,ipmask4)';
92 this.placeholder = _('Add IPv4 address…');
93 }
94 else {
95 this.datatype = 'ip4addr("nomask")';
96 }
97
98 var node = form[widget].prototype.renderWidget.apply(this, [ section_id, option_index, cfgvalue ]);
99
100 if (widget == 'Value')
101 L.dom.append(node, E('button', {
102 'class': 'cbi-button cbi-button-neutral',
103 'title': _('Switch to CIDR list notation'),
104 'aria-label': _('Switch to CIDR list notation'),
105 'click': L.bind(this.handleSwitch, this, section_id, option_index)
106 }, '…'));
107
108 return node;
109 },
110
111 validate: validateBroadcast
112 }),
113
114 CBINetmaskValue: form.Value.extend({
115 render: function(option_index, section_id, in_table) {
116 var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
117 addrval = addropt ? addropt.cfgvalue(section_id) : null;
118
119 if (addrval != null && isCIDR(addrval))
120 return E([]);
121
122 this.value('255.255.255.0');
123 this.value('255.255.0.0');
124 this.value('255.0.0.0');
125
126 return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
127 },
128
129 datatype: 'ip4addr("true")',
130 validate: validateBroadcast
131 }),
132
133 CBIGatewayValue: form.Value.extend({
134 datatype: 'ip4addr("nomask")',
135
136 render: function(option_index, section_id, in_table) {
137 return network.getWANNetworks().then(L.bind(function(wans) {
138 if (wans.length == 1) {
139 var gwaddr = wans[0].getGatewayAddr();
140 this.placeholder = gwaddr ? '%s (%s)'.format(gwaddr, wans[0].getName()) : '';
141 }
142
143 return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
144 }, this));
145 },
146
147 validate: function(section_id, value) {
148 var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
149 addrval = addropt ? L.toArray(addropt.cfgvalue(section_id)) : null;
150
151 if (addrval != null) {
152 for (var i = 0; i < addrval.length; i++) {
153 var addr = addrval[i].split('/')[0];
154 if (value == addr)
155 return _('The gateway address must not be a local IP address');
156 }
157 }
158
159 return true;
160 }
161 }),
162
163 CBIBroadcastValue: form.Value.extend({
164 datatype: 'ip4addr("nomask")',
165
166 render: function(option_index, section_id, in_table) {
167 this.placeholder = calculateBroadcast(this.section, true);
168 return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
169 }
170 }),
171
172 getI18n: function() {
173 return _('Static address');
174 },
175
176 renderFormOptions: function(s) {
177 var o;
178
179 s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address'));
180 s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask'));
181 s.taboption('general', this.CBIGatewayValue, 'gateway', _('IPv4 gateway'));
182 s.taboption('general', this.CBIBroadcastValue, 'broadcast', _('IPv4 broadcast'));
183
184 o = s.taboption('general', form.DynamicList, 'ip6addr', _('IPv6 address'));
185 o.datatype = 'ip6addr';
186 o.placeholder = _('Add IPv6 address…');
187 o.depends('ip6assign', '');
188
189 o = s.taboption('general', form.Value, 'ip6gw', _('IPv6 gateway'));
190 o.datatype = 'ip6addr("nomask")';
191 o.depends('ip6assign', '');
192
193 o = s.taboption('general', form.Value, 'ip6prefix', _('IPv6 routed prefix'), _('Public prefix routed to this device for distribution to clients.'));
194 o.datatype = 'ip6addr';
195 o.depends('ip6assign', '');
196 }
197 });