luci-app-travelmate: release 2.0

- sync with travelmate 2.0
- app migrated to client side JS

Signed-off-by: Dirk Brenken <dev@brenken.org>
This commit is contained in:
Dirk Brenken 2020-09-19 00:01:07 +02:00
parent 45c914a016
commit e3b357ac0e
No known key found for this signature in database
GPG key ID: 9D71CD547BFAE684
54 changed files with 21752 additions and 12476 deletions

View file

@ -1,13 +1,14 @@
# Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
# Copyright 2017-2020 Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the Apache License, Version 2.0
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Travelmate
LUCI_DEPENDS:=+luci-compat +travelmate +luci-lib-jsonc
LUCI_DEPENDS:=+travelmate +luci-lib-jsonc
LUCI_PKGARCH:=all
PKG_LICENSE:=Apache-2.0
include ../../luci.mk
# call BuildPackage - OpenWrt buildroot signature

View file

@ -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
});

View file

@ -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 <a href="https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md" target="_blank" rel="noreferrer noopener" >check the online documentation</a>. <br /> \
<em>Please note:</em> 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 <em>Wireguard</em> or <em>OpenVPN</em> package.<br /><p>&#xa0;</p>'));
s.tab('adv_email', _('E-Mail Settings'), _('Please note: E-Mail notifications require the separate setup of the <em>mstmp</em> package.<br /><p>&#xa0;</p>'));
/*
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 \'<station>\'';
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
});

View file

@ -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.<br /> \
You can edit, remove or prioritize existing uplinks by drag \&#38; 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 <em>n</em> minutes, e.g. for timed connections.<br /> \
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 <em>n</em> minutes, e.g. after failed login attempts.<br /> \
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 = "<em>hidden</em>";
}
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
});

View file

@ -1,69 +0,0 @@
-- Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
module("luci.controller.travelmate", package.seeall)
local sys = require("luci.sys")
local util = require("luci.util")
local http = require("luci.http")
local i18n = require("luci.i18n")
local json = require("luci.jsonc")
local uci = require("luci.model.uci").cursor()
function index()
if not nixio.fs.access("/etc/config/travelmate") then
return
end
local e = entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40)
e.dependent = false
e.acl_depends = { "luci-app-travelmate" }
entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true
entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true
entry({"admin", "services", "travelmate", "log"}, template("travelmate/logread"), _("View Logfile"), 30).leaf = true
entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100)
entry({"admin", "services", "travelmate", "advanced", "configuration"}, form("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true
entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, form("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true
entry({"admin", "services", "travelmate", "advanced", "cfg_network"}, form("travelmate/cfg_network_tab"), _("Edit Network Configuration"), 130).leaf = true
entry({"admin", "services", "travelmate", "advanced", "cfg_firewall"}, form("travelmate/cfg_firewall_tab"), _("Edit Firewall Configuration"), 140).leaf = true
entry({"admin", "services", "travelmate", "logread"}, call("logread"), nil).leaf = true
entry({"admin", "services", "travelmate", "status"}, call("status_update"), nil).leaf = true
entry({"admin", "services", "travelmate", "action"}, call("trm_action"), nil).leaf = true
entry({"admin", "services", "travelmate", "wifiscan"}, template("travelmate/wifi_scan")).leaf = true
entry({"admin", "services", "travelmate", "wifiadd"}, form("travelmate/wifi_add", {hideresetbtn=true, hidesavebtn=true})).leaf = true
entry({"admin", "services", "travelmate", "wifiedit"}, form("travelmate/wifi_edit", {hideresetbtn=true, hidesavebtn=true})).leaf = true
entry({"admin", "services", "travelmate", "wifidelete"}, form("travelmate/wifi_delete", {hideresetbtn=true, hidesavebtn=true})).leaf = true
entry({"admin", "services", "travelmate", "wifiorder"}, form("travelmate/wifi_order", {hideresetbtn=true, hidesavebtn=true})).leaf = true
end
function trm_action(name)
if name == "do_restart" then
luci.sys.call("/etc/init.d/travelmate restart >/dev/null 2>&1")
end
luci.http.prepare_content("text/plain")
luci.http.write("0")
end
function status_update()
local rt_file
local content
rt_file = uci:get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json"
if nixio.fs.access(rt_file) then
content = json.parse(nixio.fs.readfile(rt_file) or "")
http.prepare_content("application/json")
http.write_json(content)
end
end
function logread()
local content = util.trim(util.exec("logread -e 'travelmate-'")) or ""
if content == "" then
content = "No travelmate related logs yet!"
end
http.write(content)
end

View file

@ -1,41 +0,0 @@
-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local util = require("luci.util")
local input = "/etc/config/firewall"
if not fs.access(input) then
m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
return m
end
m = SimpleForm("input", nil)
m:append(Template("travelmate/travelmate_css"))
m.submit = translate("Save")
m.reset = false
s = m:section(SimpleSection, nil,
translate("This form allows you to modify the content of the main firewall configuration file (/etc/config/firewall)."))
f = s:option(TextValue, "data")
f.rows = 20
f.rmempty = true
function f.cfgvalue()
return fs.readfile(input) or ""
end
function f.write(self, section, data)
return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
end
function f.remove(self, section, value)
return fs.writefile(input, "")
end
function s.handle(self, state, data)
return true
end
return m

View file

@ -1,41 +0,0 @@
-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local util = require("luci.util")
local input = "/etc/config/network"
if not fs.access(input) then
m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
return m
end
m = SimpleForm("input", nil)
m:append(Template("travelmate/travelmate_css"))
m.submit = translate("Save")
m.reset = false
s = m:section(SimpleSection, nil,
translate("This form allows you to modify the content of the main network configuration file (/etc/config/network)."))
f = s:option(TextValue, "data")
f.rows = 20
f.rmempty = true
function f.cfgvalue()
return fs.readfile(input) or ""
end
function f.write(self, section, data)
return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
end
function f.remove(self, section, value)
return fs.writefile(input, "")
end
function s.handle(self, state, data)
return true
end
return m

View file

@ -1,41 +0,0 @@
-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local util = require("luci.util")
local input = "/etc/config/wireless"
if not fs.access(input) then
m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
return m
end
m = SimpleForm("input", nil)
m:append(Template("travelmate/travelmate_css"))
m.submit = translate("Save")
m.reset = false
s = m:section(SimpleSection, nil,
translate("This form allows you to modify the content of the main wireless configuration file (/etc/config/wireless)."))
f = s:option(TextValue, "data")
f.rows = 20
f.rmempty = true
function f.cfgvalue()
return fs.readfile(input) or ""
end
function f.write(self, section, data)
return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
end
function f.remove(self, section, value)
return fs.writefile(input, "")
end
function s.handle(self, state, data)
return true
end
return m

View file

@ -1,43 +0,0 @@
-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local util = require("luci.util")
local input = "/etc/config/travelmate"
if not fs.access(input) then
m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
m.reset = false
m.submit = false
return m
end
m = SimpleForm("input", nil)
m:append(Template("travelmate/travelmate_css"))
m.submit = translate("Save")
m.reset = false
s = m:section(SimpleSection, nil,
translate("This form allows you to modify the content of the main travelmate configuration file (/etc/config/travelmate)."))
f = s:option(TextValue, "data")
f.rows = 20
f.rmempty = true
function f.cfgvalue()
return fs.readfile(input) or ""
end
function f.write(self, section, data)
return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
end
function f.remove(self, section, value)
return fs.writefile(input, "")
end
function s.handle(self, state, data)
return true
end
return m

View file

@ -1,153 +0,0 @@
-- Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
local util = require("luci.util")
local nw = require("luci.model.network").init()
local fw = require("luci.model.firewall").init()
local dump = util.ubus("network.interface", "dump", {})
local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
local uplink = uci:get("network", trmiface) or ""
m = Map("travelmate", translate("Travelmate"),
translate("Configuration of the travelmate package to to enable travel router functionality. ")
.. translatef("For further information "
.. "<a href=\"%s\" target=\"_blank\">"
.. "see online documentation</a>", "https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md"))
m:chain("network")
m:chain("firewall")
-- Interface Wizard
if uplink == "" then
ds = m:section(NamedSection, "global", "travelmate", translate("Interface Wizard"))
o = ds:option(Value, "trm_iface", translate("Create Uplink interface"),
translate("Create a new wireless wan uplink interface, configure it to use dhcp and ")
.. translate("add it to the wan zone of the firewall. ")
.. translate("This step has only to be done once."))
o.datatype = "and(uciname,rangelength(3,15))"
o.default = trmiface
o.rmempty = false
function o.validate(self, value)
if value then
local nwnet = nw:get_network(value)
local zone = fw:get_zone("wan")
local fwnet = fw:get_zone_by_network(value)
if not nwnet then
nwnet = nw:add_network(value, { proto = "dhcp" })
end
if zone and not fwnet then
fwnet = zone:add_network(value)
end
end
return value
end
return m
end
-- Main travelmate options
s = m:section(NamedSection, "global", "travelmate")
o1 = s:option(Flag, "trm_enabled", translate("Enable Travelmate"))
o1.default = o1.disabled
o1.rmempty = false
o2 = s:option(Flag, "trm_captive", translate("Captive Portal Detection"),
translate("Check the internet availability, log captive portal redirections and keep the uplink connection 'alive'."))
o2.default = o2.enabled
o2.rmempty = false
o3 = s:option(Flag, "trm_netcheck", translate("Net Error Check"),
translate("Treat missing internet availability as an error."))
o3:depends("trm_captive", 1)
o3.default = o3.disabled
o3.rmempty = false
o4 = s:option(Flag, "trm_proactive", translate("ProActive Uplink Switch"),
translate("Proactively scan and switch to a higher prioritized uplink, despite of an already existing connection."))
o4.default = o4.enabled
o4.rmempty = false
o5 = s:option(Flag, "trm_autoadd", translate("Add Open Uplinks"),
translate("Automatically add open uplinks like hotel captive portals to your wireless config."))
o5.default = o5.disabled
o5.rmempty = false
o6 = s:option(ListValue, "trm_iface", translate("Uplink / Trigger interface"),
translate("Name of the used uplink interface."))
if dump then
local i, v
for i, v in ipairs(dump.interface) do
if v.interface ~= "loopback" and v.interface ~= "lan" then
local device = v.l3_device or v.device or "-"
o6:value(v.interface, v.interface.. " (" ..device.. ")")
end
end
end
o6.default = trmiface
o6.rmempty = false
-- Runtime information
ds = s:option(DummyValue, "_dummy")
ds.template = "travelmate/runtime"
-- Extra options
e = m:section(NamedSection, "global", "travelmate", translate("Extra Options"),
translate("Options for further tweaking in case the defaults are not suitable for you."))
e1 = e:option(Flag, "trm_debug", translate("Enable Verbose Debug Logging"))
e1.default = e1.disabled
e1.rmempty = false
e2 = e:option(Value, "trm_radio", translate("Radio Selection / Order"),
translate("Restrict travelmate to a single radio (e.g. 'radio1') or change the overall scanning order (e.g. 'radio1 radio2 radio0')."))
e2.rmempty = true
e3 = e:option(Value, "trm_listexpiry", translate("List Auto Expiry"),
translate("Automatically resets the 'Faulty Stations' list after n minutes. Default is '0' which means no expiry."))
e3.datatype = "range(0,300)"
e3.default = 0
e3.rmempty = false
e4 = e:option(Value, "trm_triggerdelay", translate("Trigger Delay"),
translate("Additional trigger delay in seconds before travelmate processing begins."))
e4.datatype = "range(1,60)"
e4.default = 2
e4.rmempty = false
e5 = e:option(Value, "trm_maxretry", translate("Connection Limit"),
translate("Retry limit to connect to an uplink."))
e5.default = 5
e5.datatype = "range(1,10)"
e5.rmempty = false
e6 = e:option(Value, "trm_minquality", translate("Signal Quality Threshold"),
translate("Minimum signal quality threshold as percent for conditional uplink (dis-) connections."))
e6.default = 35
e6.datatype = "range(20,80)"
e6.rmempty = false
e7 = e:option(Value, "trm_maxwait", translate("Interface Timeout"),
translate("How long should travelmate wait for a successful wlan uplink connection."))
e7.default = 30
e7.datatype = "range(20,40)"
e7.rmempty = false
e8 = e:option(Value, "trm_timeout", translate("Overall Timeout"),
translate("Overall retry timeout in seconds."))
e8.default = 60
e8.datatype = "range(30,300)"
e8.rmempty = false
e10 = e:option(Value, "trm_scanbuffer", translate("Scan Buffer Size"),
translate("Buffer size in bytes to prepare nearby scan results."))
e10.default = 1024
e10.datatype = "range(512,4096)"
e10.optional = true
return m

View file

@ -1,226 +0,0 @@
-- Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
local http = require("luci.http")
local util = require("luci.util")
local scripts = util.split(util.trim(util.exec("ls /etc/travelmate/*.login 2>/dev/null")), "\n", nil, true) or {}
local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
local encr_psk = {"psk", "psk2", "psk-mixed", "sae", "owe", "sae-mixed"}
local encr_wpa = {"wpa", "wpa2", "wpa-mixed"}
m = SimpleForm("add", translate("Add Wireless Uplink Configuration"))
m.submit = translate("Save")
m.cancel = translate("Back to overview")
m.reset = false
function m.on_cancel()
http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
end
m.hidden = {
device = http.formvalue("device"),
ssid = http.formvalue("ssid"),
bssid = http.formvalue("bssid"),
description = http.formvalue("description"),
wep = tonumber(http.formvalue("wep")) or 0,
wpa_suites = http.formvalue("wpa_suites"),
wpa_version = tonumber(http.formvalue("wpa_version")) or 0
}
if m.hidden.wpa_version == 4 then
if string.find(m.hidden.description, "OWE") then
m.hidden.wpa_version = 5
end
end
if m.hidden.ssid == "" then
wssid = m:field(Value, "ssid", translate("SSID (hidden)"))
else
wssid = m:field(Value, "ssid", translate("SSID"))
end
wssid.datatype = "rangelength(1,32)"
wssid.default = m.hidden.ssid or ""
nobssid = m:field(Flag, "no_bssid", translate("Ignore BSSID"))
if m.hidden.ssid == "" then
nobssid.default = nobssid.disabled
else
nobssid.default = nobssid.enabled
end
bssid = m:field(Value, "bssid", translate("BSSID"),
translatef("The BSSID information '%s' is optional and only required for hidden networks", m.hidden.bssid or ""))
bssid:depends("no_bssid", 0)
bssid.datatype = "macaddr"
bssid.default = m.hidden.bssid or ""
if m.hidden.wep == 1 then
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("wep", "WEP")
encr:value("wep+open", "WEP Open System")
encr:value("wep+mixed", "WEP mixed")
encr:value("wep+shared", "WEP Shared Key")
encr.default = "wep+open"
wkey = m:field(Value, "key", translate("WEP-Passphrase"))
wkey.password = true
wkey.datatype = "wepkey"
elseif m.hidden.wpa_version > 0 then
if m.hidden.wpa_suites == "802.1X" then
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("wpa", "WPA Enterprise")
encr:value("wpa-mixed", "WPA/WPA2 Enterprise mixed")
encr:value("wpa2", "WPA2 Enterprise")
encr.default = encr_wpa[m.hidden.wpa_version] or "wpa2"
ciph = m:field(ListValue, "cipher", translate("Cipher"))
ciph:value("auto", translate("Automatic"))
ciph:value("ccmp", translate("Force CCMP (AES)"))
ciph:value("tkip", translate("Force TKIP"))
ciph:value("tkip+ccmp", translate("Force TKIP and CCMP (AES)"))
ciph.default = "auto"
eaptype = m:field(ListValue, "eap_type", translate("EAP-Method"))
eaptype:value("tls", "TLS")
eaptype:value("ttls", "TTLS")
eaptype:value("peap", "PEAP")
eaptype:value("fast", "FAST")
eaptype.default = "peap"
authentication = m:field(ListValue, "auth", translate("Authentication"))
authentication:value("PAP")
authentication:value("CHAP")
authentication:value("MSCHAP")
authentication:value("MSCHAPV2")
authentication:value("EAP-GTC")
authentication:value("EAP-MD5")
authentication:value("EAP-MSCHAPV2")
authentication:value("EAP-TLS")
authentication:value("auth=PAP")
authentication:value("auth=MSCHAPV2")
authentication.default = "EAP-MSCHAPV2"
ident = m:field(Value, "identity", translate("Identity"))
wkey = m:field(Value, "password", translate("Password"))
wkey.password = true
wkey.datatype = "wpakey"
cacert = m:field(Value, "ca_cert", translate("Path to CA-Certificate"))
cacert.rmempty = true
clientcert = m:field(Value, "client_cert", translate("Path to Client-Certificate"))
clientcert:depends("eap_type","tls")
clientcert.rmempty = true
privkey = m:field(Value, "priv_key", translate("Path to Private Key"))
privkey:depends("eap_type","tls")
privkey.rmempty = true
privkeypwd = m:field(Value, "priv_key_pwd", translate("Password of Private Key"))
privkeypwd:depends("eap_type","tls")
privkeypwd.datatype = "wpakey"
privkeypwd.password = true
privkeypwd.rmempty = true
else
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("psk", "WPA-PSK")
encr:value("psk2", "WPA2-PSK")
encr:value("psk-mixed", "WPA/WPA2 mixed")
encr:value("sae", "WPA3-SAE")
encr:value("owe", "OWE (open network)")
encr:value("sae-mixed", "WPA2/WPA3 mixed")
encr.default = encr_psk[m.hidden.wpa_version] or "psk2"
ciph = m:field(ListValue, "cipher", translate("Cipher"))
ciph:value("auto", translate("Automatic"))
ciph:value("ccmp", translate("Force CCMP (AES)"))
ciph:value("tkip", translate("Force TKIP"))
ciph:value("tkip+ccmp", translate("Force TKIP and CCMP (AES)"))
ciph:depends("encryption", "psk")
ciph:depends("encryption", "psk2")
ciph:depends("encryption", "psk-mixed")
ciph.default = "auto"
wkey = m:field(Value, "key", translate("WPA-Passphrase"))
wkey.password = true
wkey.datatype = "wpakey"
wkey:depends("encryption", "psk")
wkey:depends("encryption", "psk2")
wkey:depends("encryption", "psk-mixed")
wkey:depends("encryption", "sae")
wkey:depends("encryption", "sae-mixed")
end
end
local login_section = (m.hidden.device or "") .. "_" .. (m.hidden.ssid or "") .. "_" .. (m.hidden.bssid or "")
login_section = login_section:gsub("[^%w_]", "_")
local cmd = uci:get("travelmate", login_section, "command")
local cmd_args_default = uci:get("travelmate", login_section, "command_args")
cmd_list = m:field(ListValue, "cmdlist", translate("Auto Login Script"),
translate("External script reference which will be called for automated captive portal logins."))
cmd_args = m:field(Value, "cmdargs", translate("Optional Arguments"),
translate("Space separated list of additional arguments passed to the Auto Login Script, i.e. username and password"))
for _, z in ipairs(scripts) do
cmd_list:value(z)
cmd_args:depends("cmdlist", z)
end
cmd_list:value("none")
cmd_list.default = cmd or "none"
cmd_args.default = cmd_args_default
function wssid.write(self, section, value)
login_section = (m.hidden.device or "") .. "_" .. (wssid:formvalue(section) or "") .. "_" .. (bssid:formvalue(section) or "")
login_section = login_section:gsub("[^%w_]", "_")
newsection = uci:section("wireless", "wifi-iface", login_section, {
mode = "sta",
network = trmiface,
device = m.hidden.device,
ssid = wssid:formvalue(section),
bssid = bssid:formvalue(section),
disabled = "1"
})
if encr then
if string.find(encr:formvalue(section), '^wep') then
uci:set("wireless", newsection, "encryption", encr:formvalue(section))
uci:set("wireless", newsection, "key", wkey:formvalue(section) or "")
elseif string.find(encr:formvalue(section), '^wpa') then
uci:set("wireless", newsection, "eap_type", eaptype:formvalue(section))
uci:set("wireless", newsection, "auth", authentication:formvalue(section))
uci:set("wireless", newsection, "identity", ident:formvalue(section) or "")
uci:set("wireless", newsection, "password", wkey:formvalue(section) or "")
uci:set("wireless", newsection, "ca_cert", cacert:formvalue(section) or "")
uci:set("wireless", newsection, "client_cert", clientcert:formvalue(section) or "")
uci:set("wireless", newsection, "priv_key", privkey:formvalue(section) or "")
uci:set("wireless", newsection, "priv_key_pwd", privkeypwd:formvalue(section) or "")
elseif encr:formvalue(section) ~= "owe" then
uci:set("wireless", newsection, "key", wkey:formvalue(section) or "")
end
if ciph and ciph:formvalue(section) and ciph:formvalue(section) ~= "auto" then
uci:set("wireless", newsection, "encryption", encr:formvalue(section) .. "+" .. ciph:formvalue(section))
else
uci:set("wireless", newsection, "encryption", encr:formvalue(section))
end
else
uci:set("wireless", newsection, "encryption", "none")
end
if not uci:get("travelmate", login_section) and cmd_list:formvalue(section) ~= "none" then
uci:set("travelmate", login_section, "login")
end
if uci:get("travelmate", login_section) then
uci:set("travelmate", login_section, "command", cmd_list:formvalue(section))
uci:set("travelmate", login_section, "command_args", cmd_args:formvalue(section))
uci:save("travelmate")
uci:commit("travelmate")
end
uci:save("wireless")
uci:commit("wireless")
luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
end
return m

View file

@ -1,14 +0,0 @@
-- Copyright 2017 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local uci = require("luci.model.uci").cursor()
local http = require("luci.http")
local cfg = http.formvalue("cfg")
if cfg ~= nil then
uci:delete("wireless", cfg)
uci:save("wireless")
uci:commit("wireless")
luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
end
http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))

View file

@ -1,207 +0,0 @@
-- Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
local http = require("luci.http")
local util = require("luci.util")
local scripts = util.split(util.trim(util.exec("ls /etc/travelmate/*.login 2>/dev/null")), "\n", nil, true) or {}
m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration"))
m.submit = translate("Save")
m.cancel = translate("Back to overview")
m.reset = false
function m.on_cancel()
http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
end
m.hidden = {
cfg = http.formvalue("cfg")
}
local s = uci:get_all("wireless", m.hidden.cfg)
if s ~= nil then
wssid = m:field(Value, "ssid", translate("SSID"))
wssid.datatype = "rangelength(1,32)"
wssid.default = s.ssid or ""
bssid = m:field(Value, "bssid", translate("BSSID"))
bssid.datatype = "macaddr"
bssid.default = s.bssid or ""
if string.match(s.encryption, '%+') and not string.match(s.encryption, '^wep') then
s.pos = string.find(s.encryption, '%+')
s.cipher = string.sub(s.encryption, s.pos + 1)
s.encryption = string.sub(s.encryption, 0, s.pos - 1)
end
if s.encryption and s.encryption ~= "none" then
if string.match(s.encryption, '^wep') then
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("wep", "WEP")
encr:value("wep+open", "WEP Open System")
encr:value("wep+mixed", "WEP mixed")
encr:value("wep+shared", "WEP Shared Key")
encr.default = s.encryption
wkey = m:field(Value, "key", translate("Passphrase"))
wkey.datatype = "wepkey"
wkey.password = true
wkey.default = s.key
elseif string.match(s.encryption, '^wpa') then
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("wpa", "WPA Enterprise")
encr:value("wpa-mixed", "WPA/WPA2 Enterprise mixed")
encr:value("wpa2", "WPA2 Enterprise")
encr.default = s.encryption
ciph = m:field(ListValue, "cipher", translate("Cipher"))
ciph:value("auto", translate("Automatic"))
ciph:value("ccmp", translate("Force CCMP (AES)"))
ciph:value("tkip", translate("Force TKIP"))
ciph:value("tkip+ccmp", translate("Force TKIP and CCMP (AES)"))
ciph.default = s.cipher
eaptype = m:field(ListValue, "eap_type", translate("EAP-Method"))
eaptype:value("tls", "TLS")
eaptype:value("ttls", "TTLS")
eaptype:value("peap", "PEAP")
eaptype:value("fast", "FAST")
eaptype.default = s.eap_type or "peap"
authentication = m:field(ListValue, "auth", translate("Authentication"))
authentication:value("PAP")
authentication:value("CHAP")
authentication:value("MSCHAP")
authentication:value("MSCHAPV2")
authentication:value("EAP-GTC")
authentication:value("EAP-MD5")
authentication:value("EAP-MSCHAPV2")
authentication:value("EAP-TLS")
authentication:value("auth=PAP")
authentication:value("auth=MSCHAPV2")
authentication.default = s.auth or "EAP-MSCHAPV2"
ident = m:field(Value, "identity", translate("Identity"))
ident.default = s.identity or ""
wkey = m:field(Value, "password", translate("Passphrase"))
wkey.datatype = "wpakey"
wkey.password = true
wkey.default = s.password
cacert = m:field(Value, "ca_cert", translate("Path to CA-Certificate"))
cacert.rmempty = true
cacert.default = s.ca_cert or ""
clientcert = m:field(Value, "client_cert", translate("Path to Client-Certificate"))
clientcert:depends("eap_type","tls")
clientcert.rmempty = true
clientcert.default = s.client_cert or ""
privkey = m:field(Value, "priv_key", translate("Path to Private Key"))
privkey:depends("eap_type","tls")
privkey.rmempty = true
privkey.default = s.priv_key or ""
privkeypwd = m:field(Value, "priv_key_pwd", translate("Password of Private Key"))
privkeypwd:depends("eap_type","tls")
privkeypwd.datatype = "wpakey"
privkeypwd.password = true
privkeypwd.rmempty = true
privkeypwd.default = s.priv_key_pwd or ""
else
encr = m:field(ListValue, "encryption", translate("Encryption"))
encr:value("psk", "WPA-PSK")
encr:value("psk2", "WPA2-PSK")
encr:value("psk-mixed", "WPA/WPA2 mixed")
encr:value("sae", "WPA3-SAE")
encr:value("owe", "OWE (open network)")
encr:value("sae-mixed", "WPA2/WPA3 mixed")
encr.default = s.encryption
ciph = m:field(ListValue, "cipher", translate("Cipher"))
ciph:value("auto", translate("Automatic"))
ciph:value("ccmp", translate("Force CCMP (AES)"))
ciph:value("tkip", translate("Force TKIP"))
ciph:value("tkip+ccmp", translate("Force TKIP and CCMP (AES)"))
ciph:depends("encryption", "psk")
ciph:depends("encryption", "psk2")
ciph:depends("encryption", "psk-mixed")
ciph.default = s.cipher or "auto"
wkey = m:field(Value, "key", translate("Passphrase"))
wkey.datatype = "wpakey"
wkey.password = true
wkey:depends("encryption", "psk")
wkey:depends("encryption", "psk2")
wkey:depends("encryption", "psk-mixed")
wkey:depends("encryption", "sae")
wkey:depends("encryption", "sae-mixed")
wkey.default = s.key
end
end
else
m.on_cancel()
end
local login_section = (s.device or "") .. "_" .. (s.ssid or "") .. "_" .. (s.bssid or "")
login_section = login_section:gsub("[^%w_]", "_")
local cmd = uci:get("travelmate", login_section, "command")
local cmd_args_default = uci:get("travelmate", login_section, "command_args")
cmd_list = m:field(ListValue, "cmdlist", translate("Auto Login Script"),
translate("External script reference which will be called for automated captive portal logins."))
cmd_args = m:field(Value, "cmdargs", translate("Optional Arguments"),
translate("Space separated list of additional arguments passed to the Auto Login Script, i.e. username and password"))
for _, z in ipairs(scripts) do
cmd_list:value(z)
cmd_args:depends("cmdlist", z)
end
cmd_list:value("none")
cmd_list.default = cmd or "none"
cmd_args.default = cmd_args_default
function wssid.write(self, section, value)
uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section))
uci:set("wireless", m.hidden.cfg, "bssid", bssid:formvalue(section))
if encr then
if string.find(encr:formvalue(section), '^wep') then
uci:set("wireless", m.hidden.cfg, "encryption", encr:formvalue(section))
uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section) or "")
elseif string.find(encr:formvalue(section), '^wpa') then
uci:set("wireless", m.hidden.cfg, "eap_type", eaptype:formvalue(section))
uci:set("wireless", m.hidden.cfg, "auth", authentication:formvalue(section))
uci:set("wireless", m.hidden.cfg, "identity", ident:formvalue(section) or "")
uci:set("wireless", m.hidden.cfg, "password", wkey:formvalue(section) or "")
uci:set("wireless", m.hidden.cfg, "ca_cert", cacert:formvalue(section) or "")
uci:set("wireless", m.hidden.cfg, "client_cert", clientcert:formvalue(section) or "")
uci:set("wireless", m.hidden.cfg, "priv_key", privkey:formvalue(section) or "")
uci:set("wireless", m.hidden.cfg, "priv_key_pwd", privkeypwd:formvalue(section) or "")
elseif encr:formvalue(section) ~= "owe" then
uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section) or "")
end
if ciph and ciph:formvalue(section) and ciph:formvalue(section) ~= "auto" then
uci:set("wireless", m.hidden.cfg, "encryption", encr:formvalue(section) .. "+" .. ciph:formvalue(section))
else
uci:set("wireless", m.hidden.cfg, "encryption", encr:formvalue(section))
end
end
if not uci:get("travelmate", login_section) and cmd_list:formvalue(section) ~= "none" then
uci:set("travelmate", login_section, "login")
end
if uci:get("travelmate", login_section) then
uci:set("travelmate", login_section, "command", cmd_list:formvalue(section))
uci:set("travelmate", login_section, "command_args", cmd_args:formvalue(section))
uci:save("travelmate")
uci:commit("travelmate")
end
uci:save("wireless")
uci:commit("wireless")
luci.sys.call("env -i /bin/ubus call network reload >/dev/null 2>&1")
m.on_cancel()
end
return m

View file

@ -1,36 +0,0 @@
-- Copyright 2017 Dirk Brenken (dev@brenken.org)
-- This is free software, licensed under the Apache License, Version 2.0
local http = require("luci.http")
local cfg = http.formvalue("cfg")
local dir = http.formvalue("dir")
local uci = require("luci.model.uci").cursor()
local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
if cfg ~= nil then
local section = ""
local idx = ""
local idx_change = ""
local changed = ""
uci:foreach("wireless", "wifi-iface", function(s)
local iface = s.network or ""
if iface == trmiface then
section = s['.name']
if cfg == section then
idx = s['.index']
else
idx_change = s['.index']
end
if (dir == "up" and idx ~= "" and idx_change ~= "" and idx_change < idx) or
(dir == "down" and idx ~= "" and idx_change ~= "" and idx_change > idx) then
changed = uci:reorder("wireless", cfg, idx_change)
idx = ""
end
end
end)
if changed ~= "" then
uci:save("wireless")
uci:commit("wireless")
end
end
http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))

View file

@ -1,36 +0,0 @@
<%#
Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
This is free software, licensed under the Apache License, Version 2.0
-%>
<%+header%>
<%+travelmate/travelmate_css%>
<script type="text/javascript">
//<![CDATA[
function log_update()
{
XHR.poll(-1, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "logread")%>', null,
function(x)
{
if (!x)
{
return;
}
var view = document.getElementById("view_id");
view.value = x.responseText;
view.scrollTop = view.scrollHeight;
});
}
window.onload = log_update();
//]]>
</script>
<div class="cbi-map">
<div class="cbi-section">
<div class="cbi-section-descr"><%:The syslog output, pre-filtered for travelmate related messages only.%></div>
<textarea id="view_id" readonly="readonly" wrap="off" value=""></textarea>
</div>
</div>
<%+footer%>

View file

@ -1,223 +0,0 @@
<%#
Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
This is free software, licensed under the Apache License, Version 2.0
-%>
<%+travelmate/travelmate_css%>
<script type="text/javascript">
//<![CDATA[
function status_update(json)
{
var btn1 = document.getElementById("btn1");
var view = document.getElementById("value_1");
var input = json.data.travelmate_status;
btn1.value = "<%:Restart%>";
btn1.name = "do_restart";
view.innerHTML = input || "-";
view = document.getElementById("value_2");
input = json.data.travelmate_version;
view.innerHTML = input || "-";
view = document.getElementById("value_3");
input = json.data.station_id;
view.innerHTML = input || "-";
view = document.getElementById("value_4");
input = json.data.station_interface;
view.innerHTML = input || "-";
view = document.getElementById("value_5");
input = json.data.faulty_stations;
view.innerHTML = input || "-";
view = document.getElementById("value_6");
input = json.data.wpa_capabilities;
view.innerHTML = input || "-";
view = document.getElementById("value_7");
input = json.data.last_rundate;
view.innerHTML = input || "-";
}
function btn_action(action)
{
var btn1 = document.getElementById("btn1");
var btn1_running = document.getElementById("btn1_running");
btn1.disabled = true;
running(btn1_running, 1);
new XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate")%>/action/' + action.name, null,
function(x)
{
if (!x)
{
return;
}
btn1.disabled = false;
running(btn1_running, 0);
});
}
function running(element, state)
{
if (state === 1)
{
var running_html = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" width="16" height="16" style="vertical-align:middle" />';
element.innerHTML = running_html;
}
else
{
element.innerHTML = '';
}
}
function toggle_qrcode() {
var view = document.getElementById("qrcode");
if (view.style.display === "none") {
view.style.display = "block";
} else {
view.style.display = "none";
}
}
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
function(x, json_info)
{
if (!x || !json_info)
{
return;
}
status_update(json_info)
});
XHR.poll(-1, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
function(x, json_info)
{
if (!x || !json_info)
{
return;
}
status_update(json_info)
});
//]]>
</script>
<h3><%:Runtime Information%></h3>
<div class="cbi-value" id="status_1">
<label class="cbi-value-title" for="status_1"><%:Travelmate Status (Quality)%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_1">-</span>
</div>
</div>
<div class="cbi-value" id="status_2">
<label class="cbi-value-title" for="status_2"><%:Travelmate Version%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_2">-</span>
</div>
</div>
<div class="cbi-value" id="status_3">
<label class="cbi-value-title" for="status_3"><%:Station ID (RADIO/SSID/BSSID)%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_3">-</span>
</div>
</div>
<div class="cbi-value" id="status_4">
<label class="cbi-value-title" for="status_4"><%:Station Interface%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_4">-</span>
</div>
</div>
<div class="cbi-value" id="status_5">
<label class="cbi-value-title" for="status_5"><%:Faulty Stations%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_5">-</span>
</div>
</div>
<div class="cbi-value" id="status_6">
<label class="cbi-value-title" for="status_6"><%:WPA Capabilities%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_6">-</span>
</div>
</div>
<div class="cbi-value" id="status_7">
<label class="cbi-value-title" for="status_7"><%:Last Run%></label>
<div class="cbi-value-field">
<span class="runtime" id="value_7">-</span>
</div>
</div>
<hr />
<div class="cbi-value" id="button_1">
<label class="cbi-value-title" for="button_1"><%:Restart Travelmate%></label>
<div class="cbi-value-field">
<input class="cbi-button cbi-button-reset" id="btn1" type="button" name="do_restart" value="<%:Restart%>" onclick="btn_action(this)" />
<span id="btn1_running" class="btn_running"></span>
</div>
</div>
<div class="cbi-value" id="button_2">
<label class="cbi-value-title" for="button_2"><%:View AP QR-Codes%></label>
<div class="cbi-value-field">
<input class="cbi-button cbi-button-apply" type="button" value="<%:Show/Hide QR-Codes%>" onclick="toggle_qrcode()" />
</div>
</div>
<div class="cbi-value" style="margin-bottom: 0px">
<span class="cbi-value" style="display: none" id="qrcode">
<%-
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
local qrcode
uci:foreach("wireless", "wifi-iface", function(s)
local device = s.device or ""
local mode = s.mode or ""
local ssid = s.ssid or ""
local enc = s.encryption or ""
local key = s.key or ""
local hidden = s.hidden or "false"
local disabled = s.disabled or ""
local wep_slots = {s.key1 or "", s.key2 or "", s.key3 or "", s.key4 or ""}
if device and mode == "ap" and disabled ~= "1" then
if string.match(enc, '^psk') then
enc = "WPA"
elseif string.match(enc, '^wep') then
enc = "WEP"
if tonumber(key) then
key = wep_slots[tonumber(key)]
end
elseif enc == "none" then
enc = "nopass"
key = "nokey"
else
enc = ""
end
if hidden == "1" then
hidden = "true"
end
if ssid and enc and key then
local e_ssid = string.gsub(ssid,"[\"\\';:,()&`|<> ]",[[\\\%1]])
local e_key = string.gsub(key,"[\"\\';:,()&`|<> ]",[[\\\%1]])
if fs.access("/usr/bin/qrencode") then
qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- 'WIFI:S:\"'" .. e_ssid .. "'\";T:'" .. enc .. "';P:\"'" .. e_key .. "'\";H:'" .. hidden .. "';'")
-%>
<div class="qr-code">
<%=qrcode%>
</div>
<div class="qr-code">
<em><%:AP on %><%=device%><%: with SSID %>"<%=ssid%>"</em>
<hr />
</div>
<%-
end
end
end
end)
if not qrcode then
-%>
<div class="qr-code">
<em><%:For QR-Code support please install package 'qrencode'!%></em>
</div>
<%-
end
-%>
</span>
</div>

View file

@ -1,154 +0,0 @@
<%#
Copyright 2017-2019 Dirk Brenken (dev@brenken.org)
This is free software, licensed under the Apache License, Version 2.0
-%>
<%-
local uci = require("luci.model.uci").cursor()
local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
-%>
<%+header%>
<script type="text/javascript">
//<![CDATA[
function status_update(json)
{
var i;
var j;
var search;
var view;
var list;
var status = json.data.travelmate_status;
var faulty = json.data.faulty_stations;
if (faulty)
{
var faulty_array = faulty.split(' ');
for (i = 0; i < faulty_array.length; i++)
{
for (j = 1; j <= 5; j++)
{
search = j + "_" + faulty_array[i];
view = document.getElementById(search);
if (view)
{
view.setAttribute("name", "station_nok");
view.setAttribute("style", "text-align: left !important; color: #a22; font-weight: bold");
}
}
}
}
else
{
list = document.getElementsByName("station_nok");
if (list.length > 0)
{
for (i = 0; i < list.length; i++)
{
list[i].removeAttribute("style");
}
}
}
if (status.startsWith("connected"))
{
for (i = 1; i <= 5; i++)
{
search = i + "_" + json.data.station_id;
view = document.getElementById(search);
if (view)
{
view.setAttribute("style", "text-align: left !important; color: #37c; font-weight: bold");
}
}
}
else
{
list = document.getElementsByName("station_ok");
if (list.length > 0)
{
for (i = 0; i < list.length; i++)
{
list[i].removeAttribute("style");
}
}
}
}
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
function(x, json_info)
{
if (!x || !json_info)
{
return;
}
status_update(json_info)
});
XHR.poll(-1, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
function(x, json_info)
{
if (!x || !json_info)
{
return;
}
status_update(json_info)
});
//]]>
</script>
<div class="cbi-map">
<div class="cbi-map-descr">
<%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, remove or re-order/prioritize existing uplinks or scan for new ones. The currently used uplink is emphasized in blue, faulty stations in red.", trmiface)%>
</div>
<div class="cbi-section">
<div class="table cbi-section-table">
<div class="tr cbi-section-table-titles">
<div class="th left"><%:Device%></div>
<div class="th left"><%:SSID%></div>
<div class="th left"><%:BSSID%></div>
<div class="th left"><%:Encryption%></div>
<div class="th center"><%:Action%></div>
</div>
<%- uci:foreach("wireless", "wifi-iface", function(s)
local iface = s.network or ""
if iface == trmiface then
local section = s['.name'] or ""
local device = s.device or "-"
local ssid = s.ssid or "-"
local bssid = s.bssid or "-"
local encr = s.encryption or "-"
-%>
<div class="tr cbi-section-table-row cbi-rowstyle-1" name="station_ok" id="1_<%=device%>/<%=ssid%>/<%=bssid%>">
<div class="td left" style="text-align: left !important" name="station_ok" id="2_<%=device%>/<%=ssid%>/<%=bssid%>"><%=device%></div>
<div class="td left" style="text-align: left !important" name="station_ok" id="3_<%=device%>/<%=ssid%>/<%=bssid%>"><%=ssid%></div>
<div class="td left" style="text-align: left !important" name="station_ok" id="4_<%=device%>/<%=ssid%>/<%=bssid%>"><%=bssid%></div>
<div class="td left" style="text-align: left !important" name="station_ok" id="5_<%=device%>/<%=ssid%>/<%=bssid%>"><%=encr%></div>
<div class="td middle cbi-section-actions">
<div>
<input class="cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=up'" alt="<%:Move up%>" title="<%:Move up%>" />
<input class="cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=down'" alt="<%:Move down%>" title="<%:Move down%>" />
<input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" />
<input class="cbi-button cbi-button-remove" type="button" value="<%:Remove%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Remove this Uplink%>" />
</div>
</div>
</div>
<%- end; end) -%>
</div>
</div>
<div class="cbi-page-actions right">
<%- uci:foreach("wireless", "wifi-device", function(s)
local device = s[".name"]
local hwmode = s.hwmode or "-" -%>
<form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
<input type="hidden" name="device" value="<%=device%>" />
<input type="hidden" name="token" value="<%=token%>" />
<input type="submit" class="cbi-button cbi-button-action important" title="<%:Find and join network on%> <%=device%>" value="<%:Scan%> <%=device%> (<%=hwmode%>)" />
</form>
<%- end) -%>
</div>
</div>
<%+footer%>

View file

@ -1,94 +0,0 @@
<style type="text/css">
textarea
{
width: 100% !important;
height: 450px !important;
border: 1px solid #cccccc;
padding: 5px;
font-size: 12px;
font-family: monospace;
resize: none;
white-space: pre;
overflow-wrap: normal;
overflow-x: scroll;
}
select[readonly],
textarea[readonly]
{
width: 100% !important;
height: 450px !important;
border: 1px solid #cccccc;
padding: 5px;
font-size: 12px;
font-family: monospace;
resize: none;
pointer-events: auto;
cursor: auto;
}
.table.cbi-section-table .th,
.table.cbi-section-table .td,
.cbi-section-table-cell,
.cbi-section-table-row,
.tr[data-title]::before
{
text-align: left !important;
vertical-align: top;
margin-left: 0px;
padding-left: 2px;
}
.table.cbi-section-table .th
{
white-space: nowrap;
}
.cbi-section-table-row > .cbi-value-field .cbi-input-select,
.table.cbi-section-table select
{
width: 70px;
}
.item,
.add-item
{
white-space: nowrap;
width: 8.2em;
}
.cbi-input-checkbox
{
height: 1em;
}
.cbi-button
{
-webkit-appearance: menulist;
}
.runtime
{
color: #37c;
font-weight: bold;
display: inline-block;
width: 100%;
padding-top: 0.5rem;
}
.qr-code
{
border-bottom: 0px;
margin-bottom: 0px;
padding: 0.25em 0.6em;
text-align: left
}
.button_running
{
display: inline-block;
width: 16px;
height: 16px;
margin: 0 5px;
}
</style>

View file

@ -1,95 +0,0 @@
<%#
Copyright 2017-2020 Dirk Brenken (dev@brenken.org)
This is free software, licensed under the Apache License, Version 2.0
-%>
<%-
local sys = require("luci.sys")
local utl = require("luci.util")
local xml = require("luci.xml")
local dev = luci.http.formvalue("device")
local ifn = utl.trim(sys.exec("/bin/ubus -S call network.wireless status 2>/dev/null | jsonfilter -l1 -e '@." .. dev .. ".interfaces[@.config.mode=\"sta\"].ifname' 2>/dev/null"))
local iw
if ifn ~= "" then
iw = sys.wifi.getiwinfo(ifn)
else
iw = sys.wifi.getiwinfo(dev)
end
if not iw then
luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
end
function percent_wifi_signal(info)
local qc = info.quality or 0
local qm = info.quality_max or 0
if info.bssid and qc > 0 and qm > 0 then
return math.floor((100 / qm) * qc)
else
return 0
end
end
-%>
<%+header%>
<div class="cbi-map">
<h3><%:Wireless Scan%></h3>
<div class="cbi-section">
<div class="table cbi-section-table">
<div class="tr cbi-section-table-titles">
<div class="th left"><%:Uplink SSID%></div>
<div class="th left"><%:Uplink BSSID%></div>
<div class="th left"><%:Encryption%></div>
<div class="th left"><%:Signal strength%></div>
<div class="th center"><%:Action%></div>
</div>
<%- for i, net in ipairs(iw.scanlist or { }) do -%>
<div class="tr cbi-section-table-row cbi-rowstyle-1">
<div class="td left" style="text-align: left !important">
<%=net.ssid and xml.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%>
</div>
<div class="td left" style="text-align: left !important">
<%=net.bssid and xml.pcdata(net.bssid)%>
</div>
<div class="td left" style="text-align: left !important">
<%=net.encryption.description%>
</div>
<div class="td left" style="text-align: left !important">
<%=percent_wifi_signal(net)%> %
</div>
<div class="td cbi-section-actions">
<form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiadd')%>" method="post">
<input type="hidden" name="token" value="<%=token%>"/>
<input type="hidden" name="device" value="<%=xml.pcdata(dev)%>"/>
<input type="hidden" name="ssid" value="<%=xml.pcdata(net.ssid)%>"/>
<input type="hidden" name="bssid" value="<%=xml.pcdata(net.bssid)%>"/>
<input type="hidden" name="description" value="<%=net.encryption.description%>"/>
<input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>"/>
<%- if net.encryption.wpa then -%>
<input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>"/>
<%- for _, v in ipairs(net.encryption.auth_suites) do -%>
<input type="hidden" name="wpa_suites" value="<%=v%>"/>
<%- end -%>
<%- end -%>
<input class="cbi-button cbi-button-apply" type="submit" value="<%:Add Uplink%>"/>
</form>
</div>
</div>
<%- end -%>
</div>
</div>
<div class="cbi-page-actions right">
<form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/stations')%>" method="get">
<input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/>
</form>
<form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
<input type="hidden" name="token" value="<%=token%>"/>
<input type="hidden" name="device" value="<%=xml.pcdata(dev)%>"/>
<input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>"/>
</form>
</div>
</div>
<%+footer%>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
{
"admin/services/travelmate": {
"title": "Travelmate",
"order": "60",
"action": {
"type": "alias",
"path": "admin/services/travelmate/overview"
},
"depends": {
"acl": [ "luci-app-travelmate" ],
"fs": {
"/usr/bin/travelmate.sh": "executable",
"/etc/init.d/travelmate": "executable"
},
"uci": { "travelmate": true }
}
},
"admin/services/travelmate/overview": {
"title": "Overview",
"order": 10,
"action": {
"type": "view",
"path": "travelmate/overview"
}
},
"admin/services/travelmate/stations": {
"title": "Wireless Stations",
"order": 20,
"action": {
"type": "view",
"path": "travelmate/stations"
}
},
"admin/services/travelmate/logread": {
"title": "Log View",
"order": 30,
"action": {
"type": "view",
"path": "travelmate/logread"
}
}
}

View file

@ -1,11 +1,28 @@
{
"luci-app-travelmate": {
"description": "Grant UCI access for luci-app-travelmate",
"read": {
"description": "Grant access to LuCI app travelmate",
"write": {
"file": {
"/var/run/travelmate.refresh": [ "write" ]
},
"uci": [ "travelmate" ]
},
"write": {
"uci": [ "travelmate" ]
"read": {
"cgi-io": [ "exec" ],
"file": {
"/etc/travelmate/*.login": [ "list" ],
"/var/run/travelmate.pid": [ "read" ],
"/var/run/travelmate.refresh": [ "read" ],
"/tmp/trm_runtime.json": [ "read" ],
"/sbin/logread -e trm-": [ "exec" ],
"/usr/sbin/logread -e trm-": [ "exec" ],
"/etc/init.d/travelmate reload" : [ "exec" ],
"/etc/init.d/travelmate restart" : [ "exec" ],
"/etc/init.d/travelmate setup [0-9a-z_]* [0-9a-z_]* [0-9]*" : [ "exec" ],
"/etc/init.d/travelmate scan radio[0-9]" : [ "exec" ],
"/usr/bin/qrencode --inline --8bit --type=SVG --output=- *" : [ "exec" ]
},
"uci": [ "travelmate", "wireless" ]
}
}
}