From e3b357ac0eb2b3942fd3c948fce9cecbf0ed30cc Mon Sep 17 00:00:00 2001 From: Dirk Brenken Date: Sat, 19 Sep 2020 00:01:07 +0200 Subject: luci-app-travelmate: release 2.0 - sync with travelmate 2.0 - app migrated to client side JS Signed-off-by: Dirk Brenken --- .../resources/view/travelmate/logread.js | 42 + .../resources/view/travelmate/overview.js | 430 ++++++++++ .../resources/view/travelmate/stations.js | 879 +++++++++++++++++++++ 3 files changed, 1351 insertions(+) create mode 100644 applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logread.js create mode 100644 applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js create mode 100644 applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js (limited to 'applications/luci-app-travelmate/htdocs') diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logread.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logread.js new file mode 100644 index 000000000..d004ed4d2 --- /dev/null +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logread.js @@ -0,0 +1,42 @@ +'use strict'; +'require view'; +'require poll'; +'require fs'; + +return view.extend({ + load: function() { + return Promise.all([ + L.resolveDefault(fs.stat('/sbin/logread'), null), + L.resolveDefault(fs.stat('/usr/sbin/logread'), null) + ]); + }, + render: function(stat) { + var logger = stat[0] ? stat[0].path : stat[1] ? stat[1].path : null; + poll.add(function() { + return L.resolveDefault(fs.exec_direct(logger, ['-e', 'trm-'])).then(function(res) { + var log = document.getElementById("logfile"); + if (res) { + log.value = res.trim(); + } + else { + log.value = _('No travelmate related logs yet!'); + } + log.scrollTop = log.scrollHeight; + }); + }); + return E('div', { class: 'cbi-map' }, + E('div', { class: 'cbi-section' }, [ + E('div', { class: 'cbi-section-descr' }, _('The syslog output, pre-filtered for travelmate related messages only.')), + E('textarea', { + 'id': 'logfile', + 'style': 'width: 100% !important; padding: 5px; font-family: monospace', + 'readonly': 'readonly', + 'wrap': 'off', + 'rows': 25 + }) + ])); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js new file mode 100644 index 000000000..0ea48766e --- /dev/null +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js @@ -0,0 +1,430 @@ +'use strict'; +'require view'; +'require poll'; +'require fs'; +'require ui'; +'require uci'; +'require form'; +'require tools.widgets as widgets'; + +/* + button handling +*/ +async function handleAction(ev) { + if (ev === 'restart') { + fs.exec_direct('/etc/init.d/travelmate', [ev]) + } + if (ev === 'setup') { + var ifaceValue = String(uci.get('travelmate', 'global', 'trm_iface') || ''); + L.ui.showModal(_('Interface Wizard'), [ + E('p', _('To use Travelmate, you have to set up an uplink interface once. This wizard creates an IPv4- and an IPv6 alias network interface with all required network- and firewall settings.')), + E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ + E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ + E('input', { 'class': 'cbi-input-text', 'id': 'iface', 'placeholder': 'trm_wwan', 'value': ifaceValue, 'maxlength': '15', 'spellcheck': 'false' }, [ + ]), + '\xa0\xa0\xa0', + _('The uplink interface name') + ]), + E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ + E('input', { 'class': 'cbi-input-text', 'id': 'zone', 'placeholder': 'wan', 'maxlength': '15', 'spellcheck': 'false' }), + '\xa0\xa0\xa0', + _('The firewall zone name') + ]), + E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ + E('input', { 'class': 'cbi-input-text', 'id': 'metric', 'placeholder': '100', 'maxlength': '3', 'spellcheck': 'false' }), + '\xa0\xa0\xa0', + _('The interface metric') + ]) + ]), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn', + 'click': L.hideModal + }, _('Dismiss')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-positive important', + 'click': ui.createHandlerFn(this, function(ev) { + var iface = document.getElementById('iface').value || 'trm_wwan', + zone = document.getElementById('zone').value || 'wan', + metric = document.getElementById('metric').value || '100'; + L.resolveDefault(fs.exec_direct('/etc/init.d/travelmate', ['setup', iface, zone, metric])) + .then(function(res) { + if (res) { + ui.addNotification(null, E('p', res.trim() + '.'), 'error'); + } else { + ui.addNotification(null, E('p', _('The uplink interface has been updated.')), 'info'); + } + }); + L.hideModal(); + }) + }, _('Save')) + ]) + ]); + return document.getElementById('iface').focus(); + } + + if (ev === 'qrcode') { + return Promise.all([ + uci.load('wireless') + ]).then(function() { + var w_sid, w_device, w_ssid, w_enc, w_key, w_hidden, result, + w_sections = uci.sections('wireless', 'wifi-iface'), + optionsAP = [E('option', { value: '' }, [_('-- AP Selection --')])]; + for (var i = 0; i < w_sections.length; i++) { + if (w_sections[i].mode === 'ap' && w_sections[i].disabled !== '1') { + w_sid = i; + w_device = w_sections[i].device; + w_ssid = w_sections[i].ssid; + optionsAP.push(E('option', { value: w_sid }, w_device + ', ' + w_ssid)); + } + } + var selectAP = E('select', { + id: 'selectID', + class: 'cbi-input-select', + change: function(ev) { + result = document.getElementById('qrcode'); + if (document.getElementById("selectID").value) { + w_sid = document.getElementById("selectID").value; + w_ssid = w_sections[w_sid].ssid; + w_enc = w_sections[w_sid].encryption; + w_key = w_sections[w_sid].key; + w_hidden = (w_sections[w_sid].hidden == 1 ? 'true' : 'false'); + if (w_enc.startsWith('psk')) { + w_enc = 'WPA'; + } + else if (w_enc === 'none') { + w_enc = 'nopass'; + w_key = 'nokey'; + } + L.resolveDefault(fs.exec_direct('/usr/bin/qrencode', ['--inline', '--8bit', '--type=SVG', '--output=-', 'WIFI:S:' + w_ssid + ';T:' + w_enc + ';P:' + w_key + ';H:' + w_hidden + ';']), null).then(function(res) { + if (res) { + result.innerHTML = res.trim(); + } + else { + result.innerHTML = _('The QR-Code could not be generated!'); + } + }); + } + else { + result.innerHTML = ''; + } + } + }, optionsAP); + L.ui.showModal(_('QR-Code Overview'), [ + E('p', _('Render the QR-Code of the selected Access Point to comfortably transfer the WLAN credentials to your mobile devices.')), + E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ + E('label', { 'class': 'cbi-input-select', 'style': 'padding-top:.5em' }, [ + selectAP, + ]) + ]), + '\xa0', + E('div', { + 'id': 'qrcode' + }), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn', + 'click': L.hideModal + }, _('Dismiss')) + ]) + ]); + }); + return; + } +} + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('travelmate') + ]); + }, + + render: function(result) { + var m, s, o; + + m = new form.Map('travelmate', 'Travelmate', _('Configuration of the travelmate package to to enable travel router functionality. \ + For further information check the online documentation.
\ + Please note: On first start please call the \'Interface Wizard\' once, to make the necessary network- and firewall settings.')); + + /* + poll runtime information + */ + pollData: poll.add(function() { + return L.resolveDefault(fs.stat('/tmp/trm_runtime.json'), null).then(function(res) { + var status = document.getElementById('status'); + if (res) { + L.resolveDefault(fs.read_direct('/tmp/trm_runtime.json'), null).then(function(res) { + if (res) { + var info = JSON.parse(res); + if (status && info) { + status.textContent = (info.data.travelmate_status || '-') + ' / ' + (info.data.travelmate_version || '-'); + if (info.data.travelmate_status.startsWith('running')) { + if (!status.classList.contains("spinning")) { + status.classList.add("spinning"); + } + } else { + if (status.classList.contains("spinning")) { + status.classList.remove("spinning"); + } + } + } else if (status) { + status.textContent = '-'; + if (status.classList.contains("spinning")) { + status.classList.remove("spinning"); + } + } + var station_id = document.getElementById('station_id'); + if (station_id && info) { + station_id.textContent = info.data.station_id || '-'; + } + var station_mac = document.getElementById('station_mac'); + if (station_mac && info) { + station_mac.textContent = info.data.station_mac || '-'; + } + var station_interface = document.getElementById('station_interface'); + if (station_interface && info) { + station_interface.textContent = info.data.station_interface || '-'; + } + var wpa_flags = document.getElementById('wpa_flags'); + if (wpa_flags && info) { + wpa_flags.textContent = info.data.wpa_flags || '-'; + } + var run_flags = document.getElementById('run_flags'); + if (run_flags && info) { + run_flags.textContent = info.data.run_flags || '-'; + } + var ext_hooks = document.getElementById('ext_hooks'); + if (ext_hooks && info) { + ext_hooks.textContent = info.data.ext_hooks || '-'; + } + var run = document.getElementById('run'); + if (run && info) { + run.textContent = info.data.last_run || '-'; + } + } + }); + } + else { + if (status && status.classList.contains("spinning")) { + status.textContent = '-'; + status.classList.remove("spinning"); + } + } + }); + }, 1); + + /* + runtime information and buttons + */ + s = m.section(form.NamedSection, 'global'); + s.render = L.bind(function(view, section_id) { + return E('div', { 'class': 'cbi-section' }, [ + E('h3', _('Information')), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Status / Version')), + E('div', { 'class': 'cbi-value-field', 'id': 'status', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Station ID')), + E('div', { 'class': 'cbi-value-field', 'id': 'station_id', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Station MAC')), + E('div', { 'class': 'cbi-value-field', 'id': 'station_mac', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Station Interface')), + E('div', { 'class': 'cbi-value-field', 'id': 'station_interface', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('WPA Flags')), + E('div', { 'class': 'cbi-value-field', 'id': 'wpa_flags', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Run Flags')), + E('div', { 'class': 'cbi-value-field', 'id': 'run_flags', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Ext. Hooks')), + E('div', { 'class': 'cbi-value-field', 'id': 'ext_hooks', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { 'class': 'cbi-value', 'style': 'margin-bottom:5px' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Last Run')), + E('div', { 'class': 'cbi-value-field', 'id': 'run', 'style': 'font-weight: bold;margin-bottom:5px;color:#37c' },'-')]), + E('div', { class: 'right' }, [ + E('button', { + 'class': 'cbi-button cbi-button-apply', + 'id': 'btn_suspend', + 'click': ui.createHandlerFn(this, function() { + L.resolveDefault(fs.stat('/usr/bin/qrencode'), null).then(function(res) { + if (res) { + return handleAction('qrcode'); + } + return ui.addNotification(null, E('p', _('Please install the separate \'qrencode\' package.')), 'info'); + }) + }) + }, [ _('AP QR-Codes...') ]), + '\xa0', + E('button', { + 'class': 'cbi-button cbi-button-reset', + 'click': ui.createHandlerFn(this, function() { + return handleAction('setup'); + }) + }, [ _('Interface Wizard...') ]) + ]) + ]); + }, o, this); + this.pollData; + + /* + tabbed config section + */ + s = m.section(form.NamedSection, 'global', 'travelmate', _('Settings')); + s.addremove = false; + s.tab('general', _('General Settings')); + s.tab('additional', _('Additional Settings')); + s.tab('adv_vpn', _('VPN Settings'), _('Please note: VPN connections require the separate setup of the Wireguard or OpenVPN package.

 

')); + s.tab('adv_email', _('E-Mail Settings'), _('Please note: E-Mail notifications require the separate setup of the mstmp package.

 

')); + + /* + general settings tab + */ + o = s.taboption('general', form.Flag, 'trm_enabled', _('Enabled'), _('Enable the travelmate service.')); + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'trm_debug', _('Verbose Debug Logging'), _('Enable verbose debug logging in case of any processing errors.')); + o.rmempty = false; + + o = s.taboption('general', form.Value, 'trm_radio', _('Radio Selection'), _('Restrict travelmate to a single radio or change the overall scanning order (e.g. \'radio1 radio0\').')); + o.placeholder = 'radio0'; + o.rmempty = true; + + o = s.taboption('general', form.Flag, 'trm_captive', _('Captive Portal Detection'), _('Check the internet availability, handle captive portal redirections and keep the uplink connection \'alive\'.')); + o.default = 1; + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'trm_netcheck', _('Net Error Check'), _('Treat missing internet availability as an error.')); + o.depends('trm_captive', '1'); + o.default = 0; + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'trm_proactive', _('ProActive Uplink Switch'), _('Proactively scan and switch to a higher prioritized uplink, despite of an already existing connection.')); + o.default = 1; + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'trm_autoadd', _('AutoAdd Open Uplinks'), _('Automatically add open uplinks like hotel captive portals to your wireless config.')); + o.default = 0; + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'trm_randomize', _('Randomize MAC Addresses'), _('Generate a random unicast MAC address for each uplink connection.')); + o.default = 0; + o.rmempty = false; + + /* + additional settings tab + */ + o = s.taboption('additional', form.Value, 'trm_triggerdelay', _('Trigger Delay'), _('Additional trigger delay in seconds before travelmate processing begins.')); + o.placeholder = '2'; + o.datatype = 'range(1,60)'; + o.rmempty = true; + + o = s.taboption('additional', form.Value, 'trm_maxretry', _('Connection Limit'), _('Retry limit to connect to an uplink.')); + o.placeholder = '3'; + o.datatype = 'range(1,10)'; + o.rmempty = true; + + o = s.taboption('additional', form.Value, 'trm_minquality', _('Signal Quality Threshold'), _('Minimum signal quality threshold as percent for conditional uplink (dis-) connections.')); + o.placeholder = '35'; + o.datatype = 'range(20,80)'; + o.rmempty = true; + + o = s.taboption('additional', form.Value, 'trm_maxwait', _('Interface Timeout'), _('How long should travelmate wait for a successful wlan uplink connection.')); + o.placeholder = '30'; + o.datatype = 'range(20,40)'; + o.rmempty = true; + + o = s.taboption('additional', form.Value, 'trm_timeout', _('Overall Timeout'), _('Overall retry timeout in seconds.')); + o.placeholder = '60'; + o.datatype = 'range(30,300)'; + o.rmempty = true; + + o = s.taboption('additional', form.Value, 'trm_scanbuffer', _('Scan Buffer Size'), _('Buffer size in bytes to prepare nearby scan results.')); + o.placeholder = '1024'; + o.datatype = 'range(256,4096)'; + o.rmempty = true; + + o = s.taboption('additional', form.ListValue, 'trm_captiveurl', _('Captive Portal URL'), _('The selected URL will be used for connectivity- and captive portal checks.')); + o.value('http://captive.apple.com', 'Apple (default)'); + o.value('http://connectivity-check.ubuntu.com', 'Ubuntu'); + o.value('http://connectivitycheck.android.com/generate_204', 'Google'); + o.value('http://www.msftncsi.com/ncsi.txt', 'Microsoft'); + o.optional = true; + o.rmempty = true; + + o = s.taboption('additional', form.ListValue, 'trm_useragent', _('User Agent'), _('The selected user agent will be used for connectivity- and captive portal checks.')); + o.value('Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0', 'Firefox (default)'); + o.value('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36', 'Chromium'); + o.value('Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/85.0.4183.92 Mobile/15E148 Safari/604.1', 'Safari'); + o.value('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44', 'Edge'); + o.value('Mozilla/5.0 (Linux; Android 10; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.81 Mobile Safari/537.36 OPR/59.1.2926.54067', 'Opera'); + o.optional = true; + o.rmempty = true; + + o = s.taboption('additional', form.ListValue, 'trm_nice', _('Service Priority'), _('The selected priority will be used for travelmate processes.')); + o.value('-20', 'Highest Priority'); + o.value('-10', 'High Priority'); + o.value('0', 'Normal Priority (default)'); + o.value('10', 'Less Priority'); + o.value('19', 'Least Priority'); + o.optional = true; + o.rmempty = true; + + /* + advanced vpn settings tab + */ + o = s.taboption('adv_vpn', form.Flag, 'trm_vpn', _('VPN Hook'), _('Automatically handle VPN (re-) connections.')); + o.rmempty = false; + + o = s.taboption('adv_vpn', form.ListValue, 'trm_vpnservice', _('VPN Service')); + o.depends('trm_vpn', '1'); + o.value('wireguard'); + o.value('openvpn'); + o.rmempty = true; + + o = s.taboption('adv_vpn', widgets.NetworkSelect, 'trm_vpniface', _('VPN Interface'), _('The logical vpn network interface, e.g. \'wg0\' or \'tun0\'.')); + o.depends('trm_vpn', '1'); + o.unspecified = false; + o.nocreate = true; + o.rmempty = true; + + o = s.taboption('adv_vpn', widgets.DeviceSelect, 'trm_landevice', _('LAN Device'), _('The lan network device, e.g. \'br-lan\'.')); + o.depends('trm_vpn', '1'); + o.unspecified = false; + o.nocreate = true; + o.rmempty = true; + + /* + advanced email settings tab + */ + o = s.taboption('adv_email', form.Flag, 'trm_mail', _('E-Mail Hook'), _('Sends notification E-Mails after every succesful uplink connect.')); + o.rmempty = false; + + o = s.taboption('adv_email', form.Value, 'trm_mailreceiver', _('E-Mail Receiver Address'), _('Receiver address for travelmate notification E-Mails.')); + o.depends('trm_mail', '1'); + o.placeholder = 'name@example.com'; + o.rmempty = true; + + o = s.taboption('adv_email', form.Value, 'trm_mailsender', _('E-Mail Sender Address'), _('Sender address for travelmate notification E-Mails.')); + o.depends({ 'trm_mailreceiver': '@', '!contains': true }); + o.placeholder = 'no-reply@travelmate'; + o.rmempty = true; + + o = s.taboption('adv_email', form.Value, 'trm_mailtopic', _('E-Mail Topic'), _('Topic for travelmate notification E-Mails.')); + o.depends({ 'trm_mailreceiver': '@', '!contains': true }); + o.placeholder = 'travelmate connection to \'\''; + o.rmempty = true; + + o = s.taboption('adv_email', form.Value, 'trm_mailprofile', _('E-Mail Profile'), _('Profile used by \'msmtp\' for travelmate notification E-Mails.')); + o.depends({ 'trm_mailreceiver': '@', '!contains': true }); + o.placeholder = 'trm_notify'; + o.rmempty = true; + + return m.render(); + }, + handleReset: null +}); diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js new file mode 100644 index 000000000..d99763630 --- /dev/null +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js @@ -0,0 +1,879 @@ +'use strict'; +'require view'; +'require poll'; +'require fs'; +'require ui'; +'require uci'; +'require form'; +'require network'; +'require tools.widgets as widgets'; + +/* + remove wireless and stale travelmate sections +*/ +function handleRemove(sid) { + var w_sections, t_sections, match, changes; + + uci.remove('wireless', sid); + w_sections = uci.sections('wireless', 'wifi-iface'); + t_sections = uci.sections('travelmate', 'uplink'); + for (var i = 0; i < t_sections.length; i++) { + match = false; + for (var j = 0; j < w_sections.length; j++) { + 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) { + match = true; + break; + } + } + if (match === false) { + uci.remove('travelmate', t_sections[i]['.name']); + } + } + uci.save(); +} + +/* + add missing travelmate sections +*/ +function handleSectionsAdd(iface) { + var w_sections, t_sections, match, changes; + + w_sections = uci.sections('wireless', 'wifi-iface'); + t_sections = uci.sections('travelmate', 'uplink'); + for (var i = 0; i < w_sections.length; i++) { + if (w_sections[i].mode !== 'sta' || w_sections[i].network !== iface) { + continue; + } + match = false; + for (var j = 0; j < t_sections.length; j++) { + 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) { + match = true; + break; + } + } + if (match === false) { + var sid = uci.add('travelmate', 'uplink'); + uci.set('travelmate', sid, 'enabled', '1'); + uci.set('travelmate', sid, 'device', w_sections[i].device); + uci.set('travelmate', sid, 'ssid', w_sections[i].ssid); + uci.set('travelmate', sid, 'bssid', w_sections[i].bssid); + uci.set('travelmate', sid, 'con_start_expiry', '0'); + uci.set('travelmate', sid, 'con_end_expiry', '0'); + } + } +} + +/* + update travelmate sections +*/ +function handleSectionsVal(action, section_id, option, value) { + var date, oldValue, w_device, w_ssid, w_bssid, t_sections; + + w_device = uci.get('wireless', section_id, 'device'); + w_ssid = uci.get('wireless', section_id, 'ssid'); + w_bssid = uci.get('wireless', section_id, 'bssid'); + t_sections = uci.sections('travelmate', 'uplink'); + + for (var i = 0; i < t_sections.length; i++) { + if (t_sections[i].device === w_device && t_sections[i].ssid === w_ssid && t_sections[i].bssid === w_bssid) { + if (action === 'get') { + return t_sections[i][option]; + } + else if (action === 'set') { + if (option === 'enabled') { + oldValue = t_sections[i][option]; + if (oldValue !== value && value === '0') { + date = new Date(new Date().getTime() - new Date().getTimezoneOffset()*60*1000).toISOString().substr(0,19).replace(/-/g, '.').replace('T', '-'); + uci.set('travelmate', t_sections[i]['.name'], 'con_end', date); + } + else if (oldValue !== value && value === '1') { + uci.unset('travelmate', t_sections[i]['.name'], 'con_end'); + } + } + return uci.set('travelmate', t_sections[i]['.name'], option, value); + } + else if (action === 'del') { + return uci.unset('travelmate', t_sections[i]['.name'], option); + } + } + } +} + +/* + update travelmate status +*/ +function handleStatus() { + poll.add(function() { + L.resolveDefault(fs.stat('/var/run/travelmate.refresh'), null).then(function(res) { + if (res) { + L.resolveDefault(fs.read_direct('/var/run/travelmate.refresh'), null).then(function(res) { + fs.remove('/var/run/travelmate.refresh'); + if (res && res === 'ui_reload') { + location.reload(); + } + else if (res && res === 'cfg_reload') { + if (document.readyState === 'complete') { + uci.unload('wireless'); + uci.unload('travelmate'); + } + return Promise.all([ + uci.load('wireless'), + uci.load('travelmate') + ]).then(function() { + var item, value, + container = document.querySelectorAll('.cbi-section-table-row[data-sid]'); + for (var i = 0; i < container.length; i++) { + item = container[i].querySelector('.cbi-value-field[data-title="Enabled"]'); + value = handleSectionsVal('get', container[i].getAttribute('data-sid'), 'enabled'); + item.textContent = (value == 0 ? 'No' : 'Yes'); + } + }); + } + }); + } + }); + return L.resolveDefault(fs.stat('/tmp/trm_runtime.json'), null).then(function(res) { + if (res) { + L.resolveDefault(fs.read_direct('/tmp/trm_runtime.json'), null).then(function(res) { + if (res) { + var info = JSON.parse(res); + if (info) { + var t_device, t_ssid, t_bssid, oldUplinkView, newUplinkView, + uplinkId = info.data.station_id.trim().split('/'), + oldUplinkView = document.getElementsByName('uplinkStation'), + w_sections = uci.sections('wireless', 'wifi-iface'); + + t_device = uplinkId[0]; + t_bssid = uplinkId[uplinkId.length-1]; + for (var i = 1; i < uplinkId.length-1; i++) { + if (!t_ssid) { + t_ssid = uplinkId[i]; + } + else { + t_ssid = t_ssid + '/' + uplinkId[i]; + } + } + if (t_ssid === '-') { + if (oldUplinkView.length > 0) { + oldUplinkView[0].removeAttribute('style'); + oldUplinkView[0].removeAttribute('name', 'uplinkStation'); + } + } + else { + for (var i = 0; i < w_sections.length; i++) { + newUplinkView = document.getElementById('cbi-wireless-' + w_sections[i]['.name']); + if (t_device === w_sections[i].device && t_ssid === w_sections[i].ssid && t_bssid === (w_sections[i].bssid || '-')) { + if (oldUplinkView.length === 0 && newUplinkView) { + newUplinkView.setAttribute('name', 'uplinkStation'); + newUplinkView.setAttribute('style', 'text-align: left !important; color: #37c !important;font-weight: bold !important;'); + } + else if (oldUplinkView.length > 0 && newUplinkView && oldUplinkView[0].getAttribute('id') !== newUplinkView.getAttribute('id')) { + oldUplinkView[0].removeAttribute('style'); + oldUplinkView[0].removeAttribute('name', 'uplinkStation'); + newUplinkView.setAttribute('name', 'uplinkStation'); + newUplinkView.setAttribute('style', 'text-align: left !important; color: #37c !important;font-weight: bold !important;'); + } + } + } + } + } + } + }); + } + }); + }, 1); +} + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('wireless'), + uci.load('travelmate') + ]); + }, + + render: function() { + var m, s, o, + iface = uci.get('travelmate', 'global', 'trm_iface') || 'trm_wwan'; + + m = new form.Map('wireless'); + m.chain('travelmate'); + s = m.section(form.GridSection, 'wifi-iface', null, _('Overview of all configured uplinks for travelmate.
\ + You can edit, remove or prioritize existing uplinks by drag \& drop and scan for new ones. The currently used uplink is emphasized in blue.')); + s.anonymous = true; + s.sortable = true; + s.filter = function(section_id) { + return (uci.get('wireless', section_id, 'network') == iface && uci.get('wireless', section_id, 'mode') == 'sta'); + }; + s.tab('wireless', _('Wireless Settings')); + s.tab('travelmate', _('Travelmate Settings')); + s.renderRowActions = function(section_id) { + var btns; + btns = [ + E('button', { + 'class': 'btn cbi-button drag-handle center', + 'title': _('Drag to reorder'), + 'style': 'cursor:move', + 'disabled': this.map.readonly || null + }, '☰'), + E('button', { + 'class': 'cbi-button cbi-button-action important', + 'title': _('Edit this network'), + 'click': ui.createHandlerFn(this, 'renderMoreOptionsModal', section_id) + }, _('Edit')), + E('button', { + 'class': 'cbi-button cbi-button-negative remove', + 'title': _('Delete this network'), + 'click': ui.createHandlerFn(this, handleRemove, section_id) + }, _('Del')) + ]; + return E('div', { 'class': 'td middle cbi-section-actions' }, E('div', btns)); + }; + + o = s.taboption('travelmate', form.Flag, '_enabled', _('Enabled')); + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'enabled'; + o.rmempty = false; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'enabled'); + } + o.write = function(section_id, value) { + return handleSectionsVal('set', section_id, 'enabled', value); + } + + o = s.taboption('wireless', form.Value, 'device', _('Device')); + o.readonly = true; + + o = s.taboption('wireless', form.Value, 'ssid', _('SSID')); + o.datatype = 'maxlength(32)'; + o.readonly = true; + + o = s.taboption('wireless', form.Value, 'bssid', _('BSSID')); + o.datatype = 'macaddr'; + o.readonly = true; + + o = s.taboption('wireless', form.ListValue, 'encryption', _('Encryption')); + o.value('sae', _('WPA3 Pers.')); + o.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)')); + o.value('psk2', _('WPA2 Pers.')); + o.value('psk2+ccmp', _('WPA2 Pers. (CCMP)')); + o.value('psk2+tkip', _('WPA2 Pers. (TKIP)')); + o.value('psk', _('WPA Pers.')); + o.value('psk+ccmp', _('WPA Pers. (CCMP)')); + o.value('psk+tkip', _('WPA Pers. (TKIP)')); + o.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)')); + o.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)')); + o.value('wpa3', _('WPA3 Ent.')); + o.value('wpa3-mixed', _('WPA3/WPA2 Ent.')); + o.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)')); + o.value('wpa2+tkip', _('WPA2 Ent. (TKIP)')); + o.value('wpa+ccmp', _('WPA Ent. (CCMP)')); + o.value('wpa+tkip', _('WPA Ent. (TKIP)')); + o.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)')); + o.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)')); + o.value('owe', _('OWE')); + o.value('none', _('none')); + o.default = 'none'; + o.textvalue = function(section_id) { + var cfgvalue = this.map.data.get('wireless', section_id, 'encryption'); + switch (cfgvalue) { + case 'sae': + cfgvalue = 'WPA3 Pers. (SAE)'; + break; + case 'sae-mixed': + cfgvalue = 'WPA2/WPA3 Pers. (CCMP)'; + break; + case 'psk2': + cfgvalue = 'WPA2 Pers.'; + break; + case 'psk2+ccmp': + cfgvalue = 'WPA2 Pers. (CCMP)'; + break; + case 'psk2+tkip': + cfgvalue = 'WPA2 Ent. (TKIP)'; + break; + case 'psk': + cfgvalue = 'WPA Pers.'; + break; + case 'psk-mixed+ccmp': + cfgvalue = 'WPA/WPA2 Pers. (CCMP)'; + break; + case 'psk-mixed+tkip': + cfgvalue = 'WPA/WPA2 Pers. (TKIP)'; + break; + case 'wpa3': + cfgvalue = 'WPA3 Ent.'; + break; + case 'wpa3-mixed': + cfgvalue = 'WPA3/WPA2 Ent.'; + break; + case 'wpa2+ccmp': + cfgvalue = 'WPA2 Ent. (CCMP)'; + break; + case 'wpa2+tkip': + cfgvalue = 'WPA2 Ent. (TKIP)'; + break; + case 'wpa+ccmp': + cfgvalue = 'WPA Ent. (CCMP)'; + break; + case 'wpa+tkip': + cfgvalue = 'WPA Ent. (TKIP)'; + break; + case 'wpa-mixed+ccmp': + cfgvalue = 'WPA/WPA2 Ent. (CCMP)'; + break; + case 'wpa-mixed+tkip': + cfgvalue = 'WPA/WPA2 Ent. (TKIP)'; + break; + case 'owe': + cfgvalue = 'WPA3 OWE (CCMP)'; + break; + case 'none': + cfgvalue = 'none'; + break; + } + return cfgvalue; + }; + handleStatus(); + + /* + modal wireless tab + */ + o = s.taboption('wireless', form.Value, 'key', _('Password')); + o.datatype = 'wpakey'; + o.depends({ encryption: 'sae', '!contains': true }); + o.depends({ encryption: 'psk', '!contains': true }); + o.depends({ encryption: 'wpa', '!contains': true }); + o.modalonly = true; + o.password = true; + + o = s.taboption('wireless', form.ListValue, 'eap_type', _('EAP-Method')); + o.value('tls', _('TLS')); + o.value('ttls', _('TTLS')); + o.value('peap', _('PEAP')); + o.value('fast', _('FAST')); + o.default = 'peap'; + o.depends({ encryption: 'wpa', '!contains': true }); + o.modalonly = true; + + o = s.taboption('wireless', form.ListValue, 'auth', _('Authentication')); + o.value('PAP', _('PAP')); + o.value('CHAP', _('CHAP')); + o.value('MSCHAP', _('MSCHAP')); + o.value('MSCHAPV2', _('MSCHAPV2')); + o.value('EAP-GTC', _('EAP-GTC')); + o.value('EAP-MD5', _('EAP-MD5')); + o.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2')); + o.value('EAP-TLS', _('EAP-TLS')); + o.value('auth=PAP', _('auth=PAP')); + o.value('auth=MSCHAPV2', _('auth=MSCHAPV2')); + o.default = 'EAP-MSCHAPV2'; + o.depends({ encryption: 'wpa', '!contains': true }); + o.modalonly = true; + + o = s.taboption('wireless', form.Value, 'identify', _('Identify')); + o.depends({ encryption: 'wpa', '!contains': true }); + o.modalonly = true; + + o = s.taboption('wireless', form.Value, 'ca_cert', _('Path to CA-Certificate')); + o.depends({ eap_type: 'tls' }); + o.modalonly = true; + o.rmempty = true; + + o = s.taboption('wireless', form.Value, 'client_cert', _('Path to Client-Certificate')); + o.depends({ eap_type: 'tls' }); + o.modalonly = true; + o.rmempty = true; + + o = s.taboption('wireless', form.Value, 'priv_key', _('Path to Private Key')); + o.depends({ eap_type: 'tls' }); + o.modalonly = true; + o.rmempty = true; + + o = s.taboption('wireless', form.Value, 'priv_key_pwd', _('Password of Private Key')); + o.datatype = 'wpakey'; + o.depends({ eap_type: 'tls' }); + o.modalonly = true; + o.password = true; + o.rmempty = true; + + /* + modal travelmate tab + */ + o = s.taboption('travelmate', form.Value, '_ssid', _('SSID')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'ssid'; + o.rmempty = false; + o.readonly = true; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'ssid'); + } + + o = s.taboption('travelmate', form.Value, '_bssid', _('BSSID')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'bssid'; + o.rmempty = true; + o.readonly = true; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'bssid'); + } + + o = s.taboption('travelmate', form.Value, '_con_start', _('Connection Start')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'con_start'; + o.rmempty = true; + o.readonly = true; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'con_start'); + } + + o = s.taboption('travelmate', form.Value, '_con_end', _('Connection End')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'con_end'; + o.rmempty = true; + o.readonly = true; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'con_end'); + } + + o = s.taboption('travelmate', form.Value, '_con_start_expiry', _('Connection Start Expiry'), + _('Automatically disable the uplink after n minutes, e.g. for timed connections.
\ + The default of \'0\' disables this feature.')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'con_start_expiry'; + o.rmempty = false; + o.placeholder = '0'; + o.default = '0'; + o.datatype = 'range(0,720)'; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'con_start_expiry'); + } + o.write = function(section_id, value) { + return handleSectionsVal('set', section_id, 'con_start_expiry', value); + } + + o = s.taboption('travelmate', form.Value, '_con_end_expiry', _('Connection End Expiry'), + _('Automatically (re-)enable the uplink after n minutes, e.g. after failed login attempts.
\ + The default of \'0\' disables this feature.')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'con_end_expiry'; + o.rmempty = false; + o.placeholder = '0'; + o.default = '0'; + o.datatype = 'range(0,720)'; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'con_end_expiry'); + } + o.write = function(section_id, value) { + return handleSectionsVal('set', section_id, 'con_end_expiry', value); + } + + o = s.taboption('travelmate', form.FileUpload, '_script', _('Auto Login Script'), + _('External script reference which will be called for automated captive portal logins.')); + o.root_directory = '/etc/travelmate'; + o.enable_remove = false; + o.enable_upload = false; + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'script'; + o.renderWidget = function(section_id, option_index, cfgvalue) { + var browserEl = new ui.FileUpload((cfgvalue != null) ? cfgvalue : this.default, { + id: this.cbid(section_id), + name: this.cbid(section_id), + show_hidden: this.show_hidden, + enable_upload: this.enable_upload, + enable_remove: this.enable_remove, + root_directory: this.root_directory, + disabled: (this.readonly != null) ? this.readonly : this.map.readonly + }); + browserEl.renderListing = function(container, path, list) { + return ui.FileUpload.prototype.renderListing.apply(this, [ + container, path, + list.filter(function(entry) { + return ((entry.type == 'directory') || (entry.type == 'file' && entry.name.match(/\.login$/))); + }) + ]); + }; + return browserEl.render(); + }; + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'script'); + } + o.write = function(section_id, value) { + return handleSectionsVal('set', section_id, 'script', value); + } + o.remove = function(section_id) { + return handleSectionsVal('del', section_id, 'script'); + } + + o = s.taboption('travelmate', form.Value, '_args', _('Script Arguments'), + _('Space separated list of additional arguments passed to the Auto Login Script, i.e. username and password')); + o.modalonly = true; + o.uciconfig = 'travelmate'; + o.ucisection = 'uplink'; + o.ucioption = 'script_args'; + o.rmempty = true; + o.depends({ _script: '/etc/travelmate', '!contains': true }); + o.cfgvalue = function(section_id) { + return handleSectionsVal('get', section_id, 'script_args'); + } + o.write = function(section_id, value) { + return handleSectionsVal('set', section_id, 'script_args', value); + } + o.remove = function(section_id) { + return handleSectionsVal('del', section_id, 'script_args'); + } + + /* + scan buttons + */ + s = m.section(form.GridSection, 'wifi-device'); + s.anonymous = true; + s.addremove = false; + s.render = function() { + return network.getWifiDevices().then(L.bind(function(radios) { + var radio, ifname, btns = []; + for (var i = 0; i < radios.length; i++) { + radio = radios[i].sid; + if (radio) { + btns.push(E('button', { + 'class': 'cbi-button cbi-button-apply', + 'id': radio, + 'click': ui.createHandlerFn(this, 'handleScan', radio) + }, [ _('Scan on ' + radio + '...') ]), + '\xa0') + } + } + return E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, E('div', { 'class': 'left', 'style': 'padding-top:5px; padding-bottom:5px' }, btns)); + }, this)) + }; + + /* + modal 'scan' dialog + */ + s.handleScan = function(radio) { + var table = E('div', { 'class': 'table' }, [ + E('div', { 'class': 'tr table-titles' }, [ + E('div', { 'class': 'th col-1 middle left' }, _('Strength')), + E('div', { 'class': 'th col-1 middle left hide-xs' }, _('Channel')), + E('div', { 'class': 'th col-2 middle left' }, _('SSID')), + E('div', { 'class': 'th col-2 middle left' }, _('BSSID')), + E('div', { 'class': 'th col-3 middle left' }, _('Encryption')), + E('div', { 'class': 'th cbi-section-actions right' }, '\xa0') + ]) + ]); + cbi_update_table(table, [], E('em', { class: 'spinning' }, _('Starting wireless scan on \'' + radio + '\'...'))); + + var md = ui.showModal(_('Wireless Scan'), [ + table, + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Dismiss')), + '\xa0', + E('button', { + 'class': 'cbi-button cbi-button-positive important', + 'click': L.bind(this.handleScan, this, radio) + }, _('Repeat Scan')) + ]) + ]); + + md.style.maxWidth = '90%'; + md.style.maxHeight = 'none'; + + return L.resolveDefault(fs.exec_direct('/etc/init.d/travelmate', [ 'scan', radio ]), null) + .then(L.bind(function(res) { + if (res) { + var lines, strength, channel, encryption, tbl_encryption, bssid, ssid, tbl_ssid, rows = []; + lines = res.trim().split('\n'); + for (var i = 0; i < lines.length; i++) { + if (lines[i].match(/^\s+[0-9]/)) { + encryption = lines[i].slice(80).trim(); + if (!encryption.includes('WEP')) { + strength = lines[i].slice(4,7).trim(); + channel = lines[i].slice(15,18).trim(); + bssid = lines[i].slice(60,77).trim(); + ssid = lines[i].slice(25,59).trim(); + if (ssid.startsWith('"')) { + ssid = ssid.slice(1, ssid.length-1); + tbl_ssid = ssid; + } + else { + ssid = "hidden"; + tbl_ssid = "hidden"; + } + switch (encryption) { + case 'WPA3 PSK (SAE)': + encryption = 'sae'; + tbl_encryption = 'WPA3 Pers. (SAE)'; + break; + case 'mixed WPA2/WPA3 PSK/SAE (CCMP)': + encryption = 'sae-mixed'; + tbl_encryption = 'WPA2/WPA3 Pers. (CCMP)'; + break; + case 'WPA2 PSK (CCMP)': + encryption = 'psk2+ccmp'; + tbl_encryption = 'WPA2 Pers. (CCMP)'; + break; + case 'WPA2 PSK (TKIP)': + encryption = 'psk2+tkip'; + tbl_encryption = 'WPA2 Pers. (TKIP)'; + break; + case 'mixed WPA/WPA2 PSK (TKIP, CCMP)': + encryption = 'psk-mixed+ccmp'; + tbl_encryption = 'WPA/WPA2 Pers. (CCMP)'; + break; + case 'WPA PSK (CCMP)': + encryption = 'psk2+ccmp'; + tbl_encryption = 'WPA Pers. (CCMP)'; + break; + case 'WPA PSK (TKIP)': + encryption = 'psk2+tkip'; + tbl_encryption = 'WPA Pers. (TKIP)'; + break; + case 'WPA2 802.1X (CCMP)': + encryption = 'wpa2+ccmp'; + tbl_encryption = 'WPA2 Ent. (CCMP)'; + break; + case 'WPA3 OWE (CCMP)': + encryption = 'owe'; + tbl_encryption = 'WPA3 OWE (CCMP)'; + break; + case 'none': + encryption = 'none'; + tbl_encryption = 'none'; + break; + } + rows.push([ + strength, + channel, + tbl_ssid, + bssid, + tbl_encryption, + E('div', { 'class': 'right' }, E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(this, 'handleAdd', radio, iface, ssid, bssid, encryption) + }, _('Add Uplink...'))) + ]); + } + } + else if (lines[i] === '::: No scan results') { + rows.push([ + 'No scan results' + ]); + } + } + } + else { + rows.push([ + 'No scan results' + ]); + } + cbi_update_table(table, rows); + }, this)); + }; + + /* + modal 'add' dialog + */ + s.handleAdd = function(radio, iface, ssid, bssid, encryption, ev) { + ui.hideModal; + var m2, s2, o2; + + m2 = new form.Map('wireless'), + s2 = m2.section(form.NamedSection, '_add_trm'); + + s2.render = function() { + return Promise.all([ + {}, + this.renderUCISection('_add_trm') + ]).then(this.renderContents.bind(this)); + }; + + o2 = s2.option(form.Value, 'device', _('Device Name')); + o2.default = radio; + o2.readonly = true; + + o2 = s2.option(form.Value, 'network', _('Interface Name')); + o2.default = iface; + o2.readonly = true; + + if (ssid === "hidden") { + o2 = s2.option(form.Value, 'ssid', _('SSID (hidden)')); + o2.placeholder = 'hidden SSID'; + } + else { + o2 = s2.option(form.Value, 'ssid', _('SSID')); + o2.default = ssid; + } + o2.datatype = 'maxlength(32)'; + o2.rmempty = false; + + o2 = s2.option(form.Flag, 'ignore_bssid', _('Ignore BSSID')); + if (ssid === "hidden") { + o2.default = '0'; + } + else { + o2.default = '1'; + } + + o2 = s2.option(form.Value, 'bssid', _('BSSID')); + o2.depends({ ignore_bssid: '0' }); + o2.datatype = 'macaddr'; + o2.rmempty = true; + o2.default = bssid; + + o2 = s2.option(form.ListValue, 'encryption', _('Encryption')); + o2.value('sae', _('WPA3 Pers. (SAE)')); + o2.value('sae-mixed', _('WPA2/WPA3 Pers. (CCMP)')); + o2.value('psk2', _('WPA2 Pers.')); + o2.value('psk2+ccmp', _('WPA2 Pers. (CCMP)')); + o2.value('psk2+tkip', _('WPA2 Pers. (TKIP)')); + o2.value('psk', _('WPA Pers.')); + o2.value('psk+ccmp', _('WPA Pers. (CCMP)')); + o2.value('psk+tkip', _('WPA Pers. (TKIP)')); + o2.value('psk-mixed+ccmp', _('WPA/WPA2 Pers. (CCMP)')); + o2.value('psk-mixed+tkip', _('WPA/WPA2 Pers. (TKIP)')); + o2.value('wpa3', _('WPA3 Ent.')); + o2.value('wpa3-mixed', _('WPA2/WPA3 Ent.')); + o2.value('wpa2+ccmp', _('WPA2 Ent. (CCMP)')); + o2.value('wpa2+tkip', _('WPA2 Ent. (TKIP)')); + o2.value('wpa+ccmp', _('WPA Ent. (CCMP)')); + o2.value('wpa+tkip', _('WPA Ent. (TKIP)')); + o2.value('wpa-mixed+ccmp', _('WPA/WPA2 Ent. (CCMP)')); + o2.value('wpa-mixed+tkip', _('WPA/WPA2 Ent. (TKIP)')); + o2.value('owe', _('WPA3 OWE (CCMP)')); + o2.value('none', _('none')); + o2.default = encryption; + + o2 = s2.option(form.Value, 'key', _('Password')); + o2.depends({ encryption: 'sae', '!contains': true }); + o2.depends({ encryption: 'psk', '!contains': true }); + o2.depends({ encryption: 'wpa', '!contains': true }); + o2.datatype = 'wpakey'; + o2.password = true; + + o2 = s2.option(form.ListValue, 'eap_type', _('EAP-Method')); + o2.depends({ encryption: 'wpa', '!contains': true }); + o2.value('tls', _('TLS')); + o2.value('ttls', _('TTLS')); + o2.value('peap', _('PEAP')); + o2.value('fast', _('FAST')); + o2.default = 'peap'; + + o2 = s2.option(form.ListValue, 'auth', _('Authentication')); + o2.depends({ encryption: 'wpa', '!contains': true }); + o2.value('PAP', _('PAP')); + o2.value('CHAP', _('CHAP')); + o2.value('MSCHAP', _('MSCHAP')); + o2.value('MSCHAPV2', _('MSCHAPV2')); + o2.value('EAP-GTC', _('EAP-GTC')); + o2.value('EAP-MD5', _('EAP-MD5')); + o2.value('EAP-MSCHAPV2', _('EAP-MSCHAPV2')); + o2.value('EAP-TLS', _('EAP-TLS')); + o2.value('auth=PAP', _('auth=PAP')); + o2.value('auth=MSCHAPV2', _('auth=MSCHAPV2')); + o2.default = 'EAP-MSCHAPV2'; + + o2 = s2.option(form.Value, 'identify', _('Identify')); + o2.depends({ encryption: 'wpa', '!contains': true }); + + o2 = s2.option(form.Value, 'ca_cert', _('Path to CA-Certificate')); + o2.depends({ eap_type: 'tls' }); + o2.rmempty = true; + + o2 = s2.option(form.Value, 'client_cert', _('Path to Client-Certificate')); + o2.depends({ eap_type: 'tls' }); + o2.rmempty = true; + + o2 = s2.option(form.Value, 'priv_key', _('Path to Private Key')); + o2.depends({ eap_type: 'tls' }); + o2.rmempty = true; + + o2 = s2.option(form.Value, 'priv_key_pwd', _('Password of Private Key')); + o2.depends({ eap_type: 'tls' }); + o2.datatype = 'wpakey'; + o2.password = true; + o2.rmempty = true; + + return m2.render().then(L.bind(function(elements) { + ui.showModal(_('Add Uplink %q').replace(/%q/, '"%h"'.format(ssid)), [ + elements, + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Dismiss')), + '\xa0', + E('button', { + 'class': 'cbi-button cbi-button-positive important', + 'click': ui.createHandlerFn(this, 'handleSave', m2) + }, _('Save')) + ]) + ]); + }, this)); + }; + + /* + save new uplink + */ + s.handleSave = function(map, ev) { + var w_sections = uci.sections('wireless', 'wifi-iface'), + device = L.toArray(map.lookupOption('device', '_add_trm'))[0].formvalue('_add_trm'), + network = L.toArray(map.lookupOption('network', '_add_trm'))[0].formvalue('_add_trm'), + ssid = L.toArray(map.lookupOption('ssid', '_add_trm'))[0].formvalue('_add_trm'), + ignore_bssid = L.toArray(map.lookupOption('ignore_bssid', '_add_trm'))[0].formvalue('_add_trm'), + bssid = L.toArray(map.lookupOption('bssid', '_add_trm'))[0].formvalue('_add_trm'), + encryption = L.toArray(map.lookupOption('encryption', '_add_trm'))[0].formvalue('_add_trm'), + password = L.toArray(map.lookupOption('key', '_add_trm'))[0].formvalue('_add_trm'); + if (!ssid || ((encryption.includes('psk') || encryption.includes('wpa') || encryption.includes('sae')) && !password)) { + return; + } + for (var i = 0; i < w_sections.length; i++) { + if (w_sections[i].device === device && w_sections[i].ssid === ssid) { + if (ignore_bssid === '1' || (ignore_bssid === '0' && w_sections[i].bssid === bssid)) { + ui.hideModal(); + return; + } + } + } + + var offset = w_sections.length, + new_sid = 'trm_uplink' + (++offset); + while (uci.get('wireless', new_sid)) { + new_sid = 'trm_uplink' + (++offset); + } + uci.add('wireless', 'wifi-iface', new_sid); + uci.set('wireless', new_sid, 'device', device); + uci.set('wireless', new_sid, 'mode', 'sta'); + uci.set('wireless', new_sid, 'network', network); + uci.set('wireless', new_sid, 'ssid', ssid); + if (ignore_bssid === '0') { + uci.set('wireless', new_sid, 'bssid', bssid); + } + uci.set('wireless', new_sid, 'encryption', encryption); + uci.set('wireless', new_sid, 'key', password); + uci.set('wireless', new_sid, 'disabled', '1'); + handleSectionsAdd(network); + uci.save(); + ui.hideModal(); + }; + return m.render(); + }, + handleReset: null +}); -- cgit v1.2.3