prole/prole-app/Sources/PortMappingsXML.swift
chrisfu d1e4cb5db3 Remove inline shell from app; bundle external init script; fix build and packaging
•
Strip all embedded shell from LaunchAgentManager.swift; use external etc/init-port-fowards.sh copied from app Resources at runtime; set executable perms
•
Set defaultHelperScript to empty to eliminate inline script
•
Build script: bundle init-port-fowards.sh into Prole.app/Contents/Resources and chmod +x; capture spm_build_binary path via tail -n1
•
Fix malformed multiline XML in PortMappingsXML.save()
•
Preferences: handle new .agentPF tab in didSelect and windowDidResize
Result: Prole.app builds cleanly on arm64; no inline shell remains; embedded dylibs packaged and codesigned
2025-12-15 00:39:52 -08:00

78 lines
3.1 KiB
Swift

import Foundation
struct PortMappingXML: Equatable {
var id: String
var namespace: String
var target: String
var address: String
var hostPort: String
var servicePort: String
var proto: String
var description: String
}
enum PortMappingsXMLStore {
private static func configURL() -> URL {
// Determine PROLE_HOME similarly to PFScriptBridge
let env = ProcessInfo.processInfo.environment
if let home = env["PROLE_HOME"], !home.isEmpty {
return URL(fileURLWithPath: home).appendingPathComponent("conf/port-mappings.properties")
}
let defaultHome = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("dev/prole")
return defaultHome.appendingPathComponent("conf/port-mappings.properties")
}
static func load() -> [PortMappingXML] {
let url = configURL()
guard let data = try? Data(contentsOf: url) else { return [] }
let parser = XMLParser(data: data)
let delegate = ParserDelegate()
parser.delegate = delegate
if parser.parse() { return delegate.items }
return []
}
static func save(_ items: [PortMappingXML]) throws {
let url = configURL()
var xml = """
<?xml version="1.0" encoding="UTF-8"?>
<portMappings>
"""
for m in items {
xml += " <mapping id=\"\(escape(m.id))\" namespace=\"\(escape(m.namespace))\" target=\"\(escape(m.target))\" address=\"\(escape(m.address))\" hostPort=\"\(escape(m.hostPort))\" servicePort=\"\(escape(m.servicePort))\" protocol=\"\(escape(m.proto))\" description=\"\(escape(m.description))\"/>\n"
}
xml += """
</portMappings>
"""
try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true)
try xml.data(using: .utf8)?.write(to: url)
}
private static func escape(_ s: String) -> String {
return s.replacingOccurrences(of: "&", with: "&amp;")
.replacingOccurrences(of: "\"", with: "&quot;")
.replacingOccurrences(of: "<", with: "&lt;")
.replacingOccurrences(of: ">", with: "&gt;")
}
private final class ParserDelegate: NSObject, XMLParserDelegate {
var items: [PortMappingXML] = []
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if elementName == "mapping" {
let m = PortMappingXML(
id: attributeDict["id"] ?? "",
namespace: attributeDict["namespace"] ?? "default",
target: attributeDict["target"] ?? "",
address: attributeDict["address"] ?? "127.0.0.1",
hostPort: attributeDict["hostPort"] ?? "",
servicePort: attributeDict["servicePort"] ?? "",
proto: attributeDict["protocol"] ?? "TCP",
description: attributeDict["description"] ?? ""
)
items.append(m)
}
}
}
}