NODEDC_MISSION_CORE/plugins/xgrids-k1/macos/associate_wifi.swift

80 lines
2.5 KiB
Swift

import CoreWLAN
import Foundation
private struct AssociationRequest: Decodable {
let ssid: String
let password: String
}
private struct AssociationResponse: Encodable {
let ok: Bool
let alreadyAssociated: Bool?
let reasonCode: String?
enum CodingKeys: String, CodingKey {
case ok
case alreadyAssociated = "already_associated"
case reasonCode = "reason_code"
}
}
private func emit(_ response: AssociationResponse, exitCode: Int32) -> Never {
let encoder = JSONEncoder()
if let data = try? encoder.encode(response) {
FileHandle.standardOutput.write(data)
}
exit(exitCode)
}
private let input = FileHandle.standardInput.readDataToEndOfFile()
guard input.count <= 1024 else {
emit(
AssociationResponse(ok: false, alreadyAssociated: nil, reasonCode: "request-too-large"),
exitCode: 1
)
}
do {
let request = try JSONDecoder().decode(AssociationRequest.self, from: input)
guard let ssidData = request.ssid.data(using: .utf8),
(1 ... 32).contains(ssidData.count),
let passwordData = request.password.data(using: .utf8),
(1 ... 64).contains(passwordData.count)
else {
emit(
AssociationResponse(ok: false, alreadyAssociated: nil, reasonCode: "credential-bounds"),
exitCode: 1
)
}
guard let interface = CWWiFiClient.shared().interface() else {
emit(
AssociationResponse(ok: false, alreadyAssociated: nil, reasonCode: "wifi-interface-unavailable"),
exitCode: 1
)
}
if interface.ssid() == request.ssid {
emit(AssociationResponse(ok: true, alreadyAssociated: true, reasonCode: nil), exitCode: 0)
}
let networks = try interface.scanForNetworks(withSSID: ssidData)
guard let network = networks.first(where: { $0.ssid == request.ssid }) else {
emit(
AssociationResponse(ok: false, alreadyAssociated: nil, reasonCode: "network-not-found"),
exitCode: 1
)
}
try interface.associate(to: network, password: request.password)
// CoreWLAN's synchronous association call throws on failure. Reading the
// current SSID again would require Location authorization on recent macOS
// versions and could turn a successful association into a false negative.
emit(AssociationResponse(ok: true, alreadyAssociated: false, reasonCode: nil), exitCode: 0)
} catch {
emit(
AssociationResponse(ok: false, alreadyAssociated: nil, reasonCode: "corewlan-error"),
exitCode: 1
)
}