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');
73 for (var i
= 0; i
< w_sections
.length
; i
++) {
74 if (w_sections
[i
].mode
!== 'sta' || w_sections
[i
].network
!== iface
) {
78 for (var j
= 0; j
< t_sections
.length
; j
++) {
79 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
) {
84 if (match
=== false) {
85 var sid
= uci
.add('travelmate', 'uplink');
86 uci
.set('travelmate', sid
, 'enabled', '1');
87 uci
.set('travelmate', sid
, 'device', w_sections
[i
].device
);
88 uci
.set('travelmate', sid
, 'ssid', w_sections
[i
].ssid
);
89 uci
.set('travelmate', sid
, 'bssid', w_sections
[i
].bssid
);
90 uci
.set('travelmate', sid
, 'con_start_expiry', '0');
91 uci
.set('travelmate', sid
, 'con_end_expiry', '0');
97 update travelmate sections
99 function handleSectionsVal(action
, section_id
, option
, value
) {
100 var date
, oldValue
, w_device
, w_ssid
, w_bssid
, t_sections
;
102 w_device
= uci
.get('wireless', section_id
, 'device');
103 w_ssid
= uci
.get('wireless', section_id
, 'ssid');
104 w_bssid
= uci
.get('wireless', section_id
, 'bssid');
105 t_sections
= uci
.sections('travelmate', 'uplink');
107 for (var i
= 0; i
< t_sections
.length
; i
++) {
108 if (t_sections
[i
].device
=== w_device
&& t_sections
[i
].ssid
=== w_ssid
&& t_sections
[i
].bssid
=== w_bssid
) {
109 if (action
=== 'get') {
110 return t_sections
[i
][option
];
112 else if (action
=== 'set') {
113 if (option
=== 'enabled') {
114 oldValue
= t_sections
[i
][option
];
115 if (oldValue
!== value
&& value
=== '0') {
116 date
= new Date(new Date().getTime() - new Date().getTimezoneOffset() * 60 * 1000).toISOString().substr(0, 19).replace(/-/g
, '.').replace('T', '-');
117 uci
.set('travelmate', t_sections
[i
]['.name'], 'con_end', date
);
119 else if (oldValue
!== value
&& value
=== '1') {
120 uci
.unset('travelmate', t_sections
[i
]['.name'], 'con_end');
123 return uci
.set('travelmate', t_sections
[i
]['.name'], option
, value
);
125 else if (action
=== 'del') {
126 return uci
.unset('travelmate', t_sections
[i
]['.name'], option
);
133 update travelmate status
135 function handleStatus() {
136 poll
.add(function () {
137 L
.resolveDefault(fs
.stat('/var/state/travelmate.refresh'), null).then(function (res
) {
139 L
.resolveDefault(fs
.read_direct('/var/state/travelmate.refresh'), null).then(async
function (res
) {
140 fs
.remove('/var/state/travelmate.refresh');
141 if (res
&& res
=== 'ui_reload') {
144 else if (res
&& res
=== 'cfg_reload') {
145 if (document
.readyState
=== 'complete') {
146 uci
.unload('wireless');
147 uci
.unload('travelmate');
150 uci
.load('wireless'),
151 uci
.load('travelmate')
153 var rows
, item
, value
;
154 rows
= document
.querySelectorAll('.cbi-section-table-row[data-sid]');
155 for (var i
= 0; i
< rows
.length
; i
++) {
156 item
= rows
[i
].querySelector('.cbi-value-field[data-title="Enabled"]');
157 value
= handleSectionsVal('get', rows
[i
].getAttribute('data-sid'), 'enabled');
158 item
.textContent
= (value
== 0 ? 'No' : 'Yes');
164 return L
.resolveDefault(fs
.stat('/tmp/trm_runtime.json'), null).then(function (res
) {
166 L
.resolveDefault(fs
.read_direct('/tmp/trm_runtime.json'), null).then(function (res
) {
168 var info
= JSON
.parse(res
);
170 var t_device
, t_ssid
, t_bssid
, oldUplinkView
, newUplinkView
,
171 uplinkId
= info
.data
.station_id
.trim().split('/'),
172 oldUplinkView
= document
.getElementsByName('uplinkStation'),
173 w_sections
= uci
.sections('wireless', 'wifi-iface');
175 t_device
= uplinkId
[0];
176 t_bssid
= uplinkId
[uplinkId
.length
- 1];
177 for (var i
= 1; i
< uplinkId
.length
- 1; i
++) {
179 t_ssid
= uplinkId
[i
];
182 t_ssid
= t_ssid
+ '/' + uplinkId
[i
];
185 if (t_ssid
=== '-') {
186 if (oldUplinkView
.length
> 0) {
187 oldUplinkView
[0].removeAttribute('style');
188 oldUplinkView
[0].removeAttribute('name', 'uplinkStation');
192 for (var i
= 0; i
< w_sections
.length
; i
++) {
193 newUplinkView
= document
.getElementById('cbi-wireless-' + w_sections
[i
]['.name']);
194 if (t_device
=== w_sections
[i
].device
&& t_ssid
=== w_sections
[i
].ssid
&& t_bssid
=== (w_sections
[i
].bssid
|| '-')) {
195 if (oldUplinkView
.length
=== 0 && newUplinkView
) {
196 newUplinkView
.setAttribute('name', 'uplinkStation');
197 newUplinkView
.setAttribute('style', 'text-align: left !important; color: #37c !important;font-weight: bold !important;');
199 else if (oldUplinkView
.length
> 0 && newUplinkView
&& oldUplinkView
[0].getAttribute('id') !== newUplinkView
.getAttribute('id')) {
200 oldUplinkView
[0].removeAttribute('style');
201 oldUplinkView
[0].removeAttribute('name', 'uplinkStation');
202 newUplinkView
.setAttribute('name', 'uplinkStation');
203 newUplinkView
.setAttribute('style', 'text-align: left !important; color: #37c !important;font-weight: bold !important;');
219 L
.resolveDefault(fs
.exec_direct('/etc/init.d/travelmate', ['assoc']), {}),
220 uci
.load('wireless'),
221 uci
.load('travelmate')
225 render: function (result
) {
227 iface
= uci
.get('travelmate', 'global', 'trm_iface') || 'trm_wwan';
229 m
= new form
.Map('wireless');
230 m
.chain('travelmate');
231 s
= m
.section(form
.GridSection
, 'wifi-iface', null, _('Overview of all configured uplinks for travelmate.<br /> \
232 You can edit, remove or prioritize existing uplinks by drag \& drop and scan for new ones. The currently used uplink is emphasized in blue.'));
235 s
.filter = function (section_id
) {
236 return (uci
.get('wireless', section_id
, 'network') == iface
&& uci
.get('wireless', section_id
, 'mode') == 'sta');
238 s
.tab('wireless', _('Wireless Settings'));
239 s
.tab('travelmate', _('Travelmate Settings'));
240 s
.tab('vpn', _('VPN Settings'));
241 s
.renderRowActions = function (section_id
) {
245 'class': 'btn cbi-button drag-handle center',
246 'title': _('Drag to reorder'),
247 'style': 'cursor:move',
248 'disabled': this.map
.readonly
|| null
251 'class': 'cbi-button cbi-button-action important',
252 'title': _('Edit this network'),
253 'click': ui
.createHandlerFn(this, 'renderMoreOptionsModal', section_id
)
256 'class': 'cbi-button cbi-button-apply',
257 'title': _('Enable/Disable this network'),
258 'click': ui
.createHandlerFn(this, handleToggle
, section_id
)
261 'class': 'cbi-button cbi-button-negative remove',
262 'title': _('Remove this network'),
263 'click': ui
.createHandlerFn(this, handleRemove
, section_id
)
266 return E('td', { 'class': 'td middle cbi-section-actions' }, E('div', btns
));
269 o
= s
.taboption('travelmate', form
.Flag
, '_enabled', _('Enabled'));
270 o
.uciconfig
= 'travelmate';
271 o
.ucisection
= 'uplink';
272 o
.ucioption
= 'enabled';
274 o
.cfgvalue = function (section_id
) {
275 return handleSectionsVal('get', section_id
, 'enabled');
277 o
.write = function (section_id
, value
) {
278 return handleSectionsVal('set', section_id
, 'enabled', value
);
281 o
= s
.taboption('wireless', form
.Value
, 'device', _('Device'));
284 o
= s
.taboption('wireless', form
.Value
, 'ssid', _('SSID'));
285 o
.datatype
= 'maxlength(32)';
288 o
= s
.taboption('wireless', form
.Value
, 'bssid', _('BSSID'));
289 o
.datatype
= 'macaddr';
292 o
= s
.taboption('wireless', form
.ListValue
, 'encryption', _('Encryption'));
293 o
.value('sae', _('WPA3 Pers. (SAE)'));
294 o
.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)'));
295 o
.value('psk2', _('WPA2 Pers.'));
296 o
.value('psk2+ccmp', _('WPA2 Pers. (CCMP)'));
297 o
.value('psk2+tkip', _('WPA2 Pers. (TKIP)'));
298 o
.value('psk', _('WPA Pers.'));
299 o
.value('psk+ccmp', _('WPA Pers. (CCMP)'));
300 o
.value('psk+tkip', _('WPA Pers. (TKIP)'));
301 o
.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)'));
302 o
.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)'));
303 o
.value('wpa3', _('WPA3 Ent. (CCMP)'));
304 o
.value('wpa3-mixed', _('WPA2/WPA3 Ent. (CCMP)'));
305 o
.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)'));
306 o
.value('wpa2+tkip', _('WPA2 Ent. (TKIP)'));
307 o
.value('wpa+ccmp', _('WPA Ent. (CCMP)'));
308 o
.value('wpa+tkip', _('WPA Ent. (TKIP)'));
309 o
.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)'));
310 o
.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)'));
311 o
.value('owe', _('OWE'));
312 o
.value('none', _('none'));
314 o
.textvalue = function (section_id
) {
315 var cfgvalue
= this.map
.data
.get('wireless', section_id
, 'encryption');
318 cfgvalue
= 'WPA3 Pers. (SAE)';
321 cfgvalue
= 'WPA2/WPA3 Pers. (CCMP)';
324 cfgvalue
= 'WPA2 Pers.';
327 cfgvalue
= 'WPA2 Pers. (CCMP)';
330 cfgvalue
= 'WPA2 Ent. (TKIP)';
333 cfgvalue
= 'WPA Pers.';
335 case 'psk-mixed+ccmp':
336 cfgvalue
= 'WPA/WPA2 Pers. (CCMP)';
338 case 'psk-mixed+tkip':
339 cfgvalue
= 'WPA/WPA2 Pers. (TKIP)';
342 cfgvalue
= 'WPA3 Ent. (CCMP)';
345 cfgvalue
= 'WPA2/WPA3 Ent. (CCMP)';
348 cfgvalue
= 'WPA2 Ent. (CCMP)';
351 cfgvalue
= 'WPA2 Ent. (TKIP)';
354 cfgvalue
= 'WPA Ent. (CCMP)';
357 cfgvalue
= 'WPA Ent. (TKIP)';
359 case 'wpa-mixed+ccmp':
360 cfgvalue
= 'WPA/WPA2 Ent. (CCMP)';
362 case 'wpa-mixed+tkip':
363 cfgvalue
= 'WPA/WPA2 Ent. (TKIP)';
366 cfgvalue
= 'WPA3 OWE (CCMP)';
379 o
= s
.taboption('wireless', form
.Value
, 'key', _('Password'));
380 o
.datatype
= 'wpakey';
381 o
.depends({ encryption
: 'sae', '!contains': true });
382 o
.depends({ encryption
: 'psk', '!contains': true });
383 o
.depends({ encryption
: 'wpa', '!contains': true });
387 o
= s
.taboption('wireless', form
.ListValue
, 'eap_type', _('EAP-Method'));
388 o
.value('tls', _('TLS'));
389 o
.value('ttls', _('TTLS'));
390 o
.value('peap', _('PEAP'));
391 o
.value('fast', _('FAST'));
393 o
.depends({ encryption
: 'wpa', '!contains': true });
396 o
= s
.taboption('wireless', form
.ListValue
, 'auth', _('Authentication'));
397 o
.value('PAP', _('PAP'));
398 o
.value('CHAP', _('CHAP'));
399 o
.value('MSCHAP', _('MSCHAP'));
400 o
.value('MSCHAPV2', _('MSCHAPV2'));
401 o
.value('EAP-GTC', _('EAP-GTC'));
402 o
.value('EAP-MD5', _('EAP-MD5'));
403 o
.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2'));
404 o
.value('EAP-TLS', _('EAP-TLS'));
405 o
.value('auth=PAP', _('auth=PAP'));
406 o
.value('auth=MSCHAPV2', _('auth=MSCHAPV2'));
407 o
.default = 'EAP-MSCHAPV2';
408 o
.depends({ encryption
: 'wpa', '!contains': true });
411 o
= s
.taboption('wireless', form
.Value
, 'identify', _('Identify'));
412 o
.depends({ encryption
: 'wpa', '!contains': true });
415 o
= s
.taboption('wireless', form
.Value
, 'ca_cert', _('Path to CA-Certificate'));
416 o
.depends({ eap_type
: 'tls' });
420 o
= s
.taboption('wireless', form
.Value
, 'client_cert', _('Path to Client-Certificate'));
421 o
.depends({ eap_type
: 'tls' });
425 o
= s
.taboption('wireless', form
.Value
, 'priv_key', _('Path to Private Key'));
426 o
.depends({ eap_type
: 'tls' });
430 o
= s
.taboption('wireless', form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
431 o
.datatype
= 'wpakey';
432 o
.depends({ eap_type
: 'tls' });
440 var mac
, mac_array
= [];
442 mac_array
= result
[0].trim().split('\n');
445 o
= s
.taboption('travelmate', form
.Value
, '_ssid', _('SSID'));
447 o
.uciconfig
= 'travelmate';
448 o
.ucisection
= 'uplink';
449 o
.ucioption
= 'ssid';
452 o
.cfgvalue = function (section_id
) {
453 return handleSectionsVal('get', section_id
, 'ssid');
456 o
= s
.taboption('travelmate', form
.Value
, '_bssid', _('BSSID'));
458 o
.uciconfig
= 'travelmate';
459 o
.ucisection
= 'uplink';
460 o
.ucioption
= 'bssid';
463 o
.cfgvalue = function (section_id
) {
464 return handleSectionsVal('get', section_id
, 'bssid');
467 o
= s
.taboption('travelmate', form
.Value
, '_con_start', _('Connection Start'));
469 o
.uciconfig
= 'travelmate';
470 o
.ucisection
= 'uplink';
471 o
.ucioption
= 'con_start';
474 o
.cfgvalue = function (section_id
) {
475 return handleSectionsVal('get', section_id
, 'con_start');
478 o
= s
.taboption('travelmate', form
.Value
, '_con_end', _('Connection End'));
480 o
.uciconfig
= 'travelmate';
481 o
.ucisection
= 'uplink';
482 o
.ucioption
= 'con_end';
485 o
.cfgvalue = function (section_id
) {
486 return handleSectionsVal('get', section_id
, 'con_end');
489 o
= s
.taboption('travelmate', form
.Flag
, '_opensta', _('Auto Added Open Uplink'),
490 _('This option is selected by default if this uplink was added automatically and counts as \'Open Uplink\'.'));
493 o
.uciconfig
= 'travelmate';
494 o
.ucisection
= 'uplink';
495 o
.ucioption
= 'opensta';
496 o
.cfgvalue = function (section_id
) {
497 return handleSectionsVal('get', section_id
, 'opensta');
499 o
.write = function (section_id
, value
) {
500 return handleSectionsVal('set', section_id
, 'opensta', value
);
502 o
.remove = function (section_id
, value
) {
503 return handleSectionsVal('set', section_id
, 'opensta', value
);
506 o
= s
.taboption('travelmate', form
.Value
, '_macaddr', _('MAC Address'),
507 _('Use the specified MAC address for this uplink.'));
508 for (var i
= 0; i
< mac_array
.length
; i
++) {
509 if (mac_array
[i
].match(/^\s+([0-9A-Fa-f]{2}[:]?){5}[0-9A-Fa-f]{2}/)) {
510 mac
= mac_array
[i
].slice(4).trim();
515 o
.uciconfig
= 'travelmate';
516 o
.ucisection
= 'uplink';
517 o
.ucioption
= 'macaddr';
519 o
.unspecified
= true;
521 o
.datatype
= 'macaddr';
522 o
.cfgvalue = function (section_id
) {
523 return handleSectionsVal('get', section_id
, 'macaddr');
525 o
.write = function (section_id
, value
) {
526 return handleSectionsVal('set', section_id
, 'macaddr', value
);
528 o
.remove = function (section_id
, value
) {
529 return handleSectionsVal('set', section_id
, 'macaddr', value
);
532 o
= s
.taboption('travelmate', form
.Value
, '_con_start_expiry', _('Connection Start Expiry'),
533 _('Automatically disable the uplink after <em>n</em> minutes, e.g. for timed connections.<br /> \
534 The default of \'0\' disables this feature.'));
536 o
.uciconfig
= 'travelmate';
537 o
.ucisection
= 'uplink';
538 o
.ucioption
= 'con_start_expiry';
542 o
.datatype
= 'range(0,720)';
543 o
.cfgvalue = function (section_id
) {
544 return handleSectionsVal('get', section_id
, 'con_start_expiry');
546 o
.write = function (section_id
, value
) {
547 return handleSectionsVal('set', section_id
, 'con_start_expiry', value
);
550 o
= s
.taboption('travelmate', form
.Value
, '_con_end_expiry', _('Connection End Expiry'),
551 _('Automatically (re-)enable the uplink after <em>n</em> minutes, e.g. after failed login attempts.<br /> \
552 The default of \'0\' disables this feature.'));
554 o
.uciconfig
= 'travelmate';
555 o
.ucisection
= 'uplink';
556 o
.ucioption
= 'con_end_expiry';
560 o
.datatype
= 'range(0,720)';
561 o
.cfgvalue = function (section_id
) {
562 return handleSectionsVal('get', section_id
, 'con_end_expiry');
564 o
.write = function (section_id
, value
) {
565 return handleSectionsVal('set', section_id
, 'con_end_expiry', value
);
568 o
= s
.taboption('travelmate', form
.FileUpload
, '_script', _('Auto Login Script'),
569 _('External script reference which will be called for automated captive portal logins.'));
570 o
.root_directory
= '/etc/travelmate';
571 o
.enable_remove
= false;
572 o
.enable_upload
= false;
574 o
.uciconfig
= 'travelmate';
575 o
.ucisection
= 'uplink';
576 o
.ucioption
= 'script';
577 o
.renderWidget = function (section_id
, option_index
, cfgvalue
) {
578 var browserEl
= new ui
.FileUpload((cfgvalue
!= null) ? cfgvalue
: this.default, {
579 id
: this.cbid(section_id
),
580 name
: this.cbid(section_id
),
581 show_hidden
: this.show_hidden
,
582 enable_upload
: this.enable_upload
,
583 enable_remove
: this.enable_remove
,
584 root_directory
: this.root_directory
,
585 disabled
: (this.readonly
!= null) ? this.readonly
: this.map
.readonly
587 browserEl
.renderListing = function (container
, path
, list
) {
588 return ui
.FileUpload
.prototype.renderListing
.apply(this, [
590 list
.filter(function (entry
) {
591 return ((entry
.type
== 'directory') || (entry
.type
== 'file' && entry
.name
.match(/\.login$/)));
595 return browserEl
.render();
597 o
.cfgvalue = function (section_id
) {
598 return handleSectionsVal('get', section_id
, 'script');
600 o
.write = function (section_id
, value
) {
601 return handleSectionsVal('set', section_id
, 'script', value
);
603 o
.remove = function (section_id
) {
604 return handleSectionsVal('del', section_id
, 'script');
607 o
= s
.taboption('travelmate', form
.Value
, '_args', _('Script Arguments'),
608 _('Space separated list of additional arguments passed to the Auto Login Script, i.e. username and password'));
610 o
.uciconfig
= 'travelmate';
611 o
.ucisection
= 'uplink';
612 o
.ucioption
= 'script_args';
614 o
.depends({ _script
: '/etc/travelmate', '!contains': true });
615 o
.cfgvalue = function (section_id
) {
616 return handleSectionsVal('get', section_id
, 'script_args');
618 o
.write = function (section_id
, value
) {
619 return handleSectionsVal('set', section_id
, 'script_args', value
);
621 o
.remove = function (section_id
) {
622 return handleSectionsVal('del', section_id
, 'script_args');
628 o
= s
.taboption('vpn', form
.Flag
, '_vpn', _('VPN Hook'), _('Automatically handle VPN connections.<br /> \
629 Please note: This feature requires the additional configuration of <em>Wireguard</em> or <em>OpenVPN</em>.'));
632 o
.uciconfig
= 'travelmate';
633 o
.ucisection
= 'uplink';
635 o
.cfgvalue = function (section_id
) {
636 return handleSectionsVal('get', section_id
, 'vpn');
638 o
.write = function (section_id
, value
) {
639 return handleSectionsVal('set', section_id
, 'vpn', value
);
641 o
.remove = function (section_id
, value
) {
642 return handleSectionsVal('set', section_id
, 'vpn', value
);
645 o
= s
.taboption('vpn', form
.ListValue
, '_vpnservice', _('VPN Service'));
646 o
.value('wireguard');
650 o
.uciconfig
= 'travelmate';
651 o
.ucisection
= 'uplink';
652 o
.ucioption
= 'vpnservice';
653 o
.cfgvalue = function (section_id
) {
654 return handleSectionsVal('get', section_id
, 'vpnservice');
656 o
.write = function (section_id
, value
) {
657 return handleSectionsVal('set', section_id
, 'vpnservice', value
);
660 o
= s
.taboption('vpn', widgets
.NetworkSelect
, '_vpniface', _('VPN Interface'), _('The logical vpn network interface, e.g. \'wg0\' or \'tun0\'.'));
661 o
.unspecified
= false;
665 o
.uciconfig
= 'travelmate';
666 o
.ucisection
= 'uplink';
667 o
.ucioption
= 'vpniface';
668 o
.cfgvalue = function (section_id
) {
669 return handleSectionsVal('get', section_id
, 'vpniface');
671 o
.write = function (section_id
, value
) {
672 return handleSectionsVal('set', section_id
, 'vpniface', value
);
678 s
= m
.section(form
.GridSection
, 'wifi-device');
681 s
.render = function () {
682 return network
.getWifiDevices().then(L
.bind(function (radios
) {
683 var radio
, ifname
, btns
= [];
684 for (var i
= 0; i
< radios
.length
; i
++) {
685 radio
= radios
[i
].sid
;
687 btns
.push(E('button', {
688 'class': 'cbi-button cbi-button-positive',
690 'click': ui
.createHandlerFn(this, 'handleScan', radio
)
691 }, [_('Scan on ' + radio
+ '...')]),
695 return E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, E('div', { 'class': 'left', 'style': 'padding-top:5px; padding-bottom:5px' }, btns
));
702 s
.handleScan = function (radio
) {
703 var table
= E('table', { 'class': 'table' }, [
704 E('tr', { 'class': 'tr table-titles' }, [
705 E('th', { 'class': 'th col-1 middle left' }, _('Strength')),
706 E('th', { 'class': 'th col-1 middle left hide-xs' }, _('Channel')),
707 E('th', { 'class': 'th col-2 middle left' }, _('SSID')),
708 E('th', { 'class': 'th col-2 middle left' }, _('BSSID')),
709 E('th', { 'class': 'th col-3 middle left' }, _('Encryption')),
710 E('th', { 'class': 'th cbi-section-actions right' }, '\xa0')
713 cbi_update_table(table
, [], E('em', { class: 'spinning' }, _('Starting wireless scan on \'' + radio
+ '\'...')));
715 var md
= ui
.showModal(_('Wireless Scan'), [
717 E('div', { 'class': 'right' }, [
720 'click': ui
.hideModal
724 'class': 'cbi-button cbi-button-positive important',
725 'click': L
.bind(this.handleScan
, this, radio
)
730 md
.style
.maxWidth
= '90%';
731 md
.style
.maxHeight
= 'none';
733 return L
.resolveDefault(fs
.exec_direct('/etc/init.d/travelmate', ['scan', radio
]), null)
734 .then(L
.bind(function (res
) {
735 var lines
, strength
, channel
, encryption
, tbl_encryption
, bssid
, ssid
, tbl_ssid
, rows
= [];
737 lines
= res
.trim().split('\n');
738 for (var i
= 0; i
< lines
.length
; i
++) {
739 if (lines
[i
].match(/^\s+[0-9]/)) {
740 encryption
= lines
[i
].slice(80).trim();
741 if (!encryption
.includes('WEP')) {
742 strength
= lines
[i
].slice(4, 7).trim();
743 channel
= lines
[i
].slice(15, 18).trim();
744 bssid
= lines
[i
].slice(60, 77).trim();
745 ssid
= lines
[i
].slice(25, 59).trim();
746 if (ssid
.startsWith('"')) {
747 ssid
= ssid
.slice(1, ssid
.length
- 1);
752 tbl_ssid
= "<em>hidden</em>";
754 switch (encryption
) {
755 case 'WPA3 PSK (SAE)':
757 tbl_encryption
= 'WPA3 Pers. (SAE)';
759 case 'mixed WPA2/WPA3 PSK/SAE (CCMP)':
760 encryption
= 'sae-mixed';
761 tbl_encryption
= 'WPA2/WPA3 Pers. (CCMP)';
763 case 'WPA2 PSK (CCMP)':
764 encryption
= 'psk2+ccmp';
765 tbl_encryption
= 'WPA2 Pers. (CCMP)';
767 case 'WPA2 PSK (TKIP)':
768 encryption
= 'psk2+tkip';
769 tbl_encryption
= 'WPA2 Pers. (TKIP)';
771 case 'mixed WPA/WPA2 PSK (TKIP, CCMP)':
772 encryption
= 'psk-mixed+ccmp';
773 tbl_encryption
= 'WPA/WPA2 Pers. (CCMP)';
775 case 'WPA3 802.1X (CCMP)':
777 tbl_encryption
= 'WPA3 Ent. (CCMP)';
779 case 'mixed WPA2/WPA3 802.1X (CCMP)':
780 encryption
= 'wpa3-mixed';
781 tbl_encryption
= 'WPA2/WPA3 Ent. (CCMP)';
783 case 'WPA PSK (CCMP)':
784 encryption
= 'psk2+ccmp';
785 tbl_encryption
= 'WPA Pers. (CCMP)';
787 case 'WPA PSK (TKIP)':
788 encryption
= 'psk2+tkip';
789 tbl_encryption
= 'WPA Pers. (TKIP)';
791 case 'WPA2 802.1X (CCMP)':
792 encryption
= 'wpa2+ccmp';
793 tbl_encryption
= 'WPA2 Ent. (CCMP)';
795 case 'WPA3 OWE (CCMP)':
797 tbl_encryption
= 'WPA3 OWE (CCMP)';
801 tbl_encryption
= 'none';
810 E('div', { 'class': 'right' }, E('button', {
811 'class': 'cbi-button cbi-button-action',
812 'click': ui
.createHandlerFn(this, 'handleAdd', radio
, iface
, ssid
, bssid
, encryption
)
813 }, _('Add Uplink...')))
817 else if (lines
[i
] === '::: Empty resultset') {
819 'No scan results (empty resultset)'
826 'No scan results (timeout)'
829 cbi_update_table(table
, rows
);
836 s
.handleAdd = function (radio
, iface
, ssid
, bssid
, encryption
, ev
) {
839 m2
= new form
.Map('wireless'),
840 s2
= m2
.section(form
.NamedSection
, '_add_trm');
842 s2
.render = function () {
845 this.renderUCISection('_add_trm')
846 ]).then(this.renderContents
.bind(this));
849 o2
= s2
.option(form
.Value
, 'device', _('Device Name'));
853 o2
= s2
.option(form
.Value
, 'network', _('Interface Name'));
857 if (ssid
=== "hidden") {
858 o2
= s2
.option(form
.Value
, 'ssid', _('SSID (hidden)'));
859 o2
.placeholder
= 'hidden SSID';
862 o2
= s2
.option(form
.Value
, 'ssid', _('SSID'));
865 o2
.datatype
= 'maxlength(32)';
868 o2
= s2
.option(form
.Flag
, 'ignore_bssid', _('Ignore BSSID'));
869 if (ssid
=== "hidden") {
876 o2
= s2
.option(form
.Value
, 'bssid', _('BSSID'));
877 o2
.depends({ ignore_bssid
: '0' });
878 o2
.datatype
= 'macaddr';
882 o2
= s2
.option(form
.ListValue
, 'encryption', _('Encryption'));
883 o2
.value('sae', _('WPA3 Pers. (SAE)'));
884 o2
.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)'));
885 o2
.value('psk2', _('WPA2 Pers.'));
886 o2
.value('psk2+ccmp', _('WPA2 Pers. (CCMP)'));
887 o2
.value('psk2+tkip', _('WPA2 Pers. (TKIP)'));
888 o2
.value('psk', _('WPA Pers.'));
889 o2
.value('psk+ccmp', _('WPA Pers. (CCMP)'));
890 o2
.value('psk+tkip', _('WPA Pers. (TKIP)'));
891 o2
.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)'));
892 o2
.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)'));
893 o2
.value('wpa3', _('WPA3 Ent.'));
894 o2
.value('wpa3-mixed', _('WPA2/WPA3 Ent.'));
895 o2
.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)'));
896 o2
.value('wpa2+tkip', _('WPA2 Ent. (TKIP)'));
897 o2
.value('wpa+ccmp', _('WPA Ent. (CCMP)'));
898 o2
.value('wpa+tkip', _('WPA Ent. (TKIP)'));
899 o2
.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)'));
900 o2
.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)'));
901 o2
.value('owe', _('WPA3 OWE (CCMP)'));
902 o2
.value('none', _('none'));
903 o2
.default = encryption
;
905 o2
= s2
.option(form
.Value
, 'key', _('Password'));
906 o2
.depends({ encryption
: 'sae', '!contains': true });
907 o2
.depends({ encryption
: 'psk', '!contains': true });
908 o2
.depends({ encryption
: 'wpa', '!contains': true });
909 o2
.datatype
= 'wpakey';
912 o2
= s2
.option(form
.ListValue
, 'eap_type', _('EAP-Method'));
913 o2
.depends({ encryption
: 'wpa', '!contains': true });
914 o2
.value('tls', _('TLS'));
915 o2
.value('ttls', _('TTLS'));
916 o2
.value('peap', _('PEAP'));
917 o2
.value('fast', _('FAST'));
920 o2
= s2
.option(form
.ListValue
, 'auth', _('Authentication'));
921 o2
.depends({ encryption
: 'wpa', '!contains': true });
922 o2
.value('PAP', _('PAP'));
923 o2
.value('CHAP', _('CHAP'));
924 o2
.value('MSCHAP', _('MSCHAP'));
925 o2
.value('MSCHAPV2', _('MSCHAPV2'));
926 o2
.value('EAP-GTC', _('EAP-GTC'));
927 o2
.value('EAP-MD5', _('EAP-MD5'));
928 o2
.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2'));
929 o2
.value('EAP-TLS', _('EAP-TLS'));
930 o2
.value('auth=PAP', _('auth=PAP'));
931 o2
.value('auth=MSCHAPV2', _('auth=MSCHAPV2'));
932 o2
.default = 'EAP-MSCHAPV2';
934 o2
= s2
.option(form
.Value
, 'identify', _('Identify'));
935 o2
.depends({ encryption
: 'wpa', '!contains': true });
937 o2
= s2
.option(form
.Value
, 'ca_cert', _('Path to CA-Certificate'));
938 o2
.depends({ eap_type
: 'tls' });
941 o2
= s2
.option(form
.Value
, 'client_cert', _('Path to Client-Certificate'));
942 o2
.depends({ eap_type
: 'tls' });
945 o2
= s2
.option(form
.Value
, 'priv_key', _('Path to Private Key'));
946 o2
.depends({ eap_type
: 'tls' });
949 o2
= s2
.option(form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
950 o2
.depends({ eap_type
: 'tls' });
951 o2
.datatype
= 'wpakey';
955 return m2
.render().then(L
.bind(function (elements
) {
956 ui
.showModal(_('Add Uplink %q').replace(/%q/, '"%h"'.format(ssid
)), [
958 E('div', { 'class': 'right' }, [
961 'click': ui
.hideModal
965 'class': 'cbi-button cbi-button-positive important',
966 'click': ui
.createHandlerFn(this, 'handleCommit', m2
)
976 s
.handleCommit = function (map
, ev
) {
977 var w_sections
= uci
.sections('wireless', 'wifi-iface'),
978 device
= L
.toArray(map
.lookupOption('device', '_add_trm'))[0].formvalue('_add_trm'),
979 network
= L
.toArray(map
.lookupOption('network', '_add_trm'))[0].formvalue('_add_trm'),
980 ssid
= L
.toArray(map
.lookupOption('ssid', '_add_trm'))[0].formvalue('_add_trm'),
981 ignore_bssid
= L
.toArray(map
.lookupOption('ignore_bssid', '_add_trm'))[0].formvalue('_add_trm'),
982 bssid
= L
.toArray(map
.lookupOption('bssid', '_add_trm'))[0].formvalue('_add_trm'),
983 encryption
= L
.toArray(map
.lookupOption('encryption', '_add_trm'))[0].formvalue('_add_trm'),
984 password
= L
.toArray(map
.lookupOption('key', '_add_trm'))[0].formvalue('_add_trm');
985 if (!ssid
|| ((encryption
.includes('psk') || encryption
.includes('wpa') || encryption
.includes('sae')) && !password
)) {
987 ui
.addNotification(null, E('p', 'Empty SSID, the uplink station could not be saved.'), 'error');
990 ui
.addNotification(null, E('p', 'Empty Password, the uplink station could not be saved.'), 'error');
992 return ui
.hideModal();
994 for (var i
= 0; i
< w_sections
.length
; i
++) {
995 if (w_sections
[i
].device
=== device
&& w_sections
[i
].ssid
=== ssid
) {
996 if (ignore_bssid
=== '1' || (ignore_bssid
=== '0' && w_sections
[i
].bssid
=== bssid
)) {
997 ui
.addNotification(null, E('p', 'Duplicate wireless entry, the uplink station could not be saved.'), 'error');
998 return ui
.hideModal();
1003 var offset
= w_sections
.length
,
1004 new_sid
= 'trm_uplink' + (++offset
);
1005 while (uci
.get('wireless', new_sid
)) {
1006 new_sid
= 'trm_uplink' + (++offset
);
1008 uci
.add('wireless', 'wifi-iface', new_sid
);
1009 uci
.set('wireless', new_sid
, 'device', device
);
1010 uci
.set('wireless', new_sid
, 'mode', 'sta');
1011 uci
.set('wireless', new_sid
, 'network', network
);
1012 uci
.set('wireless', new_sid
, 'ssid', ssid
);
1013 if (ignore_bssid
=== '0') {
1014 uci
.set('wireless', new_sid
, 'bssid', bssid
);
1016 uci
.set('wireless', new_sid
, 'encryption', encryption
);
1017 uci
.set('wireless', new_sid
, 'key', password
);
1018 uci
.set('wireless', new_sid
, 'disabled', '1');
1019 handleSectionsAdd(network
);
1021 .then(L
.bind(this.map
.load
, this.map
))
1022 .then(L
.bind(this.map
.reset
, this.map
))
1024 var row
= document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(new_sid
));
1025 row
.setAttribute('style', 'opacity: 0.5; color: #4a4 !important;');