9 'require tools.widgets as widgets';
12 change the status of travelmate stations
14 function handleToggle(sid
) {
15 var w_device
, w_ssid
, w_bssid
, t_sections
, row
, element
, value
, enabled
;
17 w_device
= uci
.get('wireless', sid
, 'device');
18 w_ssid
= uci
.get('wireless', sid
, 'ssid');
19 w_bssid
= uci
.get('wireless', sid
, 'bssid');
20 t_sections
= uci
.sections('travelmate', 'uplink');
22 for (var i
= 0; i
< t_sections
.length
; i
++) {
23 if (t_sections
[i
].device
=== w_device
&& t_sections
[i
].ssid
=== w_ssid
&& t_sections
[i
].bssid
=== w_bssid
) {
24 value
= t_sections
[i
]['enabled'];
25 value
= (value
== 0 ? 1 : 0);
26 enabled
= (value
== 0 ? 'No' : 'Yes');
27 uci
.set('travelmate', t_sections
[i
]['.name'], 'enabled', value
);
28 uci
.save().then(function () {
29 row
= document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(sid
));
30 element
= row
.querySelector('.cbi-value-field');
31 element
.textContent
= enabled
;
32 row
.setAttribute('style', 'opacity: 0.5; color: #37c !important;');
39 remove wireless and stale travelmate sections
41 function handleRemove(sid
) {
42 var w_sections
, t_sections
, match
, row
;
44 uci
.remove('wireless', sid
);
45 w_sections
= uci
.sections('wireless', 'wifi-iface');
46 t_sections
= uci
.sections('travelmate', 'uplink');
47 for (var i
= 0; i
< t_sections
.length
; i
++) {
49 for (var j
= 0; j
< w_sections
.length
; j
++) {
50 if (t_sections
[i
].device
=== w_sections
[j
].device
&& t_sections
[i
].ssid
=== w_sections
[j
].ssid
&& t_sections
[i
].bssid
=== w_sections
[j
].bssid
) {
55 if (match
=== false) {
56 uci
.remove('travelmate', t_sections
[i
]['.name']);
59 return uci
.save().then(function () {
60 row
= document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(sid
));
61 row
.setAttribute('style', 'opacity: 0.5; color: #a22 !important;');
66 add missing travelmate sections
68 function handleSectionsAdd(iface
) {
69 var w_sections
, t_sections
, match
;
71 w_sections
= uci
.sections('wireless', 'wifi-iface');
72 t_sections
= uci
.sections('travelmate', 'uplink');
74 for (var i
= 0; i
< w_sections
.length
; i
++) {
75 if (w_sections
[i
].mode
!== 'sta' || w_sections
[i
].network
!== iface
) {
79 for (var j
= 0; j
< t_sections
.length
; j
++) {
80 if (w_sections
[i
].device
=== t_sections
[j
].device
&& w_sections
[i
].ssid
=== t_sections
[j
].ssid
&& w_sections
[i
].bssid
=== t_sections
[j
].bssid
) {
85 if (match
=== false) {
86 var vpn_stdservice
= uci
.get('travelmate', 'global', 'trm_stdvpnservice');
87 var vpn_stdiface
= uci
.get('travelmate', 'global', 'trm_stdvpniface');
88 var sid
= uci
.add('travelmate', 'uplink');
90 uci
.set('travelmate', sid
, 'enabled', '1');
91 uci
.set('travelmate', sid
, 'device', w_sections
[i
].device
);
92 uci
.set('travelmate', sid
, 'ssid', w_sections
[i
].ssid
);
93 uci
.set('travelmate', sid
, 'bssid', w_sections
[i
].bssid
);
94 uci
.set('travelmate', sid
, 'con_start_expiry', '0');
95 uci
.set('travelmate', sid
, 'con_end_expiry', '0');
96 if (vpn_stdservice
&& vpn_stdiface
) {
97 uci
.set('travelmate', sid
, 'vpn', '1');
98 uci
.set('travelmate', sid
, 'vpnservice', vpn_stdservice
);
99 uci
.set('travelmate', sid
, 'vpniface', vpn_stdiface
);
106 update travelmate sections
108 function handleSectionsVal(action
, section_id
, option
, value
) {
109 var date
, oldValue
, w_device
, w_ssid
, w_bssid
, t_sections
;
111 w_device
= uci
.get('wireless', section_id
, 'device');
112 w_ssid
= uci
.get('wireless', section_id
, 'ssid');
113 w_bssid
= uci
.get('wireless', section_id
, 'bssid');
114 t_sections
= uci
.sections('travelmate', 'uplink');
116 for (var i
= 0; i
< t_sections
.length
; i
++) {
117 if (t_sections
[i
].device
=== w_device
&& t_sections
[i
].ssid
=== w_ssid
&& t_sections
[i
].bssid
=== w_bssid
) {
118 if (action
=== 'get') {
119 return t_sections
[i
][option
];
121 else if (action
=== 'set') {
122 if (option
=== 'enabled') {
123 oldValue
= t_sections
[i
][option
];
124 if (oldValue
!== value
&& value
=== '0') {
125 date
= new Date(new Date().getTime() - new Date().getTimezoneOffset() * 60 * 1000).toISOString().substr(0, 19).replace(/-/g
, '.').replace('T', '-');
126 uci
.set('travelmate', t_sections
[i
]['.name'], 'con_end', date
);
128 else if (oldValue
!== value
&& value
=== '1') {
129 uci
.unset('travelmate', t_sections
[i
]['.name'], 'con_end');
132 return uci
.set('travelmate', t_sections
[i
]['.name'], option
, value
);
134 else if (action
=== 'del') {
135 return uci
.unset('travelmate', t_sections
[i
]['.name'], option
);
142 update travelmate status
144 function handleStatus() {
145 poll
.add(function () {
146 L
.resolveDefault(fs
.stat('/var/state/travelmate.refresh'), null).then(function (res
) {
148 L
.resolveDefault(fs
.read('/var/state/travelmate.refresh'), null).then(async
function (res
) {
149 fs
.remove('/var/state/travelmate.refresh');
150 if (res
&& res
=== 'ui_reload') {
153 else if (res
&& res
=== 'cfg_reload') {
154 if (document
.readyState
=== 'complete') {
155 uci
.unload('wireless');
156 uci
.unload('travelmate');
159 uci
.load('wireless'),
160 uci
.load('travelmate')
162 var rows
, item
, value
;
163 rows
= document
.querySelectorAll('.cbi-section-table-row[data-sid]');
164 for (var i
= 0; i
< rows
.length
; i
++) {
165 item
= rows
[i
].querySelector('.cbi-value-field[data-title="Enabled"]');
166 value
= handleSectionsVal('get', rows
[i
].getAttribute('data-sid'), 'enabled');
167 item
.textContent
= (value
== 0 ? 'No' : 'Yes');
173 return L
.resolveDefault(fs
.stat('/tmp/trm_runtime.json'), null).then(function (res
) {
175 L
.resolveDefault(fs
.read('/tmp/trm_runtime.json'), null).then(function (res
) {
177 var info
= JSON
.parse(res
);
179 var t_device
, t_ssid
, t_bssid
, oldUplinkView
, newUplinkView
, uplinkColor
,
180 uplinkId
= info
.data
.station_id
.trim().split('/'),
181 oldUplinkView
= document
.getElementsByName('uplinkStation'),
182 w_sections
= uci
.sections('wireless', 'wifi-iface'),
183 vpnStatus
= info
.data
.ext_hooks
.substr(13, 1);
184 t_device
= uplinkId
[0];
185 t_bssid
= uplinkId
[uplinkId
.length
- 1];
186 for (var i
= 1; i
< uplinkId
.length
- 1; i
++) {
188 t_ssid
= uplinkId
[i
];
191 t_ssid
= t_ssid
+ '/' + uplinkId
[i
];
194 if (t_ssid
=== '-') {
195 if (oldUplinkView
.length
> 0) {
196 oldUplinkView
[0].removeAttribute('style');
197 oldUplinkView
[0].removeAttribute('name', 'uplinkStation');
201 uplinkColor
= (vpnStatus
=== "✔" ? 'rgb(68, 170, 68)' : 'rgb(51, 119, 204)');
202 for (var i
= 0; i
< w_sections
.length
; i
++) {
203 newUplinkView
= document
.getElementById('cbi-wireless-' + w_sections
[i
]['.name']);
204 if (t_device
=== w_sections
[i
].device
&& t_ssid
=== w_sections
[i
].ssid
&& t_bssid
=== (w_sections
[i
].bssid
|| '-')) {
205 if (oldUplinkView
.length
=== 0 && newUplinkView
) {
206 newUplinkView
.setAttribute('name', 'uplinkStation');
207 newUplinkView
.setAttribute('style', 'text-align: left !important; color: ' + uplinkColor
+ ' !important;font-weight: bold !important;');
209 else if (oldUplinkView
.length
> 0 && newUplinkView
&& oldUplinkView
[0].getAttribute('id') !== newUplinkView
.getAttribute('id')) {
210 oldUplinkView
[0].removeAttribute('style');
211 oldUplinkView
[0].removeAttribute('name', 'uplinkStation');
212 newUplinkView
.setAttribute('name', 'uplinkStation');
213 newUplinkView
.setAttribute('style', 'text-align: left !important; color: ' + uplinkColor
+ ' !important;font-weight: bold !important;');
215 else if (newUplinkView
&& newUplinkView
.style
.color
!= uplinkColor
) {
216 newUplinkView
.setAttribute('style', 'text-align: left !important; color: ' + uplinkColor
+ ' !important;font-weight: bold !important;');
232 L
.resolveDefault(fs
.exec('/etc/init.d/travelmate', ['assoc']), null),
233 uci
.load('wireless'),
234 uci
.load('travelmate')
238 render: function (result
) {
240 iface
= uci
.get('travelmate', 'global', 'trm_iface') || 'trm_wwan';
242 m
= new form
.Map('wireless');
243 m
.chain('travelmate');
244 s
= m
.section(form
.GridSection
, 'wifi-iface', null, _('Overview of all configured uplinks for travelmate. \
245 You can edit, remove or prioritize existing uplinks by drag \& drop and scan for new ones.<br /> \
246 The currently used uplink connection is emphasized in <span style="color:rgb(51, 119, 204);font-weight:bold">blue</span>, \
247 an encrypted VPN uplink connection is emphasized in <span style="color:rgb(68, 170, 68);font-weight:bold">green</span>.'));
250 s
.filter = function (section_id
) {
251 return (uci
.get('wireless', section_id
, 'network') == iface
&& uci
.get('wireless', section_id
, 'mode') == 'sta');
253 s
.tab('wireless', _('Wireless Settings'));
254 s
.tab('travelmate', _('Travelmate Settings'));
255 s
.tab('vpn', _('VPN Settings'));
256 s
.renderRowActions = function (section_id
) {
260 'class': 'btn cbi-button drag-handle center',
261 'title': _('Drag to reorder'),
262 'style': 'cursor:move',
263 'disabled': this.map
.readonly
|| null
266 'class': 'cbi-button cbi-button-action important',
267 'title': _('Edit this network'),
268 'click': ui
.createHandlerFn(this, 'renderMoreOptionsModal', section_id
)
271 'class': 'cbi-button cbi-button-apply',
272 'title': _('Enable/Disable this network'),
273 'click': ui
.createHandlerFn(this, handleToggle
, section_id
)
276 'class': 'cbi-button cbi-button-negative remove',
277 'title': _('Remove this network'),
278 'click': ui
.createHandlerFn(this, handleRemove
, section_id
)
281 return E('td', { 'class': 'td middle cbi-section-actions' }, E('div', btns
));
284 o
= s
.taboption('travelmate', form
.Flag
, '_enabled', _('Enabled'));
285 o
.uciconfig
= 'travelmate';
286 o
.ucisection
= 'uplink';
287 o
.ucioption
= 'enabled';
289 o
.cfgvalue = function (section_id
) {
290 return handleSectionsVal('get', section_id
, 'enabled');
292 o
.write = function (section_id
, value
) {
293 return handleSectionsVal('set', section_id
, 'enabled', value
);
296 o
= s
.taboption('wireless', form
.Value
, 'device', _('Device'));
299 o
= s
.taboption('wireless', form
.Value
, 'ssid', _('SSID'));
300 o
.datatype
= 'maxlength(32)';
303 o
= s
.taboption('wireless', form
.Value
, 'bssid', _('BSSID'));
304 o
.datatype
= 'macaddr';
307 o
= s
.taboption('wireless', form
.ListValue
, 'encryption', _('Encryption'));
308 o
.value('sae', _('WPA3 Pers. (SAE)'));
309 o
.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)'));
310 o
.value('psk2', _('WPA2 Pers.'));
311 o
.value('psk2+ccmp', _('WPA2 Pers. (CCMP)'));
312 o
.value('psk2+tkip', _('WPA2 Pers. (TKIP)'));
313 o
.value('psk', _('WPA Pers.'));
314 o
.value('psk+ccmp', _('WPA Pers. (CCMP)'));
315 o
.value('psk+tkip', _('WPA Pers. (TKIP)'));
316 o
.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)'));
317 o
.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)'));
318 o
.value('wpa3', _('WPA3 Ent. (CCMP)'));
319 o
.value('wpa3-mixed', _('WPA2/WPA3 Ent. (CCMP)'));
320 o
.value('wpa2', _('WPA2 Ent.'));
321 o
.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)'));
322 o
.value('wpa2+tkip', _('WPA2 Ent. (TKIP)'));
323 o
.value('wpa+ccmp', _('WPA Ent. (CCMP)'));
324 o
.value('wpa+tkip', _('WPA Ent. (TKIP)'));
325 o
.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)'));
326 o
.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)'));
327 o
.value('owe', _('OWE'));
328 o
.value('none', _('none'));
330 o
.textvalue = function (section_id
) {
331 var cfgvalue
= this.map
.data
.get('wireless', section_id
, 'encryption');
334 cfgvalue
= 'WPA3 Pers. (SAE)';
337 cfgvalue
= 'WPA2/WPA3 Pers. (CCMP)';
340 cfgvalue
= 'WPA2 Pers.';
343 cfgvalue
= 'WPA2 Pers. (CCMP)';
346 cfgvalue
= 'WPA2 Pers. (TKIP)';
349 cfgvalue
= 'WPA Pers.';
351 case 'psk-mixed+ccmp':
352 cfgvalue
= 'WPA/WPA2 Pers. (CCMP)';
354 case 'psk-mixed+tkip':
355 cfgvalue
= 'WPA/WPA2 Pers. (TKIP)';
358 cfgvalue
= 'WPA3 Ent. (CCMP)';
361 cfgvalue
= 'WPA2/WPA3 Ent. (CCMP)';
364 cfgvalue
= 'WPA2 Ent.';
367 cfgvalue
= 'WPA2 Ent. (CCMP)';
370 cfgvalue
= 'WPA2 Ent. (TKIP)';
373 cfgvalue
= 'WPA Ent. (CCMP)';
376 cfgvalue
= 'WPA Ent. (TKIP)';
378 case 'wpa-mixed+ccmp':
379 cfgvalue
= 'WPA/WPA2 Ent. (CCMP)';
381 case 'wpa-mixed+tkip':
382 cfgvalue
= 'WPA/WPA2 Ent. (TKIP)';
385 cfgvalue
= 'WPA3 OWE (CCMP)';
398 o
= s
.taboption('wireless', form
.Value
, 'key', _('Password'));
399 o
.datatype
= 'wpakey';
400 o
.depends({ encryption
: 'sae', '!contains': true });
401 o
.depends({ encryption
: 'psk', '!contains': true });
405 o
= s
.taboption('wireless', form
.Value
, 'password', _('Password'));
406 o
.datatype
= 'wpakey';
407 o
.depends({ encryption
: 'wpa', '!contains': true });
411 o
= s
.taboption('wireless', form
.ListValue
, 'eap_type', _('EAP-Method'));
412 o
.depends({ encryption
: 'wpa', '!contains': true });
413 o
.value('tls', _('TLS'));
414 o
.value('ttls', _('TTLS'));
415 o
.value('peap', _('PEAP'));
416 o
.value('fast', _('FAST'));
420 o
= s
.taboption('wireless', form
.ListValue
, 'auth', _('Authentication'));
421 o
.value('PAP', _('PAP'));
422 o
.value('CHAP', _('CHAP'));
423 o
.value('MSCHAP', _('MSCHAP'));
424 o
.value('MSCHAPV2', _('MSCHAPV2'));
425 o
.value('EAP-GTC', _('EAP-GTC'));
426 o
.value('EAP-MD5', _('EAP-MD5'));
427 o
.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2'));
428 o
.value('EAP-TLS', _('EAP-TLS'));
429 o
.value('auth=PAP', _('auth=PAP'));
430 o
.value('auth=MSCHAPV2', _('auth=MSCHAPV2'));
431 o
.default = 'EAP-MSCHAPV2';
432 o
.depends({ encryption
: 'wpa', '!contains': true });
435 o
= s
.taboption('wireless', form
.Value
, 'identity', _('Identity'));
436 o
.depends({ encryption
: 'wpa', '!contains': true });
439 o
= s
.taboption('wireless', form
.Value
, 'anonymous_identity', _('Anonymous Identity'));
440 o
.depends({ encryption
: 'wpa', '!contains': true });
443 o
= s
.taboption('wireless', form
.ListValue
, 'ieee80211w', _('Mgmt. Frame Protection'));
444 o
.depends({ encryption
: 'sae', '!contains': true });
445 o
.depends({ encryption
: 'owe', '!contains': true });
446 o
.depends({ encryption
: 'wpa', '!contains': true });
447 o
.depends({ encryption
: 'psk', '!contains': true });
448 o
.value('', _('Disabled'));
449 o
.value('1', _('Optional'));
450 o
.value('2', _('Required'));
453 '2': [{ encryption
: 'sae' }, { encryption
: 'owe' }, { encryption
: 'wpa3' }, { encryption
: 'wpa3-mixed' }],
454 '1': [{ encryption
: 'sae-mixed' }],
458 o
= s
.taboption('wireless', form
.Flag
, 'ca_cert_usesystem', _('Use system certificates'), _("Validate server certificate using built-in system CA bundle"));
459 o
.depends({ encryption
: 'wpa', '!contains': true });
463 o
.default = o
.disabled
;
465 o
= s
.taboption('wireless', form
.Value
, 'ca_cert', _('Path to CA-Certificate'));
466 o
.depends({ encryption
: 'wpa', '!contains': true });
467 o
.depends({ ca_cert_usesystem
: '0' });
471 o
= s
.taboption('wireless', form
.Value
, 'client_cert', _('Path to Client-Certificate'));
472 o
.depends({ eap_type
: 'tls' });
476 o
= s
.taboption('wireless', form
.Value
, 'priv_key', _('Path to Private Key'));
477 o
.depends({ eap_type
: 'tls' });
481 o
= s
.taboption('wireless', form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
482 o
.depends({ eap_type
: 'tls' });
490 var mac
, mac_array
= [];
491 if (result
[0] && result
[0].code
=== 0) {
492 mac_array
= result
[0].stdout
.trim().split('\n');
495 o
= s
.taboption('travelmate', form
.Value
, '_ssid', _('SSID'));
497 o
.uciconfig
= 'travelmate';
498 o
.ucisection
= 'uplink';
499 o
.ucioption
= 'ssid';
502 o
.cfgvalue = function (section_id
) {
503 return handleSectionsVal('get', section_id
, 'ssid');
506 o
= s
.taboption('travelmate', form
.Value
, '_bssid', _('BSSID'));
508 o
.uciconfig
= 'travelmate';
509 o
.ucisection
= 'uplink';
510 o
.ucioption
= 'bssid';
513 o
.cfgvalue = function (section_id
) {
514 return handleSectionsVal('get', section_id
, 'bssid');
517 o
= s
.taboption('travelmate', form
.Value
, '_con_start', _('Connection Start'));
519 o
.uciconfig
= 'travelmate';
520 o
.ucisection
= 'uplink';
521 o
.ucioption
= 'con_start';
524 o
.cfgvalue = function (section_id
) {
525 return handleSectionsVal('get', section_id
, 'con_start');
528 o
= s
.taboption('travelmate', form
.Value
, '_con_end', _('Connection End'));
530 o
.uciconfig
= 'travelmate';
531 o
.ucisection
= 'uplink';
532 o
.ucioption
= 'con_end';
535 o
.cfgvalue = function (section_id
) {
536 return handleSectionsVal('get', section_id
, 'con_end');
539 o
= s
.taboption('travelmate', form
.Flag
, '_opensta', _('Auto Added Open Uplink'),
540 _('This option is selected by default if this uplink was added automatically and counts as \'Open Uplink\'.'));
543 o
.uciconfig
= 'travelmate';
544 o
.ucisection
= 'uplink';
545 o
.ucioption
= 'opensta';
546 o
.cfgvalue = function (section_id
) {
547 return handleSectionsVal('get', section_id
, 'opensta');
549 o
.write = function (section_id
, value
) {
550 return handleSectionsVal('set', section_id
, 'opensta', value
);
552 o
.remove = function (section_id
, value
) {
553 return handleSectionsVal('set', section_id
, 'opensta', value
);
556 o
= s
.taboption('travelmate', form
.Value
, '_macaddr', _('MAC Address'),
557 _('Use the specified MAC address for this uplink.'));
558 for (var i
= 0; i
< mac_array
.length
; i
++) {
559 if (mac_array
[i
].match(/^\s+([0-9A-Fa-f]{2}[:]?){5}[0-9A-Fa-f]{2}/)) {
560 mac
= mac_array
[i
].slice(4).trim();
565 o
.uciconfig
= 'travelmate';
566 o
.ucisection
= 'uplink';
567 o
.ucioption
= 'macaddr';
570 o
.datatype
= 'macaddr';
571 o
.cfgvalue = function (section_id
) {
572 return handleSectionsVal('get', section_id
, 'macaddr');
574 o
.write = function (section_id
, value
) {
575 return handleSectionsVal('set', section_id
, 'macaddr', value
);
577 o
.remove = function (section_id
, value
) {
578 return handleSectionsVal('set', section_id
, 'macaddr', value
);
581 o
= s
.taboption('travelmate', form
.Value
, '_con_start_expiry', _('Connection Start Expiry'),
582 _('Automatically disable the uplink after <em>n</em> minutes, e.g. for timed connections.<br /> \
583 The default of \'0\' disables this feature.'));
585 o
.uciconfig
= 'travelmate';
586 o
.ucisection
= 'uplink';
587 o
.ucioption
= 'con_start_expiry';
591 o
.datatype
= 'range(0,720)';
592 o
.cfgvalue = function (section_id
) {
593 return handleSectionsVal('get', section_id
, 'con_start_expiry');
595 o
.write = function (section_id
, value
) {
596 return handleSectionsVal('set', section_id
, 'con_start_expiry', value
);
599 o
= s
.taboption('travelmate', form
.Value
, '_con_end_expiry', _('Connection End Expiry'),
600 _('Automatically (re-)enable the uplink after <em>n</em> minutes, e.g. after failed login attempts.<br /> \
601 The default of \'0\' disables this feature.'));
603 o
.uciconfig
= 'travelmate';
604 o
.ucisection
= 'uplink';
605 o
.ucioption
= 'con_end_expiry';
609 o
.datatype
= 'range(0,720)';
610 o
.cfgvalue = function (section_id
) {
611 return handleSectionsVal('get', section_id
, 'con_end_expiry');
613 o
.write = function (section_id
, value
) {
614 return handleSectionsVal('set', section_id
, 'con_end_expiry', value
);
617 o
= s
.taboption('travelmate', form
.FileUpload
, '_script', _('Auto Login Script'),
618 _('External script reference which will be called for automated captive portal logins.'));
619 o
.root_directory
= '/etc/travelmate';
620 o
.enable_remove
= false;
621 o
.enable_upload
= false;
623 o
.uciconfig
= 'travelmate';
624 o
.ucisection
= 'uplink';
625 o
.ucioption
= 'script';
626 o
.renderWidget = function (section_id
, option_index
, cfgvalue
) {
627 var browserEl
= new ui
.FileUpload((cfgvalue
!= null) ? cfgvalue
: this.default, {
628 id
: this.cbid(section_id
),
629 name
: this.cbid(section_id
),
630 show_hidden
: this.show_hidden
,
631 enable_upload
: this.enable_upload
,
632 enable_remove
: this.enable_remove
,
633 root_directory
: this.root_directory
,
634 disabled
: (this.readonly
!= null) ? this.readonly
: this.map
.readonly
636 browserEl
.renderListing = function (container
, path
, list
) {
637 return ui
.FileUpload
.prototype.renderListing
.apply(this, [
639 list
.filter(function (entry
) {
640 return ((entry
.type
== 'directory') || (entry
.type
== 'file' && entry
.name
.match(/\.login$/)));
644 return browserEl
.render();
646 o
.cfgvalue = function (section_id
) {
647 return handleSectionsVal('get', section_id
, 'script');
649 o
.write = function (section_id
, value
) {
650 return handleSectionsVal('set', section_id
, 'script', value
);
652 o
.remove = function (section_id
) {
653 return handleSectionsVal('del', section_id
, 'script');
656 o
= s
.taboption('travelmate', form
.Value
, '_args', _('Script Arguments'),
657 _('Space separated list of additional arguments passed to the Auto Login Script, i.e. username and password'));
659 o
.uciconfig
= 'travelmate';
660 o
.ucisection
= 'uplink';
661 o
.ucioption
= 'script_args';
663 o
.depends({ _script
: '/etc/travelmate', '!contains': true });
664 o
.cfgvalue = function (section_id
) {
665 return handleSectionsVal('get', section_id
, 'script_args');
667 o
.write = function (section_id
, value
) {
668 return handleSectionsVal('set', section_id
, 'script_args', value
);
670 o
.remove = function (section_id
) {
671 return handleSectionsVal('del', section_id
, 'script_args');
677 o
= s
.taboption('vpn', form
.Flag
, '_vpn', _('VPN Hook'), _('Automatically handle VPN connections.<br /> \
678 Please note: This feature requires the additional configuration of <em>Wireguard</em> or <em>OpenVPN</em>.'));
681 o
.uciconfig
= 'travelmate';
682 o
.ucisection
= 'uplink';
684 o
.cfgvalue = function (section_id
) {
685 return handleSectionsVal('get', section_id
, 'vpn');
687 o
.write = function (section_id
, value
) {
688 return handleSectionsVal('set', section_id
, 'vpn', value
);
690 o
.remove = function (section_id
, value
) {
691 return handleSectionsVal('set', section_id
, 'vpn', value
);
694 o
= s
.taboption('vpn', form
.ListValue
, '_vpnservice', _('VPN Service'));
695 o
.value('wireguard');
699 o
.uciconfig
= 'travelmate';
700 o
.ucisection
= 'uplink';
701 o
.ucioption
= 'vpnservice';
702 o
.cfgvalue = function (section_id
) {
703 return handleSectionsVal('get', section_id
, 'vpnservice');
705 o
.write = function (section_id
, value
) {
706 return handleSectionsVal('set', section_id
, 'vpnservice', value
);
709 o
= s
.taboption('vpn', widgets
.NetworkSelect
, '_vpniface', _('VPN Interface'), _('The logical vpn network interface like \'wg0\'.'));
713 o
.uciconfig
= 'travelmate';
714 o
.ucisection
= 'uplink';
715 o
.ucioption
= 'vpniface';
716 o
.cfgvalue = function (section_id
) {
717 return handleSectionsVal('get', section_id
, 'vpniface');
719 o
.write = function (section_id
, value
) {
720 return handleSectionsVal('set', section_id
, 'vpniface', value
);
726 s
= m
.section(form
.GridSection
, 'wifi-device');
729 s
.render = function () {
730 return network
.getWifiDevices().then(L
.bind(function (radios
) {
731 var radio
, ifname
, btns
= [];
732 for (var i
= 0; i
< radios
.length
; i
++) {
733 radio
= radios
[i
].sid
;
735 btns
.push(E('button', {
736 'class': 'cbi-button cbi-button-apply',
738 'click': ui
.createHandlerFn(this, 'handleScan', radio
)
739 }, [_('Scan on ' + radio
+ '...')]),
743 return E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, E('div', { 'class': 'left', 'style': 'padding-top:5px; padding-bottom:5px' }, btns
));
750 s
.handleScan = function (radio
) {
752 var table
= E('table', { 'class': 'table' }, [
753 E('tr', { 'class': 'tr table-titles' }, [
754 E('th', { 'class': 'th col-1 middle left' }, _('Strength')),
755 E('th', { 'class': 'th col-1 middle left hide-xs' }, _('Channel')),
756 E('th', { 'class': 'th col-2 middle left' }, _('SSID')),
757 E('th', { 'class': 'th col-2 middle left' }, _('BSSID')),
758 E('th', { 'class': 'th col-3 middle left' }, _('Encryption')),
759 E('th', { 'class': 'th cbi-section-actions right' }, '\xa0')
762 cbi_update_table(table
, [], E('em', { class: 'spinning' }, _('Starting wireless scan on \'' + radio
+ '\'...')));
764 var md
= ui
.showModal(_('Wireless Scan'), [
766 E('div', { 'class': 'right' }, [
769 'click': ui
.hideModal
773 'class': 'cbi-button cbi-button-positive important',
774 'click': L
.bind(this.handleScan
, this, radio
)
779 md
.style
.maxWidth
= '90%';
780 md
.style
.maxHeight
= 'none';
782 return L
.resolveDefault(fs
.exec('/etc/init.d/travelmate', ['scan', radio
]), null)
783 .then(L
.bind(function () {
784 return L
.resolveDefault(fs
.read('/var/run/travelmate.scan'), '')
785 .then(L
.bind(function (res
) {
786 var lines
, strength
, channel
, encryption
, tbl_encryption
, bssid
, ssid
, tbl_ssid
, rows
= [];
788 lines
= res
.split('\n');
789 for (var i
= 0; i
< lines
.length
; i
++) {
790 if (lines
[i
].match(/^\s+[0-9]/)) {
791 encryption
= lines
[i
].slice(80).trim();
792 if (!encryption
.includes('WEP')) {
793 strength
= lines
[i
].slice(4, 7).trim();
794 channel
= lines
[i
].slice(15, 18).trim();
795 bssid
= lines
[i
].slice(60, 77).trim();
796 ssid
= lines
[i
].slice(25, 59).trim();
797 if (ssid
.startsWith('"')) {
798 ssid
= ssid
.slice(1, ssid
.length
- 1);
803 tbl_ssid
= "<em>hidden</em>";
805 switch (encryption
) {
806 case 'WPA3 PSK (SAE)':
808 tbl_encryption
= 'WPA3 Pers. (SAE)';
810 case 'mixed WPA2/WPA3 PSK/SAE (CCMP)':
811 encryption
= 'sae-mixed';
812 tbl_encryption
= 'WPA2/WPA3 Pers. (CCMP)';
814 case 'WPA2 PSK (CCMP)':
815 encryption
= 'psk2+ccmp';
816 tbl_encryption
= 'WPA2 Pers. (CCMP)';
818 case 'WPA2 PSK (TKIP)':
819 encryption
= 'psk2+tkip';
820 tbl_encryption
= 'WPA2 Pers. (TKIP)';
822 case 'mixed WPA/WPA2 PSK (TKIP, CCMP)':
823 encryption
= 'psk-mixed+ccmp';
824 tbl_encryption
= 'WPA/WPA2 Pers. (CCMP)';
826 case 'WPA PSK (CCMP)':
827 encryption
= 'psk2+ccmp';
828 tbl_encryption
= 'WPA Pers. (CCMP)';
830 case 'WPA PSK (TKIP)':
831 encryption
= 'psk2+tkip';
832 tbl_encryption
= 'WPA Pers. (TKIP)';
834 case 'WPA3 802.1X (CCMP)':
836 tbl_encryption
= 'WPA3 Ent. (CCMP)';
838 case 'mixed WPA2/WPA3 802.1X (CCMP)':
839 encryption
= 'wpa3-mixed';
840 tbl_encryption
= 'WPA2/WPA3 Ent. (CCMP)';
844 tbl_encryption
= 'WPA2 Ent.';
846 case 'WPA2 802.1X (CCMP)':
847 encryption
= 'wpa2+ccmp';
848 tbl_encryption
= 'WPA2 Ent. (CCMP)';
850 case 'WPA3 OWE (CCMP)':
852 tbl_encryption
= 'WPA3 OWE (CCMP)';
856 tbl_encryption
= 'none';
865 E('div', { 'class': 'right' }, E('button', {
866 'class': 'cbi-button cbi-button-action',
867 'click': ui
.createHandlerFn(this, 'handleAdd', radio
, iface
, ssid
, bssid
, encryption
)
868 }, _('Add Uplink...')))
879 cbi_update_table(table
, rows
);
888 s
.handleAdd = function (radio
, iface
, ssid
, bssid
, encryption
, ev
) {
891 m2
= new form
.Map('wireless'),
892 s2
= m2
.section(form
.NamedSection
, '_add_trm');
894 s2
.render = function () {
897 this.renderUCISection('_add_trm')
898 ]).then(this.renderContents
.bind(this));
901 o2
= s2
.option(form
.Value
, 'device', _('Device Name'));
905 o2
= s2
.option(form
.Value
, 'network', _('Interface Name'));
909 if (ssid
=== "hidden") {
910 o2
= s2
.option(form
.Value
, 'ssid', _('SSID (hidden)'));
911 o2
.placeholder
= 'hidden SSID';
914 o2
= s2
.option(form
.Value
, 'ssid', _('SSID'));
917 o2
.datatype
= 'maxlength(32)';
920 o2
= s2
.option(form
.Flag
, 'ignore_bssid', _('Ignore BSSID'));
921 if (ssid
=== "hidden") {
928 o2
= s2
.option(form
.Value
, 'bssid', _('BSSID'));
929 o2
.depends({ ignore_bssid
: '0' });
930 o2
.datatype
= 'macaddr';
934 o2
= s2
.option(form
.ListValue
, 'encryption', _('Encryption'));
935 o2
.value('sae', _('WPA3 Pers. (SAE)'));
936 o2
.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)'));
937 o2
.value('psk2', _('WPA2 Pers.'));
938 o2
.value('psk2+ccmp', _('WPA2 Pers. (CCMP)'));
939 o2
.value('psk2+tkip', _('WPA2 Pers. (TKIP)'));
940 o2
.value('psk', _('WPA Pers.'));
941 o2
.value('psk+ccmp', _('WPA Pers. (CCMP)'));
942 o2
.value('psk+tkip', _('WPA Pers. (TKIP)'));
943 o2
.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)'));
944 o2
.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)'));
945 o2
.value('wpa3', _('WPA3 Ent.'));
946 o2
.value('wpa3-mixed', _('WPA2/WPA3 Ent.'));
947 o2
.value('wpa2', _('WPA2 Ent.'));
948 o2
.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)'));
949 o2
.value('wpa2+tkip', _('WPA2 Ent. (TKIP)'));
950 o2
.value('wpa+ccmp', _('WPA Ent. (CCMP)'));
951 o2
.value('wpa+tkip', _('WPA Ent. (TKIP)'));
952 o2
.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)'));
953 o2
.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)'));
954 o2
.value('owe', _('WPA3 OWE (CCMP)'));
955 o2
.value('none', _('none'));
956 o2
.default = encryption
;
958 o2
= s2
.option(form
.Value
, 'key', _('Password'));
959 o2
.depends({ encryption
: 'sae', '!contains': true });
960 o2
.depends({ encryption
: 'psk', '!contains': true });
961 o2
.datatype
= 'wpakey';
964 o2
= s2
.option(form
.Value
, 'password', _('Password'));
965 o2
.depends({ encryption
: 'wpa', '!contains': true });
966 o2
.datatype
= 'wpakey';
969 o2
= s2
.option(form
.ListValue
, 'eap_type', _('EAP-Method'));
970 o2
.depends({ encryption
: 'wpa', '!contains': true });
971 o2
.value('tls', _('TLS'));
972 o2
.value('ttls', _('TTLS'));
973 o2
.value('peap', _('PEAP'));
974 o2
.value('fast', _('FAST'));
977 o2
= s2
.option(form
.ListValue
, 'auth', _('Authentication'));
978 o2
.depends({ encryption
: 'wpa', '!contains': true });
979 o2
.value('PAP', _('PAP'));
980 o2
.value('CHAP', _('CHAP'));
981 o2
.value('MSCHAP', _('MSCHAP'));
982 o2
.value('MSCHAPV2', _('MSCHAPV2'));
983 o2
.value('EAP-GTC', _('EAP-GTC'));
984 o2
.value('EAP-MD5', _('EAP-MD5'));
985 o2
.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2'));
986 o2
.value('EAP-TLS', _('EAP-TLS'));
987 o2
.value('auth=PAP', _('auth=PAP'));
988 o2
.value('auth=MSCHAPV2', _('auth=MSCHAPV2'));
989 o2
.default = 'EAP-MSCHAPV2';
991 o2
= s2
.option(form
.Value
, 'identity', _('Identity'));
992 o2
.depends({ encryption
: 'wpa', '!contains': true });
994 o2
= s2
.option(form
.Value
, 'anonymous_identity', _('Anonymous Identity'));
995 o2
.depends({ encryption
: 'wpa', '!contains': true });
998 o2
= s2
.option(form
.ListValue
, 'ieee80211w', _('Mgmt. Frame Protection'));
999 o2
.depends({ encryption
: 'sae', '!contains': true });
1000 o2
.depends({ encryption
: 'owe', '!contains': true });
1001 o2
.depends({ encryption
: 'wpa', '!contains': true });
1002 o2
.depends({ encryption
: 'psk', '!contains': true });
1003 o2
.value('', _('Disabled'));
1004 o2
.value('1', _('Optional'));
1005 o2
.value('2', _('Required'));
1007 '2': [{ encryption
: 'sae' }, { encryption
: 'owe' }, { encryption
: 'wpa3' }, { encryption
: 'wpa3-mixed' }],
1008 '1': [{ encryption
: 'sae-mixed' }],
1012 o2
= s2
.option(form
.Flag
, 'ca_cert_usesystem', _('Use system certificates'), _("Validate server certificate using built-in system CA bundle"));
1013 o2
.depends({ encryption
: 'wpa', '!contains': true });
1016 o2
.default = o
.disabled
;
1018 o2
= s2
.option(form
.Value
, 'ca_cert', _('Path to CA-Certificate'));
1019 o2
.depends({ encryption
: 'wpa', '!contains': true });
1020 o2
.depends({ ca_cert_usesystem
: '0' });
1023 o2
= s2
.option(form
.Value
, 'client_cert', _('Path to Client-Certificate'));
1024 o2
.depends({ eap_type
: 'tls' });
1027 o2
= s2
.option(form
.Value
, 'priv_key', _('Path to Private Key'));
1028 o2
.depends({ eap_type
: 'tls' });
1031 o2
= s2
.option(form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
1032 o2
.depends({ eap_type
: 'tls' });
1036 return m2
.render().then(L
.bind(function (elements
) {
1037 ui
.showModal(_('Add Uplink %q').replace(/%q/, '"%h"'.format(ssid
)), [
1039 E('div', { 'class': 'right' }, [
1042 'click': ui
.hideModal
1046 'class': 'cbi-button cbi-button-positive important',
1047 'click': ui
.createHandlerFn(this, 'handleCommit', m2
)
1057 s
.handleCommit = function (map
, ev
) {
1058 var w_sections
= uci
.sections('wireless', 'wifi-iface'),
1059 device
= L
.toArray(map
.lookupOption('device', '_add_trm'))[0].formvalue('_add_trm'),
1060 network
= L
.toArray(map
.lookupOption('network', '_add_trm'))[0].formvalue('_add_trm'),
1061 ssid
= L
.toArray(map
.lookupOption('ssid', '_add_trm'))[0].formvalue('_add_trm'),
1062 ignore_bssid
= L
.toArray(map
.lookupOption('ignore_bssid', '_add_trm'))[0].formvalue('_add_trm'),
1063 bssid
= L
.toArray(map
.lookupOption('bssid', '_add_trm'))[0].formvalue('_add_trm'),
1064 encryption
= L
.toArray(map
.lookupOption('encryption', '_add_trm'))[0].formvalue('_add_trm');
1065 if (encryption
.includes('wpa')) {
1066 var eap_type
= L
.toArray(map
.lookupOption('eap_type', '_add_trm'))[0].formvalue('_add_trm'),
1067 auth
= L
.toArray(map
.lookupOption('auth', '_add_trm'))[0].formvalue('_add_trm'),
1068 identity
= L
.toArray(map
.lookupOption('identity', '_add_trm'))[0].formvalue('_add_trm'),
1069 anonymous_identity
= L
.toArray(map
.lookupOption('anonymous_identity', '_add_trm'))[0].formvalue('_add_trm'),
1070 password
= L
.toArray(map
.lookupOption('password', '_add_trm'))[0].formvalue('_add_trm'),
1071 ca_cert_usesystem
= L
.toArray(map
.lookupOption('ca_cert_usesystem', '_add_trm'))[0].formvalue('_add_trm'),
1072 ca_cert
= L
.toArray(map
.lookupOption('ca_cert', '_add_trm'))[0].formvalue('_add_trm'),
1073 ieee80211w
= L
.toArray(map
.lookupOption('ieee80211w', '_add_trm'))[0].formvalue('_add_trm');
1074 if (eap_type
.includes('tls')) {
1075 var client_cert
= L
.toArray(map
.lookupOption('client_cert', '_add_trm'))[0].formvalue('_add_trm'),
1076 priv_key
= L
.toArray(map
.lookupOption('priv_key', '_add_trm'))[0].formvalue('_add_trm'),
1077 priv_key_pwd
= L
.toArray(map
.lookupOption('priv_key_pwd', '_add_trm'))[0].formvalue('_add_trm');
1080 var password
= L
.toArray(map
.lookupOption('key', '_add_trm'))[0].formvalue('_add_trm');
1082 if (!ssid
|| ((encryption
.includes('psk') || encryption
.includes('wpa') || encryption
.includes('sae')) && !password
)) {
1084 ui
.addNotification(null, E('p', 'Empty SSID, the uplink station could not be saved.'), 'error');
1087 ui
.addNotification(null, E('p', 'Empty Password, the uplink station could not be saved.'), 'error');
1089 return ui
.hideModal();
1091 for (var i
= 0; i
< w_sections
.length
; i
++) {
1092 if (w_sections
[i
].device
=== device
&& w_sections
[i
].ssid
=== ssid
) {
1093 if (ignore_bssid
=== '1' || (ignore_bssid
=== '0' && w_sections
[i
].bssid
=== bssid
)) {
1094 ui
.addNotification(null, E('p', 'Duplicate wireless entry, the uplink station could not be saved.'), 'error');
1095 return ui
.hideModal();
1100 var offset
= w_sections
.length
,
1101 new_sid
= 'trm_uplink' + (++offset
);
1102 while (uci
.get('wireless', new_sid
)) {
1103 new_sid
= 'trm_uplink' + (++offset
);
1105 uci
.add('wireless', 'wifi-iface', new_sid
);
1106 uci
.set('wireless', new_sid
, 'device', device
);
1107 uci
.set('wireless', new_sid
, 'mode', 'sta');
1108 uci
.set('wireless', new_sid
, 'network', network
);
1109 uci
.set('wireless', new_sid
, 'ssid', ssid
);
1110 if (ignore_bssid
=== '0') {
1111 uci
.set('wireless', new_sid
, 'bssid', bssid
);
1113 uci
.set('wireless', new_sid
, 'encryption', encryption
);
1114 if (encryption
.includes('wpa')) {
1115 uci
.set('wireless', new_sid
, 'eap_type', eap_type
);
1116 uci
.set('wireless', new_sid
, 'auth', auth
);
1117 uci
.set('wireless', new_sid
, 'identity', identity
);
1118 uci
.set('wireless', new_sid
, 'anonymous_identity', anonymous_identity
);
1119 uci
.set('wireless', new_sid
, 'password', password
);
1120 uci
.set('wireless', new_sid
, 'ca_cert_usesystem', ca_cert_usesystem
);
1121 uci
.set('wireless', new_sid
, 'ca_cert', ca_cert
);
1122 uci
.set('wireless', new_sid
, 'ieee80211w', ieee80211w
);
1123 if (eap_type
.includes('tls')) {
1124 uci
.set('wireless', new_sid
, 'client_cert', client_cert
);
1125 uci
.set('wireless', new_sid
, 'priv_key', priv_key
);
1126 uci
.set('wireless', new_sid
, 'priv_key_pwd', priv_key_pwd
);
1129 uci
.set('wireless', new_sid
, 'key', password
);
1131 uci
.set('wireless', new_sid
, 'disabled', '1');
1132 handleSectionsAdd(network
);
1134 .then(L
.bind(this.map
.load
, this.map
))
1135 .then(L
.bind(this.map
.reset
, this.map
))
1137 var row
= document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(new_sid
));
1138 row
.setAttribute('style', 'opacity: 0.5; color: #4a4 !important;');