luci-app-travelmate: release 2.4.5-1
authorDirk Brenken <redacted>
Sat, 9 May 2026 19:42:22 +0000 (21:42 +0200)
committerDirk Brenken <redacted>
Sat, 9 May 2026 19:42:22 +0000 (21:42 +0200)
* sync with base package

Signed-off-by: Dirk Brenken <redacted>
applications/luci-app-travelmate/Makefile
applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/logtemplate.js
applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/overview.js
applications/luci-app-travelmate/htdocs/luci-static/resources/view/travelmate/stations.js
applications/luci-app-travelmate/root/usr/share/rpcd/acl.d/luci-app-travelmate.json

index 93bbf4482019a95dd973d914b7aa2657ca1d6b7e..3ea3295c79c4729e4242eb1a0b228e9bb91c2eec 100644 (file)
@@ -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 <dev@brenken.org>
 
index c26aa23bbdff91d61c2cd09f7c91d17a58e070f6..2026fffe8ffefd0f04accd3d04748fccb2cb40bf 100644 (file)
@@ -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'
-                                               });
-                                               const time = d.toLocaleTimeString([], {
-                                                       hour: '2-digit',
-                                                       minute: '2-digit',
-                                                       second: '2-digit',
-                                                       hour12: false
+                                               .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}`;
                                                });
-                                               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
index cc6b1332393a7f57d8919fd47244e0e1e4f9c7f8..7f454f85866209e80aa79ca2c693495c65bdf160 100644 (file)
@@ -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");
-                                                               }
+                       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();
+                                                       }
+                                               }
+                                               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");
                                                        }
-                                                       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);
+                                               } 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");
                                                }
-                                       });
-                               } 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.'));
index f92b8ae4f3173f7e3762bd6c4f2479bf2f98cd27..337bb193a4787191188f210d101096ce6915686e 100644 (file)
@@ -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 <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';
@@ -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
index 2642282dfd0247aeb5aba5833199e6c1e25e797f..d47114b17ebe42163b17e2697a8b4ce54eeffef6 100644 (file)
@@ -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
git clone https://git.99rst.org/PROJECT