tv-anarchy/Sources/TVAnarchyCore/Mesh/WGQuickParser.swift
Natalie 4a2ceb9781 feat(offline): inline star-to-keep and trash-to-cull on cache rows
Surface the existing pin (keep-from-cull) and per-file delete actions as
visible inline buttons on each offline cache row instead of context-menu-only:
a star toggles protection from auto-cull (and restore-if-missing), a trash
culls that file early. Aligns wording/icons to the star metaphor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:12:41 -04:00

74 lines
3.7 KiB
Swift

import Foundation
/// A parsed (and minimally validated) wg-quick client config the payload a
/// Device Mesh join QR carries. Parsing is the **receiving** side of the enrollment
/// `MeshJoin.clientConfig` renders; both live against the same conf shape.
public struct WGQuickConfig: Sendable, Equatable {
public let privateKey: String
/// Re-derived from the private key never trusted from the text.
public let publicKey: String
/// This device's mesh address, without the /32 suffix.
public let address: String
public let dns: String?
public let peerPublicKey: String
public let peerEndpoint: String
/// Device name from the generator's `# WireGuard config for <name>` comment,
/// when present.
public let device: String?
/// The full original text what gets persisted (it's already canonical).
public let text: String
}
public enum WGQuickParseError: Error, LocalizedError, Equatable {
case missingField(String)
case badPrivateKey
case notMeshAddress(String)
public var errorDescription: String? {
switch self {
case .missingField(let f): "That doesn't look like a WireGuard config — missing \(f)."
case .badPrivateKey: "The PrivateKey isn't a valid WireGuard key."
case .notMeshAddress(let a): "Address \(a) isn't on this install's mesh (\(MeshDefaults.meshSubnet))."
}
}
}
/// Parser for incoming wg-quick text (QR scan or paste). Strict enough to
/// reject junk before anything touches disk: the private key must be a real
/// Curve25519 scalar, the address must be on the install's mesh subnet, and the
/// `[Peer]` must carry a key + endpoint.
public enum WGQuickParser {
public static func parse(_ text: String) throws -> WGQuickConfig {
var fields: [String: String] = [:]
var device: String?
for rawLine in text.split(whereSeparator: \.isNewline) {
let line = rawLine.trimmingCharacters(in: .whitespaces)
if let name = line.wholeMatch(of: #/# WireGuard config for (\S+)/#).map({ String($0.1) }) {
device = name
continue
}
if line.isEmpty || line.hasPrefix("#") || line.hasPrefix("[") { continue }
guard let eq = line.firstIndex(of: "=") else { continue }
let key = line[..<eq].trimmingCharacters(in: .whitespaces)
let value = line[line.index(after: eq)...].trimmingCharacters(in: .whitespaces)
// First occurrence wins: Interface keys come before Peer keys except
// PublicKey/Endpoint, which only ever appear in the Peer section.
if fields[key] == nil { fields[key] = value }
}
guard let priv = fields["PrivateKey"] else { throw WGQuickParseError.missingField("PrivateKey") }
guard let keypair = WireGuardKeypair(privateKeyBase64: priv) else { throw WGQuickParseError.badPrivateKey }
guard let rawAddress = fields["Address"] else { throw WGQuickParseError.missingField("Address") }
guard let peerKey = fields["PublicKey"] else { throw WGQuickParseError.missingField("Peer PublicKey") }
guard let endpoint = fields["Endpoint"] else { throw WGQuickParseError.missingField("Peer Endpoint") }
let address = String(rawAddress.split(separator: "/").first ?? "")
guard address.hasPrefix("10.9.0.") else { throw WGQuickParseError.notMeshAddress(address) }
return WGQuickConfig(privateKey: keypair.privateKey, publicKey: keypair.publicKey,
address: address, dns: fields["DNS"],
peerPublicKey: peerKey, peerEndpoint: endpoint,
device: device, text: text)
}
}