Skip to content

Commit

Permalink
add wifi ssid whitelist for ikura rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiayang committed Feb 14, 2022
1 parent e83580a commit e9ff240
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 37 deletions.
4 changes: 2 additions & 2 deletions MoeStreamer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand Down Expand Up @@ -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 = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
enableGPUFrameCaptureMode = "3"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES"
GPUProfilerEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
Expand Down
3 changes: 3 additions & 0 deletions MoeStreamer/src/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum SettingKey : Hashable
case ikuraConsoleIp(key: String = "ikuraConsoleIp", default: String = "")
case ikuraConsolePort(key: String = "ikuraConsolePort", default: Int = 6969)
case ikuraConsolePassword(key: String = "ikuraConsolePassword", default: String = "")
case ikuraWhitelistedSSIDs(key: String = "ikuraWhitelistedSSIDs", default: String = "")

var key: String {
switch self
Expand Down Expand Up @@ -74,6 +75,7 @@ enum SettingKey : Hashable
case .ikuraConsoleIp(let key, _): return key
case .ikuraConsolePort(let key, _): return key
case .ikuraConsolePassword(let key, _): return key
case .ikuraWhitelistedSSIDs(let key, _): return key
}
}

Expand Down Expand Up @@ -107,6 +109,7 @@ enum SettingKey : Hashable
case .ikuraConsoleIp(_, let def): return def
case .ikuraConsolePort(_, let def): return def
case .ikuraConsolePassword(_, let def): return def
case .ikuraWhitelistedSSIDs(_, let def): return def
}
}

Expand Down
157 changes: 126 additions & 31 deletions MoeStreamer/src/ikurabot/Scrobbler.swift
Original file line number Diff line number Diff line change
@@ -1,59 +1,108 @@
//
// Scrobbler.swift
// MoeStreamer
//
// Created by zhiayang on 13/2/22.
// Copyright © 2022 zhiayang. All rights reserved.
//
// Client.swift
// Copyright (c) 2022, zhiayang
// SPDX-License-Identifier: Apache-2.0

import Socket
import CoreWLAN
import SwiftyJSON
import Foundation

class WifiDelegate : CWEventDelegate
{
private weak var rpc: IkuraRPC? = nil

init(rpc: IkuraRPC)
{
self.rpc = rpc
}

func ssidDidChangeForWiFiInterface(withName interfaceName: String)
{
guard let interface = CWWiFiClient.shared().interface(withName: interfaceName) else {
self.rpc?.disconnect()
return
}

self.rpc?.wifiSSIDChanged(to: interface.ssid())
}
}

class IkuraRPC
{
private var socket: Socket? = nil
private var dispatch: DispatchQueue
private var wifiDelegate: WifiDelegate!
private var allowedSSIDs: [String] = []
private var currentSSID: String? = nil

init?(model: MainModel)
{
self.dispatch = DispatchQueue.init(label: "\(Bundle.main.bundleIdentifier ?? "").ikuraRPC")
self.wifiDelegate = WifiDelegate(rpc: self)
CWWiFiClient.shared().delegate = self.wifiDelegate

model.subscribe(with: { song, state in
if let song = song {
self.updateSong(song)
}
})

try? CWWiFiClient.shared().startMonitoringEvent(with: .linkDidChange)
try? CWWiFiClient.shared().startMonitoringEvent(with: .ssidDidChange)
try? CWWiFiClient.shared().startMonitoringEvent(with: .bssidDidChange)

_ = Settings.observe(.ikuraWhitelistedSSIDs(), callback: { key in
let whitelist: String = Settings.get(key)
self.allowedSSIDs = whitelist.split(separator: ";").map({ String($0.trimmingCharacters(in: .whitespaces)) })
})

Settings.notifyObservers(for: .ikuraWhitelistedSSIDs())
}

deinit
{
try? CWWiFiClient.shared().stopMonitoringAllEvents()
self.disconnect()
}

private func ssidWhitelistContains(ssid: String?) -> 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
}

Expand All @@ -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")
}
}
20 changes: 17 additions & 3 deletions MoeStreamer/src/ui/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>(.ikuraConsoleIp())
Expand All @@ -538,9 +539,15 @@ private struct IkurabotSettingsView : View
getter: Settings.getKeychain,
setter: Settings.setKeychain)

@ObservedObject
var ikuraWhitelistedSSIDs = SavedSettingModel<String>(.ikuraWhitelistedSSIDs())

@ObservedObject
var enableIkuraScrobbling = SavedSettingModel<Bool>(.ikuraEnabled())




var body: some View {
VStack(alignment: .leading, spacing: 3) {

Expand All @@ -555,7 +562,7 @@ private struct IkurabotSettingsView : View
.padding(.bottom, 6)

HStack() {
Text("address").frame(width: 70)
Text("address").frame(width: 60)
BetterTextField<NSTextField>(placeholder: "", text: self.$ikuraConsoleIp.value, field: self.$ikuraConsoleIpField)

Stepper(value: self.$ikuraConsolePort.value, in: 1 ... 65535, step: 1) {
Expand All @@ -570,13 +577,20 @@ private struct IkurabotSettingsView : View
}

HStack() {
Text("password").frame(width: 70)
Text("password").frame(width: 60)
BetterTextField<NSSecureTextField>(placeholder: "",
text: self.$ikuraConsolePassword.value,
field: self.$ikuraConsolePasswordField)
}
.padding(.bottom, 8)

HStack() {
Text("networks").frame(width: 60).tooltip("whitelist SSIDs: separate with ';'")
BetterTextField<NSTextField>(placeholder: "",
text: self.$ikuraWhitelistedSSIDs.value,
field: self.$whitelistedSSIDsField)
}
}
// .frame(width: 240)
.padding(.vertical, 2)
}
}
Expand Down

0 comments on commit e9ff240

Please sign in to comment.