diff --git a/apps/android/app/src/main/assets/www/call.js b/apps/android/app/src/main/assets/www/call.js
index ed33745c01..b247431f4b 100644
--- a/apps/android/app/src/main/assets/www/call.js
+++ b/apps/android/app/src/main/assets/www/call.js
@@ -24,8 +24,8 @@ var TransformOperation;
let activeCall;
const processCommand = (function () {
const defaultIceServers = [
- { urls: ["stun:stun.simplex.im:5349"] },
- { urls: ["turn:turn.simplex.im:5349"], username: "private", credential: "yleob6AVkiNI87hpR94Z" },
+ { urls: ["stun:stun.simplex.im:443"] },
+ { urls: ["turn:turn.simplex.im:443"], username: "private", credential: "yleob6AVkiNI87hpR94Z" },
];
function getCallConfig(encodedInsertableStreams, iceServers, relay) {
return {
@@ -495,6 +495,7 @@ function callCryptoFunction() {
const initialPlainTextRequired = {
key: 10,
delta: 3,
+ empty: 1,
};
const IV_LENGTH = 12;
function encryptFrame(key) {
@@ -505,7 +506,9 @@ function callCryptoFunction() {
const initial = data.subarray(0, n);
const plaintext = data.subarray(n, data.byteLength);
try {
- const ciphertext = new Uint8Array(plaintext.length ? await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv.buffer }, key, plaintext) : 0);
+ const ciphertext = plaintext.length
+ ? new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv.buffer }, key, plaintext))
+ : new Uint8Array(0);
frame.data = concatN(initial, ciphertext, iv).buffer;
controller.enqueue(frame);
}
@@ -523,7 +526,9 @@ function callCryptoFunction() {
const ciphertext = data.subarray(n, data.byteLength - IV_LENGTH);
const iv = data.subarray(data.byteLength - IV_LENGTH, data.byteLength);
try {
- const plaintext = new Uint8Array(ciphertext.length ? await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext) : 0);
+ const plaintext = ciphertext.length
+ ? new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext))
+ : new Uint8Array(0);
frame.data = concatN(initial, plaintext).buffer;
controller.enqueue(frame);
}
diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index a7e85777e3..75c9b4d609 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -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
diff --git a/apps/ios/Shared/Views/Call/CallManager.swift b/apps/ios/Shared/Views/Call/CallManager.swift
index 61bd3b9b31..962d783bcb 100644
--- a/apps/ios/Shared/Views/Call/CallManager.swift
+++ b/apps/ios/Shared/Views/Call/CallManager.swift
@@ -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) {
diff --git a/apps/ios/Shared/Views/Call/WebRTC.swift b/apps/ios/Shared/Views/Call/WebRTC.swift
index 7e63f4f499..930410af97 100644
--- a/apps/ios/Shared/Views/Call/WebRTC.swift
+++ b/apps/ios/Shared/Views/Call/WebRTC.swift
@@ -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
+}
diff --git a/apps/ios/Shared/Views/UserSettings/CallSettings.swift b/apps/ios/Shared/Views/UserSettings/CallSettings.swift
index 2f7be0eebf..254820be3a 100644
--- a/apps/ios/Shared/Views/UserSettings/CallSettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/CallSettings.swift
@@ -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: {
diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift
index 10ffde6a1e..8c61ae44ed 100644
--- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift
+++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift
@@ -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 {
diff --git a/apps/ios/Shared/Views/UserSettings/RTCServers.swift b/apps/ios/Shared/Views/UserSettings/RTCServers.swift
new file mode 100644
index 0000000000..99685e6b1b
--- /dev/null
+++ b/apps/ios/Shared/Views/UserSettings/RTCServers.swift
@@ -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()
+ }
+}
diff --git a/apps/ios/Shared/Views/UserSettings/SMPServers.swift b/apps/ios/Shared/Views/UserSettings/SMPServers.swift
index 07913c59ac..886cc7a1c0 100644
--- a/apps/ios/Shared/Views/UserSettings/SMPServers.swift
+++ b/apps/ios/Shared/Views/UserSettings/SMPServers.swift
@@ -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")!
diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
index 846d9b800d..0abab83924 100644
--- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift
+++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
@@ -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) {
diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
index 69bb8221b4..bfca67beff 100644
--- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
+++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
@@ -303,6 +303,11 @@
Call already ended!
No comment provided by engineer.
+
+ Calls
+ Calls
+ No comment provided by engineer.
+
Can't invite contact!
Can't invite contact!
@@ -403,6 +408,11 @@
Colors
No comment provided by engineer.
+
+ Configure ICE servers
+ Configure ICE servers
+ No comment provided by engineer.
+
Configure SMP servers
Configure SMP servers
@@ -961,6 +971,11 @@
Error joining group
No comment provided by engineer.
+
+ Error saving ICE servers
+ Error saving ICE servers
+ No comment provided by engineer.
+
Error saving SMP servers
Error saving SMP servers
@@ -1136,6 +1151,11 @@
How to use markdown
No comment provided by engineer.
+
+ ICE servers (one per line)
+ ICE servers (one per line)
+ No comment provided by engineer.
+
If the video fails to connect, flip the camera to resolve it.
If the video fails to connect, flip the camera to resolve it.
@@ -1363,6 +1383,11 @@ We will be adding server redundancy to prevent lost messages.
Make sure SMP server addresses are in correct format, line separated and are not duplicated.
No comment provided by engineer.
+
+ Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.
+ Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.
+ No comment provided by engineer.
+
Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*
Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*
@@ -1398,6 +1423,11 @@ We will be adding server redundancy to prevent lost messages.
Message text
No comment provided by engineer.
+
+ Messages
+ Messages
+ No comment provided by engineer.
+
Migrating database archive...
Migrating database archive...
@@ -1858,6 +1888,11 @@ We will be adding server redundancy to prevent lost messages.
Saved SMP servers will be removed
No comment provided by engineer.
+
+ Saved WebRTC ICE servers will be removed
+ Saved WebRTC ICE servers will be removed
+ No comment provided by engineer.
+
Scan QR code
Scan QR code
@@ -2322,6 +2357,11 @@ To connect, please ask your contact to create another connection link and check
Waiting for image
No comment provided by engineer.
+
+ WebRTC ICE servers
+ WebRTC ICE servers
+ No comment provided by engineer.
+
Welcome %@!
Welcome %@!
@@ -2477,6 +2517,11 @@ To connect, please ask your contact to create another connection link and check
You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed
No comment provided by engineer.
+
+ Your ICE servers
+ Your ICE servers
+ No comment provided by engineer.
+
Your SMP servers
Your SMP servers
diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff
index 4c15f3f034..6cb28901b2 100644
--- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff
+++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff
@@ -303,6 +303,11 @@
Звонок уже завершен!
No comment provided by engineer.
+
+ Calls
+ Звонки
+ No comment provided by engineer.
+
Can't invite contact!
Нельзя пригласить контакт!
@@ -403,6 +408,11 @@
Цвета
No comment provided by engineer.
+
+ Configure ICE servers
+ Настройка ICE серверов
+ No comment provided by engineer.
+
Configure SMP servers
Настройка SMP серверов
@@ -961,6 +971,11 @@
Ошибка приглашения
No comment provided by engineer.
+
+ Error saving ICE servers
+ Ошибка при сохранении ICE серверов
+ No comment provided by engineer.
+
Error saving SMP servers
Ошибка при сохранении SMP серверов
@@ -1136,6 +1151,11 @@
Как форматировать
No comment provided by engineer.
+
+ ICE servers (one per line)
+ ICE серверы (один на строке)
+ No comment provided by engineer.
+
If the video fails to connect, flip the camera to resolve it.
Если видео не соединилось, переключите камеру.
@@ -1363,6 +1383,11 @@ We will be adding server redundancy to prevent lost messages.
Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.
No comment provided by engineer.
+
+ Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.
+ Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.
+ No comment provided by engineer.
+
Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*
Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*
@@ -1398,6 +1423,11 @@ We will be adding server redundancy to prevent lost messages.
Текст сообщения
No comment provided by engineer.
+
+ Messages
+ Сообщения
+ No comment provided by engineer.
+
Migrating database archive...
Данные чата перемещаются...
@@ -1858,6 +1888,11 @@ We will be adding server redundancy to prevent lost messages.
Сохраненные SMP серверы будут удалены
No comment provided by engineer.
+
+ Saved WebRTC ICE servers will be removed
+ Сохраненные WebRTC ICE серверы будут удалены
+ No comment provided by engineer.
+
Scan QR code
Сканировать QR код
@@ -2322,6 +2357,11 @@ To connect, please ask your contact to create another connection link and check
Ожидается прием изображения
No comment provided by engineer.
+
+ WebRTC ICE servers
+ WebRTC ICE серверы
+ No comment provided by engineer.
+
Welcome %@!
Здравствуйте %@!
@@ -2477,6 +2517,11 @@ To connect, please ask your contact to create another connection link and check
Вы используете инкогнито профиль для этой группы - чтобы предотвратить раскрытие вашего основного профиля, приглашать контакты не разрешено
No comment provided by engineer.
+
+ Your ICE servers
+ Ваши ICE серверы
+ No comment provided by engineer.
+
Your SMP servers
Ваши SMP серверы
diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj
index 7f2cf4c517..6977e4445f 100644
--- a/apps/ios/SimpleX.xcodeproj/project.pbxproj
+++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj
@@ -78,6 +78,7 @@
5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA91282713FD00B3292C /* CreateProfile.swift */; };
5CB0BA962827143500B3292C /* MakeConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA952827143500B3292C /* MakeConnection.swift */; };
5CB0BA9A2827FD8800B3292C /* HowItWorks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA992827FD8800B3292C /* HowItWorks.swift */; };
+ 5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB2084E28DA4B4800D024EC /* RTCServers.swift */; };
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E42868AA7F001FD2EF /* SuspendChat.swift */; };
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E62868D76D001FD2EF /* NotificationsView.swift */; };
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */; };
@@ -271,6 +272,7 @@
5CB0BA91282713FD00B3292C /* CreateProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateProfile.swift; sourceTree = ""; };
5CB0BA952827143500B3292C /* MakeConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeConnection.swift; sourceTree = ""; };
5CB0BA992827FD8800B3292C /* HowItWorks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HowItWorks.swift; sourceTree = ""; };
+ 5CB2084E28DA4B4800D024EC /* RTCServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTCServers.swift; sourceTree = ""; };
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendChat.swift; sourceTree = ""; };
5CB346E62868D76D001FD2EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; };
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushEnvironment.swift; sourceTree = ""; };
@@ -561,6 +563,7 @@
5CB924E027A867BA00ACCCDD /* UserProfile.swift */,
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
640F50E227CF991C001E05C2 /* SMPServers.swift */,
+ 5CB2084E28DA4B4800D024EC /* RTCServers.swift */,
5C3F1D592844B4DE00EC8A82 /* ExperimentalFeaturesView.swift */,
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */,
);
@@ -876,6 +879,7 @@
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */,
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */,
5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */,
+ 5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */,
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings
index 22c2ee0f91..181355ce03 100644
--- a/apps/ios/ru.lproj/Localizable.strings
+++ b/apps/ios/ru.lproj/Localizable.strings
@@ -218,6 +218,9 @@
/* call status */
"calling…" = "входящий звонок…";
+/* No comment provided by engineer. */
+"Calls" = "Звонки";
+
/* No comment provided by engineer. */
"Can't invite contact!" = "Нельзя пригласить контакт!";
@@ -284,6 +287,9 @@
/* No comment provided by engineer. */
"complete" = "соединение завершено";
+/* No comment provided by engineer. */
+"Configure ICE servers" = "Настройка ICE серверов";
+
/* No comment provided by engineer. */
"Configure SMP servers" = "Настройка SMP серверов";
@@ -683,6 +689,9 @@
/* No comment provided by engineer. */
"Error saving group profile" = "Ошибка при сохранении профиля группы";
+/* No comment provided by engineer. */
+"Error saving ICE servers" = "Ошибка при сохранении ICE серверов";
+
/* No comment provided by engineer. */
"Error saving SMP servers" = "Ошибка при сохранении SMP серверов";
@@ -791,6 +800,9 @@
/* No comment provided by engineer. */
"How to use markdown" = "Как форматировать";
+/* No comment provided by engineer. */
+"ICE servers (one per line)" = "ICE серверы (один на строке)";
+
/* No comment provided by engineer. */
"If the video fails to connect, flip the camera to resolve it." = "Если видео не соединилось, переключите камеру.";
@@ -956,6 +968,9 @@
/* No comment provided by engineer. */
"Make sure SMP server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.";
+/* No comment provided by engineer. */
+"Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.";
+
/* No comment provided by engineer. */
"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*";
@@ -986,6 +1001,9 @@
/* No comment provided by engineer. */
"Message text" = "Текст сообщения";
+/* No comment provided by engineer. */
+"Messages" = "Сообщения";
+
/* No comment provided by engineer. */
"Migrating database archive..." = "Данные чата перемещаются...";
@@ -1292,6 +1310,9 @@
/* No comment provided by engineer. */
"Saved SMP servers will be removed" = "Сохраненные SMP серверы будут удалены";
+/* No comment provided by engineer. */
+"Saved WebRTC ICE servers will be removed" = "Сохраненные WebRTC ICE серверы будут удалены";
+
/* No comment provided by engineer. */
"Scan contact's QR code" = "Сосканировать QR код контакта";
@@ -1619,6 +1640,9 @@
/* No comment provided by engineer. */
"wants to connect to you!" = "хочет соединиться с вами!";
+/* No comment provided by engineer. */
+"WebRTC ICE servers" = "WebRTC ICE серверы";
+
/* No comment provided by engineer. */
"Welcome %@!" = "Здравствуйте %@!";
@@ -1766,6 +1790,9 @@
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "Текущие данные вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными.";
+/* No comment provided by engineer. */
+"Your ICE servers" = "Ваши ICE серверы";
+
/* No comment provided by engineer. */
"Your privacy" = "Конфиденциальность";
diff --git a/packages/simplex-chat-webrtc/src/call.ts b/packages/simplex-chat-webrtc/src/call.ts
index 37ddc02b64..e62a5a419d 100644
--- a/packages/simplex-chat-webrtc/src/call.ts
+++ b/packages/simplex-chat-webrtc/src/call.ts
@@ -217,8 +217,8 @@ const processCommand = (function () {
}
const defaultIceServers: RTCIceServer[] = [
- {urls: ["stun:stun.simplex.im:5349"]},
- {urls: ["turn:turn.simplex.im:5349"], username: "private", credential: "yleob6AVkiNI87hpR94Z"},
+ {urls: ["stun:stun.simplex.im:443"]},
+ {urls: ["turn:turn.simplex.im:443"], username: "private", credential: "yleob6AVkiNI87hpR94Z"},
]
function getCallConfig(encodedInsertableStreams: boolean, iceServers?: RTCIceServer[], relay?: boolean): CallConfig {
@@ -703,16 +703,12 @@ interface CallCrypto {
decodeBase64url: (b64: Uint8Array) => Uint8Array | undefined
}
-interface RTCEncodedVideoFrame {
- type: "key" | "delta"
- data: ArrayBuffer
-}
-
// Cryptography function - it is loaded both in the main window and in worker context (if the worker is used)
function callCryptoFunction(): CallCrypto {
const initialPlainTextRequired = {
key: 10,
delta: 3,
+ empty: 1,
}
const IV_LENGTH = 12
@@ -725,9 +721,9 @@ function callCryptoFunction(): CallCrypto {
const initial = data.subarray(0, n)
const plaintext = data.subarray(n, data.byteLength)
try {
- const ciphertext = new Uint8Array(
- plaintext.length ? await crypto.subtle.encrypt({name: "AES-GCM", iv: iv.buffer}, key, plaintext) : 0
- )
+ const ciphertext = plaintext.length
+ ? new Uint8Array(await crypto.subtle.encrypt({name: "AES-GCM", iv: iv.buffer}, key, plaintext))
+ : new Uint8Array(0)
frame.data = concatN(initial, ciphertext, iv).buffer
controller.enqueue(frame)
} catch (e) {
@@ -745,7 +741,9 @@ function callCryptoFunction(): CallCrypto {
const ciphertext = data.subarray(n, data.byteLength - IV_LENGTH)
const iv = data.subarray(data.byteLength - IV_LENGTH, data.byteLength)
try {
- const plaintext = new Uint8Array(ciphertext.length ? await crypto.subtle.decrypt({name: "AES-GCM", iv}, key, ciphertext) : 0)
+ const plaintext = ciphertext.length
+ ? new Uint8Array(await crypto.subtle.decrypt({name: "AES-GCM", iv}, key, ciphertext))
+ : new Uint8Array(0)
frame.data = concatN(initial, plaintext).buffer
controller.enqueue(frame)
} catch (e) {