mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 20:42:52 +08:00
🦄 Sync 2026-09-17 23:34:47
This commit is contained in:
1 parent
fdb7b42e2b
commit
ff5040d573
29 files changed
+1816
-1317
No files matched your search
+1
-1
@@ -14,7 +14,7 @@ PKG_SOURCE_URL:=https://github.com/Ysurac/dsvpn.git
|
||||
PKG_SOURCE_VERSION:=aae011339bba733d3fa4ffe8b5dd95dab2efb4ab
|
||||
PKG_NAME:=dsvpn
|
||||
PKG_VERSION:=0.1.5
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
|
||||
+15
-1
@@ -1,6 +1,16 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter project
|
||||
|
||||
# network.globals.multipath='disable' means the kernel is running with
|
||||
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
|
||||
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
|
||||
# fallback to plain TCP, every caller has to do it itself, and most don't.
|
||||
# Only an explicit "disable" counts, so this package keeps its stock
|
||||
# behaviour wherever network.globals.multipath doesn't exist at all.
|
||||
_omr_mptcp_globally_disabled() {
|
||||
[ "$(uci -q get network.globals.multipath)" = "disable" ]
|
||||
}
|
||||
|
||||
START=90
|
||||
STOP=10
|
||||
|
||||
@@ -75,7 +85,11 @@ start_instance() {
|
||||
|
||||
_log "starting ${PROG_NAME} ${1} instance $*"
|
||||
|
||||
if version_over_5_4; then
|
||||
# mptcpize LD_PRELOADs a socket() that always asks for IPPROTO_MPTCP and
|
||||
# never falls back, so with MPTCP globally off dsvpn cannot create its
|
||||
# socket at all ("Couldn't create connection: Protocol not available")
|
||||
# and procd just respawns it forever. Run it unwrapped instead.
|
||||
if version_over_5_4 && ! _omr_mptcp_globally_disabled; then
|
||||
PROG="mptcpize run ${PROG}"
|
||||
fi
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=glorytun
|
||||
PKG_RELEASE:=4
|
||||
PKG_RELEASE:=5
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=95fa5ead2154adb546bcdb454c56c876e9f60bb5
|
||||
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
# Copyright (C) 2015 ovh.com
|
||||
# Copyright (C) 2017 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||
|
||||
# network.globals.multipath='disable' means the kernel is running with
|
||||
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
|
||||
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
|
||||
# fallback to plain TCP, every caller has to do it itself, and most don't.
|
||||
# Only an explicit "disable" counts, so this package keeps its stock
|
||||
# behaviour wherever network.globals.multipath doesn't exist at all.
|
||||
_omr_mptcp_globally_disabled() {
|
||||
[ "$(uci -q get network.globals.multipath)" = "disable" ]
|
||||
}
|
||||
|
||||
START=90
|
||||
STOP=10
|
||||
|
||||
@@ -81,6 +91,10 @@ start_instance() {
|
||||
${dev:+dev "$dev"}
|
||||
|
||||
[ "${mode}" = "listener" ] && procd_append_param command listener
|
||||
# Glorytun asks the kernel for an MPTCP socket when told "mptcp"; with
|
||||
# MPTCP globally off that socket cannot be created, so drop the argument
|
||||
# and let it run over plain TCP instead of failing to connect at all.
|
||||
[ "${mptcp}" = "1" ] && _omr_mptcp_globally_disabled && mptcp=0
|
||||
[ "${mptcp}" = "1" ] && procd_append_param command mptcp
|
||||
[ "${chacha20}" = "1" ] && procd_append_param command chacha20
|
||||
[ "${multiqueue}" = "1" ] && procd_append_param command multiqueue
|
||||
|
||||
@@ -11,8 +11,8 @@ LUCI_DESCRIPTION:=Live firewall log table (nftables/fw4 and iptables LOG) with f
|
||||
LUCI_DEPENDS:=+luci-base +logd +jsonfilter
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.42
|
||||
PKG_RELEASE:=22
|
||||
PKG_VERSION:=0.1.43
|
||||
PKG_RELEASE:=23
|
||||
|
||||
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
|
||||
@@ -19,6 +19,7 @@ LuCI **Firewall Live View** — client-side JS view polling `ubus fwlive poll` (
|
||||
| `htdocs/luci-static/resources/fwlive/buffer.js` | Ring-buffer apply/merge helpers (pause ingest + resume merge) |
|
||||
| `htdocs/luci-static/resources/fwlive/poll-coordinator.js` | Request coalescing, visibility, cadence, and disposal |
|
||||
| `htdocs/luci-static/resources/fwlive/render-policy.js` | Pure weak-device display-cap and render-cost decisions |
|
||||
| `htdocs/luci-static/resources/fwlive/render-scheduler.js` | Epoch-safe frame coalescing and render token-bucket state |
|
||||
| `htdocs/luci-static/resources/fwlive/hostname.js` | Hostname cache LRU + failure TTL helpers |
|
||||
| `root/usr/share/luci/menu.d/*.json` | Menu entry (`admin/status/fwlive`) |
|
||||
| `root/usr/share/rpcd/acl.d/*.json` | ubus ACL (read + write for logging enable/disable) |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
return baseclass.extend({
|
||||
/* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */
|
||||
APP_VERSION: '0.1.42',
|
||||
APP_VERSION: '0.1.43',
|
||||
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
|
||||
DEFAULT_ROW_LIMIT: 100,
|
||||
FETCH_MODE_OPTIONS: ['auto', 'manual'],
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
/* Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com> */
|
||||
|
||||
'require baseclass';
|
||||
|
||||
/* Own queued render intent and the token bucket; the view owns DOM work.
|
||||
* Adapters supply the epoch, paint callback, cost policy, clock and frame APIs.
|
||||
* Without a frame API, schedule() paints synchronously. */
|
||||
function createScheduler(options) {
|
||||
const { getEpoch, render, renderCost, capacity, requestFrame, cancelFrame } = options;
|
||||
if (!Number.isFinite(capacity) || capacity <= 0)
|
||||
throw new TypeError('capacity must be a positive number');
|
||||
const now = options.now || Date.now;
|
||||
|
||||
let pending = null;
|
||||
let nextForce = null;
|
||||
let frameId = null;
|
||||
let bucket = capacity;
|
||||
let bucketMs = null;
|
||||
let floodSuppressed = false;
|
||||
let lastRenderedRowCount = 0;
|
||||
let lastRenderedHeadId = '';
|
||||
let disposed = false;
|
||||
|
||||
function schedule(force) {
|
||||
if (disposed) return;
|
||||
force = !!force || nextForce !== null;
|
||||
nextForce = null;
|
||||
const epoch = getEpoch();
|
||||
if (typeof requestFrame !== 'function') {
|
||||
render(!!force);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
/* Retain display changes (e.g. resolved names) even when a new epoch
|
||||
* replaces the frame. The callback always reads current view state. */
|
||||
pending.force = pending.force || !!force;
|
||||
pending.epoch = epoch;
|
||||
return;
|
||||
}
|
||||
|
||||
pending = { epoch: epoch, force: !!force };
|
||||
frameId = requestFrame(function () {
|
||||
const request = pending;
|
||||
pending = null;
|
||||
frameId = null;
|
||||
if (disposed || request.epoch !== getEpoch()) return;
|
||||
|
||||
/* A current request queued behind stale work gets its own frame. */
|
||||
if (epoch !== request.epoch) schedule(request.force);
|
||||
else render(request.force);
|
||||
});
|
||||
}
|
||||
|
||||
/* A successful decision reserves tokens, but only markRendered commits
|
||||
* row identity after the view has actually painted. Forced paints bypass
|
||||
* the bucket without clearing an existing high-rate warning. */
|
||||
function shouldRender(rows, force, lastBatchNewIdCount) {
|
||||
if (disposed) return false;
|
||||
if (force) return true;
|
||||
|
||||
const cost = renderCost({
|
||||
visibleRowCount: rows.length,
|
||||
visibleHeadId: rows.length ? rows[0].id : '',
|
||||
lastRenderedRowCount: lastRenderedRowCount,
|
||||
lastRenderedHeadId: lastRenderedHeadId,
|
||||
lastBatchNewIdCount: lastBatchNewIdCount
|
||||
});
|
||||
if (cost <= 0) {
|
||||
floodSuppressed = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const timestamp = now();
|
||||
const elapsed = bucketMs === null ? 0 : Math.max(0, timestamp - bucketMs);
|
||||
bucketMs = timestamp;
|
||||
bucket = Math.min(capacity, bucket + (elapsed * capacity) / 1000);
|
||||
floodSuppressed = cost > bucket;
|
||||
if (floodSuppressed) return false;
|
||||
bucket -= cost;
|
||||
return true;
|
||||
}
|
||||
|
||||
function markRendered(rows) {
|
||||
lastRenderedRowCount = rows.length;
|
||||
lastRenderedHeadId = rows.length ? rows[0].id : '';
|
||||
}
|
||||
|
||||
function resetBudget() {
|
||||
bucket = capacity;
|
||||
bucketMs = now();
|
||||
floodSuppressed = false;
|
||||
}
|
||||
|
||||
/* Reserve force for the next scheduled render. The caller can cancel its
|
||||
* own unused reservation, never a queued frame or a newer reservation. */
|
||||
function forceNextRender() {
|
||||
const reservation = {};
|
||||
if (!disposed) nextForce = reservation;
|
||||
return function () {
|
||||
if (nextForce === reservation) nextForce = null;
|
||||
};
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
pending = null;
|
||||
nextForce = null;
|
||||
if (frameId !== null && typeof cancelFrame === 'function') cancelFrame(frameId);
|
||||
frameId = null;
|
||||
}
|
||||
|
||||
return {
|
||||
schedule: schedule,
|
||||
shouldRender: shouldRender,
|
||||
markRendered: markRendered,
|
||||
resetBudget: resetBudget,
|
||||
forceNextRender: forceNextRender,
|
||||
isFloodSuppressed: function () {
|
||||
return floodSuppressed;
|
||||
},
|
||||
dispose: dispose
|
||||
};
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
create: createScheduler
|
||||
});
|
||||
@@ -21,6 +21,7 @@
|
||||
'require fwlive.proto as proto';
|
||||
'require fwlive.poll-coordinator as pollCoordinator';
|
||||
'require fwlive.render-policy as renderPolicy';
|
||||
'require fwlive.render-scheduler as renderScheduler';
|
||||
|
||||
const callFwlivePoll = rpc.declare({
|
||||
object: 'fwlive',
|
||||
@@ -108,6 +109,7 @@ return view.extend({
|
||||
/* One-shot: first live poll after unpause merges instead of replacing. */
|
||||
resumeMerge: false,
|
||||
pollCoordinator: null,
|
||||
renderScheduler: null,
|
||||
pagehideHandler: null,
|
||||
/* Layer 2 — visibility / RTT cadence / shed surfacing. */
|
||||
rttStreakKind: null,
|
||||
@@ -126,14 +128,8 @@ return view.extend({
|
||||
summaryRowsShown: false,
|
||||
summaryData: null,
|
||||
resolveLoadShed: false,
|
||||
renderRaf: 0,
|
||||
filterInputTimer: null,
|
||||
messageLayout: 'wrap',
|
||||
renderBucket: constants.RENDER_CAP_PER_SEC,
|
||||
renderBucketMs: 0,
|
||||
floodSuppressed: false,
|
||||
pendingForceRender: false,
|
||||
pendingRenderEpoch: null,
|
||||
/* Session-new IDs from the last applied batch; this is not buffer growth. */
|
||||
lastBatchNewIdCount: 0,
|
||||
showHostnames: false,
|
||||
@@ -146,8 +142,6 @@ return view.extend({
|
||||
resolveGeneration: 0,
|
||||
lastPollError: false,
|
||||
lastRulesError: null,
|
||||
lastRenderedRowCount: 0,
|
||||
lastRenderedHeadId: '',
|
||||
followLive: true,
|
||||
rulesMap: {},
|
||||
firewallBackend: 'nft',
|
||||
@@ -1002,7 +996,8 @@ return view.extend({
|
||||
|
||||
const cap = this.ingestCap();
|
||||
if (this.entries.length >= cap && cap > 0) bits.push(_('buffer full'));
|
||||
if (this.floodSuppressed) bits.push(_('render paused (high rate)'));
|
||||
if (this.ensureRenderScheduler().isFloodSuppressed())
|
||||
bits.push(_('render paused (high rate)'));
|
||||
if (this.weakDevice && this.rowLimit > constants.WEAK_DEVICE_DISPLAY_ROW_CAP)
|
||||
bits.push(
|
||||
_('Display limited to %d rows on this device').format(
|
||||
@@ -1072,6 +1067,26 @@ return view.extend({
|
||||
return this.pollCoordinator;
|
||||
},
|
||||
|
||||
ensureRenderScheduler() {
|
||||
if (this.renderScheduler) return this.renderScheduler;
|
||||
this.renderScheduler = renderScheduler.create({
|
||||
getEpoch: () => this.currentPollEpoch(),
|
||||
render: (force) => this.renderRows(force),
|
||||
renderCost: renderPolicy.renderCost,
|
||||
now: () => this.nowMs(),
|
||||
capacity: constants.RENDER_CAP_PER_SEC,
|
||||
requestFrame:
|
||||
typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'
|
||||
? (cb) => window.requestAnimationFrame(cb)
|
||||
: null,
|
||||
cancelFrame:
|
||||
typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function'
|
||||
? (id) => window.cancelAnimationFrame(id)
|
||||
: null
|
||||
});
|
||||
return this.renderScheduler;
|
||||
},
|
||||
|
||||
currentPollEpoch() {
|
||||
return this.ensurePollCoordinator().getState().epoch;
|
||||
},
|
||||
@@ -1112,6 +1127,7 @@ return view.extend({
|
||||
|
||||
disposeView() {
|
||||
if (this.pollCoordinator) this.pollCoordinator.dispose();
|
||||
if (this.renderScheduler) this.renderScheduler.dispose();
|
||||
this.resolveGeneration = (this.resolveGeneration || 0) + 1;
|
||||
this.resolveInFlight = false;
|
||||
if (this.pagehideHandler) {
|
||||
@@ -1283,76 +1299,7 @@ return view.extend({
|
||||
},
|
||||
|
||||
scheduleRenderRows(force) {
|
||||
const doForce = !!force;
|
||||
const epoch = this.currentPollEpoch();
|
||||
if (typeof requestAnimationFrame !== 'function') {
|
||||
this.renderRows(doForce);
|
||||
return;
|
||||
}
|
||||
if (this.renderRaf) {
|
||||
this.pendingForceRender = this.pendingForceRender || doForce;
|
||||
this.pendingRenderEpoch = epoch;
|
||||
return;
|
||||
}
|
||||
this.renderRaf = requestAnimationFrame(
|
||||
function () {
|
||||
this.renderRaf = 0;
|
||||
const f = doForce || !!this.pendingForceRender;
|
||||
const pendingEpoch = this.pendingRenderEpoch;
|
||||
this.pendingForceRender = false;
|
||||
this.pendingRenderEpoch = null;
|
||||
if (epoch !== this.currentPollEpoch()) {
|
||||
/* A newer epoch may have requested a render while this frame was
|
||||
* queued. Drop stale paint and requeue only that current request. */
|
||||
if (pendingEpoch === this.currentPollEpoch()) this.scheduleRenderRows(f);
|
||||
return;
|
||||
}
|
||||
this.renderRows(f);
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
refillRenderBucket() {
|
||||
const now = Date.now();
|
||||
if (!this.renderBucketMs) this.renderBucketMs = now;
|
||||
|
||||
const elapsed = now - this.renderBucketMs;
|
||||
this.renderBucketMs = now;
|
||||
this.renderBucket = Math.min(
|
||||
constants.RENDER_CAP_PER_SEC,
|
||||
this.renderBucket + (elapsed * constants.RENDER_CAP_PER_SEC) / 1000
|
||||
);
|
||||
},
|
||||
|
||||
consumeRenderBudget(cost) {
|
||||
if (cost <= 0) {
|
||||
this.floodSuppressed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.refillRenderBucket();
|
||||
if (cost <= this.renderBucket) {
|
||||
this.renderBucket -= cost;
|
||||
this.floodSuppressed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.floodSuppressed = true;
|
||||
return false;
|
||||
},
|
||||
|
||||
/** Charge by new log events per poll, not full table size (avoids false throttle at high limits). */
|
||||
renderBudgetCost(rows) {
|
||||
const count = rows ? rows.length : 0;
|
||||
const headId = count ? rows[0].id : '';
|
||||
|
||||
return renderPolicy.renderCost({
|
||||
visibleRowCount: count,
|
||||
visibleHeadId: headId,
|
||||
lastRenderedRowCount: this.lastRenderedRowCount,
|
||||
lastRenderedHeadId: this.lastRenderedHeadId,
|
||||
lastBatchNewIdCount: this.lastBatchNewIdCount
|
||||
});
|
||||
this.ensureRenderScheduler().schedule(!!force);
|
||||
},
|
||||
|
||||
updateFloodBanner() {
|
||||
@@ -1360,7 +1307,7 @@ return view.extend({
|
||||
if (!el) return;
|
||||
if (!el.style) el.style = { display: '' };
|
||||
|
||||
if (this.floodSuppressed) {
|
||||
if (this.ensureRenderScheduler().isFloodSuppressed()) {
|
||||
el.style.display = 'block';
|
||||
el.textContent = _(
|
||||
'High event rate — table refresh is throttled to protect the browser. The buffer still updates; refresh will resume automatically.'
|
||||
@@ -1547,9 +1494,8 @@ return view.extend({
|
||||
this.saveRowLimit();
|
||||
this.updateHash(this.readFilters());
|
||||
/* Reset flood throttle so Limit changes paint even during ping -A. */
|
||||
this.renderBucket = constants.RENDER_CAP_PER_SEC;
|
||||
this.floodSuppressed = false;
|
||||
this.pendingForceRender = true;
|
||||
this.ensureRenderScheduler().resetBudget();
|
||||
const cancelForce = this.ensureRenderScheduler().forceNextRender();
|
||||
if (!this.tablePaused) this.renderRows(true);
|
||||
else this.updateStatus();
|
||||
const epoch = this.currentPollEpoch();
|
||||
@@ -1560,9 +1506,7 @@ return view.extend({
|
||||
if (this.tablePaused) this.updateStatus();
|
||||
else this.renderRows(true);
|
||||
})
|
||||
.finally(() => {
|
||||
this.pendingForceRender = false;
|
||||
});
|
||||
.finally(cancelForce);
|
||||
},
|
||||
|
||||
limitSelectOptions() {
|
||||
@@ -1825,17 +1769,14 @@ return view.extend({
|
||||
this.updateHash(this.readFilters());
|
||||
|
||||
const rows = this.filteredRows();
|
||||
const cost = force ? Math.max(1, rows.length) : this.renderBudgetCost(rows);
|
||||
const paint = this.ensureRenderScheduler().shouldRender(
|
||||
rows,
|
||||
!!force,
|
||||
this.lastBatchNewIdCount
|
||||
);
|
||||
this.updateLoggingToolbarUi();
|
||||
|
||||
if (!force && cost === 0) {
|
||||
this.floodSuppressed = false;
|
||||
this.updateFloodBanner();
|
||||
this.updateStatus(rows);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force && !this.consumeRenderBudget(cost)) {
|
||||
if (!paint) {
|
||||
this.updateFloodBanner();
|
||||
this.updateStatus(rows);
|
||||
return;
|
||||
@@ -1875,15 +1816,14 @@ return view.extend({
|
||||
else scroll.scrollTop = prevScroll;
|
||||
}
|
||||
|
||||
this.lastRenderedRowCount = rows.length;
|
||||
this.lastRenderedHeadId = rows.length ? rows[0].id : '';
|
||||
this.ensureRenderScheduler().markRendered(rows);
|
||||
|
||||
if (rows.length && this.rowTintEnabled() && !this.tintProbeDone) {
|
||||
const runProbe = () => {
|
||||
if (!this.tintProbeDone) this.probeRowTintPaint();
|
||||
};
|
||||
if (typeof requestAnimationFrame === 'function')
|
||||
requestAnimationFrame(() => requestAnimationFrame(runProbe));
|
||||
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function')
|
||||
window.requestAnimationFrame(() => window.requestAnimationFrame(runProbe));
|
||||
else setTimeout(runProbe, 0);
|
||||
} else if (!this.rowTintEnabled() && this.tintFallbackActive) {
|
||||
this.clearTintFallback(document.querySelector('.fwlive-map'));
|
||||
@@ -1997,7 +1937,7 @@ return view.extend({
|
||||
* and stays behind the explicit Show rows control. */
|
||||
if (this.tablePaused) this.updateStatus();
|
||||
else if (this.summaryMode) this.renderSummary();
|
||||
else this.scheduleRenderRows(!!this.pendingForceRender);
|
||||
else this.scheduleRenderRows();
|
||||
|
||||
try {
|
||||
await this.resolveHostnamesForEntries(this.filteredRows());
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com>
|
||||
#
|
||||
# Layer 1 adaptive poll cap (#306). Sourced by rpcd/fwlive.
|
||||
# Layer 1 adaptive poll cap. Sourced by rpcd/fwlive.
|
||||
# Always on unless test/triage override (no UCI / no product config):
|
||||
# FWLIVE_ADAPTIVE=0|false|off|no
|
||||
# or sentinel ${FWLIVE_ADAPTIVE_OFF_FILE:-<state-dir>/fwlive-adaptive-off}
|
||||
@@ -18,7 +18,7 @@
|
||||
# flock, lock busy, or corrupt state. Lock-busy ⇒ unlocked last-writer-wins is
|
||||
# acceptable (state stays one valid JSON line; ordering is not guaranteed).
|
||||
# Failed ubus log.read must NOT call record() — a ~0 ms failure is not "cold"
|
||||
# health and must not clear an existing hot/shed cap (#329 Hermes Q1).
|
||||
# health and must not clear an existing hot/shed cap.
|
||||
# Outside the measured duration interval: plan (pre), record/merge (post).
|
||||
# messages_received is supplied by the filter-side jsonfilter enumeration;
|
||||
# failed reads still use 0 without scanning the response in ash.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh
|
||||
# source: core/fwlive-log.js CLASSIFY_SPEC
|
||||
# Shared isFirewallEvent parity logic (shell). Sourced by fwlive-log-filter.sh and tests.
|
||||
# Sourced library: do not add set -euo here (callers own strict mode, #291 C3).
|
||||
# Sourced library: do not add set -euo here; callers own strict mode.
|
||||
# One awk process classifies a batch (MODE=json/json_reply) or one message (default).
|
||||
|
||||
# The caller sets FILTER_DIR when this file is sourced. Resolve the asset
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
# Log messages are treated as data (jsonfilter + awk stdin); never interpolated
|
||||
# into shell command strings. Usage: ubus call log read '...' | fwlive-log-filter.sh
|
||||
#
|
||||
# Perf (#219): one jsonfilter for @.log[*] plus one awk classify. The same
|
||||
# Performance: one jsonfilter for @.log[*] plus one awk classify. The same
|
||||
# awk pass counts enumerated entries for messages_received; process count is
|
||||
# constant per poll, not O(entries).
|
||||
#
|
||||
# Entry point (pipeline). The classifier sibling is sourced and must not set
|
||||
# strict mode itself (#291 C3).
|
||||
# strict mode itself.
|
||||
set -eu
|
||||
|
||||
if ! command -v jsonfilter >/dev/null 2>&1; then
|
||||
@@ -25,7 +25,7 @@ FILTER_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
. "$FILTER_DIR/fwlive-is-firewall-event.sh"
|
||||
|
||||
# The classifier is a package asset, not generated at request time. Fail
|
||||
# closed if an incomplete install or a damaged package removed it (#321).
|
||||
# closed if an incomplete install or a damaged package removed it.
|
||||
if [ ! -r "$CLASSIFY_AWK" ]; then
|
||||
printf '%s' '{"log":[],"error":"classifier_missing"}'
|
||||
exit 1
|
||||
@@ -33,9 +33,9 @@ fi
|
||||
|
||||
# Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring
|
||||
# (or paused FETCH_LINES_MAX poll) can exceed that and make jsonfilter fail
|
||||
# while this script still returns a valid empty result (#234).
|
||||
# while this script still returns a valid empty result.
|
||||
# A valid input with no log entries is not fatal; the classifier closes JSON
|
||||
# and reports messages_received:0 (#220). A jsonfilter failure is different:
|
||||
# and reports messages_received:0. A jsonfilter failure is different:
|
||||
# it must remain an error so rpcd does not record a fast healthy sample. Keep
|
||||
# jsonfilter output in a secure temporary file before classification so a
|
||||
# partial pipeline cannot produce malformed JSON on failure.
|
||||
|
||||
@@ -42,7 +42,7 @@ NFT_TIMEOUT=5
|
||||
# CLI-only iptables fixture path for __rulesmap_iptables (no arbitrary paths).
|
||||
RULESMAP_IPTABLES_FILE='/tmp/rulesmap'
|
||||
|
||||
# json_escape lives in fwlive-logging.sh so prerm can source that file alone (#222).
|
||||
# json_escape lives in fwlive-logging.sh so prerm can source that file alone.
|
||||
# shellcheck disable=SC1090,SC1091 # LOGGING_SH resolves to sibling ../fwlive-logging.sh
|
||||
. "$LOGGING_SH"
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
@@ -52,10 +52,10 @@ slug_key() {
|
||||
echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' _' '--'
|
||||
}
|
||||
|
||||
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply (#229).
|
||||
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply.
|
||||
RULES_MAP_MAX_KEYS=512
|
||||
RULES_MAP_MAX_BYTES=65536
|
||||
# Same wall-clock bound as nft for iptables-save / ip6tables-save (#229).
|
||||
# Same wall-clock bound as nft for iptables-save / ip6tables-save.
|
||||
# Overridable for host tests (e.g. FWLIVE_IPTABLES_TIMEOUT=1).
|
||||
IPTABLES_TIMEOUT="${FWLIVE_IPTABLES_TIMEOUT:-5}"
|
||||
|
||||
@@ -70,7 +70,7 @@ map_add() {
|
||||
# ("foo" vs "foobar") and value false positives ("see foo").
|
||||
# Quoting "$esc_key" makes the match literal (no glob escaping needed).
|
||||
# Callers apply UCI, then labeled (!fw4:) prefixes, then unlabeled
|
||||
# cosmetics so first-wins prefers authoritative names (#230).
|
||||
# cosmetics so first-wins prefers authoritative names.
|
||||
case "$OUT" in
|
||||
*"\"$esc_key\":"*) return 0 ;;
|
||||
esac
|
||||
@@ -92,7 +92,7 @@ map_add() {
|
||||
|
||||
uci_rule_names() {
|
||||
# uci missing / empty firewall config is "no names", not fatal.
|
||||
# pipefail + set -e cannot apply to this pipeline (#291 C3).
|
||||
# pipefail + set -e cannot apply to this pipeline.
|
||||
uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p" || true
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ run_with_timeout() {
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout "$secs" "$@" 2>/dev/null
|
||||
else
|
||||
# Fail closed (#229): never run unbounded when timeout is absent.
|
||||
# Fail closed: never run unbounded when timeout is absent.
|
||||
return 127
|
||||
fi
|
||||
}
|
||||
@@ -170,7 +170,7 @@ map_uci_rule_names() {
|
||||
# Line-wise (here-doc, not an unquoted for-loop) so a name with
|
||||
# spaces stays one token. Names that fail is_uci_style_name are
|
||||
# skipped: parseRuleHint() cannot match them, and fragments of
|
||||
# "My Rule" must not shadow a real rule named Rule (#226).
|
||||
# "My Rule" must not shadow a real rule named Rule.
|
||||
# The while-read stays in the current shell (here-doc, not a
|
||||
# pipeline) so map_add mutations reach OUT.
|
||||
while IFS= read -r name || [ -n "$name" ]; do
|
||||
@@ -187,7 +187,7 @@ EOF
|
||||
}
|
||||
|
||||
# pass=labeled|unlabeled|all — labeled before unlabeled so !fw4: beats
|
||||
# an earlier cosmetic for the same prefix under first-wins (#230).
|
||||
# an earlier cosmetic for the same prefix under first-wins.
|
||||
map_from_nft_stream() {
|
||||
pass="${1:-all}"
|
||||
while IFS= read -r line; do
|
||||
@@ -288,7 +288,7 @@ build_rules_map() {
|
||||
backend=$(detect_firewall_backend)
|
||||
|
||||
# UCI first, then labeled stream prefixes, then unlabeled cosmetics
|
||||
# (#230). First-wins keeps UCI over nft and !fw4: over cosmetic.
|
||||
# First-wins keeps UCI over nft and !fw4: over cosmetic.
|
||||
map_uci_rule_names
|
||||
|
||||
case "$backend" in
|
||||
@@ -435,10 +435,10 @@ fetch_firewall_logs() {
|
||||
shed_flag=${2:-0}
|
||||
# Adaptive plan prints: lines shed bucket
|
||||
start_cs=$(fwlive_adaptive_clock_cs)
|
||||
# Drop dead oneshot flag — only consumed on logd's stream branch (#306).
|
||||
# Drop dead oneshot flag — only consumed on logd's stream branch.
|
||||
if ! raw=$(ubus call log read "{\"lines\":$lines,\"stream\":false}" 2>/dev/null); then
|
||||
# Do not record ~0 ms as cold: failure is not healthy fast processing
|
||||
# and must not clear an existing hot/shed cap (#329 Hermes Q1).
|
||||
# and must not clear an existing hot/shed cap.
|
||||
out='{"log":[],"error":"log_read_failed"}'
|
||||
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0 0
|
||||
return 0
|
||||
@@ -446,7 +446,7 @@ fetch_firewall_logs() {
|
||||
|
||||
# Summary aggregation is part of the adaptive contract. Keep it out of
|
||||
# adaptive=0 replies, while the classifier still remains the single pass
|
||||
# over decoded log entries (#306).
|
||||
# over decoded log entries.
|
||||
if fwlive_adaptive_enabled; then
|
||||
FWLIVE_SUMMARY=1
|
||||
else
|
||||
@@ -454,7 +454,7 @@ fetch_firewall_logs() {
|
||||
fi
|
||||
export FWLIVE_SUMMARY
|
||||
|
||||
# Do not swallow a filter failure into a silent empty table (#220).
|
||||
# Do not swallow a filter failure into a silent empty table.
|
||||
# If the filter exits non-zero but still printed JSON, keep it.
|
||||
# Empty success is not valid poll JSON — the shipped filter always
|
||||
# prints {"log":[...]}, but a stub/regression must not emit a blank body.
|
||||
@@ -476,7 +476,7 @@ fetch_firewall_logs() {
|
||||
*'"error":'*) _filter_ok=0 ;;
|
||||
esac
|
||||
end_cs=$(fwlive_adaptive_clock_cs)
|
||||
# Uptime wrap / skew: end < start — do not record as cold (Grok #329 P2).
|
||||
# Uptime wrap / skew: end < start — do not record as cold.
|
||||
if [ "$_filter_ok" = 1 ] && [ "$end_cs" -ge "$start_cs" ]; then
|
||||
_dur=$(((end_cs - start_cs) * 10))
|
||||
fwlive_adaptive_record "$_dur" "$lines"
|
||||
@@ -524,7 +524,7 @@ resolve_hostname() {
|
||||
[ -n "$ip" ] || return 1
|
||||
is_resolvable_address "$ip" || return 1
|
||||
command -v nslookup >/dev/null 2>&1 || return 1
|
||||
# timeout/nslookup miss is "no PTR", not a plugin abort (#291 C3).
|
||||
# timeout/nslookup miss is "no PTR", not a plugin abort.
|
||||
out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip") || return 1
|
||||
[ -n "$out" ] || return 1
|
||||
name=$(parse_nslookup_name "$out") || return 1
|
||||
@@ -532,8 +532,8 @@ resolve_hostname() {
|
||||
printf '%s' "$name"
|
||||
}
|
||||
|
||||
# Strict IPv4/IPv6 validation before nslookup (issue #190): the old char-class
|
||||
# check admitted hostname-shaped tokens of hex chars + dots/colons
|
||||
# Strict IPv4/IPv6 validation before nslookup: the old char-class check
|
||||
# admitted hostname-shaped tokens of hex chars + dots/colons
|
||||
# (e.g. "dead.beef.cafe.baad"), letting the resolver fall back to upstream DNS
|
||||
# queries chosen by an authenticated session. Validate the real address
|
||||
# families here so nslookup only ever sees genuine numeric addresses.
|
||||
@@ -545,8 +545,8 @@ is_resolvable_address() {
|
||||
# Reject anything outside the address alphabet BEFORE awk, so embedded
|
||||
# newlines/whitespace (which awk would treat as record separators) can
|
||||
# never let a valid first record mask trailing garbage that still reaches
|
||||
# nslookup (#190, CodeRabbit/luna fold): nslookup only ever sees a single
|
||||
# token. Note the check intentionally allows only hex/colon/dot.
|
||||
# nslookup. It must see a single token. Note the check intentionally allows
|
||||
# only hex/colon/dot.
|
||||
case "$addr" in
|
||||
*[!0-9a-fA-F:.]*) return 1 ;;
|
||||
esac
|
||||
@@ -618,7 +618,7 @@ is_resolvable_address() {
|
||||
}
|
||||
|
||||
resolve_addresses() {
|
||||
# Missing resolver must not look like "no PTR" (#218). Check before
|
||||
# Missing resolver must not look like "no PTR". Check before
|
||||
# starting the budget clock or reading stdin.
|
||||
if ! command -v nslookup >/dev/null 2>&1; then
|
||||
command -v logger >/dev/null 2>&1 && logger -t fwlive "nslookup not found; resolve disabled"
|
||||
@@ -626,7 +626,7 @@ resolve_addresses() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Load shed: previous poll was hot (#306 Layer 1).
|
||||
# Load shed: previous poll was hot.
|
||||
if fwlive_adaptive_is_hot; then
|
||||
printf '{"names":{},"disabled":"load"}'
|
||||
return 0
|
||||
@@ -679,12 +679,12 @@ resolve_addresses() {
|
||||
# never START a lookup past the budget.
|
||||
now=$(date +%s)
|
||||
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
|
||||
# Missing/empty element: set -u cannot read bare $ip (#291 C3).
|
||||
# Missing/empty element: set -u cannot read bare $ip.
|
||||
if ! is_resolvable_address "${ip:-}"; then
|
||||
continue
|
||||
fi
|
||||
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e
|
||||
# cannot apply to this assignment (#291 C3).
|
||||
# cannot apply to this assignment.
|
||||
name=$(resolve_hostname "$ip") || name=
|
||||
if [ -n "$name" ]; then
|
||||
map_add "$ip" "$name"
|
||||
@@ -751,7 +751,7 @@ run_selftest() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Issue #190: hostname-shaped tokens of hex chars + dots/colons passed the
|
||||
# Hostname-shaped tokens of hex chars + dots/colons passed the
|
||||
# old char-class check and made the resolver fall back to upstream DNS. Each case
|
||||
# below must REJECT (and would be ACCEPTED by the reverted char-class code).
|
||||
if is_resolvable_address 'ab.cd.ef.01'; then
|
||||
@@ -871,7 +871,7 @@ Address: 127.0.0.1:53")
|
||||
|
||||
run_logging_selftest || return 1
|
||||
|
||||
# Strict-mode smoke (#291 C3): status JSON uses soft-fail helpers
|
||||
# Strict-mode smoke: status JSON uses soft-fail helpers
|
||||
# (uci miss, empty WAN zone). Must not abort under dash/ash set -e.
|
||||
_status=$(build_logging_status_json) || {
|
||||
echo "build_logging_status_json: aborted under set -e" >&2
|
||||
@@ -972,7 +972,7 @@ case "${1:-}" in
|
||||
build_rules_map
|
||||
;;
|
||||
poll)
|
||||
# rpcd may omit argv $3 and pass JSON on stdin (#291 C3).
|
||||
# rpcd may omit argv $3 and pass JSON on stdin.
|
||||
poll_logs "${3:-}"
|
||||
;;
|
||||
resolve)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-08-14 08:04+0000\n"
|
||||
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
|
||||
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsiperf/de/>\n"
|
||||
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/de/>\n"
|
||||
"Language: de\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -30,7 +30,7 @@ msgstr "Downstream"
|
||||
|
||||
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
|
||||
msgid "Grant UCI access for luci-app-iperf"
|
||||
msgstr ""
|
||||
msgstr "UCI-Zugriff für luci-app-iperf gewähren"
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
|
||||
msgid "Internet protocol"
|
||||
@@ -60,6 +60,8 @@ msgstr "Server"
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
|
||||
msgid "Server VPS IP is bypassed, so this will test only default route speed."
|
||||
msgstr ""
|
||||
"Die IP des VPS-Servers wird umgangen, daher wird nur die Geschwindigkeit der"
|
||||
" Standardroute getestet."
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
|
||||
msgid "Settings"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-09-21 12:51+0000\n"
|
||||
"Last-Translator: Weblate Admin <contact@openmptcprouter.com>\n"
|
||||
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsiperf/it/>\n"
|
||||
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/it/>\n"
|
||||
"Language: it\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -28,7 +28,7 @@ msgstr "Scarica"
|
||||
|
||||
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
|
||||
msgid "Grant UCI access for luci-app-iperf"
|
||||
msgstr ""
|
||||
msgstr "Concedi l'accesso UCI a luci-app-iperf"
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
|
||||
msgid "Internet protocol"
|
||||
@@ -58,6 +58,8 @@ msgstr "Server"
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
|
||||
msgid "Server VPS IP is bypassed, so this will test only default route speed."
|
||||
msgstr ""
|
||||
"L'IP del server VPS è escluso, quindi verrà testata solo la velocità della "
|
||||
"rotta predefinita."
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
|
||||
msgid "Settings"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-08-21 20:21+0000\n"
|
||||
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
|
||||
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsiperf/oc/>\n"
|
||||
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/oc/>\n"
|
||||
"Language: oc\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -28,7 +28,7 @@ msgstr "Telecargament"
|
||||
|
||||
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
|
||||
msgid "Grant UCI access for luci-app-iperf"
|
||||
msgstr ""
|
||||
msgstr "Acordar l'accès UCI a luci-app-iperf"
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
|
||||
msgid "Internet protocol"
|
||||
@@ -58,6 +58,8 @@ msgstr "Servidor"
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
|
||||
msgid "Server VPS IP is bypassed, so this will test only default route speed."
|
||||
msgstr ""
|
||||
"L'IP del servidor VPS es contornejada, doncas aquò testarà solament la "
|
||||
"velocitat de la rota per defaut."
|
||||
|
||||
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
|
||||
msgid "Settings"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-08-14 08:04+0000\n"
|
||||
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
|
||||
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsmail/de/>\n"
|
||||
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/de/>\n"
|
||||
"Language: de\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -12,7 +12,7 @@ msgstr ""
|
||||
|
||||
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
|
||||
msgid "E-Mail"
|
||||
msgstr ""
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
|
||||
msgid "From"
|
||||
@@ -20,7 +20,7 @@ msgstr "Von"
|
||||
|
||||
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
|
||||
msgid "Grant UCI access for luci-app-mail"
|
||||
msgstr ""
|
||||
msgstr "UCI-Zugriff für luci-app-mail gewähren"
|
||||
|
||||
#: luci-app-mail/luasrc/controller/mail.lua:4
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-12-01 10:49+0000\n"
|
||||
"Last-Translator: Giuseppe Dipierro <giuseppe.dipierro@uania.com>\n"
|
||||
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsmail/it/>\n"
|
||||
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/it/>\n"
|
||||
"Language: it\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -12,7 +12,7 @@ msgstr ""
|
||||
|
||||
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
|
||||
msgid "E-Mail"
|
||||
msgstr ""
|
||||
msgstr "E-mail"
|
||||
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
|
||||
msgid "From"
|
||||
@@ -20,7 +20,7 @@ msgstr "Da"
|
||||
|
||||
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
|
||||
msgid "Grant UCI access for luci-app-mail"
|
||||
msgstr ""
|
||||
msgstr "Concedi l'accesso UCI a luci-app-mail"
|
||||
|
||||
#: luci-app-mail/luasrc/controller/mail.lua:4
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2020-08-21 20:21+0000\n"
|
||||
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
|
||||
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/"
|
||||
"luciapplicationsmail/oc/>\n"
|
||||
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/oc/>\n"
|
||||
"Language: oc\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -12,7 +12,7 @@ msgstr ""
|
||||
|
||||
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
|
||||
msgid "E-Mail"
|
||||
msgstr ""
|
||||
msgstr "Corrièr electronic"
|
||||
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
|
||||
msgid "From"
|
||||
@@ -20,7 +20,7 @@ msgstr "De"
|
||||
|
||||
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
|
||||
msgid "Grant UCI access for luci-app-mail"
|
||||
msgstr ""
|
||||
msgstr "Acordar l'accès UCI a luci-app-mail"
|
||||
|
||||
#: luci-app-mail/luasrc/controller/mail.lua:4
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
|
||||
@@ -50,8 +50,8 @@ msgstr "Servidor"
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
|
||||
msgid "Set mail settings for services that need to send mails."
|
||||
msgstr ""
|
||||
"Definissètz los paramètres de messatjariá pels servicis que devon enviar d’e-"
|
||||
"mails."
|
||||
"Definissètz los paramètres de messatjariá pels servicis que devon enviar "
|
||||
"d’e-mails."
|
||||
|
||||
#: luci-app-mail/luasrc/model/cbi/mail.lua:21
|
||||
msgid "TLS"
|
||||
|
||||
+257
-183
File diff suppressed because it is too large.
Load diff
+260
-186
File diff suppressed because it is too large.
Load diff
+253
-175
File diff suppressed because it is too large.
Load diff
+280
-203
File diff suppressed because it is too large.
Load diff
+272
-203
File diff suppressed because it is too large.
Load diff
+219
-202
File diff suppressed because it is too large.
Load diff
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.9.16
|
||||
PKG_RELEASE:=277
|
||||
PKG_RELEASE:=278
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@@ -1486,6 +1486,7 @@ function gen_config(var)
|
||||
w = api.trim(w)
|
||||
if w == "" or w:find("#") == 1 then return end
|
||||
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
|
||||
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
|
||||
table.insert(domains, w)
|
||||
table.insert(domain_table.domain, w)
|
||||
end)
|
||||
@@ -1504,6 +1505,7 @@ function gen_config(var)
|
||||
w = api.trim(w)
|
||||
if w == "" or w:find("#") == 1 then return end
|
||||
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
|
||||
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
|
||||
table.insert(ip, w)
|
||||
end)
|
||||
if #ip == 0 then ip = nil end
|
||||
|
||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.9.16
|
||||
PKG_RELEASE:=126
|
||||
PKG_RELEASE:=127
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@@ -1461,6 +1461,7 @@ function gen_config(var)
|
||||
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
if w:find("#") == 1 then return end
|
||||
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
|
||||
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
|
||||
table.insert(domains, w)
|
||||
table.insert(domain_table.domain, w)
|
||||
end)
|
||||
@@ -1483,6 +1484,7 @@ function gen_config(var)
|
||||
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
|
||||
if w:find("#") == 1 then return end
|
||||
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
|
||||
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
|
||||
table.insert(ip, w)
|
||||
end)
|
||||
if #ip == 0 then ip = nil end
|
||||
|
||||
Reference in new issue
Block a user