mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
webrtc: change server ports to 443, ios: allow configuring WebRTC ICE servers (#1077)
* webrtc: change server ports to 443 * pass stun/turn servers from user default * ios: configure WebRTC ICE servers * translations * update servers * translations
This commit is contained in:
committed by
GitHub
parent
edf217111f
commit
909a8aaf9c
@@ -899,8 +899,17 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
let iceServers = getIceServers()
|
||||
logger.debug(".callOffer useRelay \(useRelay)")
|
||||
m.callCommand = .offer(offer: offer.rtcSession, iceCandidates: offer.rtcIceCandidates, media: callType.media, aesKey: sharedKey, useWorker: true, relay: useRelay)
|
||||
logger.debug(".callOffer iceServers \(String(describing: iceServers))")
|
||||
m.callCommand = .offer(
|
||||
offer: offer.rtcSession,
|
||||
iceCandidates: offer.rtcIceCandidates,
|
||||
media: callType.media, aesKey: sharedKey,
|
||||
useWorker: true,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
)
|
||||
}
|
||||
case let .callAnswer(contact, answer):
|
||||
withCall(contact) { call in
|
||||
|
||||
@@ -47,8 +47,16 @@ class CallManager {
|
||||
)
|
||||
m.showCallView = true
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
logger.debug("answerIncomingCall useRelay \(useRelay)")
|
||||
m.callCommand = .start(media: invitation.callType.media, aesKey: invitation.sharedKey, useWorker: true, relay: useRelay)
|
||||
let iceServers = getIceServers()
|
||||
logger.debug("answerIncomingCall useRelay: \(useRelay)")
|
||||
logger.debug("answerIncomingCall iceServers: \(String(describing: iceServers))")
|
||||
m.callCommand = .start(
|
||||
media: invitation.callType.media,
|
||||
aesKey: invitation.sharedKey,
|
||||
useWorker: true,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
)
|
||||
}
|
||||
|
||||
func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) {
|
||||
|
||||
@@ -394,3 +394,48 @@ struct RTCIceServer: Codable, Equatable {
|
||||
var username: String? = nil
|
||||
var credential: String? = nil
|
||||
}
|
||||
|
||||
// the servers are expected in this format:
|
||||
// stun:stun.simplex.im:443
|
||||
// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443
|
||||
func parseRTCIceServer(_ str: String) -> RTCIceServer? {
|
||||
var s = replaceScheme(str, "stun:")
|
||||
s = replaceScheme(s, "turn:")
|
||||
if let u: URL = URL(string: s),
|
||||
let scheme = u.scheme,
|
||||
let host = u.host,
|
||||
let port = u.port,
|
||||
u.path == "" && (scheme == "stun" || scheme == "turn") {
|
||||
return RTCIceServer(
|
||||
urls: ["\(scheme):\(host):\(port)"],
|
||||
username: u.user,
|
||||
credential: u.password
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func replaceScheme(_ s: String, _ scheme: String) -> String {
|
||||
s.starts(with: scheme)
|
||||
? s.replacingOccurrences(of: scheme, with: scheme + "//", options: .anchored, range: nil)
|
||||
: s
|
||||
}
|
||||
|
||||
func parseRTCIceServers(_ servers: [String]) -> [RTCIceServer]? {
|
||||
var iceServers: [RTCIceServer] = []
|
||||
for s in servers {
|
||||
if let server = parseRTCIceServer(s) {
|
||||
iceServers.append(server)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return iceServers.isEmpty ? nil : iceServers
|
||||
}
|
||||
|
||||
func getIceServers() -> [RTCIceServer]? {
|
||||
if let servers = UserDefaults.standard.stringArray(forKey: DEFAULT_WEBRTC_ICE_SERVERS) {
|
||||
return parseRTCIceServers(servers)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ struct CallSettings: View {
|
||||
List {
|
||||
Section {
|
||||
Toggle("Connect via relay", isOn: $webrtcPolicyRelay)
|
||||
|
||||
NavigationLink {
|
||||
RTCServers()
|
||||
.navigationTitle("Your ICE servers")
|
||||
} label: {
|
||||
Text("WebRTC ICE servers")
|
||||
}
|
||||
} header: {
|
||||
Text("Settings")
|
||||
} footer: {
|
||||
|
||||
@@ -37,7 +37,7 @@ struct NetworkAndServers: View {
|
||||
SMPServers()
|
||||
.navigationTitle("Your SMP servers")
|
||||
} label: {
|
||||
settingsRow("server.rack") { Text("SMP servers") }
|
||||
Text("SMP servers")
|
||||
}
|
||||
|
||||
Picker("Use .onion hosts", selection: $onionHosts) {
|
||||
@@ -49,14 +49,23 @@ struct NetworkAndServers: View {
|
||||
AdvancedNetworkSettings()
|
||||
.navigationTitle("Network settings")
|
||||
} label: {
|
||||
settingsRow("app.connected.to.app.below.fill") { Text("Advanced network settings") }
|
||||
Text("Advanced network settings")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("")
|
||||
Text("Messages")
|
||||
} footer: {
|
||||
Text("Using .onion hosts requires compatible VPN provider.")
|
||||
}
|
||||
|
||||
Section("Calls") {
|
||||
NavigationLink {
|
||||
RTCServers()
|
||||
.navigationTitle("Your ICE servers")
|
||||
} label: {
|
||||
Text("WebRTC ICE servers")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// RTCServers.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 20/09/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
private let howToUrl = URL(string: "https://github.com/simplex-chat/simplex-chat/blob/stable/docs/WEBRTC.md#configure-mobile-apps")!
|
||||
|
||||
|
||||
struct RTCServers: View {
|
||||
@State private var userRTCServers: [String] = []
|
||||
@State private var isUserRTCServers = false
|
||||
@State private var isUserRTCServersToggle = false
|
||||
@State private var editRTCServers = true
|
||||
@State private var userRTCServersStr = ""
|
||||
@State private var showBadServersAlert = false
|
||||
@State private var showResetServersAlert = false
|
||||
@FocusState private var keyboardVisible: Bool
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
Toggle("Configure ICE servers", isOn: $isUserRTCServersToggle)
|
||||
.onChange(of: isUserRTCServersToggle) { _ in
|
||||
if (isUserRTCServersToggle) {
|
||||
isUserRTCServers = true
|
||||
} else if (userRTCServers.isEmpty) {
|
||||
resetRTCServers()
|
||||
} else {
|
||||
showResetServersAlert = true
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $showResetServersAlert) {
|
||||
Alert(
|
||||
title: Text("Use SimpleX Chat servers?"),
|
||||
message: Text("Saved WebRTC ICE servers will be removed"),
|
||||
primaryButton: .destructive(Text("Confirm")) {
|
||||
resetRTCServers()
|
||||
}, secondaryButton: .cancel() {
|
||||
withAnimation() {
|
||||
isUserRTCServersToggle = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} header: {
|
||||
Text("")
|
||||
} footer: {
|
||||
if !isUserRTCServers {
|
||||
Text("Using SimpleX Chat servers.")
|
||||
}
|
||||
}
|
||||
|
||||
if isUserRTCServers {
|
||||
Section {
|
||||
if editRTCServers {
|
||||
TextEditor(text: $userRTCServersStr)
|
||||
.focused($keyboardVisible)
|
||||
.font(serversFont)
|
||||
.disableAutocorrection(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
.padding(.horizontal, -5)
|
||||
.padding(.top, -8)
|
||||
.frame(height: 160, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
} else {
|
||||
ScrollView {
|
||||
Text(userRTCServersStr)
|
||||
.font(serversFont)
|
||||
.frame(minHeight: 0, alignment: .topLeading)
|
||||
.textSelection(.enabled)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(height: 160)
|
||||
}
|
||||
} header: {
|
||||
Text("ICE servers (one per line)")
|
||||
} footer: {
|
||||
HStack(spacing: 20) {
|
||||
if editRTCServers {
|
||||
Button("Cancel") {
|
||||
initialize()
|
||||
}
|
||||
Button("Save") {
|
||||
saveUserRTCServers()
|
||||
}
|
||||
.alert(isPresented: $showBadServersAlert) {
|
||||
Alert(title: Text("Error saving ICE servers"), message: Text("Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated."))
|
||||
}
|
||||
Spacer()
|
||||
howToButton()
|
||||
} else {
|
||||
Button("Edit") {
|
||||
editRTCServers = true
|
||||
}
|
||||
Spacer()
|
||||
howToButton()
|
||||
}
|
||||
}
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear { initialize() }
|
||||
}
|
||||
|
||||
private func initialize() {
|
||||
userRTCServers = UserDefaults.standard.stringArray(forKey: DEFAULT_WEBRTC_ICE_SERVERS) ?? []
|
||||
isUserRTCServers = !userRTCServers.isEmpty
|
||||
isUserRTCServersToggle = isUserRTCServers
|
||||
editRTCServers = !isUserRTCServers
|
||||
userRTCServersStr = isUserRTCServers ? userRTCServers.joined(separator: "\n") : ""
|
||||
}
|
||||
|
||||
private func resetRTCServers() {
|
||||
isUserRTCServers = false
|
||||
userRTCServers = []
|
||||
UserDefaults.standard.removeObject(forKey: DEFAULT_WEBRTC_ICE_SERVERS)
|
||||
}
|
||||
|
||||
private func saveUserRTCServers() {
|
||||
let srvs = userRTCServersStr.components(separatedBy: "\n")
|
||||
if srvs.count > 0 && Set(srvs).count == srvs.count && parseRTCIceServers(srvs) != nil {
|
||||
userRTCServers = srvs
|
||||
UserDefaults.standard.set(srvs, forKey: DEFAULT_WEBRTC_ICE_SERVERS)
|
||||
editRTCServers = false
|
||||
} else {
|
||||
showBadServersAlert = true
|
||||
}
|
||||
}
|
||||
|
||||
func howToButton() -> some View {
|
||||
Button {
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.open(howToUrl)
|
||||
}
|
||||
} label: {
|
||||
HStack{
|
||||
Text("How to")
|
||||
Image(systemName: "arrow.up.right.circle")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RTCServers_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RTCServers()
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
private let serversFont = Font.custom("Menlo", size: 14)
|
||||
let serversFont = Font.custom("Menlo", size: 14)
|
||||
|
||||
private let howToUrl = URL(string: "https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent")!
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ let DEFAULT_SHOW_LA_NOTICE = "showLocalAuthenticationNotice"
|
||||
let DEFAULT_LA_NOTICE_SHOWN = "localAuthenticationNoticeShown"
|
||||
let DEFAULT_PERFORM_LA = "performLocalAuthentication"
|
||||
let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
|
||||
let DEFAULT_WEBRTC_ICE_SERVERS = "webrtcICEServers"
|
||||
let DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
|
||||
let DEFAULT_EXPERIMENTAL_CALLS = "experimentalCalls"
|
||||
@@ -193,7 +194,7 @@ struct SettingsView: View {
|
||||
} label: {
|
||||
settingsRow("terminal") { Text("Chat console") }
|
||||
}
|
||||
settingsRow("gear") {
|
||||
settingsRow("chevron.left.forwardslash.chevron.right") {
|
||||
Toggle("Developer tools", isOn: $developerTools)
|
||||
}
|
||||
ZStack(alignment: .leading) {
|
||||
|
||||
Reference in New Issue
Block a user