Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2020-07-22 01:19:33 +03:00
commit e28ec6b7ca
13 changed files with 150 additions and 393 deletions

View File

@ -1105,4 +1105,22 @@ public final class MediaBox {
return EmptyDisposable
}
}
public func allFileContexts() -> Signal<[(partial: String, complete: String)], NoError> {
return Signal { subscriber in
self.dataQueue.async {
var result: [(partial: String, complete: String)] = []
for (id, _) in self.fileContexts {
let paths = self.storePathsForId(id.id)
result.append((partial: paths.partial, complete: paths.complete))
}
subscriber.putNext(result)
subscriber.putCompletion()
}
return EmptyDisposable
}
}
}

View File

@ -1047,7 +1047,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
#if DEBUG
//debugSaveState(basePath: basePath, name: "previous1")
debugRestoreState(basePath: basePath, name: "previous1")
//debugRestoreState(basePath: basePath, name: "previous1")
#endif
let startTime = CFAbsoluteTimeGetCurrent()

View File

@ -73,6 +73,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case playerEmbedding(Bool)
case playlistPlayback(Bool)
case videoCalls(Bool)
case videoCallsReference(Bool)
case videoCallsInfo(PresentationTheme, String)
case hostInfo(PresentationTheme, String)
case versionInfo(PresentationTheme)
@ -89,7 +90,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.experiments.rawValue
case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback:
return DebugControllerSection.experiments.rawValue
case .videoCalls, .videoCallsInfo:
case .videoCalls, .videoCallsReference, .videoCallsInfo:
return DebugControllerSection.videoExperiments.rawValue
case .hostInfo, .versionInfo:
return DebugControllerSection.info.rawValue
@ -150,12 +151,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 25
case .videoCalls:
return 26
case .videoCallsInfo:
case .videoCallsReference:
return 27
case .hostInfo:
case .videoCallsInfo:
return 28
case .versionInfo:
case .hostInfo:
return 29
case .versionInfo:
return 30
}
}
@ -583,6 +586,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case let .videoCallsReference(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Reference Implementation", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
settings.videoCallsReference = value
return settings
})
}).start()
})
case let .videoCallsInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .hostInfo(theme, string):
@ -631,6 +644,9 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
entries.append(.videoCalls(experimentalSettings.videoCalls))
if experimentalSettings.videoCalls {
entries.append(.videoCallsReference(experimentalSettings.videoCallsReference))
}
entries.append(.videoCallsInfo(presentationData.theme, "Enables experimental transmission of electromagnetic radiation synchronized with pressure waves. Needs to be enabled on both sides."))
if let backupHostOverride = networkSettings?.backupHostOverride {

View File

@ -288,9 +288,9 @@ public final class PresentationCallImpl: PresentationCall {
self.isVideo = startWithVideo
if self.isVideo {
self.videoCapturer = OngoingCallVideoCapturer()
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .outgoingRequested, remoteVideoState: .inactive))
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .outgoingRequested, remoteVideoState: .active))
} else {
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .possible : .notAvailable, remoteVideoState: .inactive))
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .possible : .notAvailable, remoteVideoState: .active))
}
self.serializedData = serializedData
@ -483,7 +483,7 @@ public final class PresentationCallImpl: PresentationCall {
} else {
mappedVideoState = .notAvailable
}
if videoWasActive {
if self.videoWasActive {
mappedRemoteVideoState = .active
} else {
mappedRemoteVideoState = .inactive

View File

@ -117,8 +117,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return OngoingCallContext.maxLayer
}
public static func voipVersions(includeExperimental: Bool) -> [String] {
return OngoingCallContext.versions(includeExperimental: includeExperimental)
public static func voipVersions(includeExperimental: Bool, includeReference: Bool) -> [String] {
return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
}
public init(accountManager: AccountManager, enableVideoCalls: Bool, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) {

View File

@ -251,7 +251,7 @@ public final class AccountContextImpl: AccountContext {
self.experimentalUISettingsDisposable = (sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.experimentalUISettings])
|> deliverOnMainQueue).start(next: { sharedData in
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings {
account.callSessionManager.updateVersions(versions: PresentationCallManagerImpl.voipVersions(includeExperimental: settings.videoCalls))
account.callSessionManager.updateVersions(versions: PresentationCallManagerImpl.voipVersions(includeExperimental: settings.videoCalls, includeReference: settings.videoCallsReference))
}
})
}

View File

@ -401,7 +401,7 @@ final class SharedApplicationContext {
}
}
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, voipVersions: PresentationCallManagerImpl.voipVersions(includeExperimental: false), appData: self.deviceToken.get()
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, voipVersions: PresentationCallManagerImpl.voipVersions(includeExperimental: false, includeReference: false), appData: self.deviceToken.get()
|> map { token in
let data = buildConfig.bundleData(withAppToken: token, signatureDict: signatureDict)
if let data = data, let jsonString = String(data: data, encoding: .utf8) {

View File

@ -10,6 +10,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
public var knockoutWallpaper: Bool
public var foldersTabAtBottom: Bool
public var videoCalls: Bool
public var videoCallsReference: Bool
public var playerEmbedding: Bool
public var playlistPlayback: Bool
@ -22,6 +23,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
knockoutWallpaper: false,
foldersTabAtBottom: false,
videoCalls: false,
videoCallsReference: true,
playerEmbedding: false,
playlistPlayback: false
)
@ -35,6 +37,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
knockoutWallpaper: Bool,
foldersTabAtBottom: Bool,
videoCalls: Bool,
videoCallsReference: Bool,
playerEmbedding: Bool,
playlistPlayback: Bool
) {
@ -45,6 +48,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
self.knockoutWallpaper = knockoutWallpaper
self.foldersTabAtBottom = foldersTabAtBottom
self.videoCalls = videoCalls
self.videoCallsReference = videoCallsReference
self.playerEmbedding = playerEmbedding
self.playlistPlayback = playlistPlayback
}
@ -57,6 +61,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0
self.foldersTabAtBottom = decoder.decodeInt32ForKey("foldersTabAtBottom", orElse: 0) != 0
self.videoCalls = decoder.decodeInt32ForKey("videoCalls", orElse: 0) != 0
self.videoCallsReference = decoder.decodeInt32ForKey("videoCallsReference", orElse: 1) != 0
self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0
self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0
}
@ -69,6 +74,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
encoder.encodeInt32(self.knockoutWallpaper ? 1 : 0, forKey: "knockoutWallpaper")
encoder.encodeInt32(self.foldersTabAtBottom ? 1 : 0, forKey: "foldersTabAtBottom")
encoder.encodeInt32(self.videoCalls ? 1 : 0, forKey: "videoCalls")
encoder.encodeInt32(self.videoCallsReference ? 1 : 0, forKey: "videoCallsReference")
encoder.encodeInt32(self.playerEmbedding ? 1 : 0, forKey: "playerEmbedding")
encoder.encodeInt32(self.playlistPlayback ? 1 : 0, forKey: "playlistPlayback")
}

View File

@ -248,6 +248,7 @@ private protocol OngoingCallThreadLocalContextProtocol: class {
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer)
func nativeAcceptVideo(_ capturer: OngoingCallVideoCapturer)
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
func nativeBeginTermination()
func nativeDebugInfo() -> String
func nativeVersion() -> String
func nativeGetDerivedState() -> Data
@ -270,6 +271,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
self.stop(completion)
}
func nativeBeginTermination() {
}
func nativeSetIsMuted(_ value: Bool) {
self.setIsMuted(value)
}
@ -336,6 +340,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
self.stop(completion)
}
func nativeBeginTermination() {
self.beginTermination()
}
func nativeSetIsMuted(_ value: Bool) {
self.setIsMuted(value)
}
@ -458,14 +466,12 @@ public final class OngoingCallContext {
public static var maxLayer: Int32 {
return OngoingCallThreadLocalContext.maxLayer()
//return max(OngoingCallThreadLocalContext.maxLayer(), OngoingCallThreadLocalContextWebrtc.maxLayer())
}
public static func versions(includeExperimental: Bool) -> [String] {
public static func versions(includeExperimental: Bool, includeReference: Bool) -> [String] {
var result: [String] = [OngoingCallThreadLocalContext.version()]
if includeExperimental {
result.append(OngoingCallThreadLocalContextWebrtc.version())
//result.append(OngoingCallThreadLocalContextWebrtcCustom.version())
result.append(contentsOf: OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: includeReference))
}
return result
}
@ -489,7 +495,7 @@ public final class OngoingCallContext {
|> take(1)
|> deliverOn(queue)).start(next: { [weak self] _ in
if let strongSelf = self {
if version == OngoingCallThreadLocalContextWebrtc.version() {
if OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: true).contains(version) {
var voipProxyServer: VoipProxyServerWebrtc?
if let proxyServer = proxyServer {
switch proxyServer.connection {
@ -520,7 +526,7 @@ public final class OngoingCallContext {
))
}
}
let context = OngoingCallThreadLocalContextWebrtc(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, rtcServers: rtcServers, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtc(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtc), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, rtcServers: rtcServers, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtc(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtc), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
}, videoCapturer: video?.impl)
@ -631,6 +637,12 @@ public final class OngoingCallContext {
}
}
public func beginTermination() {
self.withContext { context in
context.nativeBeginTermination()
}
}
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise<String?>) {
let account = self.account
let logPath = self.logPath

View File

@ -1,353 +0,0 @@
#include "NetworkManager.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/client/basic_port_allocator.h"
#include "p2p/base/p2p_transport_channel.h"
#include "p2p/base/basic_async_resolver_factory.h"
#include "api/packet_socket_factory.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "p2p/base/ice_credentials_iterator.h"
#include "api/jsep_ice_candidate.h"
extern "C" {
#include <openssl/sha.h>
#include <openssl/aes.h>
#include <openssl/modes.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
}
#ifdef TGVOIP_NAMESPACE
namespace TGVOIP_NAMESPACE {
#endif
static void KDF2(unsigned char *encryptionKey, unsigned char *msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv) {
uint8_t sA[32], sB[32];
uint8_t buf[16 + 36];
memcpy(buf, msgKey, 16);
memcpy(buf + 16, encryptionKey + x, 36);
SHA256(buf, 16 + 36, sA);
memcpy(buf, encryptionKey + 40 + x, 36);
memcpy(buf + 36, msgKey, 16);
SHA256(buf, 36 + 16, sB);
memcpy(aesKey, sA, 8);
memcpy(aesKey + 8, sB + 8, 16);
memcpy(aesKey + 8 + 16, sA + 24, 8);
memcpy(aesIv, sB, 8);
memcpy(aesIv + 8, sA + 8, 16);
memcpy(aesIv + 8 + 16, sB + 24, 8);
}
static void aesIgeEncrypt(uint8_t *in, uint8_t *out, size_t length, uint8_t *key, uint8_t *iv) {
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
static void aesIgeDecrypt(uint8_t *in, uint8_t *out, size_t length, uint8_t *key, uint8_t *iv) {
AES_KEY akey;
AES_set_decrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
static absl::optional<rtc::CopyOnWriteBuffer> decryptPacket(const rtc::CopyOnWriteBuffer &packet, const TgVoipEncryptionKey &encryptionKey) {
if (packet.size() < 16 + 16) {
return absl::nullopt;
}
unsigned char msgKey[16];
memcpy(msgKey, packet.data(), 16);
int x = encryptionKey.isOutgoing ? 8 : 0;
unsigned char aesKey[32];
unsigned char aesIv[32];
KDF2((unsigned char *)encryptionKey.value.data(), msgKey, x, aesKey, aesIv);
size_t decryptedSize = packet.size() - 16;
if (decryptedSize < 0 || decryptedSize > 128 * 1024) {
return absl::nullopt;
}
if (decryptedSize % 16 != 0) {
return absl::nullopt;
}
rtc::Buffer decryptionBuffer(decryptedSize);
aesIgeDecrypt(((uint8_t *)packet.data()) + 16, decryptionBuffer.begin(), decryptionBuffer.size(), aesKey, aesIv);
rtc::ByteBufferWriter msgKeyData;
msgKeyData.WriteBytes((const char *)encryptionKey.value.data() + 88 + x, 32);
msgKeyData.WriteBytes((const char *)decryptionBuffer.data(), decryptionBuffer.size());
unsigned char msgKeyLarge[32];
SHA256((uint8_t *)msgKeyData.Data(), msgKeyData.Length(), msgKeyLarge);
uint16_t innerSize;
memcpy(&innerSize, decryptionBuffer.data(), 2);
unsigned char checkMsgKey[16];
memcpy(checkMsgKey, msgKeyLarge + 8, 16);
if (memcmp(checkMsgKey, msgKey, 16) != 0) {
return absl::nullopt;
}
if (innerSize < 0 || innerSize > decryptionBuffer.size() - 2) {
return absl::nullopt;
}
rtc::CopyOnWriteBuffer decryptedPacket;
decryptedPacket.AppendData((const char *)decryptionBuffer.data() + 2, innerSize);
return decryptedPacket;
}
static absl::optional<rtc::Buffer> encryptPacket(const rtc::CopyOnWriteBuffer &packet, const TgVoipEncryptionKey &encryptionKey) {
if (packet.size() > UINT16_MAX) {
return absl::nullopt;
}
rtc::ByteBufferWriter innerData;
uint16_t packetSize = (uint16_t)packet.size();
innerData.WriteBytes((const char *)&packetSize, 2);
innerData.WriteBytes((const char *)packet.data(), packet.size());
size_t innerPadding = 16 - innerData.Length() % 16;
uint8_t paddingData[16];
RAND_bytes(paddingData, (int)innerPadding);
innerData.WriteBytes((const char *)paddingData, innerPadding);
if (innerData.Length() % 16 != 0) {
assert(false);
return absl::nullopt;
}
int x = encryptionKey.isOutgoing ? 0 : 8;
rtc::ByteBufferWriter msgKeyData;
msgKeyData.WriteBytes((const char *)encryptionKey.value.data() + 88 + x, 32);
msgKeyData.WriteBytes(innerData.Data(), innerData.Length());
unsigned char msgKeyLarge[32];
SHA256((uint8_t *)msgKeyData.Data(), msgKeyData.Length(), msgKeyLarge);
unsigned char msgKey[16];
memcpy(msgKey, msgKeyLarge + 8, 16);
unsigned char aesKey[32];
unsigned char aesIv[32];
KDF2((unsigned char *)encryptionKey.value.data(), msgKey, x, aesKey, aesIv);
rtc::Buffer encryptedPacket;
encryptedPacket.AppendData((const char *)msgKey, 16);
rtc::Buffer encryptionBuffer(innerData.Length());
aesIgeEncrypt((uint8_t *)innerData.Data(), encryptionBuffer.begin(), innerData.Length(), aesKey, aesIv);
encryptedPacket.AppendData(encryptionBuffer.begin(), encryptionBuffer.size());
/*rtc::CopyOnWriteBuffer testBuffer;
testBuffer.AppendData(encryptedPacket.data(), encryptedPacket.size());
TgVoipEncryptionKey testKey;
testKey.value = encryptionKey.value;
testKey.isOutgoing = !encryptionKey.isOutgoing;
decryptPacket(testBuffer, testKey);*/
return encryptedPacket;
}
NetworkManager::NetworkManager(
rtc::Thread *thread,
TgVoipEncryptionKey encryptionKey,
bool enableP2P,
std::vector<TgVoipRtcServer> const &rtcServers,
std::function<void (const NetworkManager::State &)> stateUpdated,
std::function<void (const rtc::CopyOnWriteBuffer &)> packetReceived,
std::function<void (const std::vector<uint8_t> &)> signalingDataEmitted
) :
_thread(thread),
_encryptionKey(encryptionKey),
_stateUpdated(stateUpdated),
_packetReceived(packetReceived),
_signalingDataEmitted(signalingDataEmitted) {
assert(_thread->IsCurrent());
_socketFactory.reset(new rtc::BasicPacketSocketFactory(_thread));
_networkManager = std::make_unique<rtc::BasicNetworkManager>();
_portAllocator.reset(new cricket::BasicPortAllocator(_networkManager.get(), _socketFactory.get(), nullptr, nullptr));
uint32_t flags = cricket::PORTALLOCATOR_DISABLE_TCP;
if (!enableP2P) {
flags |= cricket::PORTALLOCATOR_DISABLE_UDP;
flags |= cricket::PORTALLOCATOR_DISABLE_STUN;
}
_portAllocator->set_flags(_portAllocator->flags() | flags);
_portAllocator->Initialize();
cricket::ServerAddresses stunServers;
std::vector<cricket::RelayServerConfig> turnServers;
if (rtcServers.size() == 0 || rtcServers[0].host == "hlgkfjdrtjfykgulhijkljhulyo.uksouth.cloudapp.azure.com") {
rtc::SocketAddress defaultStunAddress = rtc::SocketAddress("134.122.52.178", 3478);
stunServers.insert(defaultStunAddress);
turnServers.push_back(cricket::RelayServerConfig(
rtc::SocketAddress("134.122.52.178", 3478),
"openrelay",
"openrelay",
cricket::PROTO_UDP
));
} else {
for (auto &server : rtcServers) {
if (server.isTurn) {
turnServers.push_back(cricket::RelayServerConfig(
rtc::SocketAddress(server.host, server.port),
server.login,
server.password,
cricket::PROTO_UDP
));
} else {
rtc::SocketAddress stunAddress = rtc::SocketAddress(server.host, server.port);
stunServers.insert(stunAddress);
}
}
}
_portAllocator->SetConfiguration(stunServers, turnServers, 2, webrtc::NO_PRUNE);
_asyncResolverFactory = std::make_unique<webrtc::BasicAsyncResolverFactory>();
_transportChannel.reset(new cricket::P2PTransportChannel("transport", 0, _portAllocator.get(), _asyncResolverFactory.get(), nullptr));
cricket::IceConfig iceConfig;
iceConfig.continual_gathering_policy = cricket::GATHER_CONTINUALLY;
_transportChannel->SetIceConfig(iceConfig);
cricket::IceParameters localIceParameters(
"gcp3",
"zWDKozH8/3JWt8he3M/CMj5R",
false
);
cricket::IceParameters remoteIceParameters(
"acp3",
"aWDKozH8/3JWt8he3M/CMj5R",
false
);
_transportChannel->SetIceParameters(_encryptionKey.isOutgoing ? localIceParameters : remoteIceParameters);
_transportChannel->SetIceRole(_encryptionKey.isOutgoing ? cricket::ICEROLE_CONTROLLING : cricket::ICEROLE_CONTROLLED);
_transportChannel->SignalCandidateGathered.connect(this, &NetworkManager::candidateGathered);
_transportChannel->SignalGatheringState.connect(this, &NetworkManager::candidateGatheringState);
_transportChannel->SignalIceTransportStateChanged.connect(this, &NetworkManager::transportStateChanged);
_transportChannel->SignalReadPacket.connect(this, &NetworkManager::transportPacketReceived);
_transportChannel->MaybeStartGathering();
_transportChannel->SetRemoteIceMode(cricket::ICEMODE_FULL);
_transportChannel->SetRemoteIceParameters((!_encryptionKey.isOutgoing) ? localIceParameters : remoteIceParameters);
}
NetworkManager::~NetworkManager() {
assert(_thread->IsCurrent());
_transportChannel.reset();
_asyncResolverFactory.reset();
_portAllocator.reset();
_networkManager.reset();
_socketFactory.reset();
}
void NetworkManager::receiveSignalingData(const rtc::CopyOnWriteBuffer &data) {
rtc::ByteBufferReader reader((const char *)data.data(), data.size());
uint32_t candidateCount = 0;
if (!reader.ReadUInt32(&candidateCount)) {
return;
}
std::vector<std::string> candidates;
for (uint32_t i = 0; i < candidateCount; i++) {
uint32_t candidateLength = 0;
if (!reader.ReadUInt32(&candidateLength)) {
return;
}
std::string candidate;
if (!reader.ReadString(&candidate, candidateLength)) {
return;
}
candidates.push_back(candidate);
}
for (auto &serializedCandidate : candidates) {
webrtc::JsepIceCandidate parseCandidate("", 0);
if (parseCandidate.Initialize(serializedCandidate, nullptr)) {
auto parsedCandidate = parseCandidate.candidate();
_transportChannel->AddRemoteCandidate(parsedCandidate);
}
}
}
void NetworkManager::sendPacket(const rtc::CopyOnWriteBuffer &packet) {
auto encryptedPacket = encryptPacket(packet, _encryptionKey);
if (encryptedPacket.has_value()) {
rtc::PacketOptions packetOptions;
_transportChannel->SendPacket((const char *)encryptedPacket->data(), encryptedPacket->size(), packetOptions, 0);
}
}
void NetworkManager::candidateGathered(cricket::IceTransportInternal *transport, const cricket::Candidate &candidate) {
assert(_thread->IsCurrent());
webrtc::JsepIceCandidate iceCandidate("", 0);
iceCandidate.SetCandidate(candidate);
std::string serializedCandidate;
if (!iceCandidate.ToString(&serializedCandidate)) {
return;
}
std::vector<std::string> candidates;
candidates.push_back(serializedCandidate);
rtc::ByteBufferWriter writer;
writer.WriteUInt32((uint32_t)candidates.size());
for (auto string : candidates) {
writer.WriteUInt32((uint32_t)string.size());
writer.WriteString(string);
}
std::vector<uint8_t> data;
data.resize(writer.Length());
memcpy(data.data(), writer.Data(), writer.Length());
_signalingDataEmitted(data);
}
void NetworkManager::candidateGatheringState(cricket::IceTransportInternal *transport) {
assert(_thread->IsCurrent());
}
void NetworkManager::transportStateChanged(cricket::IceTransportInternal *transport) {
assert(_thread->IsCurrent());
auto state = transport->GetIceTransportState();
bool isConnected = false;
switch (state) {
case webrtc::IceTransportState::kConnected:
case webrtc::IceTransportState::kCompleted:
isConnected = true;
break;
default:
break;
}
NetworkManager::State emitState;
emitState.isReadyToSendData = isConnected;
_stateUpdated(emitState);
}
void NetworkManager::transportReadyToSend(cricket::IceTransportInternal *transport) {
assert(_thread->IsCurrent());
}
void NetworkManager::transportPacketReceived(rtc::PacketTransportInternal *transport, const char *bytes, size_t size, const int64_t &timestamp, int unused) {
assert(_thread->IsCurrent());
rtc::CopyOnWriteBuffer packet;
packet.AppendData(bytes, size);
auto decryptedPacket = decryptPacket(packet, _encryptionKey);
if (decryptedPacket.has_value()) {
_packetReceived(decryptedPacket.value());
}
}
#ifdef TGVOIP_NAMESPACE
}
#endif

View File

@ -107,12 +107,14 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
+ (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction;
+ (void)applyServerConfig:(NSString * _Nullable)data;
+ (int32_t)maxLayer;
+ (NSString * _Nonnull)version;
+ (NSArray<NSString *> * _Nonnull)versionsWithIncludeReference:(bool)includeReference;
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc);
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray<VoipRtcServerWebrtc *> * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray<VoipRtcServerWebrtc *> * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
- (void)beginTermination;
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
- (bool)needRate;

View File

@ -7,6 +7,8 @@
#import "Instance.h"
#import "InstanceImpl.h"
#import "reference/InstanceImplReference.h"
#import "VideoCaptureInterface.h"
#ifndef WEBRTC_IOS
@ -102,7 +104,26 @@
@end
@interface OngoingCallThreadLocalContextWebrtcTerminationResult : NSObject
@property (nonatomic, readonly) tgcalls::FinalState finalState;
@end
@implementation OngoingCallThreadLocalContextWebrtcTerminationResult
- (instancetype)initWithFinalState:(tgcalls::FinalState)finalState {
self = [super init];
if (self != nil) {
_finalState = finalState;
}
return self;
}
@end
@interface OngoingCallThreadLocalContextWebrtc () {
NSString *_version;
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
int32_t _contextId;
@ -113,6 +134,7 @@
NSTimeInterval _callPacketTimeout;
std::unique_ptr<tgcalls::Instance> _tgVoip;
OngoingCallThreadLocalContextWebrtcTerminationResult *_terminationResult;
OngoingCallStateWebrtc _state;
OngoingCallVideoStateWebrtc _videoState;
@ -213,16 +235,23 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
return 92;
}
+ (NSString *)version {
return @"2.7.7";
+ (NSArray<NSString *> * _Nonnull)versionsWithIncludeReference:(bool)includeReference {
if (includeReference) {
return @[@"2.7.7", @"2.8.8"];
} else {
return @[@"2.7.7"];
}
}
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray<VoipRtcServerWebrtc *> * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray<VoipRtcServerWebrtc *> * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
self = [super init];
if (self != nil) {
_version = version;
_queue = queue;
assert([queue isCurrent]);
assert([[OngoingCallThreadLocalContextWebrtc versionsWithIncludeReference:true] containsObject:version]);
_callReceiveTimeout = 20.0;
_callRingTimeout = 90.0;
_callConnectTimeout = 30.0;
@ -235,7 +264,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
_remoteVideoState = OngoingCallRemoteVideoStateActive;
} else {
_videoState = OngoingCallVideoStatePossible;
_remoteVideoState = OngoingCallRemoteVideoStateInactive;
_remoteVideoState = OngoingCallRemoteVideoStateActive;
}
std::vector<uint8_t> derivedStateValue;
@ -302,9 +331,8 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
.maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer]
};
std::vector<uint8_t> encryptionKeyValue;
encryptionKeyValue.resize(key.length);
memcpy(encryptionKeyValue.data(), key.bytes, key.length);
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
memcpy(encryptionKeyValue->data(), key.bytes, key.length);
tgcalls::EncryptionKey encryptionKey(encryptionKeyValue, isOutgoing);
@ -312,8 +340,9 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
tgcalls::Register<tgcalls::InstanceImpl>();
tgcalls::Register<tgcalls::InstanceImplReference>();
});
_tgVoip = tgcalls::Meta::Create("2.7.7", (tgcalls::Descriptor){
_tgVoip = tgcalls::Meta::Create([version UTF8String], (tgcalls::Descriptor){
.config = config,
.persistentState = (tgcalls::PersistentState){ derivedStateValue },
.endpoints = endpoints,
@ -346,8 +375,16 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
}];
},
.signalBarsUpdated = [](int value) {
.signalBarsUpdated = [weakSelf, queue](int value) {
[queue dispatch:^{
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_signalBars = value;
if (strongSelf->_signalBarsChanged) {
strongSelf->_signalBarsChanged(value);
}
}
}];
},
.remoteVideoIsActiveUpdated = [weakSelf, queue](bool isActive) {
[queue dispatch:^{
@ -396,15 +433,34 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
return false;
}
- (void)stopInstanceIfNeeded {
if (!_tgVoip) {
return;
}
tgcalls::FinalState finalState = _tgVoip->stop();
_tgVoip = nil;
_terminationResult = [[OngoingCallThreadLocalContextWebrtcTerminationResult alloc] initWithFinalState:finalState];
}
- (void)beginTermination {
[self stopInstanceIfNeeded];
}
- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
if (_tgVoip) {
tgcalls::FinalState finalState = _tgVoip->stop();
NSString *debugLog = [NSString stringWithUTF8String:finalState.debugLog.c_str()];
_lastDerivedState = [[NSData alloc] initWithBytes:finalState.persistentState.value.data() length:finalState.persistentState.value.size()];
if (completion) {
completion(debugLog, finalState.trafficStats.bytesSentWifi, finalState.trafficStats.bytesReceivedWifi, finalState.trafficStats.bytesSentMobile, finalState.trafficStats.bytesReceivedMobile);
[self stopInstanceIfNeeded];
if (completion) {
if (_terminationResult) {
NSString *debugLog = [NSString stringWithUTF8String:_terminationResult.finalState.debugLog.c_str()];
_lastDerivedState = [[NSData alloc] initWithBytes:_terminationResult.finalState.persistentState.value.data() length:_terminationResult.finalState.persistentState.value.size()];
if (completion) {
completion(debugLog, _terminationResult.finalState.trafficStats.bytesSentWifi, _terminationResult.finalState.trafficStats.bytesReceivedWifi, _terminationResult.finalState.trafficStats.bytesSentMobile, _terminationResult.finalState.trafficStats.bytesReceivedMobile);
}
} else {
if (completion) {
completion(@"", 0, 0, 0, 0);
}
}
}
}
@ -421,7 +477,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
- (NSString *)version {
return @"2.7.7";
return _version;
}
- (NSData * _Nonnull)getDerivedState {

@ -1 +1 @@
Subproject commit 83c85d20ccdde154acca4b964317de1e695f95d1
Subproject commit 8e9d3e56d43ffa4ed9ababd5fe7a4b5df8ec94d1