Own the network: details, VPN, enterprise Wi-Fi, and a firewall that can also allow
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -57,6 +57,12 @@ Singleton {
|
||||
return root.exposed.filter(entry => root.allowedByRange(entry));
|
||||
}
|
||||
|
||||
// zone name -> the helper's zone-info shape, for the zone browser. Cached
|
||||
// because browsing means asking about the same handful of zones as a chip
|
||||
// row repaints, and each answer is two firewall-cmd calls.
|
||||
property var zoneDetails: ({})
|
||||
property var pendingZones: []
|
||||
|
||||
function refresh(): void {
|
||||
if (query.running)
|
||||
return;
|
||||
@@ -64,6 +70,52 @@ Singleton {
|
||||
query.running = true;
|
||||
}
|
||||
|
||||
// What a zone allows, for reading before choosing one. Returns the cached
|
||||
// answer, or null while the first one is on its way -- and asks for it, so
|
||||
// a chip that binds to this fills itself in.
|
||||
//
|
||||
// Read-only, so it needs no authorization and never prompts: this is the
|
||||
// difference between looking at a zone and moving an interface into it.
|
||||
function zoneInfo(zoneName: string): var {
|
||||
if (zoneName === "")
|
||||
return null;
|
||||
if (root.zoneDetails[zoneName] !== undefined)
|
||||
return root.zoneDetails[zoneName];
|
||||
root.requestZoneInfo(zoneName);
|
||||
return null;
|
||||
}
|
||||
|
||||
function requestZoneInfo(zoneName: string): void {
|
||||
if (zoneName === "" || root.pendingZones.indexOf(zoneName) >= 0)
|
||||
return;
|
||||
root.pendingZones = root.pendingZones.concat([zoneName]);
|
||||
root.drainZones();
|
||||
}
|
||||
|
||||
function drainZones(): void {
|
||||
if (zoneQuery.running || root.pendingZones.length === 0)
|
||||
return;
|
||||
zoneQuery.subject = root.pendingZones[0];
|
||||
zoneQuery.command = [root.helperPath, "zone-info", zoneQuery.subject];
|
||||
zoneQuery.running = true;
|
||||
}
|
||||
|
||||
function absorbZone(zoneName: string, text: string): void {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
// Reassigned rather than mutated: QML does not notice a property
|
||||
// change made inside a var object.
|
||||
const next = Object.assign({}, root.zoneDetails);
|
||||
next[zoneName] = parsed;
|
||||
root.zoneDetails = next;
|
||||
if (String(parsed.error ?? "") !== "")
|
||||
root.lastError = String(parsed.error);
|
||||
} catch (error) {
|
||||
root.lastError = "Could not read what that zone allows.";
|
||||
console.warn("Firewall: could not parse zone-info output:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function absorb(text: string): void {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
@@ -90,6 +142,10 @@ Singleton {
|
||||
if (mutation.running)
|
||||
return;
|
||||
root.lastError = "";
|
||||
// Every mutation here can change what a zone allows or which zone an
|
||||
// interface is in, so the browser's cached descriptions are dropped
|
||||
// rather than left to describe the firewall as it used to be.
|
||||
root.zoneDetails = ({});
|
||||
mutation.command = [root.helperPath].concat(arguments);
|
||||
mutation.running = true;
|
||||
}
|
||||
@@ -120,4 +176,31 @@ Singleton {
|
||||
onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
|
||||
}
|
||||
}
|
||||
|
||||
// Kept out of `busy` on purpose: reading what a zone allows changes nothing,
|
||||
// so it must not disable the buttons that do.
|
||||
Process {
|
||||
id: zoneQuery
|
||||
|
||||
property string subject: ""
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: root.absorbZone(zoneQuery.subject, this.text)
|
||||
}
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
|
||||
}
|
||||
onExited: {
|
||||
root.pendingZones = root.pendingZones.filter(name => name !== zoneQuery.subject);
|
||||
zoneDrain.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// One tick later, because `running` has not gone false inside onExited and
|
||||
// the queue would stall on its own guard.
|
||||
Timer {
|
||||
id: zoneDrain
|
||||
interval: 0
|
||||
onTriggered: root.drainZones()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user