mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-20 02:41:31 +00: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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -303,6 +303,11 @@
|
||||
<target>Call already ended!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Calls" xml:space="preserve">
|
||||
<source>Calls</source>
|
||||
<target>Calls</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't invite contact!" xml:space="preserve">
|
||||
<source>Can't invite contact!</source>
|
||||
<target>Can't invite contact!</target>
|
||||
@@ -403,6 +408,11 @@
|
||||
<target>Colors</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure ICE servers" xml:space="preserve">
|
||||
<source>Configure ICE servers</source>
|
||||
<target>Configure ICE servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure SMP servers" xml:space="preserve">
|
||||
<source>Configure SMP servers</source>
|
||||
<target>Configure SMP servers</target>
|
||||
@@ -961,6 +971,11 @@
|
||||
<target>Error joining group</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving ICE servers" xml:space="preserve">
|
||||
<source>Error saving ICE servers</source>
|
||||
<target>Error saving ICE servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving SMP servers" xml:space="preserve">
|
||||
<source>Error saving SMP servers</source>
|
||||
<target>Error saving SMP servers</target>
|
||||
@@ -1136,6 +1151,11 @@
|
||||
<target>How to use markdown</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ICE servers (one per line)" xml:space="preserve">
|
||||
<source>ICE servers (one per line)</source>
|
||||
<target>ICE servers (one per line)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If the video fails to connect, flip the camera to resolve it." xml:space="preserve">
|
||||
<source>If the video fails to connect, flip the camera to resolve it.</source>
|
||||
<target>If the video fails to connect, flip the camera to resolve it.</target>
|
||||
@@ -1363,6 +1383,11 @@ We will be adding server redundancy to prevent lost messages.</target>
|
||||
<target>Make sure SMP server addresses are in correct format, line separated and are not duplicated.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." xml:space="preserve">
|
||||
<source>Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.</source>
|
||||
<target>Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" xml:space="preserve">
|
||||
<source>Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*</source>
|
||||
<target>Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*</target>
|
||||
@@ -1398,6 +1423,11 @@ We will be adding server redundancy to prevent lost messages.</target>
|
||||
<target>Message text</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Messages" xml:space="preserve">
|
||||
<source>Messages</source>
|
||||
<target>Messages</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrating database archive..." xml:space="preserve">
|
||||
<source>Migrating database archive...</source>
|
||||
<target>Migrating database archive...</target>
|
||||
@@ -1858,6 +1888,11 @@ We will be adding server redundancy to prevent lost messages.</target>
|
||||
<target>Saved SMP servers will be removed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Saved WebRTC ICE servers will be removed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code" xml:space="preserve">
|
||||
<source>Scan QR code</source>
|
||||
<target>Scan QR code</target>
|
||||
@@ -2322,6 +2357,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Waiting for image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WebRTC ICE servers" xml:space="preserve">
|
||||
<source>WebRTC ICE servers</source>
|
||||
<target>WebRTC ICE servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome %@!" xml:space="preserve">
|
||||
<source>Welcome %@!</source>
|
||||
<target>Welcome %@!</target>
|
||||
@@ -2477,6 +2517,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your ICE servers" xml:space="preserve">
|
||||
<source>Your ICE servers</source>
|
||||
<target>Your ICE servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SMP servers" xml:space="preserve">
|
||||
<source>Your SMP servers</source>
|
||||
<target>Your SMP servers</target>
|
||||
|
||||
@@ -303,6 +303,11 @@
|
||||
<target>Звонок уже завершен!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Calls" xml:space="preserve">
|
||||
<source>Calls</source>
|
||||
<target>Звонки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't invite contact!" xml:space="preserve">
|
||||
<source>Can't invite contact!</source>
|
||||
<target>Нельзя пригласить контакт!</target>
|
||||
@@ -403,6 +408,11 @@
|
||||
<target>Цвета</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure ICE servers" xml:space="preserve">
|
||||
<source>Configure ICE servers</source>
|
||||
<target>Настройка ICE серверов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure SMP servers" xml:space="preserve">
|
||||
<source>Configure SMP servers</source>
|
||||
<target>Настройка SMP серверов</target>
|
||||
@@ -961,6 +971,11 @@
|
||||
<target>Ошибка приглашения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving ICE servers" xml:space="preserve">
|
||||
<source>Error saving ICE servers</source>
|
||||
<target>Ошибка при сохранении ICE серверов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving SMP servers" xml:space="preserve">
|
||||
<source>Error saving SMP servers</source>
|
||||
<target>Ошибка при сохранении SMP серверов</target>
|
||||
@@ -1136,6 +1151,11 @@
|
||||
<target>Как форматировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ICE servers (one per line)" xml:space="preserve">
|
||||
<source>ICE servers (one per line)</source>
|
||||
<target>ICE серверы (один на строке)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If the video fails to connect, flip the camera to resolve it." xml:space="preserve">
|
||||
<source>If the video fails to connect, flip the camera to resolve it.</source>
|
||||
<target>Если видео не соединилось, переключите камеру.</target>
|
||||
@@ -1363,6 +1383,11 @@ We will be adding server redundancy to prevent lost messages.</source>
|
||||
<target>Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." xml:space="preserve">
|
||||
<source>Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.</source>
|
||||
<target>Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" xml:space="preserve">
|
||||
<source>Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*</source>
|
||||
<target>Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*</target>
|
||||
@@ -1398,6 +1423,11 @@ We will be adding server redundancy to prevent lost messages.</source>
|
||||
<target>Текст сообщения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Messages" xml:space="preserve">
|
||||
<source>Messages</source>
|
||||
<target>Сообщения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrating database archive..." xml:space="preserve">
|
||||
<source>Migrating database archive...</source>
|
||||
<target>Данные чата перемещаются...</target>
|
||||
@@ -1858,6 +1888,11 @@ We will be adding server redundancy to prevent lost messages.</source>
|
||||
<target>Сохраненные SMP серверы будут удалены</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Сохраненные WebRTC ICE серверы будут удалены</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code" xml:space="preserve">
|
||||
<source>Scan QR code</source>
|
||||
<target>Сканировать QR код</target>
|
||||
@@ -2322,6 +2357,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Ожидается прием изображения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WebRTC ICE servers" xml:space="preserve">
|
||||
<source>WebRTC ICE servers</source>
|
||||
<target>WebRTC ICE серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome %@!" xml:space="preserve">
|
||||
<source>Welcome %@!</source>
|
||||
<target>Здравствуйте %@!</target>
|
||||
@@ -2477,6 +2517,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Вы используете инкогнито профиль для этой группы - чтобы предотвратить раскрытие вашего основного профиля, приглашать контакты не разрешено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your ICE servers" xml:space="preserve">
|
||||
<source>Your ICE servers</source>
|
||||
<target>Ваши ICE серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SMP servers" xml:space="preserve">
|
||||
<source>Your SMP servers</source>
|
||||
<target>Ваши SMP серверы</target>
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
5CB0BA952827143500B3292C /* MakeConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeConnection.swift; sourceTree = "<group>"; };
|
||||
5CB0BA992827FD8800B3292C /* HowItWorks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HowItWorks.swift; sourceTree = "<group>"; };
|
||||
5CB2084E28DA4B4800D024EC /* RTCServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTCServers.swift; sourceTree = "<group>"; };
|
||||
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendChat.swift; sourceTree = "<group>"; };
|
||||
5CB346E62868D76D001FD2EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushEnvironment.swift; sourceTree = "<group>"; };
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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" = "Конфиденциальность";
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user