From e9ff240aa262c895369df5b1e4e68db9bc118a5b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 14 Feb 2022 21:29:06 +0800 Subject: [PATCH] add wifi ssid whitelist for ikura rpc --- MoeStreamer.xcodeproj/project.pbxproj | 4 +- .../xcschemes/MoeStreamer.xcscheme | 5 +- MoeStreamer/src/Settings.swift | 3 + MoeStreamer/src/ikurabot/Scrobbler.swift | 157 ++++++++++++++---- MoeStreamer/src/ui/SettingsView.swift | 20 ++- 5 files changed, 152 insertions(+), 37 deletions(-) diff --git a/MoeStreamer.xcodeproj/project.pbxproj b/MoeStreamer.xcodeproj/project.pbxproj index 0c78902..52dd044 100644 --- a/MoeStreamer.xcodeproj/project.pbxproj +++ b/MoeStreamer.xcodeproj/project.pbxproj @@ -665,7 +665,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.17.5; + MARKETING_VERSION = 0.17.6; PRODUCT_BUNDLE_IDENTIFIER = com.zhiayang.MoeStreamer; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; @@ -700,7 +700,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.17.5; + MARKETING_VERSION = 0.17.6; PRODUCT_BUNDLE_IDENTIFIER = com.zhiayang.MoeStreamer; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; diff --git a/MoeStreamer.xcodeproj/xcshareddata/xcschemes/MoeStreamer.xcscheme b/MoeStreamer.xcodeproj/xcshareddata/xcschemes/MoeStreamer.xcscheme index aff19d7..e0f9391 100644 --- a/MoeStreamer.xcodeproj/xcshareddata/xcschemes/MoeStreamer.xcscheme +++ b/MoeStreamer.xcodeproj/xcshareddata/xcschemes/MoeStreamer.xcscheme @@ -39,7 +39,10 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + enableGPUFrameCaptureMode = "3" + enableGPUValidationMode = "1" + allowLocationSimulation = "YES" + GPUProfilerEnabled = "No"> Bool + { + guard let ssid = ssid else { + return false + } + return self.allowedSSIDs.contains(ssid) + } + private func updateSong(_ song: Song) { + guard let socket = self.socket else { + return + } + let json = JSON([ "title": song.title, "artist": song.artists.joined(separator: ", ") ]) - do - { - // note: no options = not pretty printed! - guard let ser = json.rawString(options: []) else { - return - } - - try self.socket?.write(from: "/scrobble_song \(ser)\n") + // note: no options = not pretty printed! the default options are to pretty print, + // which we don't want (because it takes multiple lines) + guard let ser = json.rawString(options: []) else { + return } - catch - { - Logger.log("ikura", msg: "write failed: \(error)") + + self.dispatch.async { + _ = socket.tryWrite("/scrobble_song \(ser)\n") } } func connect() -> Bool { + if !self.ssidWhitelistContains(ssid: CWWiFiClient.shared().interface()?.ssid()) { + return false + } + self.socket = try? Socket.create() - guard self.socket != nil else { + guard let socket = self.socket else { return false } @@ -63,38 +112,84 @@ class IkuraRPC do { - try self.socket?.connect(to: ip, port: Int32(port)) + try socket.connect(to: ip, port: Int32(port)) } catch { Logger.log("ikura", msg: "failed to connect: \(error)") + self.socket = nil return false } - guard let foo = try? self.socket?.readString(), foo.starts(with: "csrf: ") else { + guard let foo = try? socket.readString(), foo.starts(with: "csrf: ") else { Logger.log("ikura", msg: "could not read CSRF") return false } let csrf = foo.components(separatedBy: .newlines)[0].dropFirst("csrf: ".count) - print("csrf = \(csrf)") + guard socket.tryWrite(csrf + "\n") && socket.tryWrite(password + "\n") else { + return false + } + + print("ikura: connected") + return true + } + + func disconnect() + { + self.dispatch.async { + _ = self.socket?.tryWrite("/q\n") + self.socket = nil + + print("ikura: disconnected") + } + } + + func wifiSSIDChanged(to ssid: String?) + { + self.dispatch.async { + guard self.currentSSID != ssid else { + return + } + + self.currentSSID = ssid + guard self.ssidWhitelistContains(ssid: ssid) else { + if self.socket != nil + { + Logger.log("ikura", msg: "ssid not in whitelist, disconnecting") + self.disconnect() + } + return + } + + // if we are disconnected, then connect automatically + if self.socket == nil { + // wait a while for the hardware to catch up + self.dispatch.asyncAfter(deadline: .now() + 5) { + Logger.log("ikura", msg: "ssid in whitelist, reconnecting") + _ = self.connect() + } + } + } + } +} + + + +fileprivate extension Socket +{ + func tryWrite(_ string: String) -> Bool + { do { - try self.socket?.write(from: csrf + "\n") - try self.socket?.write(from: password + "\n") + try self.write(from: string) + return true } catch { Logger.log("ikura", msg: "write failed: \(error)") return false } - - return true - } - - func disconnect() - { - _ = try? self.socket?.write(from: "/q\n") } } diff --git a/MoeStreamer/src/ui/SettingsView.swift b/MoeStreamer/src/ui/SettingsView.swift index 9b67f95..c21f971 100644 --- a/MoeStreamer/src/ui/SettingsView.swift +++ b/MoeStreamer/src/ui/SettingsView.swift @@ -526,6 +526,7 @@ private struct IkurabotSettingsView : View { @State var ikuraConsoleIpField: NSTextField! = nil @State var ikuraConsolePasswordField: NSSecureTextField! = nil + @State var whitelistedSSIDsField: NSTextField! = nil @ObservedObject var ikuraConsoleIp = SavedSettingModel(.ikuraConsoleIp()) @@ -538,9 +539,15 @@ private struct IkurabotSettingsView : View getter: Settings.getKeychain, setter: Settings.setKeychain) + @ObservedObject + var ikuraWhitelistedSSIDs = SavedSettingModel(.ikuraWhitelistedSSIDs()) + @ObservedObject var enableIkuraScrobbling = SavedSettingModel(.ikuraEnabled()) + + + var body: some View { VStack(alignment: .leading, spacing: 3) { @@ -555,7 +562,7 @@ private struct IkurabotSettingsView : View .padding(.bottom, 6) HStack() { - Text("address").frame(width: 70) + Text("address").frame(width: 60) BetterTextField(placeholder: "", text: self.$ikuraConsoleIp.value, field: self.$ikuraConsoleIpField) Stepper(value: self.$ikuraConsolePort.value, in: 1 ... 65535, step: 1) { @@ -570,13 +577,20 @@ private struct IkurabotSettingsView : View } HStack() { - Text("password").frame(width: 70) + Text("password").frame(width: 60) BetterTextField(placeholder: "", text: self.$ikuraConsolePassword.value, field: self.$ikuraConsolePasswordField) } + .padding(.bottom, 8) + + HStack() { + Text("networks").frame(width: 60).tooltip("whitelist SSIDs: separate with ';'") + BetterTextField(placeholder: "", + text: self.$ikuraWhitelistedSSIDs.value, + field: self.$whitelistedSSIDsField) + } } -// .frame(width: 240) .padding(.vertical, 2) } }