mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-25 23:47:58 +00:00
Merge branch 'master' into holes
This commit is contained in:
@@ -344,7 +344,7 @@ private final class SharedApplicationContext {
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
let networkArguments = NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManager.voipMaxLayer)
|
||||
let networkArguments = NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManager.voipMaxLayer, appData: BuildConfig.shared().bundleData)
|
||||
|
||||
let appGroupName = "group.\(Bundle.main.bundleIdentifier!)"
|
||||
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
||||
@@ -1076,8 +1076,8 @@ private final class SharedApplicationContext {
|
||||
}
|
||||
})
|
||||
|
||||
if !BuildConfig.shared().hockeyAppId.isEmpty {
|
||||
BITHockeyManager.shared().configure(withIdentifier: BuildConfig.shared().hockeyAppId, delegate: self)
|
||||
if let hockeyAppId = BuildConfig.shared().hockeyAppId, !hockeyAppId.isEmpty {
|
||||
BITHockeyManager.shared().configure(withIdentifier: hockeyAppId, delegate: self)
|
||||
BITHockeyManager.shared().crashManager.crashManagerStatus = .alwaysAsk
|
||||
BITHockeyManager.shared().start()
|
||||
BITHockeyManager.shared().authenticator.authenticateInstallation()
|
||||
@@ -1612,81 +1612,84 @@ private final class SharedApplicationContext {
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
|
||||
if let peerId = peerIdFromNotification(response.notification) {
|
||||
var messageId: MessageId? = nil
|
||||
if response.notification.request.content.categoryIdentifier == "watch" {
|
||||
messageId = messageIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
}
|
||||
self.openChatWhenReady(accountId: accountIdFromNotification(response.notification), peerId: peerId, messageId: messageId)
|
||||
}
|
||||
completionHandler()
|
||||
} else if response.actionIdentifier == "reply", let peerId = peerIdFromNotification(response.notification), let accountId = accountIdFromNotification(response.notification) {
|
||||
guard let response = response as? UNTextInputNotificationResponse, !response.userText.isEmpty else {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
let text = response.userText
|
||||
let signal = self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
|
||||
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||
return sharedContext.sharedContext.activeAccounts
|
||||
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
|
||||
for account in accounts {
|
||||
if account.1.id == accountId {
|
||||
return .single(account.1)
|
||||
}
|
||||
let _ = (accountIdFromNotification(response.notification, sharedContext: self.sharedContextPromise.get())
|
||||
|> deliverOnMainQueue).start(next: { accountId in
|
||||
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
|
||||
if let peerId = peerIdFromNotification(response.notification) {
|
||||
var messageId: MessageId? = nil
|
||||
if response.notification.request.content.categoryIdentifier == "watch" {
|
||||
messageId = messageIdFromNotification(peerId: peerId, notification: response.notification)
|
||||
}
|
||||
return .complete()
|
||||
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
|
||||
}
|
||||
completionHandler()
|
||||
} else if response.actionIdentifier == "reply", let peerId = peerIdFromNotification(response.notification), let accountId = accountId {
|
||||
guard let response = response as? UNTextInputNotificationResponse, !response.userText.isEmpty else {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
let text = response.userText
|
||||
let signal = self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { account -> Signal<Void, NoError> in
|
||||
if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) {
|
||||
let _ = applyMaxReadIndexInteractively(postbox: account.postbox, stateManager: account.stateManager, index: MessageIndex(id: messageId, timestamp: 0)).start()
|
||||
}
|
||||
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)])
|
||||
|> map { messageIds -> MessageId? in
|
||||
if messageIds.isEmpty {
|
||||
return nil
|
||||
} else {
|
||||
return messageIds[0]
|
||||
}
|
||||
}
|
||||
|> mapToSignal { messageId -> Signal<Void, NoError> in
|
||||
if let messageId = messageId {
|
||||
return account.postbox.unsentMessageIdsView()
|
||||
|> filter { view in
|
||||
return !view.ids.contains(messageId)
|
||||
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
|
||||
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||
return sharedContext.sharedContext.activeAccounts
|
||||
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
|
||||
for account in accounts {
|
||||
if account.1.id == accountId {
|
||||
return .single(account.1)
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { account -> Signal<Void, NoError> in
|
||||
if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) {
|
||||
let _ = applyMaxReadIndexInteractively(postbox: account.postbox, stateManager: account.stateManager, index: MessageIndex(id: messageId, timestamp: 0)).start()
|
||||
}
|
||||
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)])
|
||||
|> map { messageIds -> MessageId? in
|
||||
if messageIds.isEmpty {
|
||||
return nil
|
||||
} else {
|
||||
return messageIds[0]
|
||||
}
|
||||
}
|
||||
|> mapToSignal { messageId -> Signal<Void, NoError> in
|
||||
if let messageId = messageId {
|
||||
return account.postbox.unsentMessageIdsView()
|
||||
|> filter { view in
|
||||
return !view.ids.contains(messageId)
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
disposable.set((signal
|
||||
|> afterDisposed { [weak disposable] in
|
||||
Queue.mainQueue().async {
|
||||
if let disposable = disposable {
|
||||
self.replyFromNotificationsDisposables.remove(disposable)
|
||||
|> deliverOnMainQueue
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
disposable.set((signal
|
||||
|> afterDisposed { [weak disposable] in
|
||||
Queue.mainQueue().async {
|
||||
if let disposable = disposable {
|
||||
self.replyFromNotificationsDisposables.remove(disposable)
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
}).start())
|
||||
self.replyFromNotificationsDisposables.add(disposable)
|
||||
} else {
|
||||
completionHandler()
|
||||
}
|
||||
}).start())
|
||||
self.replyFromNotificationsDisposables.add(disposable)
|
||||
} else {
|
||||
completionHandler()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func registerForNotifications(context: AccountContext, authorize: Bool = true, completion: @escaping (Bool) -> Void = { _ in }) {
|
||||
@@ -1731,7 +1734,7 @@ private final class SharedApplicationContext {
|
||||
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
|
||||
@@ -1884,11 +1887,14 @@ private final class SharedApplicationContext {
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
if let context = self.contextValue {
|
||||
if let accountId = accountIdFromNotification(notification), context.context.account.id != accountId {
|
||||
completionHandler([.alert])
|
||||
let _ = (accountIdFromNotification(notification, sharedContext: self.sharedContextPromise.get())
|
||||
|> deliverOnMainQueue).start(next: { accountId in
|
||||
if let context = self.contextValue {
|
||||
if let accountId = accountId, context.context.account.id != accountId {
|
||||
completionHandler([.alert])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override var next: UIResponder? {
|
||||
@@ -1913,13 +1919,70 @@ private final class SharedApplicationContext {
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
private func accountIdFromNotification(_ notification: UNNotification) -> AccountRecordId? {
|
||||
if let id = notification.request.content.userInfo["accountId"] as? Int64 {
|
||||
return AccountRecordId(rawValue: id)
|
||||
} else {
|
||||
private func notificationPayloadKey(data: Data) -> Data? {
|
||||
if data.count < 8 {
|
||||
return nil
|
||||
}
|
||||
return data.subdata(in: 0 ..< 8)
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
private func accountIdFromNotification(_ notification: UNNotification, sharedContext: Signal<SharedApplicationContext, NoError>) -> Signal<AccountRecordId?, NoError> {
|
||||
if let id = notification.request.content.userInfo["accountId"] as? Int64 {
|
||||
return .single(AccountRecordId(rawValue: id))
|
||||
} else {
|
||||
var encryptedData: Data?
|
||||
if var encryptedPayload = notification.request.content.userInfo["p"] as? String {
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/")
|
||||
while encryptedPayload.count % 4 != 0 {
|
||||
encryptedPayload.append("=")
|
||||
}
|
||||
encryptedData = Data(base64Encoded: encryptedPayload)
|
||||
}
|
||||
if let encryptedData = encryptedData, let notificationKeyId = notificationPayloadKey(data: encryptedData) {
|
||||
return sharedContext
|
||||
|> take(1)
|
||||
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
||||
return sharedContext.sharedContext.activeAccounts
|
||||
|> take(1)
|
||||
|> mapToSignal { _, accounts, _ -> Signal<AccountRecordId?, NoError> in
|
||||
let keys = accounts.map { _, account, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in
|
||||
return masterNotificationsKey(account: account, ignoreDisabled: true)
|
||||
|> map { key in
|
||||
return (account.id, key)
|
||||
}
|
||||
}
|
||||
return combineLatest(keys)
|
||||
|> map { keys -> AccountRecordId? in
|
||||
for idAndKey in keys {
|
||||
if let (id, key) = idAndKey, key.id == notificationKeyId {
|
||||
return id
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let userId = notification.request.content.userInfo["userId"] as? Int {
|
||||
return sharedContext
|
||||
|> take(1)
|
||||
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
||||
return sharedContext.sharedContext.activeAccounts
|
||||
|> take(1)
|
||||
|> map { _, accounts, _ -> AccountRecordId? in
|
||||
for (_, account, _) in accounts {
|
||||
if Int(account.peerId.id) == userId {
|
||||
return account.id
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
|
||||
@@ -649,7 +649,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
if !didAppear {
|
||||
Queue.mainQueue().after(0.15, {
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments.init(presentationAnimation: .modalSheet))
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
@@ -698,7 +698,7 @@ final class AuthorizedApplicationContext {
|
||||
if let strongSelf = self{
|
||||
for text in alerts {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
@@ -712,10 +712,10 @@ final class AuthorizedApplicationContext {
|
||||
})
|
||||
|
||||
let importableContacts = self.context.sharedContext.contactDataManager?.importable() ?? .single([:])
|
||||
self.context.account.importableContacts.set(self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings])
|
||||
|> mapToSignal { sharedData -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in
|
||||
let settings: ContactSynchronizationSettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings) ?? .defaultSettings
|
||||
if settings.synchronizeDeviceContacts {
|
||||
self.context.account.importableContacts.set(self.context.account.postbox.preferencesView(keys: [PreferencesKeys.contactsSettings])
|
||||
|> mapToSignal { preferences -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in
|
||||
let settings: ContactsSettings = (preferences.values[PreferencesKeys.contactsSettings] as? ContactsSettings) ?? .defaultSettings
|
||||
if settings.synchronizeContacts {
|
||||
return importableContacts
|
||||
} else {
|
||||
return .single([:])
|
||||
@@ -783,7 +783,7 @@ final class AuthorizedApplicationContext {
|
||||
navigateToMessage()
|
||||
} else {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
|
||||
let controller = textAlertController(context: strongSelf.context, title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
@@ -840,7 +840,7 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|
||||
func openRootSearch() {
|
||||
self.rootController.openChatsSearch()
|
||||
self.rootController.openChatsController(activateSearch: true)
|
||||
}
|
||||
|
||||
func openRootCompose() {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
+ (instancetype _Nonnull)sharedBuildConfig;
|
||||
|
||||
@property (nonatomic, strong, readonly) NSString * _Nonnull hockeyAppId;
|
||||
@property (nonatomic, strong, readonly) NSData * _Nullable bundleData;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nullable hockeyAppId;
|
||||
@property (nonatomic, readonly) int32_t apiId;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nonnull apiHash;
|
||||
@property (nonatomic, readonly) bool isInternalBuild;
|
||||
|
||||
@@ -1,7 +1,265 @@
|
||||
#import "BuildConfig.h"
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import <MtProtoKitDynamic/MtProtoKitDynamic.h>
|
||||
|
||||
static uint32_t funcSwap32(uint32_t input)
|
||||
{
|
||||
return OSSwapBigToHostInt32(input);
|
||||
}
|
||||
|
||||
static uint32_t funcNoSwap32(uint32_t input)
|
||||
{
|
||||
return OSSwapLittleToHostInt32(input);
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic numbers used by Code Signing
|
||||
*/
|
||||
enum {
|
||||
kSecCodeMagicRequirement = 0xfade0c00, /* single requirement */
|
||||
kSecCodeMagicRequirementSet = 0xfade0c01, /* requirement set */
|
||||
kSecCodeMagicCodeDirectory = 0xfade0c02, /* CodeDirectory */
|
||||
kSecCodeMagicEmbeddedSignature = 0xfade0cc0, /* single-architecture embedded signature */
|
||||
kSecCodeMagicDetachedSignature = 0xfade0cc1, /* detached multi-architecture signature */
|
||||
kSecCodeMagicEntitlement = 0xfade7171, /* entitlement blob */
|
||||
|
||||
kSecCodeMagicByte = 0xfa /* shared first byte */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Structure of an embedded-signature SuperBlob
|
||||
*/
|
||||
typedef struct __BlobIndex {
|
||||
uint32_t type; /* type of entry */
|
||||
uint32_t offset; /* offset of entry */
|
||||
} CS_BlobIndex;
|
||||
|
||||
typedef struct __Blob {
|
||||
uint32_t magic; /* magic number */
|
||||
uint32_t length; /* total length of SuperBlob */
|
||||
} CS_Blob;
|
||||
|
||||
typedef struct __SuperBlob {
|
||||
CS_Blob blob;
|
||||
uint32_t count; /* number of index entries following */
|
||||
CS_BlobIndex index[]; /* (count) entries */
|
||||
/* followed by Blobs in no particular order as indicated by offsets in index */
|
||||
} CS_SuperBlob;
|
||||
|
||||
|
||||
/*
|
||||
* C form of a CodeDirectory.
|
||||
*/
|
||||
typedef struct __CodeDirectory {
|
||||
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
|
||||
uint32_t length; /* total length of CodeDirectory blob */
|
||||
uint32_t version; /* compatibility version */
|
||||
uint32_t flags; /* setup and mode flags */
|
||||
uint32_t hashOffset; /* offset of hash slot element at index zero */
|
||||
uint32_t identOffset; /* offset of identifier string */
|
||||
uint32_t nSpecialSlots; /* number of special hash slots */
|
||||
uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
|
||||
uint32_t codeLimit; /* limit to main image signature range */
|
||||
uint8_t hashSize; /* size of each hash in bytes */
|
||||
uint8_t hashType; /* type of hash (cdHashType* constants) */
|
||||
uint8_t spare1; /* unused (must be zero) */
|
||||
uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
|
||||
uint32_t spare2; /* unused (must be zero) */
|
||||
/* followed by dynamic content as located by offset fields above */
|
||||
} CS_CodeDirectory;
|
||||
|
||||
static MTPKCS * _Nullable parseSignature(const char* buffer, size_t size) {
|
||||
CS_SuperBlob* sb = (CS_SuperBlob*)buffer;
|
||||
if (OSSwapBigToHostInt32(sb->blob.magic) != kSecCodeMagicEmbeddedSignature)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t count = OSSwapBigToHostInt32(sb->count);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
{
|
||||
uint32_t offset = OSSwapBigToHostInt32(sb->index[i].offset);
|
||||
|
||||
const CS_Blob* blob = (const CS_Blob*)(buffer + offset);
|
||||
|
||||
if (OSSwapBigToHostInt32(blob->magic) == 0xfade0b01) // signature
|
||||
{
|
||||
printf("Embedded signature, length: %d\n", OSSwapBigToHostInt32(blob->length));
|
||||
|
||||
if (OSSwapBigToHostInt32(blob->length) != 8)
|
||||
{
|
||||
const unsigned char* message = (const unsigned char*)buffer + offset + 8;
|
||||
MTPKCS *result = [MTPKCS parse:message size:(OSSwapBigToHostInt32(blob->length) - 8)];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static MTPKCS * _Nullable parseArch(const char* buffer, size_t size) {
|
||||
uint32_t (*swap32)(uint32_t) = funcNoSwap32;
|
||||
|
||||
uint32_t offset = 0;
|
||||
|
||||
const struct mach_header* header = (struct mach_header*)(buffer + offset);
|
||||
|
||||
switch (header->magic) {
|
||||
case MH_CIGAM:
|
||||
swap32 = funcSwap32;
|
||||
case MH_MAGIC:
|
||||
offset += sizeof(struct mach_header);
|
||||
break;
|
||||
case MH_CIGAM_64:
|
||||
swap32 = funcSwap32;
|
||||
case MH_MAGIC_64:
|
||||
offset += sizeof(struct mach_header_64);
|
||||
break;
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
||||
const NXArchInfo *archInfo = NXGetArchInfoFromCpuType(swap32(header->cputype), swap32(header->cpusubtype));
|
||||
if (archInfo != NULL) {
|
||||
printf("Architecture: %s\n", archInfo->name);
|
||||
}
|
||||
|
||||
uint32_t commandCount = swap32(header->ncmds);
|
||||
|
||||
for (uint32_t i = 0; i < commandCount; i++) {
|
||||
const struct load_command* loadCommand = (const struct load_command*)(buffer + offset);
|
||||
uint32_t commandSize = swap32(loadCommand->cmdsize);
|
||||
|
||||
uint32_t commandType = swap32(loadCommand->cmd);
|
||||
if (commandType == LC_CODE_SIGNATURE) {
|
||||
const struct linkedit_data_command* dataCommand = (const struct linkedit_data_command*)(buffer + offset);
|
||||
uint32_t dataOffset = swap32(dataCommand->dataoff);
|
||||
uint32_t dataSize = swap32(dataCommand->datasize);
|
||||
|
||||
return parseSignature(buffer + dataOffset, dataSize);
|
||||
}
|
||||
|
||||
offset += commandSize;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static MTPKCS * _Nullable parseFat(const char *buffer, size_t size) {
|
||||
size_t offset = 0;
|
||||
|
||||
const struct fat_header* fatHeader = (const struct fat_header*)(buffer + offset);
|
||||
offset += sizeof(*fatHeader);
|
||||
|
||||
uint32_t archCount = OSSwapBigToHostInt32(fatHeader->nfat_arch);
|
||||
|
||||
for (uint32_t i = 0; i < archCount; i++) {
|
||||
const struct fat_arch* arch = (const struct fat_arch*)(buffer + offset);
|
||||
offset += sizeof(*arch);
|
||||
|
||||
uint32_t archOffset = OSSwapBigToHostInt32(arch->offset);
|
||||
uint32_t archSize = OSSwapBigToHostInt32(arch->size);
|
||||
|
||||
MTPKCS *result = parseArch(buffer + archOffset, archSize);
|
||||
if (result != nil) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static MTPKCS * _Nullable parseMachO(const char* buffer, size_t size) {
|
||||
const uint32_t* magic = (const uint32_t*)buffer;
|
||||
|
||||
if (*magic == FAT_CIGAM || *magic == FAT_MAGIC) {
|
||||
return parseFat(buffer, size);
|
||||
} else {
|
||||
return parseArch(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
static MTPKCS * _Nullable checkSignature(const char *filename) {
|
||||
char *buffer = NULL;
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
|
||||
buffer = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
|
||||
|
||||
if (buffer == MAP_FAILED) {
|
||||
if (buffer) {
|
||||
munmap(buffer, (size_t)st.st_size);
|
||||
}
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
MTPKCS *result = parseMachO(buffer, (size_t)st.st_size);
|
||||
if (buffer) {
|
||||
munmap(buffer, (size_t)st.st_size);
|
||||
}
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@interface BuildConfig () {
|
||||
NSData * _Nullable _bundleData;
|
||||
int32_t _apiId;
|
||||
NSString * _Nonnull _apiHash;
|
||||
NSString * _Nullable _hockeyAppId;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BuildConfig
|
||||
|
||||
+ (NSString *)bundleId {
|
||||
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
(__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecClass,
|
||||
@"bundleSeedID", kSecAttrAccount,
|
||||
@"", kSecAttrService,
|
||||
(id)kCFBooleanTrue, kSecReturnAttributes,
|
||||
nil];
|
||||
CFDictionaryRef result = nil;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
|
||||
if (status == errSecItemNotFound) {
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
|
||||
}
|
||||
if (status != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
|
||||
NSArray *components = [accessGroup componentsSeparatedByString:@"."];
|
||||
NSString *bundleSeedID = [[components objectEnumerator] nextObject];
|
||||
CFRelease(result);
|
||||
return bundleSeedID;
|
||||
}
|
||||
|
||||
+ (instancetype _Nonnull)sharedBuildConfig {
|
||||
static BuildConfig *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
@@ -11,16 +269,101 @@
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
char buf[3];
|
||||
buf[2] = '\0';
|
||||
NSString *hex = @(APP_CONFIG_DATA);
|
||||
assert(0 == [hex length] % 2);
|
||||
unsigned char *bytes = malloc([hex length]/2);
|
||||
unsigned char *bp = bytes;
|
||||
for (CFIndex i = 0; i < [hex length]; i += 2) {
|
||||
buf[0] = [hex characterAtIndex:i];
|
||||
buf[1] = [hex characterAtIndex:i+1];
|
||||
char *b2 = NULL;
|
||||
*bp++ = strtol(buf, &b2, 16);
|
||||
assert(b2 == buf + 2);
|
||||
}
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES];
|
||||
if ([data length] == 0) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
const char *streamCode = "Cypher";
|
||||
int keyLength = (int)strlen(streamCode);
|
||||
int keyOffset = 0;
|
||||
for (NSUInteger i = 0; i < data.length; i++) {
|
||||
((uint8_t *)data.mutableBytes)[i] ^= ((uint8_t *)streamCode)[keyOffset % keyLength];
|
||||
keyOffset += 1;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
uint32_t header = 0;
|
||||
[data getBytes:&header range:NSMakeRange(offset, 4)];
|
||||
offset += 4;
|
||||
if (header != 0xabcdef01U) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
[data getBytes:&_apiId range:NSMakeRange(offset, 4)];
|
||||
offset += 4;
|
||||
|
||||
int32_t apiHashLength = 0;
|
||||
[data getBytes:&apiHashLength range:NSMakeRange(offset, 4)];
|
||||
offset += 4;
|
||||
|
||||
if (apiHashLength > 0) {
|
||||
_apiHash = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(offset, apiHashLength)] encoding:NSUTF8StringEncoding];
|
||||
offset += apiHashLength;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int32_t hockeyappIdLength = 0;
|
||||
[data getBytes:&hockeyappIdLength range:NSMakeRange(offset, 4)];
|
||||
offset += 4;
|
||||
|
||||
if (hockeyappIdLength > 0) {
|
||||
_hockeyAppId = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(offset, hockeyappIdLength)] encoding:NSUTF8StringEncoding];
|
||||
offset += hockeyappIdLength;
|
||||
}
|
||||
|
||||
NSString *bundleId = [BuildConfig bundleId];
|
||||
|
||||
MTPKCS *signature = checkSignature([[[NSBundle mainBundle] executablePath] UTF8String]);
|
||||
NSMutableDictionary *dataDict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (bundleId != nil) {
|
||||
dataDict[@"bundleId"] = bundleId;
|
||||
}
|
||||
if (signature.name != nil) {
|
||||
dataDict[@"name"] = signature.name;
|
||||
}
|
||||
if (signature.data != nil) {
|
||||
dataDict[@"data"] = [MTSha1(signature.data) base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
_bundleData = [NSJSONSerialization dataWithJSONObject:dataDict options:0 error:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)bundleData {
|
||||
return _bundleData;
|
||||
}
|
||||
|
||||
- (int32_t)apiId {
|
||||
return APP_CONFIG_API_ID;
|
||||
return _apiId;
|
||||
}
|
||||
|
||||
- (NSString * _Nonnull)apiHash {
|
||||
return @(APP_CONFIG_API_HASH);
|
||||
return _apiHash;
|
||||
}
|
||||
|
||||
- (NSString * _Nonnull)hockeyAppId {
|
||||
return @(APP_CONFIG_HOCKEYAPPID);
|
||||
- (NSString * _Nullable)hockeyAppId {
|
||||
return _hockeyAppId;
|
||||
}
|
||||
|
||||
- (bool)isInternalBuild {
|
||||
|
||||
@@ -1 +1 @@
|
||||
#include "../../Telegram-iOS-Shared/Config-AppStore.xcconfig"
|
||||
#include "../../Telegram-iOS-Shared/Config/AppStore/Config.xcconfig"
|
||||
|
||||
@@ -1 +1 @@
|
||||
#include "../../Telegram-iOS-Shared/Config-AppStoreLLC.xcconfig"
|
||||
#include "../../Telegram-iOS-Shared/Config/AppStoreLLC/Config.xcconfig"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../Telegram-iOS-Shared/Config-Hockeyapp Internal.xcconfig"
|
||||
1
Telegram-iOS/Config-Hockeyapp-Internal.xcconfig
Normal file
1
Telegram-iOS/Config-Hockeyapp-Internal.xcconfig
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../Telegram-iOS-Shared/Config/Hockeyapp-Internal/Config.xcconfig"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../Telegram-iOS-Shared/Config-Hockeyapp.xcconfig"
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.4.1</string>
|
||||
<string>5.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -452,14 +452,6 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
|
||||
return settings
|
||||
})
|
||||
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings, { current in
|
||||
var settings: ContactSynchronizationSettings = current as? ContactSynchronizationSettings ?? .defaultSettings
|
||||
if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync {
|
||||
settings.synchronizeDeviceContacts = false
|
||||
}
|
||||
return settings
|
||||
})
|
||||
|
||||
if let secretInlineBotsInitialized = secretInlineBotsInitialized, secretInlineBotsInitialized {
|
||||
ApplicationSpecificNotice.setSecretChatInlineBotUsage(transaction: transaction)
|
||||
}
|
||||
@@ -469,6 +461,13 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.contactsSettings, { current in
|
||||
var settings = current as? ContactsSettings ?? ContactsSettings.defaultSettings
|
||||
if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync {
|
||||
settings.synchronizeContacts = false
|
||||
}
|
||||
return settings
|
||||
})
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
|
||||
@@ -86,7 +86,7 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P
|
||||
|
||||
if let forward = message.forwardInfo {
|
||||
let bridgeForward = TGBridgeForwardedMessageMediaAttachment()
|
||||
bridgeForward.peerId = makeBridgeIdentifier(forward.author.id)
|
||||
bridgeForward.peerId = forward.author.flatMap({ makeBridgeIdentifier($0.id) }) ?? 0
|
||||
if let sourceMessageId = forward.sourceMessageId {
|
||||
bridgeForward.mid = sourceMessageId.id
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"PUSH_CHANNEL_MESSAGE_GIF" = "%1$@ posted a GIF";
|
||||
"PUSH_CHANNEL_MESSAGES" = "%1$@ posted %2$@ messages";
|
||||
|
||||
"PUSH_PINNED_TEXT" = "%1$@ pinned \"%2$@\"";
|
||||
"PUSH_PINNED_TEXT" = "%1$@ pinned \"%2$@\" ";
|
||||
"PUSH_PINNED_NOTEXT" = "%1$@ pinned a message";
|
||||
"PUSH_PINNED_PHOTO" = "%1$@ pinned a photo";
|
||||
"PUSH_PINNED_VIDEO" = "%1$@ pinned a video";
|
||||
@@ -188,7 +188,7 @@
|
||||
"PUSH_CHAT_MESSAGES_1" = "%2$@|%1$@ sent a message";
|
||||
"PUSH_CHAT_MESSAGES_any" = "%2$@|%1$@ sent %3$d messages";
|
||||
|
||||
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\"";
|
||||
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
|
||||
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
|
||||
"PUSH_PINNED_PHOTO" = "%1$@|pinned a photo";
|
||||
"PUSH_PINNED_VIDEO" = "%1$@|pinned a video";
|
||||
@@ -500,9 +500,9 @@
|
||||
"Notification.Kicked" = "%@ removed %@";
|
||||
"Notification.CreatedChat" = "%@ created a group";
|
||||
"Notification.CreatedChannel" = "Channel created";
|
||||
"Notification.CreatedChatWithTitle" = "%@ created the group \"%@\"";
|
||||
"Notification.CreatedChatWithTitle" = "%@ created the group \"%@\" ";
|
||||
"Notification.Joined" = "%@ joined Telegram";
|
||||
"Notification.ChangedGroupName" = "%@ changed group name to \"%@\"";
|
||||
"Notification.ChangedGroupName" = "%@ changed group name to \"%@\" ";
|
||||
"Notification.NewAuthDetected" = "%1$@,\nWe detected a login into your account from a new device on %2$@, %3$@ at %4$@\n\nDevice: %5$@\nLocation: %6$@\n\nIf this wasn't you, you can go to Settings — Privacy and Security — Sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Privacy and Security settings.\n\nSincerely,\nThe Telegram Team";
|
||||
"Notification.MessageLifetimeChanged" = "%1$@ set the self-destruct timer to %2$@";
|
||||
"Notification.MessageLifetimeChangedOutgoing" = "You set the self-destruct timer to %1$@";
|
||||
@@ -1488,7 +1488,7 @@
|
||||
|
||||
"Channel.MessagePhotoUpdated" = "Channel photo updated";
|
||||
"Channel.MessagePhotoRemoved" = "Channel photo removed";
|
||||
"Channel.MessageTitleUpdated" = "Channel renamed to \"%@\"";
|
||||
"Channel.MessageTitleUpdated" = "Channel renamed to \"%@\" ";
|
||||
"Channel.TitleInfo" = "Channel Info";
|
||||
|
||||
"Channel.UpdatePhotoItem" = "Set Channel Photo";
|
||||
@@ -1896,7 +1896,7 @@
|
||||
"Group.Username.InvalidTooShort" = "Group names must have at least 5 characters.";
|
||||
"Group.Username.InvalidStartsWithNumber" = "Group names can't start with a number.";
|
||||
|
||||
"Notification.PinnedTextMessage" = "%@ pinned \"%@\"";
|
||||
"Notification.PinnedTextMessage" = "%@ pinned \"%@\" ";
|
||||
"Notification.PinnedPhotoMessage" = "%@ pinned a photo";
|
||||
"Notification.PinnedVideoMessage" = "%@ pinned a video";
|
||||
"Notification.PinnedRoundMessage" = "%@ pinned a video message";
|
||||
@@ -1909,7 +1909,7 @@
|
||||
"Notification.PinnedDeletedMessage" = "%@ pinned deleted message";
|
||||
"Notification.PinnedPollMessage" = "%@ pinned a poll";
|
||||
|
||||
"Message.PinnedTextMessage" = "pinned \"%@\"";
|
||||
"Message.PinnedTextMessage" = "pinned \"%@\" ";
|
||||
"Message.PinnedPhotoMessage" = "pinned photo";
|
||||
"Message.PinnedVideoMessage" = "pinned video";
|
||||
"Message.PinnedAudioMessage" = "pinned voice message";
|
||||
@@ -3052,7 +3052,7 @@ Unused sets are archived when you add more.";
|
||||
"Appearance.ThemeNight" = "Night";
|
||||
"Appearance.ThemeNightBlue" = "Night Blue";
|
||||
"Appearance.PreviewReplyAuthor" = "Lucio";
|
||||
"Appearance.PreviewReplyText" = "Reinhart, we need to find you some...";
|
||||
"Appearance.PreviewReplyText" = "Reinhardt, we need to find you some...";
|
||||
"Appearance.PreviewIncomingText" = "Ah you kids today with techno music! Enjoy the classics, like Hasselhoff!";
|
||||
"Appearance.PreviewOutgoingText" = "I can't take you seriously right now. Sorry..";
|
||||
"Appearance.AccentColor" = "Accent Color";
|
||||
@@ -4056,3 +4056,142 @@ Unused sets are archived when you add more.";
|
||||
"NotificationSettings.ShowNotificationsAllAccounts" = "All Accounts";
|
||||
"NotificationSettings.ShowNotificationsAllAccountsInfoOn" = "Turn this off if you want to receive notifications only from your active account.";
|
||||
"NotificationSettings.ShowNotificationsAllAccountsInfoOff" = "Turn this on if you want to receive notifications from all your accounts.";
|
||||
|
||||
"Gif.Search" = "Search GIFs";
|
||||
"Gif.NoGifsFound" = "No GIFs Found";
|
||||
"Gif.NoGifsPlaceholder" = "You have no saved GIFs yet.";
|
||||
|
||||
"Privacy.ProfilePhoto" = "Profile Photo";
|
||||
"Privacy.Forwards" = "Forwarded Messages";
|
||||
|
||||
"Privacy.ProfilePhoto.WhoCanSeeMyPhoto" = "WHO CAN SEE MY PROFILE PHOTO";
|
||||
"Privacy.ProfilePhoto.CustomHelp" = "You can restrict who can see your profile photo with granular precision.";
|
||||
"Privacy.ProfilePhoto.AlwaysShareWith.Title" = "Always Share With";
|
||||
"Privacy.ProfilePhoto.NeverShareWith.Title" = "Never Share With";
|
||||
|
||||
"Privacy.Forwards.WhoCanForward" = "WHO CAN ADD LINK TO MY ACCOUNT WHEN FORWARDING MY MESSAGES";
|
||||
"Privacy.Forwards.CustomHelp" = "When forwarded to other chats, messages you send will not link back to your account.";
|
||||
"Privacy.Forwards.AlwaysAllow.Title" = "Always Allow";
|
||||
"Privacy.Forwards.NeverAllow.Title" = "Never Allow";
|
||||
|
||||
"Conversation.ContextMenuCancelSending" = "Cancel Sending";
|
||||
|
||||
"Conversation.ForwardAuthorHiddenTooltip" = "The account was hidden by the user";
|
||||
|
||||
"Privacy.Forwards.Preview" = "PREVIEW";
|
||||
"Privacy.Forwards.PreviewMessageText" = "Reinhardt, we need to find you some new music.";
|
||||
"Privacy.Forwards.AlwaysLink" = "Link to your account";
|
||||
"Privacy.Forwards.LinkIfAllowed" = "Link if allowed by settings below";
|
||||
"Privacy.Forwards.NeverLink" = "Not a link to your account";
|
||||
|
||||
"Chat.UnsendMyMessagesAlertTitle" = "Unsending will also delete messages you sent on %@'s side.";
|
||||
"Chat.UnsendMyMessages" = "Unsend My Messages";
|
||||
|
||||
"Chat.DeleteMessagesConfirmation_1" = "Delete message";
|
||||
"Chat.DeleteMessagesConfirmation_any" = "Delete %@ messages";
|
||||
|
||||
"Settings.Search" = "Search";
|
||||
|
||||
"SettingsSearch.FAQ" = "FAQ";
|
||||
|
||||
"SettingsSearch.Synonyms.EditProfile.Title" = " ";
|
||||
"SettingsSearch.Synonyms.EditProfile.Bio" = " ";
|
||||
"SettingsSearch.Synonyms.EditProfile.PhoneNumber" = " ";
|
||||
"SettingsSearch.Synonyms.EditProfile.Username" = " ";
|
||||
"SettingsSearch.Synonyms.EditProfile.AddAccount" = " ";
|
||||
"SettingsSearch.Synonyms.EditProfile.Logout" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Calls.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Calls.CallTab" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Stickers.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Stickers.SuggestStickers" = " ";
|
||||
"SettingsSearch.Synonyms.Stickers.FeaturedPacks" = " ";
|
||||
"SettingsSearch.Synonyms.Stickers.ArchivedPacks" = " ";
|
||||
"SettingsSearch.Synonyms.Stickers.Masks" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Notifications.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.MessageNotificationsAlert" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.MessageNotificationsPreview" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.MessageNotificationsSound" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.MessageNotificationsExceptions" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.GroupNotificationsAlert" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.GroupNotificationsPreview" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.GroupNotificationsSound" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.GroupNotificationsExceptions" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ChannelNotificationsAlert" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ChannelNotificationsPreview" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ChannelNotificationsSound" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ChannelNotificationsExceptions" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.InAppNotificationsSound" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.InAppNotificationsVibrate" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.InAppNotificationsPreview" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.DisplayNamesOnLockScreen" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.BadgeIncludeMutedChats" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.BadgeIncludeMutedPublicGroups" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.BadgeIncludeMutedChannels" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.BadgeCountUnreadMessages" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ContactJoined" = " ";
|
||||
"SettingsSearch.Synonyms.Notifications.ResetAllNotifications" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Privacy.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.BlockedUsers" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.LastSeen" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.ProfilePhoto" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Forwards" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Calls" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.GroupsAndChannels" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Passcode" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.PasscodeAndTouchId" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.PasscodeAndFaceId" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.TwoStepAuth" = "Password";
|
||||
"SettingsSearch.Synonyms.Privacy.AuthSessions" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.DeleteAccountIfAwayFor" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Privacy.Data.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.ContactsReset" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.ContactsSync" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.TopPeers" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.DeleteDrafts" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.ClearPaymentsInfo" = " ";
|
||||
"SettingsSearch.Synonyms.Privacy.Data.SecretChatLinkPreview" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Data.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Data.Storage.Title" = "Cache";
|
||||
"SettingsSearch.Synonyms.Data.Storage.KeepMedia" = " ";
|
||||
"SettingsSearch.Synonyms.Data.Storage.ClearCache" = " ";
|
||||
"SettingsSearch.Synonyms.Data.NetworkUsage" = " ";
|
||||
"SettingsSearch.Synonyms.Data.AutoDownloadUsingCellular" = " ";
|
||||
"SettingsSearch.Synonyms.Data.AutoDownloadUsingWifi" = " ";
|
||||
"SettingsSearch.Synonyms.Data.AutoDownloadReset" = " ";
|
||||
"SettingsSearch.Synonyms.Data.AutoplayGifs" = " ";
|
||||
"SettingsSearch.Synonyms.Data.AutoplayVideos" = " ";
|
||||
"SettingsSearch.Synonyms.Data.CallsUseLessData" = " ";
|
||||
"SettingsSearch.Synonyms.Data.SaveIncomingPhotos" = " ";
|
||||
"SettingsSearch.Synonyms.Data.SaveEditedPhotos" = " ";
|
||||
"SettingsSearch.Synonyms.Data.DownloadInBackground" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Proxy.Title" = "SOCKS5\nMTProto";
|
||||
"SettingsSearch.Synonyms.Proxy.AddProxy" = " ";
|
||||
"SettingsSearch.Synonyms.Proxy.UseForCalls" = " ";
|
||||
|
||||
"SettingsSearch.Synonyms.Appearance.Title" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.TextSize" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.ChatBackground" = "Wallpaper";
|
||||
"SettingsSearch.Synonyms.Appearance.ChatBackground.SetColor" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.ChatBackground.Custom" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.AutoNightTheme" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.ColorTheme" = " ";
|
||||
"SettingsSearch.Synonyms.Appearance.Animations" = "Animations";
|
||||
|
||||
"SettingsSearch.Synonyms.SavedMessages" = " ";
|
||||
"SettingsSearch.Synonyms.AppLanguage" = " ";
|
||||
"SettingsSearch.Synonyms.Passport" = " ";
|
||||
"SettingsSearch.Synonyms.Watch" = "Apple Watch";
|
||||
"SettingsSearch.Synonyms.Support" = "Support";
|
||||
"SettingsSearch.Synonyms.FAQ" = " ";
|
||||
|
||||
"ChatList.DeleteForCurrentUser" = "Delete just for me";
|
||||
"ChatList.DeleteForEveryone" = "Delete for me and %@";
|
||||
|
||||
"ChatList.ClearChatConfirmation" = "Are you sure you want to delete all\nmessages in the chat with %@?";
|
||||
|
||||
Reference in New Issue
Block a user