diff --git a/applications/luci-app-travelmate/Makefile b/applications/luci-app-travelmate/Makefile index 93bbf44820..3ea3295c79 100644 --- a/applications/luci-app-travelmate/Makefile +++ b/applications/luci-app-travelmate/Makefile @@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for Travelmate LUCI_DEPENDS:=+luci-base +luci-lib-uqr +travelmate -PKG_VERSION:=2.4.0 -PKG_RELEASE:=2 +PKG_VERSION:=2.4.5 +PKG_RELEASE:=1 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Dirk Brenken diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logtemplate.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logtemplate.js index c26aa23bbd..2026fffe8f 100644 --- a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logtemplate.js +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logtemplate.js @@ -12,37 +12,29 @@ function Logview(logtag, name) { return L.view.extend({ load: () => Promise.resolve(), - render: () => { - L.Poll.add(() => { + render: function () { + const pollFn = () => { return callLogRead(1000, false, true).then(res => { const logEl = document.getElementById('logfile'); if (!logEl) return; - const filtered = (res?.log ?? []) - .filter(entry => !logtag || entry.msg.includes(logtag)) - .map(entry => { - const d = new Date(entry.time); - const date = d.toLocaleDateString([], { - year: 'numeric', - month: '2-digit', - day: '2-digit' + .filter(entry => !logtag || entry.msg.includes(logtag)) + .map(entry => { + const d = new Date(entry.time); + const pad = n => String(n).padStart(2, '0'); + const date = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()}`; + const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; + return `[${date}-${time}] ${entry.msg}`; }); - const time = d.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false - }); - return `[${date}-${time}] ${entry.msg}`; - }); - if (filtered.length > 0) { - logEl.value = filtered.join('\n'); - } else { - logEl.value = _('No %s related logs yet!').format(name); - } + logEl.value = filtered.length > 0 + ? filtered.join('\n') + : _('No %s related logs yet!').format(name); logEl.scrollTop = logEl.scrollHeight; }); - }); + }; + + this._pollFn = pollFn; + L.Poll.add(pollFn); return E('div', { class: 'cbi-map' }, [ E('div', { class: 'cbi-section' }, [ @@ -58,6 +50,13 @@ function Logview(logtag, name) { ]); }, + unload: function () { + if (this._pollFn) { + L.Poll.remove(this._pollFn); + this._pollFn = null; + } + }, + handleSaveApply: null, handleSave: null, handleReset: null diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js index cc6b133239..7f454f8586 100644 --- a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js @@ -192,50 +192,78 @@ return view.extend({ /* poll runtime information */ + let parseErrCount = 0; poll.add(function () { - return L.resolveDefault(fs.stat('/tmp/trm_runtime.json'), null).then(function (res) { - const status = document.getElementById('status'); - if (res && res.size > 0) { - L.resolveDefault(fs.read_direct('/tmp/trm_runtime.json'), null).then(function (res) { - if (res) { - let info = JSON.parse(res); - if (status && info) { - status.textContent = `${info.data.travelmate_status || '-'} (frontend: ${info.data.frontend_ver || '-'} / backend: ${info.data.backend_ver || '-'})`; - 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"); - } - } - if (info) { - setText('station_id', info.data.station_id); - setText('station_mac', info.data.station_mac); - setText('station_interfaces', info.data.station_interfaces); - setText('station_subnet', info.data.station_subnet); - setText('run_flags', info.data.run_flags); - setText('ext_hooks', info.data.ext_hooks); - setText('run', info.data.last_run); - setText('sys', info.data.system); + return L.resolveDefault(fs.stat('/var/run/travelmate/travelmate.runtime.json'), null).then(function (res) { + if (!res) { + return; + } + return L.resolveDefault(fs.read_direct('/var/run/travelmate/travelmate.runtime.json'), null).then(function (res) { + const status = document.getElementById('status'); + const buttons = document.querySelectorAll('.cbi-page-actions button'); + let info = null; + try { + info = JSON.parse(res); + parseErrCount = 0; + if (!poll.active()) { + poll.start(); + } + } catch (e) { + info = null; + parseErrCount++; + if (status) { + status.textContent = '-'; + buttons.forEach(function (btn) { + btn.disabled = false; + }); + status.classList.remove('spinning'); + if (parseErrCount >= 5) { + ui.addNotification(null, E('p', _('Unable to parse the travelmate runtime information!')), 'error'); + poll.stop(); } } - }); - } else if (status) { - status.textContent = '-'; - if (status.classList.contains("spinning")) { - status.classList.remove("spinning"); + return; } - } + if (status && info) { + status.textContent = `${info.data.travelmate_status || '-'} (frontend: ${info.data.frontend_ver || '-'} / backend: ${info.data.backend_ver || '-'})`; + if (info.data.travelmate_status === 'processing') { + buttons.forEach(function (btn) { + btn.disabled = true; + btn.blur(); + }); + if (!status.classList.contains("spinning")) { + status.classList.add("spinning"); + } + } else { + if (status.classList.contains("spinning")) { + status.classList.remove("spinning"); + } + buttons.forEach(function (btn) { + btn.disabled = false; + }); + } + } else if (status) { + status.textContent = '-'; + if (status.classList.contains("spinning")) { + status.classList.remove("spinning"); + } + buttons.forEach(function (btn) { + btn.disabled = false; + }); + } + if (info) { + setText('station_id', info.data.station_id); + setText('station_mac', info.data.station_mac); + setText('station_interfaces', info.data.station_interfaces); + setText('station_subnet', info.data.station_subnet); + setText('run_flags', info.data.run_flags); + setText('ext_hooks', info.data.ext_hooks); + setText('run', info.data.last_run); + setText('sys', info.data.system); + } + }); }); - }, 1); + }, 2); /* runtime information and buttons @@ -357,6 +385,10 @@ return view.extend({ o.default = 0; o.rmempty = false; + o = s.taboption('general', form.Flag, 'trm_eviltwin', _('Evil Twin Protection'), _('Detect and skip access points with locally administered (LAA) BSSIDs to mitigate evil twin attacks.')); + o.default = 0; + 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; @@ -400,9 +432,9 @@ return view.extend({ 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 = s.taboption('additional', form.Value, 'trm_maxretry', _('Connection Limit'), _('Retry limit to connect to an uplink. Use \'0\' for unlimited retries.')); o.placeholder = '3'; - o.datatype = 'range(1,10)'; + o.datatype = 'range(0,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.')); diff --git a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js index f92b8ae4f3..337bb193a4 100644 --- a/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js +++ b/applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js @@ -121,8 +121,6 @@ function handleSectionsAdd(iface) { 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'); if (vpn_stdservice && vpn_stdiface) { uci.set('travelmate', sid, 'vpn', '1'); uci.set('travelmate', sid, 'vpnservice', vpn_stdservice); @@ -136,7 +134,7 @@ function handleSectionsAdd(iface) { update travelmate sections */ function handleSectionsVal(action, section_id, option, value) { - let date, oldValue, w_device, w_ssid, w_bssid, t_sections; + let w_device, w_ssid, w_bssid, t_sections; w_device = uci.get('wireless', section_id, 'device'); w_ssid = uci.get('wireless', section_id, 'ssid'); @@ -148,15 +146,6 @@ function handleSectionsVal(action, section_id, option, value) { 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().substring(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); @@ -169,11 +158,12 @@ function handleSectionsVal(action, section_id, option, value) { update travelmate status */ function handleStatus() { + let parseErrCount = 0; poll.add(function () { - L.resolveDefault(fs.stat('/var/state/travelmate.refresh'), null).then(function (res) { + L.resolveDefault(fs.stat('/var/run/travelmate/travelmate.refresh'), null).then(function (res) { if (res) { - return L.resolveDefault(fs.read_direct('/var/state/travelmate.refresh'), null).then(async function (res) { - fs.remove('/var/state/travelmate.refresh'); + return L.resolveDefault(fs.read_direct('/var/run/travelmate/travelmate.refresh'), null).then(async function (res) { + fs.remove('/var/run/travelmate/travelmate.refresh'); if (res && res === 'ui_reload') { location.reload(); } else if (res && res === 'cfg_reload') { @@ -196,17 +186,29 @@ function handleStatus() { }); } }); - return L.resolveDefault(fs.stat('/tmp/trm_runtime.json'), null).then(function (res) { + return L.resolveDefault(fs.stat('/var/run/travelmate/travelmate.runtime.json'), null).then(function (res) { if (res) { - return L.resolveDefault(fs.read_direct('/tmp/trm_runtime.json'), null).then(function (res) { + return L.resolveDefault(fs.read_direct('/var/run/travelmate/travelmate.runtime.json'), null).then(function (res) { if (res) { - let info = JSON.parse(res); + let info = null; + try { + info = JSON.parse(res); + parseErrCount = 0; + } catch (e) { + parseErrCount++; + if (parseErrCount >= 5) { + ui.addNotification(null, E('p', _('Unable to parse the travelmate runtime information!')), 'error'); + poll.stop(); + } + return; + } if (info) { + const vpnMatch = (info.data.ext_hooks || '').match(/vpn:\s*(.)/); let t_device, t_ssid, t_bssid, newUplinkView, uplinkColor, uplinkId = info.data.station_id.trim().split('/'), oldUplinkView = document.getElementsByName('uplinkStation'), w_sections = uci.sections('wireless', 'wifi-iface'), - vpnStatus = info.data.ext_hooks.substr(13, 1); + vpnStatus = vpnMatch ? vpnMatch[1] : '✘'; t_device = uplinkId[0]; t_bssid = uplinkId[uplinkId.length - 1]; for (let i = 1; i < uplinkId.length - 1; i++) { @@ -552,28 +554,6 @@ return view.extend({ 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.Flag, '_opensta', _('Auto Added Open Uplink'), _('This option is selected by default if this uplink was added automatically and counts as \'Open Uplink\'.')); o.rmempty = true; @@ -618,42 +598,6 @@ return view.extend({ return handleSectionsVal('set', section_id, 'macaddr', value); } - o = s.taboption('travelmate', form.Value, '_con_start_expiry', _('Connection Start Expiry'), - _('Automatically disable the uplink after n minutes, e.g. for timed connections.
\ - The default of \'0\' disables this feature.')); - o.modalonly = true; - o.uciconfig = 'travelmate'; - o.ucisection = 'uplink'; - o.ucioption = 'con_start_expiry'; - o.rmempty = false; - o.placeholder = '0'; - o.default = '0'; - o.datatype = 'range(0,720)'; - o.cfgvalue = function (section_id) { - return handleSectionsVal('get', section_id, 'con_start_expiry'); - } - o.write = function (section_id, value) { - return handleSectionsVal('set', section_id, 'con_start_expiry', value); - } - - o = s.taboption('travelmate', form.Value, '_con_end_expiry', _('Connection End Expiry'), - _('Automatically (re-)enable the uplink after n minutes, e.g. after failed login attempts.
\ - The default of \'0\' disables this feature.')); - o.modalonly = true; - o.uciconfig = 'travelmate'; - o.ucisection = 'uplink'; - o.ucioption = 'con_end_expiry'; - o.rmempty = false; - o.placeholder = '0'; - o.default = '0'; - o.datatype = 'range(0,720)'; - o.cfgvalue = function (section_id) { - return handleSectionsVal('get', section_id, 'con_end_expiry'); - } - o.write = function (section_id, value) { - return handleSectionsVal('set', section_id, 'con_end_expiry', value); - } - o = s.taboption('travelmate', form.FileUpload, '_script', _('Auto Login Script'), _('External script reference which will be called for automated captive portal logins.')); o.root_directory = '/etc/travelmate'; @@ -823,7 +767,7 @@ return view.extend({ return L.resolveDefault(fs.exec_direct('/etc/init.d/travelmate', ['scan', radio])) .then(L.bind(function () { - return L.resolveDefault(fs.read_direct('/var/run/travelmate.scan'), '') + return L.resolveDefault(fs.read_direct('/var/run/travelmate/travelmate.scan'), '') .then(L.bind(function (res) { let lines, strength, channel, bssid, wpa, cipher, auth, tbl_ssid, ssid, rows = []; @@ -1213,4 +1157,4 @@ return view.extend({ return m.render(); }, handleReset: null -}); +}); \ No newline at end of file diff --git a/applications/luci-app-travelmate/root/usr/share/rpcd/acl.d/luci-app-travelmate.json b/applications/luci-app-travelmate/root/usr/share/rpcd/acl.d/luci-app-travelmate.json index 2642282dfd..d47114b17e 100644 --- a/applications/luci-app-travelmate/root/usr/share/rpcd/acl.d/luci-app-travelmate.json +++ b/applications/luci-app-travelmate/root/usr/share/rpcd/acl.d/luci-app-travelmate.json @@ -3,27 +3,60 @@ "description": "Grant access to LuCI app travelmate", "write": { "file": { - "/var/state/travelmate.refresh": [ "write" ] + "/var/run/travelmate/travelmate.refresh": [ + "write" + ] }, - "uci": [ "travelmate" ] + "uci": [ + "travelmate" + ] }, "read": { - "cgi-io": [ "exec" ], + "cgi-io": [ + "exec" + ], "file": { - "/etc/travelmate/*.login": [ "list" ], - "/var/run/travelmate.pid": [ "read" ], - "/var/run/travelmate.scan": [ "read" ], - "/var/state/travelmate.refresh": [ "read" ], - "/tmp/trm_runtime.json": [ "read" ], - "/sbin/ifup *": [ "exec" ], - "/etc/init.d/travelmate start" : [ "exec" ], - "/etc/init.d/travelmate restart" : [ "exec" ], - "/etc/init.d/travelmate stop" : [ "exec" ], - "/etc/init.d/travelmate setup [0-9a-z_]* [0-9a-z_]* [0-9]*" : [ "exec" ], - "/etc/init.d/travelmate scan radio[0-9]" : [ "exec" ] + "/etc/travelmate/*.login": [ + "list" + ], + "/var/run/travelmate/travelmate.pid": [ + "read" + ], + "/var/run/travelmate/travelmate.scan": [ + "read" + ], + "/var/run/travelmate/travelmate.refresh": [ + "read" + ], + "/var/run/travelmate/travelmate.runtime.json": [ + "read" + ], + "/sbin/ifup *": [ + "exec" + ], + "/etc/init.d/travelmate start": [ + "exec" + ], + "/etc/init.d/travelmate restart": [ + "exec" + ], + "/etc/init.d/travelmate stop": [ + "exec" + ], + "/etc/init.d/travelmate setup *": [ + "exec" + ], + "/etc/init.d/travelmate scan *": [ + "exec" + ] }, - "uci": [ "travelmate", "wireless" ], - "log": [ "read" ] + "uci": [ + "travelmate", + "wireless" + ], + "log": [ + "read" + ] } } -} +} \ No newline at end of file